summaryrefslogtreecommitdiff
path: root/os/boot/puma
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/puma')
-rw-r--r--os/boot/puma/8250.c312
-rw-r--r--os/boot/puma/alarm.c123
-rw-r--r--os/boot/puma/armv4.h99
-rw-r--r--os/boot/puma/boot.h12
-rw-r--r--os/boot/puma/bootp.c502
-rw-r--r--os/boot/puma/cga.c92
-rw-r--r--os/boot/puma/clock.c154
-rw-r--r--os/boot/puma/conf.c181
-rw-r--r--os/boot/puma/console.c181
-rw-r--r--os/boot/puma/dat.h205
-rw-r--r--os/boot/puma/div.s122
-rw-r--r--os/boot/puma/donprint.c332
-rw-r--r--os/boot/puma/dosboot.c614
-rw-r--r--os/boot/puma/dosfs.h110
-rw-r--r--os/boot/puma/ebsit.trap.c206
-rw-r--r--os/boot/puma/ether.c156
-rw-r--r--os/boot/puma/ether.h82
-rw-r--r--os/boot/puma/ether8900.c555
-rw-r--r--os/boot/puma/flash.c226
-rw-r--r--os/boot/puma/fns.h111
-rw-r--r--os/boot/puma/hard.c773
-rw-r--r--os/boot/puma/io.h3
-rw-r--r--os/boot/puma/ip.h98
-rw-r--r--os/boot/puma/kbd.c482
-rw-r--r--os/boot/puma/l.s427
-rw-r--r--os/boot/puma/lib.h107
-rw-r--r--os/boot/puma/main.c552
-rw-r--r--os/boot/puma/mem.h38
-rw-r--r--os/boot/puma/mkfile71
-rw-r--r--os/boot/puma/outb.c20
-rw-r--r--os/boot/puma/plan9boot.c93
-rw-r--r--os/boot/puma/puma.c123
-rw-r--r--os/boot/puma/puma.h435
-rw-r--r--os/boot/puma/qio.c128
-rw-r--r--os/boot/puma/rmap.c104
-rw-r--r--os/boot/puma/squeeze.h34
-rw-r--r--os/boot/puma/sum.c13
-rw-r--r--os/boot/puma/trap.c190
-rw-r--r--os/boot/puma/ureg.h22
-rw-r--r--os/boot/puma/zqs.c216
40 files changed, 8304 insertions, 0 deletions
diff --git a/os/boot/puma/8250.c b/os/boot/puma/8250.c
new file mode 100644
index 00000000..b906b2ba
--- /dev/null
+++ b/os/boot/puma/8250.c
@@ -0,0 +1,312 @@
+#include "boot.h"
+
+/*
+ * INS8250 uart
+ */
+enum
+{
+ /*
+ * register numbers
+ */
+ Data= 0, /* xmit/rcv buffer */
+ Iena= 1, /* interrupt enable */
+ Ircv= (1<<0), /* for char rcv'd */
+ Ixmt= (1<<1), /* for xmit buffer empty */
+ Irstat=(1<<2), /* for change in rcv'er status */
+ Imstat=(1<<3), /* for change in modem status */
+ Istat= 2, /* interrupt flag (read) */
+ Tctl= 2, /* test control (write) */
+ Format= 3, /* byte format */
+ Bits8= (3<<0), /* 8 bits/byte */
+ Stop2= (1<<2), /* 2 stop bits */
+ Pena= (1<<3), /* generate parity */
+ Peven= (1<<4), /* even parity */
+ Pforce=(1<<5), /* force parity */
+ Break= (1<<6), /* generate a break */
+ Dra= (1<<7), /* address the divisor */
+ Mctl= 4, /* modem control */
+ Dtr= (1<<0), /* data terminal ready */
+ Rts= (1<<1), /* request to send */
+ Ri= (1<<2), /* ring */
+ Inton= (1<<3), /* turn on interrupts */
+ Loop= (1<<4), /* loop back */
+ Lstat= 5, /* line status */
+ Inready=(1<<0), /* receive buffer full */
+ Oerror=(1<<1), /* receiver overrun */
+ Perror=(1<<2), /* receiver parity error */
+ Ferror=(1<<3), /* rcv framing error */
+ Outready=(1<<5), /* output buffer empty */
+ Mstat= 6, /* modem status */
+ Ctsc= (1<<0), /* clear to send changed */
+ Dsrc= (1<<1), /* data set ready changed */
+ Rire= (1<<2), /* rising edge of ring indicator */
+ Dcdc= (1<<3), /* data carrier detect changed */
+ Cts= (1<<4), /* complement of clear to send line */
+ Dsr= (1<<5), /* complement of data set ready line */
+ Ring= (1<<6), /* complement of ring indicator line */
+ Dcd= (1<<7), /* complement of data carrier detect line */
+ Scratch=7, /* scratchpad */
+ Dlsb= 0, /* divisor lsb */
+ Dmsb= 1, /* divisor msb */
+
+ Serial= 0,
+ Modem= 1,
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ int port;
+ int setup;
+ uchar sticky[8]; /* sticky write register values */
+ uchar txbusy;
+
+ Queue *iq;
+ Queue *oq;
+ void (*rx)(Queue *, int);
+
+ ulong frame;
+ ulong overrun;
+};
+
+Uart uart[1];
+
+static void uartkick(void*);
+
+
+#define UartFREQ 1843200
+
+#define uartwrreg(u,r,v) outb((u)->port + r, (u)->sticky[r] | (v))
+#define uartrdreg(u,r) inb((u)->port + r)
+
+/*
+ * set the baud rate by calculating and setting the baudrate
+ * generator constant. This will work with fairly non-standard
+ * baud rates.
+ */
+static void
+uartsetbaud(Uart *up, int rate)
+{
+ ulong brconst;
+
+ brconst = (UartFREQ+8*rate-1)/(16*rate);
+
+ uartwrreg(up, Format, Dra);
+ outb(up->port+Dmsb, (brconst>>8) & 0xff);
+ outb(up->port+Dlsb, brconst & 0xff);
+ uartwrreg(up, Format, 0);
+}
+
+/*
+ * toggle DTR
+ */
+static void
+uartdtr(Uart *up, int n)
+{
+ if(n)
+ up->sticky[Mctl] |= Dtr;
+ else
+ up->sticky[Mctl] &= ~Dtr;
+ uartwrreg(up, Mctl, 0);
+}
+
+/*
+ * toggle RTS
+ */
+static void
+uartrts(Uart *up, int n)
+{
+ if(n)
+ up->sticky[Mctl] |= Rts;
+ else
+ up->sticky[Mctl] &= ~Rts;
+ uartwrreg(up, Mctl, 0);
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+ Uart *up;
+ int ch;
+ int s, l, loops;
+
+ up = arg;
+ for(loops = 0; loops < 1024; loops++){
+ s = uartrdreg(up, Istat);
+ switch(s){
+ case 6: /* receiver line status */
+ l = uartrdreg(up, Lstat);
+ if(l & Ferror)
+ up->frame++;
+ if(l & Oerror)
+ up->overrun++;
+ break;
+
+ case 4: /* received data available */
+ case 12:
+ ch = inb(up->port+Data);
+ if(up->iq)
+ if(up->rx)
+ (*up->rx)(up->iq, ch);
+ else
+ qbputc(up->iq, ch);
+ break;
+
+ case 2: /* transmitter empty */
+ ch = -1;
+ if(up->oq)
+ ch = qbgetc(up->oq);
+ if(ch != -1)
+ outb(up->port+Data, ch);
+ else
+ up->txbusy = 0;
+ break;
+
+ case 0: /* modem status */
+ uartrdreg(up, Mstat);
+ break;
+
+ default:
+ if(s&1)
+ return;
+ print("weird modem interrupt #%2.2ux\n", s);
+ break;
+ }
+ }
+ panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat));
+}
+
+/*
+ * turn on a port's interrupts. set DTR and RTS
+ */
+static void
+uartenable(Uart *up)
+{
+ /*
+ * turn on interrupts
+ */
+ up->sticky[Iena] = 0;
+ if(up->oq)
+ up->sticky[Iena] |= Ixmt;
+ if(up->iq)
+ up->sticky[Iena] |= Ircv|Irstat;
+ uartwrreg(up, Iena, 0);
+
+ /*
+ * turn on DTR and RTS
+ */
+ uartdtr(up, 1);
+ uartrts(up, 1);
+}
+
+void
+uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue *, int))
+{
+ Uart *up = &uart[0];
+
+ if(up->setup)
+ return;
+ up->setup = 1;
+
+ *iq = up->iq = qopen(4*1024, 0, 0, 0);
+ *oq = up->oq = qopen(16*1024, 0, uartkick, up);
+ switch(port){
+
+ case 0:
+ up->port = 0x3F8;
+ setvec(V_COM1, uartintr, up);
+ break;
+
+ case 1:
+ up->port = 0x2F8;
+ setvec(V_COM2, uartintr, up);
+ break;
+
+ default:
+ return;
+ }
+
+ /*
+ * set rate to 9600 baud.
+ * 8 bits/character.
+ * 1 stop bit.
+ * interrupts enabled.
+ */
+ uartsetbaud(up, 9600);
+ up->sticky[Format] = Bits8;
+ uartwrreg(up, Format, 0);
+ up->sticky[Mctl] |= Inton;
+ uartwrreg(up, Mctl, 0x0);
+
+ up->rx = rx;
+ uartenable(up);
+ if(baud)
+ uartsetbaud(up, baud);
+}
+
+static void
+uartputc(int c)
+{
+ Uart *up = &uart[0];
+ int i;
+
+ for(i = 0; i < 100; i++){
+ if(uartrdreg(up, Lstat) & Outready)
+ break;
+ delay(1);
+ }
+ outb(up->port+Data, c);
+}
+
+void
+uartputs(char *s, int n)
+{
+ Uart *up = &uart[0];
+ Block *b;
+ int nl;
+ char *p;
+
+ nl = 0;
+ for(p = s; p < s+n; p++)
+ if(*p == '\n')
+ nl++;
+ b = iallocb(n+nl);
+ while(n--){
+ if(*s == '\n')
+ *b->wp++ = '\r';
+ *b->wp++ = *s++;
+ }
+ qbwrite(up->oq, b);
+}
+
+/*
+ * (re)start output
+ */
+static void
+uartkick(void *arg)
+{
+ Uart *up = arg;
+ int x, n, c;
+
+ x = splhi();
+ while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1) {
+ n = 0;
+ while((uartrdreg(up, Lstat) & Outready) == 0){
+ if(++n > 100000){
+ print("stuck serial line\n");
+ break;
+ }
+ }
+ outb(up->port + Data, c);
+ }
+ splx(x);
+}
+
+void
+uartwait(void)
+{
+ Uart *up = &uart[0];
+
+ while(up->txbusy)
+ ;
+}
diff --git a/os/boot/puma/alarm.c b/os/boot/puma/alarm.c
new file mode 100644
index 00000000..14cc93d3
--- /dev/null
+++ b/os/boot/puma/alarm.c
@@ -0,0 +1,123 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#define MAXALARM 10
+
+Alarm alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+ if(where == 0){
+ new->next = *head;
+ *head = new;
+ }else{
+ new->next = where->next;
+ where->next = new;
+ }
+
+}
+
+/*
+ * Delete old from list. where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+ if(where == 0){
+ *head = old->next;
+ return;
+ }
+ where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+ int i;
+ Alarm *a;
+
+ for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+ if(a->busy==0 && a->f==0){
+ a->f = 0;
+ a->arg = 0;
+ a->busy = 1;
+ return a;
+ }
+ panic("newalarm");
+ return 0; /* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+ Alarm *a, *w, *pw;
+ ulong s;
+
+ if(ms < 0)
+ ms = 0;
+ s = splhi();
+ a = newalarm();
+ a->dt = MS2TK(ms);
+ a->f = f;
+ a->arg = arg;
+ pw = 0;
+ for(w=m->alarm; w; pw=w, w=w->next){
+ if(w->dt <= a->dt){
+ a->dt -= w->dt;
+ continue;
+ }
+ w->dt -= a->dt;
+ break;
+ }
+ insert(&m->alarm, pw, a);
+ splx(s);
+ return a;
+}
+
+void
+cancel(Alarm *a)
+{
+ a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10 /* alarms per clock tick */
+void
+checkalarms(void)
+{
+ int i, n, s;
+ Alarm *a;
+ void (*f)(void*);
+ Alarm *alist[NA];
+
+ s = splhi();
+ a = m->alarm;
+ if(a){
+ for(n=0; a && a->dt<=0 && n<NA; n++){
+ alist[n] = a;
+ delete(&m->alarm, 0, a);
+ a = m->alarm;
+ }
+ if(a)
+ a->dt--;
+
+ for(i = 0; i < n; i++){
+ f = alist[i]->f; /* avoid race with cancel */
+ if(f)
+ (*f)(alist[i]);
+ alist[i]->busy = 0;
+ }
+ }
+ splx(s);
+}
diff --git a/os/boot/puma/armv4.h b/os/boot/puma/armv4.h
new file mode 100644
index 00000000..f9386e0f
--- /dev/null
+++ b/os/boot/puma/armv4.h
@@ -0,0 +1,99 @@
+/*
+ * PSR
+ */
+#define PsrMusr 0x10 /* mode */
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMabt 0x17
+#define PsrMund 0x1B
+#define PsrMsys 0x1F
+#define PsrMask 0x1F
+
+#define PsrDfiq 0x00000040 /* disable FIQ interrupts */
+#define PsrDirq 0x00000080 /* disable IRQ interrupts */
+
+#define PsrV 0x10000000 /* overflow */
+#define PsrC 0x20000000 /* carry/borrow/extend */
+#define PsrZ 0x40000000 /* zero */
+#define PsrN 0x80000000 /* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID 0 /* R: */
+#define CpControl 1 /* R: */
+#define CpTTB 2 /* W: translation table base */
+#define CpDAC 3 /* W: domain access control */
+#define CpFSR 5 /* R: fault status */
+#define CpTLBflush 5 /* W: */
+#define CpFAR 6 /* R: fault address */
+#define CpTLBpurge 6 /* W: */
+#define CpCacheCtl 7 /* W: */
+
+#define CpDebug 14 /* R/W: debug registers */
+/*
+ * Coprocessors
+ */
+#define CpMMU 15
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCmmu 0x00000001 /* M: MMU enable */
+#define CpCalign 0x00000002 /* A: alignment fault enable */
+#define CpCDcache 0x00000004 /* C: instruction/data cache on */
+#define CpCwb 0x00000008 /* W: write buffer turned on */
+#define CpCi32 0x00000010 /* P: 32-bit programme space */
+#define CpCd32 0x00000020 /* D: 32-bit data space */
+#define CpCbe 0x00000080 /* B: big-endian operation */
+#define CpCsystem 0x00000100 /* S: system permission */
+#define CpCrom 0x00000200 /* R: ROM permission */
+#define CpCIcache 0x00001000 /* C: Instruction Cache on */
+
+/*
+ * Debug support internal registers
+ */
+#define CpDBAR 0
+#define CpDBVR 1
+#define CpDBMR 2
+#define CpDBCR 3
+#define CpIBCR 8
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ * L1: 12-bit index -> 4096 descriptors -> 16Kb
+ * L2: 8-bit index -> 256 descriptors -> 1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ * TTB + L1Tx gives address of L1 descriptor
+ * L1 descriptor gives PTBA
+ * PTBA + L2Tx gives address of L2 descriptor
+ * L2 descriptor gives PBA
+ */
+#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */
+#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */
+#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */
+#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */
+#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */
+#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */
+
+#define MmuL1page 0x011 /* descriptor is for L2 pages */
+#define MmuL1section 0x012 /* descriptor is for section */
+
+#define MmuL2invalid 0x000
+#define MmuL2large 0x001 /* large */
+#define MmuL2small 0x002 /* small */
+#define MmuWB 0x004 /* data goes through write buffer */
+#define MmuIDC 0x008 /* data placed in cache */
+
+#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */
+#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */
+#define MmuL1AP(v) MmuAP(3, (v))
+#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro 0 /* supervisor rw */
+#define MmuAPsrw 1 /* supervisor rw */
+#define MmuAPuro 2 /* supervisor rw + user ro */
+#define MmuAPurw 3 /* supervisor rw + user rw */
diff --git a/os/boot/puma/boot.h b/os/boot/puma/boot.h
new file mode 100644
index 00000000..afa9ab30
--- /dev/null
+++ b/os/boot/puma/boot.h
@@ -0,0 +1,12 @@
+#include <u.h>
+#include "lib.h"
+
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "armv4.h"
+#include "puma.h"
+
diff --git a/os/boot/puma/bootp.c b/os/boot/puma/bootp.c
new file mode 100644
index 00000000..95a1f607
--- /dev/null
+++ b/os/boot/puma/bootp.c
@@ -0,0 +1,502 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+uchar broadcast[Eaddrlen] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+ uchar header[4];
+ uchar data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+ ptr[0] = val>>8;
+ ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+ ptr[0] = val>>24;
+ ptr[1] = val>>16;
+ ptr[2] = val>>8;
+ ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+ return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+ return ((ptr[0]<<8) | ptr[1]);
+}
+
+static short endian = 1;
+static char* aendian = (char*)&endian;
+#define LITTLE *aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+ uchar *addr;
+ ulong t1, t2;
+ ulong losum, hisum, mdsum, x;
+
+ addr = a;
+ losum = 0;
+ hisum = 0;
+ mdsum = 0;
+
+ x = 0;
+ if((ulong)addr & 1) {
+ if(len) {
+ hisum += addr[0];
+ len--;
+ addr++;
+ }
+ x = 1;
+ }
+ while(len >= 16) {
+ t1 = *(ushort*)(addr+0);
+ t2 = *(ushort*)(addr+2); mdsum += t1;
+ t1 = *(ushort*)(addr+4); mdsum += t2;
+ t2 = *(ushort*)(addr+6); mdsum += t1;
+ t1 = *(ushort*)(addr+8); mdsum += t2;
+ t2 = *(ushort*)(addr+10); mdsum += t1;
+ t1 = *(ushort*)(addr+12); mdsum += t2;
+ t2 = *(ushort*)(addr+14); mdsum += t1;
+ mdsum += t2;
+ len -= 16;
+ addr += 16;
+ }
+ while(len >= 2) {
+ mdsum += *(ushort*)addr;
+ len -= 2;
+ addr += 2;
+ }
+ if(x) {
+ if(len)
+ losum += addr[0];
+ if(LITTLE)
+ losum += mdsum;
+ else
+ hisum += mdsum;
+ } else {
+ if(len)
+ hisum += addr[0];
+ if(LITTLE)
+ hisum += mdsum;
+ else
+ losum += mdsum;
+ }
+
+ losum += hisum >> 8;
+ losum += (hisum & 0xff) << 8;
+ while(hisum = losum>>16)
+ losum = hisum + (losum & 0xffff);
+
+ return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+ int len;
+ ulong sum = 0;
+
+ len = (addr[0]&0xf)<<2;
+
+ while(len > 0) {
+ sum += addr[0]<<8 | addr[1] ;
+ len -= 2;
+ addr += 2;
+ }
+
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return (sum^0xffff);
+}
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+ Udphdr *uh;
+ Etherhdr *ip;
+ Etherpkt pkt;
+ int len, ptcllen;
+
+
+ uh = (Udphdr*)&pkt;
+
+ memset(uh, 0, sizeof(Etherpkt));
+ memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+ /*
+ * UDP portion
+ */
+ ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+ uh->ttl = 0;
+ uh->udpproto = IP_UDPPROTO;
+ uh->frag[0] = 0;
+ uh->frag[1] = 0;
+ hnputs(uh->udpplen, ptcllen);
+ hnputl(uh->udpsrc, myaddr.ip);
+ hnputs(uh->udpsport, myaddr.port);
+ hnputl(uh->udpdst, a->ip);
+ hnputs(uh->udpdport, a->port);
+ hnputs(uh->udplen, ptcllen);
+ uh->udpcksum[0] = 0;
+ uh->udpcksum[1] = 0;
+ dlen = (dlen+1)&~1;
+ hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+ /*
+ * IP portion
+ */
+ ip = (Etherhdr*)&pkt;
+ len = sizeof(Udphdr)+dlen;
+ ip->vihl = IP_VER|IP_HLEN;
+ ip->tos = 0;
+ ip->ttl = 255;
+ hnputs(ip->length, len-ETHER_HDR);
+ hnputs(ip->id, Id++);
+ ip->frag[0] = 0;
+ ip->frag[1] = 0;
+ ip->cksum[0] = 0;
+ ip->cksum[1] = 0;
+ hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+ /*
+ * Ethernet MAC portion
+ */
+ hnputs(ip->type, ET_IP);
+ memmove(ip->d, a->ea, sizeof(ip->d));
+
+ ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+ int n;
+ char buf[128];
+
+ buf[0] = 0;
+ buf[1] = Tftp_ERROR;
+ buf[2] = 0;
+ buf[3] = code;
+ strcpy(buf+4, msg);
+ n = strlen(msg) + 4 + 1;
+ udpsend(ctlrno, a, buf, n);
+ if(report)
+ print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+ int n, len;
+ ushort csm;
+ Udphdr *h;
+ ulong addr, timo;
+ Etherpkt pkt;
+ static int rxactive;
+
+ if(rxactive == 0)
+ timo = 1000;
+ else
+ timo = Timeout;
+ timo += TK2MS(m->ticks);
+ while(timo > TK2MS(m->ticks)){
+ n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+
+ if(n <= 0)
+ continue;
+
+ h = (Udphdr*)&pkt;
+ if(nhgets(h->type) != ET_IP)
+ continue;
+
+ if(ip_csum(&h->vihl)) {
+ print("ip chksum error\n");
+ continue;
+ }
+ if(h->vihl != (IP_VER|IP_HLEN)) {
+ print("ip bad vers/hlen\n");
+ continue;
+ }
+
+ if(h->udpproto != IP_UDPPROTO)
+ continue;
+
+ h->ttl = 0;
+ len = nhgets(h->udplen);
+ hnputs(h->udpplen, len);
+
+ if(nhgets(h->udpcksum)) {
+ csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+ if(csm != 0) {
+ print("udp chksum error csum #%4lux len %d\n", csm, n);
+ break;
+ }
+ }
+
+ if(a->port != 0 && nhgets(h->udpsport) != a->port)
+ continue;
+
+ addr = nhgetl(h->udpsrc);
+ if(a->ip != Bcastip && addr != a->ip)
+ continue;
+
+ len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+ if(len > dlen) {
+ print("udp: packet too big\n");
+ continue;
+ }
+
+ memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+ a->ip = addr;
+ a->port = nhgets(h->udpsport);
+ memmove(a->ea, pkt.s, sizeof(a->ea));
+
+ rxactive = 1;
+ return len;
+ }
+
+ return 0;
+}
+
+static int tftpblockno;
+
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+ int i, len, rlen, oport;
+ char buf[Segsize+2];
+
+ buf[0] = 0;
+ buf[1] = Tftp_READ;
+ len = sprint(buf+2, "%s", name) + 2;
+ len += sprint(buf+len+1, "octet") + 2;
+
+ oport = a->port;
+ for(i = 0; i < 5; i++){
+ a->port = oport;
+ udpsend(ctlrno, a, buf, len);
+ a->port = 0;
+ if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+ continue;
+
+ switch((tftp->header[0]<<8)|tftp->header[1]){
+
+ case Tftp_ERROR:
+ print("tftpopen: error (%d): %s\n",
+ (tftp->header[2]<<8)|tftp->header[3], tftp->data);
+ return -1;
+
+ case Tftp_DATA:
+ tftpblockno = 1;
+ len = (tftp->header[2]<<8)|tftp->header[3];
+ if(len != tftpblockno){
+ print("tftpopen: block error: %d\n", len);
+ nak(ctlrno, a, 1, "block error", 0);
+ return -1;
+ }
+ return rlen-sizeof(tftp->header);
+ }
+ }
+
+ print("tftpopen: failed to connect to server\n");
+ return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+ int blockno, len, retry;
+ uchar buf[4];
+
+ buf[0] = 0;
+ buf[1] = Tftp_ACK;
+ buf[2] = tftpblockno>>8;
+ buf[3] = tftpblockno;
+ tftpblockno++;
+
+ dlen += sizeof(tftp->header);
+
+ retry = 0;
+buggery:
+ udpsend(ctlrno, a, buf, sizeof(buf));
+
+ if((len = udprecv(ctlrno, a, tftp, dlen)) != dlen){
+ print("tftpread: %d != %d\n", len, dlen);
+ nak(ctlrno, a, 2, "short read", 0);
+ if(retry++ < 5)
+ goto buggery;
+ return -1;
+ }
+
+ blockno = (tftp->header[2]<<8)|tftp->header[3];
+ if(blockno != tftpblockno){
+ print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno);
+
+ if(blockno == tftpblockno-1 && retry++ < 5)
+ goto buggery;
+ nak(ctlrno, a, 1, "block error", 0);
+
+ return -1;
+ }
+
+ return len-sizeof(tftp->header);
+}
+
+int
+bootp(int ctlrno, char *file)
+{
+ Bootp req, rep;
+ int i, dlen, segsize, text, data, bss, total;
+ uchar *ea, *addr, *p;
+ ulong entry;
+ Exec *exec;
+ char name[128], *filename, *sysname;
+
+ if((ea = etheraddr(ctlrno)) == 0){
+ print("invalid ctlrno %d\n", ctlrno);
+ return -1;
+ }
+
+ filename = 0;
+ sysname = 0;
+ if(file && *file){
+ strcpy(name, file);
+ if(filename = strchr(name, ':')){
+ if(filename != name && *(filename-1) != '\\'){
+ sysname = name;
+ *filename++ = 0;
+ }
+ }
+ else
+ filename = name;
+ }
+
+
+ memset(&req, 0, sizeof(req));
+ req.op = Bootrequest;
+ req.htype = 1; /* ethernet */
+ req.hlen = Eaddrlen; /* ethernet */
+ memmove(req.chaddr, ea, Eaddrlen);
+
+ myaddr.ip = 0;
+ myaddr.port = BPportsrc;
+ memmove(myaddr.ea, ea, Eaddrlen);
+
+ for(i = 0; i < 10; i++) {
+ server.ip = Bcastip;
+ server.port = BPportdst;
+ memmove(server.ea, broadcast, sizeof(server.ea));
+ udpsend(ctlrno, &server, &req, sizeof(req));
+ if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0)
+ continue;
+ if(memcmp(req.chaddr, rep.chaddr, Eaddrlen))
+ continue;
+ if(rep.htype != 1 || rep.hlen != Eaddrlen)
+ continue;
+ if(sysname == 0 || strcmp(sysname, rep.sname) == 0)
+ break;
+ }
+ if(i >= 10) {
+ print("bootp timed out\n");
+ return -1;
+ }
+
+ if(filename == 0 || *filename == 0)
+ filename = rep.file;
+
+ if(rep.sname[0] != '\0')
+ print("%s ", rep.sname);
+ print("(%d.%d.%d.%d!%d): %s\n",
+ rep.siaddr[0],
+ rep.siaddr[1],
+ rep.siaddr[2],
+ rep.siaddr[3],
+ server.port,
+ filename);
+
+ myaddr.ip = nhgetl(rep.yiaddr);
+ myaddr.port = tftpport++;
+ server.ip = nhgetl(rep.siaddr);
+ server.port = TFTPport;
+
+ if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+ return -1;
+
+ exec = (Exec*)(tftpb.data);
+ if(dlen < sizeof(Exec) || GLLONG(exec->magic) != E_MAGIC){
+ nak(ctlrno, &server, 0, "bad magic number", 1);
+ return -1;
+ }
+ text = GLLONG(exec->text);
+ data = GLLONG(exec->data);
+ bss = GLLONG(exec->bss);
+ total = text+data+bss;
+ entry = GLLONG(exec->entry);
+print("load@%8.8lux: ", PADDR(entry));
+ print("%d", text);
+
+ addr = (uchar*)PADDR(entry);
+ p = tftpb.data+sizeof(Exec);
+ dlen -= sizeof(Exec);
+ segsize = text;
+ for(;;){
+ if(dlen == 0){
+ if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0)
+ return -1;
+ p = tftpb.data;
+ }
+ if(segsize <= dlen)
+ i = segsize;
+ else
+ i = dlen;
+ memmove(addr, p, i);
+
+ addr += i;
+ p += i;
+ segsize -= i;
+ dlen -= i;
+
+ if(segsize <= 0){
+ if(data == 0)
+ break;
+ print("+%d", data);
+ segsize = data;
+ data = 0;
+ addr = (uchar*)PGROUND((ulong)addr);
+ }
+ }
+ nak(ctlrno, &server, 3, "ok", 0); /* tftpclose */
+ print("+%d=%d\n", bss, total);
+ print("entry: 0x%lux\n", entry);
+
+ (*(void(*)(void))(PADDR(entry)))();
+
+ return 0;
+}
diff --git a/os/boot/puma/cga.c b/os/boot/puma/cga.c
new file mode 100644
index 00000000..0331be6f
--- /dev/null
+++ b/os/boot/puma/cga.c
@@ -0,0 +1,92 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "io.h"
+#include "fns.h"
+
+enum {
+ Width = 160,
+ Height = 25,
+
+ Attr = 7, /* white on black */
+};
+
+#define CGASCREENBASE ((uchar*)KADDR(0xB8000))
+
+static int pos;
+static int screeninitdone;
+
+static uchar
+cgaregr(int index)
+{
+ outb(0x3D4, index);
+ return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+ outb(0x3D4, index);
+ outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+ cgaregw(0x0E, (pos/2>>8) & 0xFF);
+ cgaregw(0x0F, pos/2 & 0xFF);
+ CGASCREENBASE[pos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+ int i;
+
+ if(c == '\n'){
+ pos = pos/Width;
+ pos = (pos+1)*Width;
+ }
+ else if(c == '\t'){
+ i = 8 - ((pos/2)&7);
+ while(i-->0)
+ cgascreenputc(' ');
+ }
+ else if(c == '\b'){
+ if(pos >= 2)
+ pos -= 2;
+ cgascreenputc(' ');
+ pos -= 2;
+ }
+ else{
+ CGASCREENBASE[pos++] = c;
+ CGASCREENBASE[pos++] = Attr;
+ }
+ if(pos >= Width*Height){
+ memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+ memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
+ pos = Width*(Height-1);
+ }
+ movecursor();
+}
+
+void
+screeninit(void)
+{
+ if(screeninitdone == 0){
+ pos = cgaregr(0x0E)<<8;
+ pos |= cgaregr(0x0F);
+ pos *= 2;
+ screeninitdone = 1;
+ }
+}
+
+void
+cgascreenputs(char* s, int n)
+{
+ if(screeninitdone == 0)
+ screeninit();
+ while(n-- > 0)
+ cgascreenputc(*s++);
+}
diff --git a/os/boot/puma/clock.c b/os/boot/puma/clock.c
new file mode 100644
index 00000000..6c8902b7
--- /dev/null
+++ b/os/boot/puma/clock.c
@@ -0,0 +1,154 @@
+#include "boot.h"
+
+ /*
+ * Control Word Read/Write Counter (mode 0) LSB, MSB
+ */
+#define PIT_RW_COUNTER0 0x30
+#define PIT_RW_COUNTER1 0x70
+#define PIT_RW_COUNTER2 0xB0
+#define PIT_COUNTERLATCH0 0x00
+#define PIT_COUNTERLATCH1 0x40
+#define PIT_COUNTERLATCH2 0x80
+
+#define PIT_MODE_0 0 /* Interrupt on Terminal Count */
+#define PIT_MODE_1 2 /* Hardware Retriggeable One-shot */
+#define PIT_MODE_2 4 /* Rate Generator */
+#define PIT_MODE_3 6 /* Square Wave Mode */
+#define PIT_MODE_4 8 /* Software Triggered Mode */
+#define PIT_MODE_5 10 /* Hardware Triggered Mode (Retriggeable) */
+
+/*
+ * Harris 82C54 Programmable Interval Timer
+ * On the Puma board the PIT is memory mapped
+ * starting at 0xf2000000 and with each of the 8-bit
+ * registers addressed on a consecutive 4-byte boundary.
+ */
+#undef inb
+#undef outb
+#define inb(port) ((*(uchar *)(port))&0xff)
+#define outb(port, data) (*(uchar *)(port) = (data))
+enum
+{
+ Cnt0= 0xf2000000, /* counter locations */
+ Cnt1= 0xf2000004, /* ... */
+ Cnt2= 0xf2000008, /* ... */
+ Ctlw= 0xf200000c, /* control word register*/
+
+ /* commands */
+ Latch0= 0x00, /* latch counter 0's value */
+ Load0= 0x30, /* load counter 0 with 2 bytes */
+ Latch1= 0x40, /* latch counter 1's value */
+ Load1= 0x70, /* load counter 1 with 2 bytes */
+
+ /* modes */
+ Square= 0x06, /* periodic square wave */
+ RateGen= 0x04, /* rate generator */
+
+ Freq= 3686400, /* Real clock frequency */
+};
+
+static int cpufreq = 233000000;
+static int aalcycles = 14;
+
+static void
+clockintr(Ureg*, void*)
+{
+ m->ticks++;
+ checkalarms();
+}
+
+/*
+ * delay for l milliseconds more or less. delayloop is set by
+ * clockinit() to match the actual CPU speed.
+ */
+void
+delay(int l)
+{
+ l *= m->delayloop;
+ if(l <= 0)
+ l = 1;
+ aamloop(l);
+}
+
+void
+microdelay(int l)
+{
+ l *= m->delayloop;
+ l /= 1000;
+ if(l <= 0)
+ l = 1;
+ aamloop(l);
+}
+
+void
+clockinit(void)
+{
+ int x, y; /* change in counter */
+ int loops, incr;
+
+ /*
+ * set vector for clock interrupts
+ */
+ setvec(V_TIMER0, clockintr, 0);
+
+ /*
+ * set clock for 1/HZ seconds
+ */
+ outb(Ctlw, Load0|Square);
+ outb(Cnt0, (Freq/HZ)); /* low byte */
+ outb(Cnt0, (Freq/HZ)>>8); /* high byte */
+
+ /* find biggest loop that doesn't wrap */
+ incr = 16000000/(aalcycles*HZ*2);
+ x = 2000;
+ for(loops = incr; loops < 64*1024; loops += incr) {
+ /*
+ * measure time for the loop
+ * TEXT aamloop(SB), $-4
+ * _aamloop:
+ * MOVW R0, R0
+ * MOVW R0, R0
+ * MOVW R0, R0
+ * SUB $1, R0
+ * CMP $0, R0
+ * BNE _aamloop
+ * RET
+ *
+ * the time for the loop should be independent of external
+ * cache and memory system since it fits in the execution
+ * prefetch buffer.
+ *
+ */
+ outb(Ctlw, Latch0);
+ x = inb(Cnt0);
+ x |= inb(Cnt0)<<8;
+ aamloop(loops);
+ outb(Ctlw, Latch0);
+ y = inb(Cnt0);
+ y |= inb(Cnt0)<<8;
+ x -= y;
+
+ if(x < 0)
+ x += Freq/HZ;
+
+ if(x > Freq/(3*HZ))
+ break;
+ }
+
+ /*
+ * counter goes at twice the frequency, once per transition,
+ * i.e., twice per square wave
+ */
+ x >>= 1;
+
+ /*
+ * figure out clock frequency and a loop multiplier for delay().
+ */
+ cpufreq = loops*((aalcycles*Freq)/x);
+ m->delayloop = (cpufreq/1000)/aalcycles; /* AAMLOOPs for 1 ms */
+
+ /*
+ * add in possible .2% error and convert to MHz
+ */
+ m->speed = (cpufreq + cpufreq/500)/1000000;
+}
diff --git a/os/boot/puma/conf.c b/os/boot/puma/conf.c
new file mode 100644
index 00000000..3661c6aa
--- /dev/null
+++ b/os/boot/puma/conf.c
@@ -0,0 +1,181 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+static char* defplan9ini =
+ "ether0=type=CS8900\r\n"
+ "vgasize=640x480x8\r\n"
+ "kernelpercent=40\r\n"
+ "console=1\r\nbaud=9600\r\n"
+;
+
+extern char **ini;
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nconf; i++)
+ if(strcmp(confname[i], name) == 0)
+ return confval[i];
+ return 0;
+}
+
+/*
+ * read configuration file
+ */
+int
+plan9ini(Dos *dos, char *val)
+{
+ Dosfile rc;
+ int i, n;
+ char *cp, *p, *q, *line[MAXCONF];
+
+ cp = BOOTARGS;
+ if(dos) {
+ if(dosstat(dos, *ini, &rc) <= 0)
+ return -1;
+
+ *cp = 0;
+ n = dosread(&rc, cp, BOOTARGSLEN-1);
+ if(n <= 0)
+ return -1;
+ cp[n] = 0;
+ } else if(val != nil){
+ if(memchr(val, 0, BOOTARGSLEN-1) == nil)
+ return -1;
+ print("Using flash configuration\n");
+ strcpy(cp, val);
+ n = strlen(cp);
+ }else{
+ print("Using default configuration\n");
+ strcpy(cp, defplan9ini);
+ n = strlen(cp);
+ }
+
+ /*
+ * Make a working copy.
+ * We could change this to pass the parsed strings
+ * to the booted programme instead of the raw
+ * string, then it only gets done once.
+ */
+ memmove(cp+BOOTARGSLEN, cp, n+1);
+ cp += BOOTARGSLEN;
+
+ /*
+ * Strip out '\r', change '\t' -> ' '.
+ */
+ p = cp;
+ for(q = cp; *q; q++){
+ if(*q == '\r')
+ continue;
+ if(*q == '\t')
+ *q = ' ';
+ *p++ = *q;
+ }
+ *p = 0;
+ n = getcfields(cp, line, MAXCONF, "\n");
+ for(i = 0; i < n; i++){
+ cp = strchr(line[i], '=');
+ if(cp == 0)
+ continue;
+ *cp++ = 0;
+ if(cp - line[i] >= NAMELEN+1)
+ *(line[i]+NAMELEN-1) = 0;
+ confname[nconf] = line[i];
+ confval[nconf] = cp;
+ nconf++;
+ }
+ return 0;
+}
+
+static int
+parseether(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ while(*p == ' ')
+ ++p;
+ for(i = 0; i < 6; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ char cc[NAMELEN], *p, *q, *r;
+ int n;
+
+ sprint(cc, "%s%d", class, ctlrno);
+ for(n = 0; n < nconf; n++){
+ if(strncmp(confname[n], cc, NAMELEN))
+ continue;
+ isa->nopt = 0;
+ p = confval[n];
+ while(*p){
+ while(*p == ' ' || *p == '\t')
+ p++;
+ if(*p == '\0')
+ break;
+ if(strncmp(p, "type=", 5) == 0){
+ p += 5;
+ for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+ if(*p == '\0' || *p == ' ' || *p == '\t')
+ break;
+ *q = *p++;
+ }
+ *q = '\0';
+ }
+ else if(strncmp(p, "port=", 5) == 0)
+ isa->port = strtoul(p+5, &p, 0);
+ else if(strncmp(p, "irq=", 4) == 0)
+ isa->irq = strtoul(p+4, &p, 0);
+ else if(strncmp(p, "mem=", 4) == 0)
+ isa->mem = strtoul(p+4, &p, 0);
+ else if(strncmp(p, "size=", 5) == 0)
+ isa->size = strtoul(p+5, &p, 0);
+ else if(strncmp(p, "ea=", 3) == 0){
+ if(parseether(isa->ea, p+3) == -1)
+ memset(isa->ea, 0, 6);
+ }
+ else if(isa->nopt < NISAOPT){
+ r = isa->opt[isa->nopt];
+ while(*p && *p != ' ' && *p != '\t'){
+ *r++ = *p++;
+ if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+ break;
+ }
+ *r = '\0';
+ isa->nopt++;
+ }
+ while(*p && *p != ' ' && *p != '\t')
+ p++;
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/os/boot/puma/console.c b/os/boot/puma/console.c
new file mode 100644
index 00000000..d863472f
--- /dev/null
+++ b/os/boot/puma/console.c
@@ -0,0 +1,181 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static Queue* consiq;
+static Queue* consoq;
+
+void
+bothputs(char *s, int n)
+{
+ uartputs(s, n);
+// cgascreenputs(s, n);
+}
+
+static void (*consputs)(char*, int) = 0;
+
+void
+consinit(void)
+{
+ char *p;
+ int baud, port;
+ static int cgadone;
+
+ if((p = getconf("console")) == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){
+ consiq = qopen(4*1024, 0, 0, 0);
+ consoq = qopen(8*1024, 0, 0, 0);
+ consputs = uartputs;
+ if(!cgadone) {
+ cgadone = 1;
+ //screeninit();
+ //kbdinit();
+ port = 1;
+ baud = 9600;
+ uartspecial(port, baud, &consiq, &consoq, kbdchar);
+ }
+ return;
+ }
+ if(0 || strstr(p, "lcd") == 0)
+ consputs = bothputs;
+ else
+ consputs = uartputs;
+
+ port = strtoul(p, 0, 0);
+ baud = 0;
+ if(p = getconf("baud"))
+ baud = strtoul(p, 0, 0);
+ if(baud == 0)
+ baud = 9600;
+ uartspecial(port, baud, &consiq, &consoq, kbdchar);
+}
+
+void
+kbdchar(Queue *q, int c)
+{
+ c &= 0x7F;
+ if(c == 0x10)
+ panic("^p");
+ if(q == 0) {
+ if(consiq != 0)
+ qbputc(consiq, c);
+ } else
+ qbputc(q, c);
+}
+
+static int
+getline(char *buf, int size, int dotimeout)
+{
+ int c, i=0;
+ ulong start;
+ char echo;
+
+ for (;;) {
+ start = m->ticks;
+ do{
+ if(dotimeout && ((m->ticks - start) > 5*HZ))
+ return -2;
+ c = qbgetc(consiq);
+ }while(c == -1);
+ if(c == '\r')
+ c = '\n'; /* turn carriage return into newline */
+ if(c == '\177')
+ c = '\010'; /* turn delete into backspace */
+ if(c == '\025')
+ echo = '\n'; /* echo ^U as a newline */
+ else
+ echo = c;
+ (*consputs)(&echo, 1);
+
+ if(c == '\010'){
+ if(i > 0)
+ i--; /* bs deletes last character */
+ continue;
+ }
+ /* a newline ends a line */
+ if (c == '\n')
+ break;
+ /* ^U wipes out the line */
+ if (c =='\025')
+ return -1;
+ if(i == size)
+ return size;
+ buf[i++] = c;
+ }
+ buf[i] = 0;
+ return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def)
+{
+ int len, isdefault;
+
+ buf[0] = 0;
+ isdefault = (def && *def);
+ for (;;) {
+ if(isdefault)
+ print("%s[default==%s]: ", prompt, def);
+ else
+ print("%s: ", prompt);
+ len = getline(buf, size, isdefault);
+ switch(len){
+ case -1:
+ /* ^U typed */
+ continue;
+ case -2:
+ /* timeout, use default */
+ (*consputs)("\n", 1);
+ len = 0;
+ break;
+ default:
+ break;
+ }
+ if(len >= size){
+ print("line too long\n");
+ continue;
+ }
+ break;
+ }
+ if(len == 0 && isdefault)
+ strcpy(buf, def);
+ return 0;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+ return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;
+}
+
+int
+print(char *fmt, ...)
+{
+ char buf[PRINTSIZE];
+ int n;
+
+ if(consputs == 0)
+ return 0;
+ n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+ (*consputs)(buf, n);
+ return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+ char buf[PRINTSIZE];
+ int n;
+
+ if(consputs){
+ (*consputs)("panic: ", 7);
+ n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
+ (*consputs)(buf, n);
+ (*consputs)("\n", 1);
+ }
+ spllo();
+ for(;;)
+ idle();
+}
diff --git a/os/boot/puma/dat.h b/os/boot/puma/dat.h
new file mode 100644
index 00000000..f96b1c0b
--- /dev/null
+++ b/os/boot/puma/dat.h
@@ -0,0 +1,205 @@
+typedef struct Block Block;
+typedef struct Queue Queue;
+
+typedef struct List {
+ void *next;
+} List;
+
+typedef struct {
+ int fake;
+ int pri;
+} Lock;
+#define lock(x)
+#define unlock(x)
+
+typedef struct Alarm {
+ List;
+ int busy;
+ long dt;
+ void (*f)(void*);
+ void *arg;
+} Alarm;
+
+enum {
+ Eaddrlen = 6,
+ ETHERMINTU = 60, /* minimum transmit size */
+ ETHERMAXTU = 1514, /* maximum transmit size */
+ ETHERHDRSIZE = 14, /* size of an ethernet header */
+
+ MaxEther = 2,
+};
+
+typedef struct {
+ uchar d[Eaddrlen];
+ uchar s[Eaddrlen];
+ uchar type[2];
+ uchar data[1500];
+ uchar crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+enum {
+ Npart = 20+2, /* 8 sub partitions, disk, and partition */
+ Maxxfer = 16*1024, /* maximum transfer size/cmd */
+};
+
+typedef struct {
+ ulong start;
+ ulong end;
+ char name[NAMELEN+1];
+} Partition;
+
+typedef struct {
+ int online;
+ int npart; /* number of real partitions */
+ Partition p[Npart];
+ ulong offset;
+ Partition *current; /* current partition */
+
+ ulong cap; /* total bytes */
+ int bytes; /* bytes/sector */
+ int sectors; /* sectors/track */
+ int heads; /* heads/cyl */
+ long cyl; /* cylinders/drive */
+
+ char lba; /* true if drive has logical block addressing */
+ char multi; /* non-zero if drive does multiple block xfers */
+} Disc;
+
+enum {
+ ScsiTestunit = 0x00,
+ ScsiExtsens = 0x03,
+ ScsiInquiry = 0x12,
+ ScsiModesense = 0x1a,
+ ScsiStartunit = 0x1B,
+ ScsiStopunit = 0x1B,
+ ScsiGetcap = 0x25,
+ ScsiRead = 0x08,
+ ScsiWrite = 0x0a,
+ ScsiExtread = 0x28,
+ ScsiExtwrite = 0x2a,
+
+ /* data direction */
+ ScsiIn = 1,
+ ScsiOut = 0,
+};
+
+typedef struct Scsibuf Scsibuf;
+typedef struct Scsibuf {
+ void* virt;
+ void* phys;
+ Scsibuf* next;
+};
+
+typedef struct Scsidata {
+ uchar* base;
+ uchar* lim;
+ uchar* ptr;
+} Scsidata;
+
+typedef struct Ureg Ureg;
+
+typedef struct Scsi {
+ ulong pid;
+ ushort target;
+ ushort lun;
+ ushort rflag;
+ ushort status;
+ Scsidata cmd;
+ Scsidata data;
+ Scsibuf* b;
+ uchar* save;
+ uchar cmdblk[16];
+} Scsi;
+
+typedef struct Segdesc {
+ ulong d0;
+ ulong d1;
+} Segdesc;
+
+typedef struct Mach {
+ ulong ticks; /* of the clock since boot time */
+ ulong delayloop;
+ int speed; /* general system clock in MHz */
+ int oscclk; /* oscillator frequency in MHz */
+ void* alarm; /* alarms bound to this clock */
+} Mach;
+
+extern Mach *m;
+
+#define E_MAGIC ((((4*20)+0)*20)+7)
+
+typedef struct Exec Exec;
+struct Exec
+{
+ uchar magic[4]; /* magic number */
+ uchar text[4]; /* size of text segment */
+ uchar data[4]; /* size of initialized data */
+ uchar bss[4]; /* size of uninitialized data */
+ uchar syms[4]; /* size of symbol table */
+ uchar entry[4]; /* entry point */
+ uchar spsz[4]; /* size of sp/pc offset table */
+ uchar pcsz[4]; /* size of pc/line number table */
+};
+
+/*
+ * bootline passed by boot program
+ */
+#define BOOTLINE ((char *)0x18000-150)
+
+/*
+ * Where we leave configuration info.
+ */
+#define BOOTARGS ((char*)(0x18000))
+#define BOOTARGSLEN 1024
+#define MAXCONF 32
+
+/*
+ * a parsed plan9.ini line
+ */
+#define ISAOPTLEN 16
+#define NISAOPT 8
+
+typedef struct ISAConf {
+ char type[NAMELEN];
+ ulong port;
+ ulong irq;
+ ulong mem;
+ ulong size;
+ uchar ea[6];
+
+ int nopt;
+ char opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct {
+ int size;
+ ulong addr;
+} Map;
+
+typedef struct {
+ char* name;
+ Map* map;
+ Map* mapend;
+
+ Lock;
+} RMap;
+
+typedef struct PCIcfg PCIcfg;
+
+extern uchar* vgamem;
+
+struct Block {
+ uchar *rp;
+ uchar *wp;
+ uchar *lim;
+ uchar *data;
+ Block* next;
+ ulong magic;
+};
+#define BLEN(b) ((b)->wp-(b)->rp)
+
+typedef struct QLock {
+ int dummy;
+} QLock;
diff --git a/os/boot/puma/div.s b/os/boot/puma/div.s
new file mode 100644
index 00000000..42406d85
--- /dev/null
+++ b/os/boot/puma/div.s
@@ -0,0 +1,122 @@
+/*
+ * Div/Mod taken from the Inferno 2.0 ebsit code
+ */
+
+Q = 0
+N = 1
+D = 2
+CC = 3
+TMP = 11
+
+TEXT save<>(SB), 1, $0
+ MOVW R(Q), 0(FP)
+ MOVW R(N), 4(FP)
+ MOVW R(D), 8(FP)
+ MOVW R(CC), 12(FP)
+
+ MOVW R(TMP), R(Q) /* numerator */
+ MOVW 20(FP), R(D) /* denominator */
+ CMP $0, R(D)
+ BNE s1
+ MOVW -1(R(D)), R(TMP) /* divide by zero fault */
+s1: RET
+
+TEXT rest<>(SB), 1, $0
+ MOVW 0(FP), R(Q)
+ MOVW 4(FP), R(N)
+ MOVW 8(FP), R(D)
+ MOVW 12(FP), R(CC)
+/*
+ * return to caller
+ * of rest<>
+ */
+ MOVW 0(R13), R14
+ ADD $20, R13
+ B (R14)
+
+TEXT div<>(SB), 1, $0
+ MOVW $32, R(CC)
+/*
+ * skip zeros 8-at-a-time
+ */
+e1:
+ AND.S $(0xff<<24),R(Q), R(N)
+ BNE e2
+ SLL $8, R(Q)
+ SUB.S $8, R(CC)
+ BNE e1
+ RET
+e2:
+ MOVW $0, R(N)
+
+loop:
+/*
+ * shift R(N||Q) left one
+ */
+ SLL $1, R(N)
+ CMP $0, R(Q)
+ ORR.LT $1, R(N)
+ SLL $1, R(Q)
+
+/*
+ * compare numerator to denominator
+ * if less, subtract and set quotent bit
+ */
+ CMP R(D), R(N)
+ ORR.HS $1, R(Q)
+ SUB.HS R(D), R(N)
+ SUB.S $1, R(CC)
+ BNE loop
+ RET
+
+TEXT _div(SB), 1, $16
+ BL save<>(SB)
+ CMP $0, R(Q)
+ BGE d1
+ RSB $0, R(Q), R(Q)
+ CMP $0, R(D)
+ BGE d2
+ RSB $0, R(D), R(D)
+d0:
+ BL div<>(SB) /* none/both neg */
+ MOVW R(Q), R(TMP)
+ B out
+d1:
+ CMP $0, R(D)
+ BGE d0
+ RSB $0, R(D), R(D)
+d2:
+ BL div<>(SB) /* one neg */
+ RSB $0, R(Q), R(TMP)
+ B out
+
+TEXT _mod(SB), 1, $16
+ BL save<>(SB)
+ CMP $0, R(D)
+ RSB.LT $0, R(D), R(D)
+ CMP $0, R(Q)
+ BGE m1
+ RSB $0, R(Q), R(Q)
+ BL div<>(SB) /* neg numerator */
+ RSB $0, R(N), R(TMP)
+ B out
+m1:
+ BL div<>(SB) /* pos numerator */
+ MOVW R(N), R(TMP)
+ B out
+
+TEXT _divu(SB), 1, $16
+ BL save<>(SB)
+ BL div<>(SB)
+ MOVW R(Q), R(TMP)
+ B out
+
+TEXT _modu(SB), 1, $16
+ BL save<>(SB)
+ BL div<>(SB)
+ MOVW R(N), R(TMP)
+ B out
+
+out:
+ BL rest<>(SB)
+ B out
diff --git a/os/boot/puma/donprint.c b/os/boot/puma/donprint.c
new file mode 100644
index 00000000..4125e690
--- /dev/null
+++ b/os/boot/puma/donprint.c
@@ -0,0 +1,332 @@
+#include "u.h"
+#include "lib.h"
+
+#define PTR sizeof(char*)
+#define SHORT sizeof(int)
+#define INT sizeof(int)
+#define LONG sizeof(long)
+#define IDIGIT 30
+#define MAXCON 30
+
+#define FLONG (1<<0)
+#define FSHORT (1<<1)
+#define FUNSIGN (1<<2)
+
+typedef struct Op Op;
+struct Op
+{
+ char *p;
+ char *ep;
+ void *argp;
+ int f1;
+ int f2;
+ int f3;
+};
+
+static int noconv(Op*);
+static int cconv(Op*);
+static int dconv(Op*);
+static int hconv(Op*);
+static int lconv(Op*);
+static int oconv(Op*);
+static int sconv(Op*);
+static int uconv(Op*);
+static int xconv(Op*);
+static int Xconv(Op*);
+static int percent(Op*);
+
+static
+int (*fmtconv[MAXCON])(Op*) =
+{
+ noconv,
+ cconv, dconv, hconv, lconv,
+ oconv, sconv, uconv, xconv,
+ Xconv, percent,
+};
+static
+char fmtindex[128] =
+{
+ ['c'] 1,
+ ['d'] 2,
+ ['h'] 3,
+ ['l'] 4,
+ ['o'] 5,
+ ['s'] 6,
+ ['u'] 7,
+ ['x'] 8,
+ ['X'] 9,
+ ['%'] 10,
+};
+
+static int convcount = { 11 };
+static int ucase;
+
+static void
+PUT(Op *o, int c)
+{
+ static int pos;
+ int opos;
+
+ if(c == '\t'){
+ opos = pos;
+ pos = (opos+8) & ~7;
+ while(opos++ < pos && o->p < o->ep)
+ *o->p++ = ' ';
+ return;
+ }
+ if(o->p < o->ep){
+ *o->p++ = c;
+ pos++;
+ }
+ if(c == '\n')
+ pos = 0;
+}
+
+int
+fmtinstall(char c, int (*f)(Op*))
+{
+
+ c &= 0177;
+ if(fmtindex[c] == 0) {
+ if(convcount >= MAXCON)
+ return 1;
+ fmtindex[c] = convcount++;
+ }
+ fmtconv[fmtindex[c]] = f;
+ return 0;
+}
+
+char*
+donprint(char *p, char *ep, char *fmt, void *argp)
+{
+ int sf1, c;
+ Op o;
+
+ o.p = p;
+ o.ep = ep;
+ o.argp = argp;
+
+loop:
+ c = *fmt++;
+ if(c != '%') {
+ if(c == 0) {
+ if(o.p < o.ep)
+ *o.p = 0;
+ return o.p;
+ }
+ PUT(&o, c);
+ goto loop;
+ }
+ o.f1 = 0;
+ o.f2 = -1;
+ o.f3 = 0;
+ c = *fmt++;
+ sf1 = 0;
+ if(c == '-') {
+ sf1 = 1;
+ c = *fmt++;
+ }
+ while(c >= '0' && c <= '9') {
+ o.f1 = o.f1*10 + c-'0';
+ c = *fmt++;
+ }
+ if(sf1)
+ o.f1 = -o.f1;
+ if(c != '.')
+ goto l1;
+ c = *fmt++;
+ while(c >= '0' && c <= '9') {
+ if(o.f2 < 0)
+ o.f2 = 0;
+ o.f2 = o.f2*10 + c-'0';
+ c = *fmt++;
+ }
+l1:
+ if(c == 0)
+ fmt--;
+ c = (*fmtconv[fmtindex[c&0177]])(&o);
+ if(c < 0) {
+ o.f3 |= -c;
+ c = *fmt++;
+ goto l1;
+ }
+ o.argp = (char*)o.argp + c;
+ goto loop;
+}
+
+void
+strconv(char *o, Op *op, int f1, int f2)
+{
+ int n, c;
+ char *p;
+
+ n = strlen(o);
+ if(f1 >= 0)
+ while(n < f1) {
+ PUT(op, ' ');
+ n++;
+ }
+ for(p=o; c = *p++;)
+ if(f2 != 0) {
+ PUT(op, c);
+ f2--;
+ }
+ if(f1 < 0) {
+ f1 = -f1;
+ while(n < f1) {
+ PUT(op, ' ');
+ n++;
+ }
+ }
+}
+
+int
+numbconv(Op *op, int base)
+{
+ char b[IDIGIT];
+ int i, f, n, r;
+ long v;
+ short h;
+
+ f = 0;
+ switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
+ case FLONG:
+ v = *(long*)op->argp;
+ r = LONG;
+ break;
+
+ case FUNSIGN|FLONG:
+ v = *(ulong*)op->argp;
+ r = LONG;
+ break;
+
+ case FSHORT:
+ h = *(int*)op->argp;
+ v = h;
+ r = SHORT;
+ break;
+
+ case FUNSIGN|FSHORT:
+ h = *(int*)op->argp;
+ v = (ushort)h;
+ r = SHORT;
+ break;
+
+ default:
+ v = *(int*)op->argp;
+ r = INT;
+ break;
+
+ case FUNSIGN:
+ v = *(unsigned*)op->argp;
+ r = INT;
+ break;
+ }
+ if(!(op->f3 & FUNSIGN) && v < 0) {
+ v = -v;
+ f = 1;
+ }
+ b[IDIGIT-1] = 0;
+ for(i = IDIGIT-2;; i--) {
+ n = (ulong)v % base;
+ n += '0';
+ if(n > '9'){
+ n += 'a' - ('9'+1);
+ if(ucase)
+ n += 'A'-'a';
+ }
+ b[i] = n;
+ if(i < 2)
+ break;
+ v = (ulong)v / base;
+ if(op->f2 >= 0 && i >= IDIGIT-op->f2)
+ continue;
+ if(v <= 0)
+ break;
+ }
+ if(f)
+ b[--i] = '-';
+ strconv(b+i, op, op->f1, -1);
+ return r;
+}
+
+static int
+noconv(Op *op)
+{
+
+ strconv("***", op, 0, -1);
+ return 0;
+}
+
+static int
+cconv(Op *op)
+{
+ char b[2];
+
+ b[0] = *(int*)op->argp;
+ b[1] = 0;
+ strconv(b, op, op->f1, -1);
+ return INT;
+}
+
+static int
+dconv(Op *op)
+{
+ return numbconv(op, 10);
+}
+
+static int
+hconv(Op*)
+{
+ return -FSHORT;
+}
+
+static int
+lconv(Op*)
+{
+ return -FLONG;
+}
+
+static int
+oconv(Op *op)
+{
+ return numbconv(op, 8);
+}
+
+static int
+sconv(Op *op)
+{
+ strconv(*(char**)op->argp, op, op->f1, op->f2);
+ return PTR;
+}
+
+static int
+uconv(Op*)
+{
+ return -FUNSIGN;
+}
+
+static int
+xconv(Op *op)
+{
+ return numbconv(op, 16);
+}
+
+static int
+Xconv(Op *op)
+{
+ int r;
+
+ ucase = 1;
+ r = numbconv(op, 16);
+ ucase = 0;
+ return r;
+}
+
+static int
+percent(Op *op)
+{
+
+ PUT(op, '%');
+ return 0;
+}
diff --git a/os/boot/puma/dosboot.c b/os/boot/puma/dosboot.c
new file mode 100644
index 00000000..365c8824
--- /dev/null
+++ b/os/boot/puma/dosboot.c
@@ -0,0 +1,614 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "dosfs.h"
+
+extern char *premature;
+
+/*
+ * predeclared
+ */
+static void bootdump(Dosboot*);
+static void setname(Dosfile*, char*);
+long dosreadseg(Dosfile*, long, long);
+
+/*
+ * debugging
+ */
+#define chatty 1
+#define chat if(chatty)print
+
+/*
+ * block io buffers
+ */
+enum
+{
+ Nbio= 16,
+};
+typedef struct Clustbuf Clustbuf;
+struct Clustbuf
+{
+ int age;
+ long sector;
+ uchar *iobuf;
+ Dos *dos;
+ int size;
+};
+Clustbuf bio[Nbio];
+
+/*
+ * get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+ Clustbuf *p, *oldest;
+ int size;
+
+ chat("getclust @ %d\n", sector);
+
+ /*
+ * if we have it, just return it
+ */
+ for(p = bio; p < &bio[Nbio]; p++){
+ if(sector == p->sector && dos == p->dos){
+ p->age = m->ticks;
+ chat("getclust %d in cache\n", sector);
+ return p;
+ }
+ }
+
+ /*
+ * otherwise, reuse the oldest entry
+ */
+ oldest = bio;
+ for(p = &bio[1]; p < &bio[Nbio]; p++){
+ if(p->age <= oldest->age)
+ oldest = p;
+ }
+ p = oldest;
+
+ /*
+ * make sure the buffer is big enough
+ */
+ size = dos->clustsize*dos->sectsize;
+ if(p->iobuf==0 || p->size < size)
+ p->iobuf = ialloc(size, 0);
+ p->size = size;
+
+ /*
+ * read in the cluster
+ */
+ chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize);
+ if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){
+ chat("can't seek block\n");
+ return 0;
+ }
+ if((*dos->read)(dos->dev, p->iobuf, size) != size){
+ chat("can't read block\n");
+ return 0;
+ }
+
+ p->age = m->ticks;
+ p->dos = dos;
+ p->sector = sector;
+ chat("getclust %d read\n", sector);
+ return p;
+}
+
+/*
+ * walk the fat one level ( n is a current cluster number ).
+ * return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+ ulong k, sect;
+ Clustbuf *p;
+ int o;
+
+ chat("fatwalk %d\n", n);
+
+ if(n < 2 || n >= dos->fatclusters)
+ return -1;
+
+ switch(dos->fatbits){
+ case 12:
+ k = (3*n)/2; break;
+ case 16:
+ k = 2*n; break;
+ default:
+ return -1;
+ }
+ if(k >= dos->fatsize*dos->sectsize)
+ panic("getfat");
+
+ sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+ o = k%(dos->sectsize*dos->clustsize);
+ p = getclust(dos, sect);
+ k = p->iobuf[o++];
+ if(o >= dos->sectsize*dos->clustsize){
+ p = getclust(dos, sect+dos->clustsize);
+ o = 0;
+ }
+ k |= p->iobuf[o]<<8;
+ if(dos->fatbits == 12){
+ if(n&1)
+ k >>= 4;
+ else
+ k &= 0xfff;
+ if(k >= 0xff8)
+ k |= 0xf000;
+ }
+ k = k < 0xfff8 ? k : -1;
+ chat("fatwalk %d -> %d\n", n, k);
+ return k;
+}
+
+/*
+ * map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+ Dos *dos = fp->dos;
+ long l;
+ long p;
+
+ chat("fileaddr %8.8s %d\n", fp->name, ltarget);
+ /*
+ * root directory is contiguous and easy
+ */
+ if(fp->pstart == 0){
+ if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+ return -1;
+ l = dos->rootaddr + ltarget*dos->clustsize;
+ chat("fileaddr %d -> %d\n", ltarget, l);
+ return l;
+ }
+
+ /*
+ * anything else requires a walk through the fat
+ */
+ if(ltarget >= fp->lcurrent && fp->pcurrent){
+ /* start at the currrent point */
+ l = fp->lcurrent;
+ p = fp->pcurrent;
+ } else {
+ /* go back to the beginning */
+ l = 0;
+ p = fp->pstart;
+ }
+ while(l != ltarget){
+ /* walk the fat */
+ p = fatwalk(dos, p);
+ if(p < 0)
+ return -1;
+ l++;
+ }
+ fp->lcurrent = l;
+ fp->pcurrent = p;
+
+ /*
+ * clusters start at 2 instead of 0 (why? - presotto)
+ */
+ l = dos->dataaddr + (p-2)*dos->clustsize;
+ chat("fileaddr %d -> %d\n", ltarget, l);
+ return l;
+}
+
+/*
+ * read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+ long addr;
+ long rv;
+ int i;
+ int off;
+ Clustbuf *p;
+ uchar *from, *to;
+
+ if((fp->attr & DDIR) == 0){
+ if(fp->offset >= fp->length)
+ return 0;
+ if(fp->offset+n > fp->length)
+ n = fp->length - fp->offset;
+ }
+
+ to = a;
+ for(rv = 0; rv < n; rv+=i){
+ /*
+ * read the cluster
+ */
+ addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+ if(addr < 0)
+ return -1;
+ p = getclust(fp->dos, addr);
+ if(p == 0)
+ return -1;
+
+ /*
+ * copy the bytes we need
+ */
+ off = fp->offset % fp->dos->clustbytes;
+ from = &p->iobuf[off];
+ i = n - rv;
+ if(i > fp->dos->clustbytes - off)
+ i = fp->dos->clustbytes - off;
+ memmove(to, from, i);
+ to += i;
+ fp->offset += i;
+ }
+
+ return rv;
+}
+
+/*
+ * walk a directory returns
+ * -1 if something went wrong
+ * 0 if not found
+ * 1 if found
+ */
+int
+doswalk(Dosfile *file, char *name)
+{
+ Dosdir d;
+ long n;
+
+ if((file->attr & DDIR) == 0){
+ chat("walking non-directory!\n");
+ return -1;
+ }
+
+ setname(file, name);
+
+ file->offset = 0; /* start at the beginning */
+ while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+ chat("comparing to %8.8s.%3.3s\n", d.name, d.ext);
+ if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+ continue;
+ if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+ continue;
+ if(d.attr & DVLABEL){
+ chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext);
+ continue;
+ }
+ file->attr = d.attr;
+ file->pstart = GSHORT(d.start);
+ file->length = GLONG(d.length);
+ file->pcurrent = 0;
+ file->lcurrent = 0;
+ file->offset = 0;
+ return 1;
+ }
+ return n >= 0 ? 0 : -1;
+}
+
+
+/*
+ * instructions that boot blocks can start with
+ */
+#define JMPSHORT 0xeb
+#define JMPNEAR 0xe9
+
+/*
+ * read dos file system properties
+ */
+int
+dosinit(Dos *dos, int start, int ishard)
+{
+ Dosboot *b;
+ int i;
+ Clustbuf *p;
+ Dospart *dp;
+ ulong mbroffset, offset;
+
+ /* defaults till we know better */
+ dos->start = start;
+ dos->sectsize = 512;
+ dos->clustsize = 1;
+ mbroffset = 0;
+
+dmddo:
+ /* get first sector */
+ p = getclust(dos, mbroffset);
+ if(p == 0){
+ chat("can't read boot block\n");
+ return -1;
+ }
+
+ /*
+ * If it's a hard disc then look for an MBR and pick either an
+ * active partition or the FAT with the lowest starting LBA.
+ * Things are tricky because we could be pointing to, amongst others:
+ * 1) a floppy BPB;
+ * 2) a hard disc MBR;
+ * 3) a hard disc extended partition table;
+ * 4) a logical drive on a hard disc;
+ * 5) a disc-manager boot block.
+ * They all have the same magic at the end of the block.
+ */
+ if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) {
+ chat("not DOS\n");
+ return -1;
+ }
+ p->dos = 0;
+ b = (Dosboot *)p->iobuf;
+ if(ishard && b->mediadesc != 0xF8){
+ dp = (Dospart*)&p->iobuf[0x1BE];
+ offset = 0xFFFFFFFF;
+ for(i = 0; i < 4; i++, dp++){
+ if(dp->type == DMDDO){
+ mbroffset = 63;
+ goto dmddo;
+ }
+ if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE)
+ continue;
+ if(dp->flag & 0x80){
+ offset = GLONG(dp->start);
+ break;
+ }
+ if(GLONG(dp->start) < offset)
+ offset = GLONG(dp->start);
+ }
+ if(i != 4 || offset != 0xFFFFFFFF){
+ dos->start = mbroffset+offset;
+ p = getclust(dos, 0);
+ if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA)
+ return -1;
+ }
+ p->dos = 0;
+ }
+
+ b = (Dosboot *)p->iobuf;
+ if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+ chat("no dos file system\n");
+ return -1;
+ }
+
+ if(chatty)
+ bootdump(b);
+
+ /*
+ * determine the systems' wondersous properties
+ */
+ dos->sectsize = GSHORT(b->sectsize);
+ dos->clustsize = b->clustsize;
+ dos->clustbytes = dos->sectsize*dos->clustsize;
+ dos->nresrv = GSHORT(b->nresrv);
+ dos->nfats = b->nfats;
+ dos->rootsize = GSHORT(b->rootsize);
+ dos->volsize = GSHORT(b->volsize);
+ if(dos->volsize == 0)
+ dos->volsize = GLONG(b->bigvolsize);
+ dos->mediadesc = b->mediadesc;
+ dos->fatsize = GSHORT(b->fatsize);
+ dos->fataddr = dos->nresrv;
+ dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+ i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+ i = i/dos->sectsize;
+ dos->dataaddr = dos->rootaddr + i;
+ dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+ if(dos->fatclusters < 4087)
+ dos->fatbits = 12;
+ else
+ dos->fatbits = 16;
+ dos->freeptr = 2;
+
+ /*
+ * set up the root
+ */
+ dos->root.dos = dos;
+ dos->root.pstart = 0;
+ dos->root.pcurrent = dos->root.lcurrent = 0;
+ dos->root.offset = 0;
+ dos->root.attr = DDIR;
+ dos->root.length = dos->rootsize*sizeof(Dosdir);
+
+ return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+ if(chatty == 0)
+ return;
+ print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ b->magic[0], b->magic[1], b->magic[2]);
+ print("version: \"%8.8s\"\n", b->version);
+ print("sectsize: %d\n", GSHORT(b->sectsize));
+ print("allocsize: %d\n", b->clustsize);
+ print("nresrv: %d\n", GSHORT(b->nresrv));
+ print("nfats: %d\n", b->nfats);
+ print("rootsize: %d\n", GSHORT(b->rootsize));
+ print("volsize: %d\n", GSHORT(b->volsize));
+ print("mediadesc: 0x%2.2x\n", b->mediadesc);
+ print("fatsize: %d\n", GSHORT(b->fatsize));
+ print("trksize: %d\n", GSHORT(b->trksize));
+ print("nheads: %d\n", GSHORT(b->nheads));
+ print("nhidden: %d\n", GLONG(b->nhidden));
+ print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+ print("driveno: %d\n", b->driveno);
+ print("reserved0: 0x%2.2x\n", b->reserved0);
+ print("bootsig: 0x%2.2x\n", b->bootsig);
+ print("volid: 0x%8.8x\n", GLONG(b->volid));
+ print("label: \"%11.11s\"\n", b->label);
+}
+
+/*
+ * grab next element from a path, return the pointer to unprocessed portion of
+ * path.
+ */
+static char *
+nextelem(char *path, char *elem)
+{
+ int i;
+
+ while(*path == '/')
+ path++;
+ if(*path==0 || *path==' ')
+ return 0;
+ for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+ if(i==28){
+ print("name component too long\n");
+ return 0;
+ }
+ *elem++ = *path++;
+ }
+ *elem = '\0';
+ return path;
+}
+
+int
+dosstat(Dos *dos, char *path, Dosfile *f)
+{
+ char element[NAMELEN];
+
+ *f = dos->root;
+ while(path = nextelem(path, element)){
+ switch(doswalk(f, element)){
+ case -1:
+ return -1;
+ case 0:
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * boot
+ */
+int
+dosboot(Dos *dos, char *path)
+{
+ Dosfile file;
+ long n;
+ long addr;
+ Exec *ep;
+ void (*b)(void);
+
+ switch(dosstat(dos, path, &file)){
+
+ case -1:
+ print("error walking to %s\n", path);
+ return -1;
+ case 0:
+ print("%s not found\n", path);
+ return -1;
+ case 1:
+ print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name,
+ file.ext, file.attr, file.pstart, file.length);
+ break;
+ }
+
+ /*
+ * read header
+ */
+ ep = (Exec*)ialloc(sizeof(Exec), 0);
+ n = sizeof(Exec);
+ if(dosreadseg(&file, n, (ulong) ep) != n){
+ print(premature);
+ return -1;
+ }
+ if(GLLONG(ep->magic) != E_MAGIC){
+ print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+ return -1;
+ }
+
+ /*
+ * read text
+ */
+ addr = PADDR(GLLONG(ep->entry));
+ n = GLLONG(ep->text);
+ print("+%d", n);
+ if(dosreadseg(&file, n, addr) != n){
+ print(premature);
+ return -1;
+ }
+
+ /*
+ * read data (starts at first page after kernel)
+ */
+ addr = PGROUND(addr+n);
+ n = GLLONG(ep->data);
+ print("+%d", n);
+ if(dosreadseg(&file, n, addr) != n){
+ print(premature);
+ return -1;
+ }
+
+ /*
+ * bss and entry point
+ */
+ print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry));
+
+ /*
+ * Go to new code. It's up to the program to get its PC relocated to
+ * the right place.
+ */
+ b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+ (*b)();
+ return 0;
+}
+
+/*
+ * read in a segment
+ */
+long
+dosreadseg(Dosfile *fp, long len, long addr)
+{
+ char *a;
+ long n, sofar;
+
+ a = (char *)addr;
+ for(sofar = 0; sofar < len; sofar += n){
+ n = 8*1024;
+ if(len - sofar < n)
+ n = len - sofar;
+ n = dosread(fp, a + sofar, n);
+ if(n <= 0)
+ break;
+ print(".");
+ }
+ return sofar;
+}
+
+/*
+ * set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+ char *to;
+
+ to = fp->name;
+ for(; *from && to-fp->name < 8; from++, to++){
+ if(*from == '.'){
+ from++;
+ break;
+ }
+ if(*from >= 'a' && *from <= 'z')
+ *to = *from + 'A' - 'a';
+ else
+ *to = *from;
+ }
+ while(to - fp->name < 8)
+ *to++ = ' ';
+
+ to = fp->ext;
+ for(; *from && to-fp->ext < 3; from++, to++){
+ if(*from >= 'a' && *from <= 'z')
+ *to = *from + 'A' - 'a';
+ else
+ *to = *from;
+ }
+ while(to-fp->ext < 3)
+ *to++ = ' ';
+
+ chat("name is %8.8s %3.3s\n", fp->name, fp->ext);
+}
diff --git a/os/boot/puma/dosfs.h b/os/boot/puma/dosfs.h
new file mode 100644
index 00000000..a45065a6
--- /dev/null
+++ b/os/boot/puma/dosfs.h
@@ -0,0 +1,110 @@
+typedef struct Dosboot Dosboot;
+typedef struct Dos Dos;
+typedef struct Dosdir Dosdir;
+typedef struct Dosfile Dosfile;
+typedef struct Dospart Dospart;
+
+struct Dospart
+{
+ uchar flag; /* active flag */
+ uchar shead; /* starting head */
+ uchar scs[2]; /* starting cylinder/sector */
+ uchar type; /* partition type */
+ uchar ehead; /* ending head */
+ uchar ecs[2]; /* ending cylinder/sector */
+ uchar start[4]; /* starting sector */
+ uchar len[4]; /* length in sectors */
+};
+
+#define FAT12 0x01
+#define FAT16 0x04
+#define FATHUGE 0x06
+#define DMDDO 0x54
+
+struct Dosboot{
+ uchar magic[3];
+ uchar version[8];
+ uchar sectsize[2];
+ uchar clustsize;
+ uchar nresrv[2];
+ uchar nfats;
+ uchar rootsize[2];
+ uchar volsize[2];
+ uchar mediadesc;
+ uchar fatsize[2];
+ uchar trksize[2];
+ uchar nheads[2];
+ uchar nhidden[4];
+ uchar bigvolsize[4];
+ uchar driveno;
+ uchar reserved0;
+ uchar bootsig;
+ uchar volid[4];
+ uchar label[11];
+ uchar reserved1[8];
+};
+
+struct Dosfile{
+ Dos *dos; /* owning dos file system */
+ char name[8];
+ char ext[3];
+ uchar attr;
+ long length;
+ long pstart; /* physical start cluster address */
+ long pcurrent; /* physical current cluster address */
+ long lcurrent; /* logical current cluster address */
+ long offset;
+};
+
+struct Dos{
+ int dev; /* device id */
+ long (*read)(int, void*, long); /* read routine */
+ long (*seek)(int, long); /* seek routine */
+
+ int start; /* start of file system */
+ int sectsize; /* in bytes */
+ int clustsize; /* in sectors */
+ int clustbytes; /* in bytes */
+ int nresrv; /* sectors */
+ int nfats; /* usually 2 */
+ int rootsize; /* number of entries */
+ int volsize; /* in sectors */
+ int mediadesc;
+ int fatsize; /* in sectors */
+ int fatclusters;
+ int fatbits; /* 12 or 16 */
+ long fataddr; /* sector number */
+ long rootaddr;
+ long dataaddr;
+ long freeptr;
+
+ Dosfile root;
+};
+
+struct Dosdir{
+ uchar name[8];
+ uchar ext[3];
+ uchar attr;
+ uchar reserved[10];
+ uchar time[2];
+ uchar date[2];
+ uchar start[2];
+ uchar length[4];
+};
+
+#define DRONLY 0x01
+#define DHIDDEN 0x02
+#define DSYSTEM 0x04
+#define DVLABEL 0x08
+#define DDIR 0x10
+#define DARCH 0x20
+
+extern int chatty;
+
+extern int dosboot(Dos*, char*);
+extern int dosinit(Dos*, int, int);
+extern long dosread(Dosfile*, void*, long);
+extern int dosstat(Dos*, char*, Dosfile*);
+extern int doswalk(Dosfile*, char*);
+
+extern int plan9ini(Dos*, char*);
diff --git a/os/boot/puma/ebsit.trap.c b/os/boot/puma/ebsit.trap.c
new file mode 100644
index 00000000..f95c0418
--- /dev/null
+++ b/os/boot/puma/ebsit.trap.c
@@ -0,0 +1,206 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "ebsit.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+
+int inpanic;
+
+#define CSR ((ushort *) 0x2000000)
+
+
+typedef struct Irqctlr {
+ uint addr;
+ uint enabled;
+ struct {
+ void (*r)(Ureg*, void*);
+ void *a;
+ } h[16];
+} Irqctlr;
+
+static Irqctlr irqctlr;
+
+void
+csrset( int bit )
+{
+static ushort *csr_val = 0x8c;
+
+ *csr_val ^= (1 << bit);
+ putcsr(*csr_val);
+}
+
+void
+intrinit( void )
+{
+int offset;
+ulong op;
+
+
+ irqctlr.addr = 1;
+ irqctlr.enabled = 0;
+
+ /* Reset Exception */
+ offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x0) = op;
+
+ /* Undefined Instruction Exception */
+ offset = ((((ulong) _vundcall) - 0x4)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x4) = op;
+
+ /* SWI Exception */
+ offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x8) = op;
+
+ /* Prefetch Abort Exception */
+ offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0xc) = op;
+
+ /* Data Abort Exception */
+ offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x10) = op;
+
+ /* IRQ Exception */
+ offset = ((((ulong) _virqcall) - 0x18)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x18) = op;
+
+
+}
+
+void
+intrenable(uint addr, int bit, void (*r)(Ureg*, void*), void* a)
+{
+ int i;
+ USED(addr);
+ for(i = 0; i < 16; i++)
+ {
+ if((bit & (1<<i)) == 0)
+ continue;
+ irqctlr.h[i].r = r;
+ irqctlr.h[i].a = a;
+ irqctlr.enabled |= (1<<i);
+ if (i < 7)
+ csrset(i);
+ }
+ return;
+}
+
+int lucifer; /* Global to store the last CSR (eric) */
+
+static void
+interrupt(Ureg* ureg)
+{
+ int i, mask;
+
+ mask = *CSR;
+ lucifer = mask; /* eric */
+ if(irqctlr.enabled == 0){
+
+ return;
+ }
+ for(i = 0; i < 16; i++)
+ {
+
+ if((irqctlr.enabled & (1<<i)) == 0)
+ continue;
+ if(( mask & (1 << i)) == 0)
+ continue;
+ if (!irqctlr.h[i].r)
+ continue;
+ (irqctlr.h[i].r)(ureg, irqctlr.h[i].a);
+ mask &= ~(1 << i);
+ }
+
+ if ((mask) && (mask < 0x90)) /* ignore non-maskable interrupts */
+ {
+ print("unknown or unhandled interrupt\n");
+ panic("unknown or unhandled interrupt: mask=%ux",mask);
+ }
+
+}
+
+static void
+dumpregs(Ureg* ureg)
+{
+ print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ print("Last Interrupt's CSR: %8.8uX\n",lucifer);
+ print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr());
+}
+
+void
+dumpstack(void)
+{
+}
+
+void
+exception(Ureg* ureg)
+{
+ static Ureg old_ureg;
+ uint far =0;
+ uint fsr =0;
+
+ static lasttype = 0;
+
+ LOWBAT;
+
+ USED(far, fsr);
+
+ lasttype = ureg->type;
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+
+ if(ureg->type == (PsrMabt+1))
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+
+ switch(ureg->type){
+
+ case PsrMfiq: /* (Fast) */
+ print("Fast\n");
+ print("We should never be here\n");
+ while(1);
+
+ case PsrMirq: /* Interrupt Request */
+ interrupt(ureg);
+ break;
+
+ case PsrMund: /* Undefined instruction */
+ print("Undefined instruction\n");
+ case PsrMsvc: /* Jump through 0, SWI or reserved trap */
+ print("SWI/SVC trap\n");
+ case PsrMabt: /* Prefetch abort */
+ print("Prefetch Abort\n");
+ case PsrMabt+1: /* Data abort */
+ print("Data Abort\n");
+
+
+ default:
+ dumpregs(ureg);
+ /* panic("exception %uX\n", ureg->type); */
+ break;
+ }
+
+ LOWBAT; /* Low bat off after interrupt */
+
+ splhi();
+
+}
diff --git a/os/boot/puma/ether.c b/os/boot/puma/ether.c
new file mode 100644
index 00000000..a3559051
--- /dev/null
+++ b/os/boot/puma/ether.c
@@ -0,0 +1,156 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ether.h"
+
+static Ctlr ether[MaxEther];
+
+static struct {
+ char *type;
+ int (*reset)(Ctlr*);
+} cards[] = {
+ { "CS8900", cs8900reset, },
+ { 0, }
+};
+
+int
+etherinit(void)
+{
+ Ctlr *ctlr;
+ int ctlrno, i, mask, n;
+
+ mask = 0;
+ for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ ctlr = &ether[ctlrno];
+ memset(ctlr, 0, sizeof(Ctlr));
+ if(isaconfig("ether", ctlrno, &ctlr->card) == 0)
+ continue;
+ for(n = 0; cards[n].type; n++){
+ if(strcmp(cards[n].type, ctlr->card.type))
+ continue;
+ ctlr->ctlrno = ctlrno;
+ if((*cards[n].reset)(ctlr))
+ break;
+
+ ctlr->iq = qopen(16*1024, 1, 0, 0);
+ ctlr->oq = qopen(16*1024, 1, 0, 0);
+
+ ctlr->present = 1;
+ mask |= 1<<ctlrno;
+
+ print("ether%d: %s: port 0x%luX irq %d",
+ ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq);
+ if(ctlr->card.mem)
+ print(" addr 0x%luX", ctlr->card.mem & ~KZERO);
+ if(ctlr->card.size)
+ print(" size 0x%luX", ctlr->card.size);
+ print(":");
+ for(i = 0; i < sizeof(ctlr->card.ea); i++)
+ print(" %2.2uX", ctlr->card.ea[i]);
+ print("\n"); uartwait();
+ setvec(ctlr->card.irq, ctlr->card.intr, ctlr);
+ break;
+ }
+ }
+
+ return mask;
+}
+
+static Ctlr*
+attach(int ctlrno)
+{
+ Ctlr *ctlr;
+
+ if(ctlrno >= MaxEther || ether[ctlrno].present == 0)
+ return 0;
+
+ ctlr = &ether[ctlrno];
+ if(ctlr->present == 1){
+ ctlr->present = 2;
+ (*ctlr->card.attach)(ctlr);
+ }
+
+ return ctlr;
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+ Ctlr *ctlr;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ return ctlr->card.ea;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt *pkt, int timo)
+{
+ int n;
+ Ctlr *ctlr;
+ Block *b;
+ ulong start;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ start = m->ticks;
+ while((b = qget(ctlr->iq)) == 0){
+ if(TK2MS(m->ticks - start) >= timo){
+ /*
+ print("ether%d: rx timeout\n", ctlrno);
+ */
+ return 0;
+ }
+ //delay(1);
+ }
+
+ n = BLEN(b);
+ memmove(pkt, b->rp, n);
+ freeb(b);
+
+ return n;
+}
+
+int
+etheriq(Ctlr *ctlr, Block *b, int freebp)
+{
+ if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0){
+ if(freebp)
+ freeb(b);
+ return 0;
+ }
+ qbwrite(ctlr->iq, b);
+ return 1;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int)
+{
+ Ctlr *ctlr;
+ Block *b;
+ int s;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ if(qlen(ctlr->oq) > 16*1024){
+ print("ether%d: tx queue full\n", ctlrno);
+ return 0;
+ }
+ b = iallocb(sizeof(Etherpkt));
+ memmove(b->wp, pkt, len);
+ memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen);
+ b->wp += len;
+ qbwrite(ctlr->oq, b);
+ s = splhi();
+ (*ctlr->card.transmit)(ctlr);
+ splx(s);
+
+ return 1;
+}
diff --git a/os/boot/puma/ether.h b/os/boot/puma/ether.h
new file mode 100644
index 00000000..26f5f64b
--- /dev/null
+++ b/os/boot/puma/ether.h
@@ -0,0 +1,82 @@
+/*
+ * All the goo for PC ethernet cards.
+ */
+typedef struct Card Card;
+typedef struct RingBuf RingBuf;
+typedef struct Type Type;
+typedef struct Ctlr Ctlr;
+
+/*
+ * Hardware interface.
+ */
+struct Card {
+ ISAConf;
+
+ int (*reset)(Ctlr*);
+ void (*attach)(Ctlr*);
+
+ void *(*read)(Ctlr*, void*, ulong, ulong);
+ void *(*write)(Ctlr*, ulong, void*, ulong);
+
+ void (*receive)(Ctlr*);
+ void (*transmit)(Ctlr*);
+ void (*intr)(Ureg*, Ctlr*);
+ void (*overflow)(Ctlr*);
+
+ uchar bit16; /* true if a 16 bit interface */
+ uchar ram; /* true if card has shared memory */
+
+ ulong dp8390; /* I/O address of 8390 (if any) */
+ ulong data; /* I/O data port if no shared memory */
+ uchar nxtpkt; /* software bndry */
+ uchar tstart; /* 8390 ring addresses */
+ uchar pstart;
+ uchar pstop;
+
+ uchar dummyrr; /* do dummy remote read */
+};
+
+/*
+ * Software ring buffer.
+ */
+struct RingBuf {
+ uchar owner;
+ uchar busy; /* unused */
+ ushort len;
+ uchar pkt[sizeof(Etherpkt)];
+};
+
+enum {
+ Host = 0, /* buffer owned by host */
+ Interface = 1, /* buffer owned by card */
+
+ Nrb = 16, /* default number of receive buffers */
+ Ntb = 2, /* default number of transmit buffers */
+};
+
+/*
+ * Software controller.
+ */
+struct Ctlr {
+ Card card; /* hardware info */
+ int ctlrno;
+ int present;
+
+ Queue* iq;
+ Queue* oq;
+
+ int inpackets;
+ int outpackets;
+ int crcs; /* input crc errors */
+ int oerrs; /* output errors */
+ int frames; /* framing errors */
+ int overflows; /* packet overflows */
+ int buffs; /* buffering errors */
+};
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
+
+extern int cs8900reset(Ctlr*);
+extern int etheriq(Ctlr*, Block*, int);
diff --git a/os/boot/puma/ether8900.c b/os/boot/puma/ether8900.c
new file mode 100644
index 00000000..b50da272
--- /dev/null
+++ b/os/boot/puma/ether8900.c
@@ -0,0 +1,555 @@
+/*
+ * Crystal CS8900 ethernet controller
+ * Specifically for the Teralogic Puma architecture
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ether.h"
+#include "puma.h"
+
+/*
+ * On the Puma board the CS8900 can be addressed from either
+ * ISA I/O space or ISA memory space at the following locations.
+ * The cs8900 address pins are shifted by 1 relative to the CPU.
+ */
+enum {
+ IsaIOBase = 0xf0000000,
+ IsaMemBase = 0xe0000000,
+
+ IOBase = 0x300,
+ MemBase = 0xc0000,
+};
+
+/* I/O accesses */
+#define out16(port, val) (*((ushort *)IsaIOBase + IOBase + (port)) = (val))
+#define in16(port) *((ushort *)IsaIOBase + IOBase + (port))
+#define in8(port) *((uchar *)IsaIOBase + ((IOBase+(port))<<1))
+#define regIOw(reg, val) do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
+#define regIOr(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData))
+#define regIOr1(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData1))
+
+/* Memory accesses */
+#define regw(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+#define regr(reg) *((ushort *)IsaMemBase + MemBase + (reg))
+
+/* Puma frame copying */
+#define copyout(src, len) { \
+ int _len = (len); \
+ ushort *_src = (ushort *)(src); \
+ ushort *_dst = (ushort *)IsaMemBase + MemBase + TxFrame; \
+ while(_len > 0) { \
+ *_dst++ = *_src++; \
+ _dst++; \
+ _len -= 2; \
+ } \
+ }
+#define copyoutIO(src, len) { \
+ int _len = (len); \
+ ushort *_src = (ushort *)(src); \
+ while(_len > 0) { \
+ out16(RxTxData, *_src); \
+ _src++; \
+ _len -= 2; \
+ } \
+ }
+#define copyin(dst, len) { \
+ int _len = (len), _len2 = (len)&~1; \
+ ushort *_src = (ushort *)IsaMemBase + MemBase + RxFrame; \
+ ushort *_dst = (ushort *)(dst); \
+ while(_len2 > 0) { \
+ *_dst++ = *_src++; \
+ _src++; \
+ _len2 -= 2; \
+ } \
+ if(_len&1) \
+ *(uchar*)_dst = (*_src)&0xff; \
+ }
+#define copyinIO(dst, len) { \
+ int _i, _len = (len), _len2 = (len)&~1; \
+ ushort *_dst = (ushort *)(dst); \
+ _i = in16(RxTxData); USED(_i); /* RxStatus */ \
+ _i = in16(RxTxData); USED(_i); /* RxLen */ \
+ while(_len2 > 0) { \
+ *_dst++ = in16(RxTxData); \
+ _len2 -= 2; \
+ } \
+ if(_len&1) \
+ *(uchar*)_dst = (in16(RxTxData))&0xff; \
+ }
+
+
+
+enum { /* I/O Mode Register Offsets */
+ RxTxData = 0x00, /* receive/transmit data - port 0 */
+ TxCmdIO = 0x04, /* transmit command */
+ TxLenIO = 0x06, /* transmit length */
+ IsqIO = 0x08, /* Interrupt status queue */
+ PpPtr = 0x0a, /* packet page pointer */
+ PpData = 0x0c, /* packet page data */
+ PpData1 = 0x0e, /* packet page data - port 1*/
+};
+
+enum { /* Memory Mode Register Offsets */
+ /* Bus Interface Registers */
+ Ern = 0x0000, /* EISA registration numberion */
+ Pic = 0x0002, /* Product identification code */
+ Iob = 0x0020, /* I/O base address */
+ Intr = 0x0022, /* interrupt number */
+ Mba = 0x002c, /* memory base address */
+
+ Ecr = 0x0040, /* EEPROM command register */
+ Edw = 0x0042, /* EEPROM data word */
+ Rbc = 0x0050, /* receive frame byte counter */
+
+ /* Status and Control Registers */
+ RxCfg = 0x0102,
+ RxCtl = 0x0104,
+ TxCfg = 0x0106,
+ BufCfg = 0x010a,
+ LineCtl = 0x0112,
+ SelfCtl = 0x0114,
+ BusCtl = 0x0116,
+ TestCtl = 0x0118,
+ Isq = 0x0120,
+ RxEvent = 0x0124,
+ TxEvent = 0x0128,
+ BufEvent = 0x012c,
+ RxMISS = 0x0130,
+ TxCol = 0x0132,
+ LineSt = 0x0134,
+ SelfSt = 0x0136,
+ BusSt = 0x0138,
+ Tdr = 0x013c,
+
+ /* Initiate Transmit Registers */
+ TxCmd = 0x0144, /* transmit command */
+ TxLen = 0x0146, /* transmit length */
+
+ /* Address Filter Registers */
+ IndAddr = 0x0158, /* individual address registers */
+
+ /* Frame Location */
+ RxStatus = 0x0400, /* receive status */
+ RxLen = 0x0402, /* receive length */
+ RxFrame = 0x0404, /* receive frame location */
+ TxFrame = 0x0a00, /* transmit frame location */
+};
+
+enum { /* Ecr */
+ Addr = 0x00ff, /* EEPROM word address (field) */
+ Opcode = 0x0300, /* command opcode (field) */
+};
+
+enum { /* Isq */
+ Regnum = 0x003f, /* register number held by Isq (field) */
+ IsqRxEvent = 0x04,
+ IsqTxEvent = 0x08,
+ IsqBufEvent = 0x0c,
+ IsqRxMiss = 0x10,
+ IsqTxCol = 0x12,
+ RegContent = 0xffc0, /* register data contents (field) */
+};
+
+enum { /* RxCfg */
+ Skip_1 = 0x0040,
+ StreamE = 0x0080,
+ RxOKiE = 0x0100,
+ RxDMAonly = 0x0200,
+ AutoRxDMAE = 0x0400,
+ BufferCRC = 0x0800,
+ CRCerroriE = 0x1000,
+ RuntiE = 0x2000,
+ ExtradataiE = 0x4000,
+};
+
+enum { /* RxEvent */
+ IAHash = 0x0040,
+ Dribblebits = 0x0080,
+ RxOK = 0x0100,
+ Hashed = 0x0200,
+ IndividualAdr = 0x0400,
+ Broadcast = 0x0800,
+ CRCerror = 0x1000,
+ Runt = 0x2000,
+ Extradata = 0x4000,
+};
+
+enum { /* RxCtl */
+ IAHashA = 0x0040,
+ PromiscuousA = 0x0080,
+ RxOKA = 0x0100,
+ MulticastA = 0x0200,
+ IndividualA = 0x0400,
+ BroadcastA = 0x0800,
+ CRCerrorA = 0x1000,
+ RuntA = 0x2000,
+ ExtradataA = 0x4000,
+};
+
+enum { /* TxCfg */
+ LossofCRSiE = 0x0040,
+ SQEerroriE = 0x0080,
+ TxOKiE = 0x0100,
+ OutofWindowiE = 0x0200,
+ JabberiE = 0x0400,
+ AnycolliE = 0x0800,
+ Coll16iE = 0x8000,
+};
+
+enum { /* TxEvent */
+ LossofCRS = 0x0040,
+ SQEerror = 0x0080,
+ TxOK = 0x0100,
+ OutofWindow = 0x0200,
+ Jabber = 0x0400,
+ NTxCols = 0x7800, /* number of Tx collisions (field) */
+ coll16 = 0x8000,
+};
+
+enum { /* BufCfg */
+ SWintX = 0x0040,
+ RxDMAiE = 0x0080,
+ Rdy4TxiE = 0x0100,
+ TxUnderruniE = 0x0200,
+ RxMissiE = 0x0400,
+ Rx128iE = 0x0800,
+ TxColOvfiE = 0x1000,
+ MissOvfloiE = 0x2000,
+ RxDestiE = 0x8000,
+};
+
+enum { /* BufEvent */
+ SWint = 0x0040,
+ RxDMAFrame = 0x0080,
+ Rdy4Tx = 0x0100,
+ TxUnderrun = 0x0200,
+ RxMiss = 0x0400,
+ Rx128 = 0x0800,
+ RxDest = 0x8000,
+};
+
+enum { /* RxMiss */
+ MissCount = 0xffc0,
+};
+
+enum { /* TxCol */
+ ColCount = 0xffc0,
+};
+
+enum { /* LineCtl */
+ SerRxOn = 0x0040,
+ SerTxOn = 0x0080,
+ Iface = 0x0300, /* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
+ ModBackoffE = 0x0800,
+ PolarityDis = 0x1000,
+ DefDis = 0x2000,
+ LoRxSquelch = 0x4000,
+};
+
+enum { /* LineSt */
+ LinkOK = 0x0080,
+ AUI = 0x0100,
+ TenBT = 0x0200,
+ PolarityOK = 0x1000,
+ CRS = 0x4000,
+};
+
+enum { /* SelfCtl */
+ RESET = 0x0040,
+ SWSuspend = 0x0100,
+ HWSleepE = 0x0200,
+ HWStandbyE = 0x0400,
+};
+
+enum { /* SelfSt */
+ INITD = 0x0080,
+ SIBUSY = 0x0100,
+ EepromPresent = 0x0200,
+ EepromOK = 0x0400,
+ ElPresent = 0x0800,
+ EeSize = 0x1000,
+};
+
+enum { /* BusCtl */
+ ResetRxDMA = 0x0040,
+ UseSA = 0x0200,
+ MemoryE = 0x0400,
+ DMABurst = 0x0800,
+ EnableIRQ = 0x8000,
+};
+
+enum { /* BusST */
+ TxBidErr = 0x0080,
+ Rdy4TxNOW = 0x0100,
+};
+
+enum { /* TestCtl */
+ FDX = 0x4000, /* full duplex */
+};
+
+enum { /* TxCmd */
+ TxStart = 0x00c0, /* bytes before transmit starts (field) */
+ TxSt5 = 0x0000, /* start after 5 bytes */
+ TxSt381 = 0x0040, /* start after 381 bytes */
+ TxSt1021 = 0x0080, /* start after 1021 bytes */
+ TxStAll = 0x00c0, /* start after the entire frame is in the cs8900 */
+ Force = 0x0100,
+ Onecoll = 0x0200,
+ InhibitCRC = 0x1000,
+ TxPadDis = 0x2000,
+};
+
+static Queue *pendingTx[MaxEther];
+
+static void
+attach(Ctlr *ctlr)
+{
+ int reg;
+
+ USED(ctlr);
+ /* enable transmit and receive */
+ reg = regr(BusCtl);
+ regw(BusCtl, reg|EnableIRQ);
+ reg = regr(LineCtl);
+ regw(LineCtl, reg|SerRxOn|SerTxOn);
+}
+
+static char pbuf[200];
+int
+sprintx(void *f, char *to, int count)
+{
+ int i, printable;
+ char *start = to;
+ uchar *from = f;
+
+ if(count < 0) {
+ print("BAD DATA COUNT %d\n", count);
+ return 0;
+ }
+ printable = 1;
+ if(count > 40)
+ count = 40;
+ for(i=0; i<count && printable; i++)
+ if((from[i]<32 && from[i] !='\n' && from[i] !='\r' && from[i] !='\b' && from[i] !='\t') || from[i]>127)
+ printable = 0;
+ *to++ = '\'';
+ if(printable){
+ memmove(to, from, count);
+ to += count;
+ }else{
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *to++ = ' ';
+ sprint(to, "%2.2ux", from[i]);
+ to += 2;
+ }
+ }
+ *to++ = '\'';
+ *to = 0;
+ return to - start;
+}
+
+static void
+transmit(Ctlr *ctlr)
+{
+ int len, status;
+ Block *b;
+
+ for(;;){
+ /* is TxCmd pending ? - check */
+ if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+ break;
+ b = qget(ctlr->oq);
+ if(b == 0)
+ break;
+ len = BLEN(b);
+ regw(TxCmd, TxSt381);
+ regw(TxLen, len);
+ status = regr(BusSt);
+ if((status & Rdy4TxNOW) == 0) {
+ qbwrite(pendingTx[ctlr->ctlrno], b);
+ break;
+ }
+ /*
+ * Copy the packet to the transmit buffer.
+ */
+ copyout(b->rp, len);
+ freeb(b);
+ }
+}
+
+static void
+interrupt(Ureg*, Ctlr *ctlr)
+{
+ int len, events, status;
+ Block *b;
+ Queue *q;
+
+ while((events = regr(Isq)) != 0) {
+ status = events&RegContent;
+
+ switch(events&Regnum) {
+
+ case IsqBufEvent:
+ if(status&Rdy4Tx) {
+ if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+ q = pendingTx[ctlr->ctlrno];
+ else
+ q = ctlr->oq;
+ b = qget(q);
+ if(b == 0)
+ break;
+ len = BLEN(b);
+ copyout(b->rp, len);
+ freeb(b);
+ } else
+ if(status&TxUnderrun) {
+ print("TxUnderrun\n");
+ } else
+ if(status&RxMiss) {
+ print("RxMiss\n");
+ } else {
+ print("IsqBufEvent status = %ux\n", status);
+ }
+ break;
+
+ case IsqRxEvent:
+ if(status&RxOK) {
+ len = regr(RxLen);
+ if((b = iallocb(len)) != 0) {
+ copyin(b->wp, len);
+ b->wp += len;
+ etheriq(ctlr, b, 1);
+ }
+ } else {
+ print("IsqRxEvent status = %ux\n", status);
+ }
+ break;
+
+ case IsqTxEvent:
+ if(status&TxOK) {
+ if(qlen(pendingTx[ctlr->ctlrno]) > 0)
+ q = pendingTx[ctlr->ctlrno];
+ else
+ q = ctlr->oq;
+ b = qget(q);
+ if(b == 0)
+ break;
+ len = BLEN(b);
+ regw(TxCmd, TxSt381);
+ regw(TxLen, len);
+if((regr(BusSt) & Rdy4TxNOW) == 0) {
+ print("IsqTxEvent and Rdy4TxNow == 0\n");
+}
+ copyout(b->rp, len);
+ freeb(b);
+ } else {
+ print("IsqTxEvent status = %ux\n", status);
+ }
+ break;
+ case IsqRxMiss:
+ break;
+ case IsqTxCol:
+ break;
+ }
+ }
+}
+
+int
+cs8900reset(Ctlr* ctlr)
+{
+ int i, reg;
+ uchar ea[Eaddrlen];
+
+ ctlr->card.irq = V_ETHERNET;
+ pendingTx[ctlr->ctlrno] = qopen(16*1024, 1, 0, 0);
+
+ /*
+ * If the Ethernet address is not set in the plan9.ini file
+ * a) try reading from the Puma board ROM. The ether address is found in
+ * bytes 4-9 of the ROM. The Teralogic Organizational Unique Id (OUI)
+ * is in bytes 4-6 and should be 00 10 8a.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0) {
+ uchar *rom = (uchar *)EPROM_BASE;
+ if(rom[4] != 0x00 || rom[5] != 0x10 || rom[6] != 0x8a)
+ panic("no ether address");
+ memmove(ea, &rom[4], Eaddrlen);
+ }
+ memmove(ctlr->card.ea, ea, Eaddrlen);
+
+ /*
+ * Identify the chip by reading the Pic register.
+ * The EISA registration number is in the low word
+ * and the product identification code in the high code.
+ * The ERN for Crystal Semiconductor is 0x630e.
+ * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
+ */
+ if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
+ panic("no cs8900 found");
+
+ /*
+ * Reset the chip and ensure 16-bit mode operation
+ */
+ regIOw(SelfCtl, RESET);
+ delay(10);
+ i=in8(PpPtr); USED(i);
+ i=in8(PpPtr+1); USED(i);
+ i=in8(PpPtr); USED(i);
+ i=in8(PpPtr+1); USED(i);
+
+ /*
+ * Wait for initialisation and EEPROM reads to complete
+ */
+ i=0;
+ for(;;) {
+ short st = regIOr(SelfSt);
+ if((st&SIBUSY) == 0 && st&INITD)
+ break;
+ if(i++ > 1000000)
+ panic("cs8900: initialisation failed");
+ }
+
+ /*
+ * Enable memory mode operation.
+ */
+ regIOw(Mba, MemBase & 0xffff);
+ regIOw(Mba+2, MemBase >> 16);
+ regIOw(BusCtl, MemoryE|UseSA);
+
+ /*
+ * Enable 10BASE-T half duplex, transmit in interrupt mode
+ */
+ reg = regr(LineCtl);
+ regw(LineCtl, reg&~Iface);
+ reg = regr(TestCtl);
+ regw(TestCtl, reg&~FDX);
+ regw(BufCfg, Rdy4TxiE|TxUnderruniE|RxMissiE);
+ regw(TxCfg, TxOKiE|JabberiE|Coll16iE);
+ regw(RxCfg, RxOKiE);
+ regw(RxCtl, RxOKA|IndividualA|BroadcastA);
+
+ for(i=0; i<Eaddrlen; i+=2)
+ regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
+
+ /* Puma IRQ tied to INTRQ0 */
+ regw(Intr, 0);
+
+ ctlr->card.reset = cs8900reset;
+ ctlr->card.port = 0x300;
+ ctlr->card.attach = attach;
+ ctlr->card.transmit = transmit;
+ ctlr->card.intr = interrupt;
+
+ print("Ether reset...\n");uartwait();
+
+ return 0;
+}
+
diff --git a/os/boot/puma/flash.c b/os/boot/puma/flash.c
new file mode 100644
index 00000000..be109f97
--- /dev/null
+++ b/os/boot/puma/flash.c
@@ -0,0 +1,226 @@
+#include "boot.h"
+
+typedef struct Flashdev Flashdev;
+struct Flashdev {
+ uchar* base;
+ int size;
+ uchar* exec;
+ char* type;
+ char* config;
+ int conflen;
+};
+
+enum {
+ FLASHSEG = 256*1024,
+ CONFIGLIM = FLASHSEG,
+ BOOTOFF = FLASHSEG,
+ BOOTLEN = 3*FLASHSEG, /* third segment might be filsys */
+ /* rest of flash is free */
+};
+
+static Flashdev flash;
+
+/*
+ * configuration data is written between the bootstrap and
+ * the end of region 0. the region ends with allocation descriptors
+ * of the following form:
+ *
+ * byte order is big endian
+ *
+ * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini
+ */
+typedef struct Flalloc Flalloc;
+struct Flalloc {
+ ulong check; /* checksum of data, or ~0 */
+ ulong base; /* base of region; ~0 if unallocated, 0 if deleted */
+ uchar len[3];
+ uchar tag; /* see below */
+ uchar sig[4];
+};
+
+enum {
+ /* tags */
+ Tdead= 0,
+ Tboot= 0x01, /* space reserved for boot */
+ Tconf= 0x02, /* configuration data */
+ Tnone= 0xFF,
+
+ Noval= ~0,
+};
+
+static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F};
+static char conftag[] = "#plan9.ini\n";
+
+static ulong
+checksum(uchar* p, int n)
+{
+ ulong s;
+
+ for(s=0; --n >= 0;)
+ s += *p++;
+ return s;
+}
+
+static int
+validptr(Flalloc *ap, uchar *p)
+{
+ return p > (uchar*)end && p < (uchar*)ap;
+}
+
+static int
+flashcheck(Flalloc *ap, char **val, int *len)
+{
+ uchar *base;
+ int n;
+
+ if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone)
+ return 0;
+ base = flash.base+ap->base;
+ if(!validptr(ap, base))
+ return 0;
+ n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2];
+ if(n == 0xFFFFFF)
+ n = 0;
+ if(n < 0)
+ return 0;
+ if(n > 0 && !validptr(ap, base+n-1))
+ return 0;
+ if(ap->check != Noval && checksum(base, n) != ap->check){
+ print("flash: bad checksum\n");
+ return 0;
+ }
+ *val = (char*)base;
+ *len = n;
+ return 1;
+}
+
+int
+flashinit(void)
+{
+ int f, n, len;
+ char *type, *val;
+ Flalloc *ap;
+
+ flash.base = 0;
+ flash.exec = 0;
+ flash.type = 0;
+ /* TODO - check for presence and type */
+/*
+ * if((m->iomem->memc[0].base & 1) == 0){
+ * print("flash: flash not present or not enabled\n");
+ * return 0;
+ * }
+ * f = (m->bcsr[2]>>28)&0xF;
+ */
+f = 0;
+ switch(f){
+ default:
+ print("flash boot: unknown or no flash\n");
+ return 0;
+ case 4: n=8; type = "SM732x8"; break;
+ case 5: n=4; type = "SM732x8"; break;
+ case 6: n=8; type = "AMD29F0x0"; break;
+ case 7: n=4; type = "AMD29F0x0"; break;
+ case 8: n=2; type = "AMD29F0x0"; break;
+ }
+ flash.type = type;
+ flash.size = n*1024*1024;
+ flash.base = KADDR(FLASH_BASE);
+ flash.exec = flash.base + BOOTOFF;
+ flash.config = nil;
+ flash.conflen = 0;
+
+ for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){
+ if(1)
+ print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base);
+ if(ap->tag == Tconf &&
+ flashcheck(ap, &val, &len) &&
+ len >= sizeof(conftag)-1 &&
+ memcmp(val, conftag, sizeof(conftag)-1) == 0){
+ flash.config = val;
+ flash.conflen = len;
+ print("flash: found config %8.8lux(%d):\n%s\n", val, len, val);
+ }
+ }
+ if(flash.config){
+ print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config);
+ flash.config = nil; /* not that daring yet */
+ }else
+ print("flash: no config\n");
+ if(issqueezed(flash.exec) == E_MAGIC){
+ print("flash: squeezed StrongARM kernel installed\n");
+ return 1<<0;
+ }
+ if(GLLONG(flash.exec) == E_MAGIC){
+ print("flash: unsqueezed stringARM kernel installed\n");
+ return 1<<0;
+ }
+ flash.exec = 0;
+ print("flash: no StrongARM kernel in Flash\n");
+ return 0;
+}
+
+char*
+flashconfig(int)
+{
+ return flash.config;
+}
+
+int
+flashbootable(int)
+{
+ return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == E_MAGIC);
+}
+
+int
+flashboot(int)
+{
+ ulong entry, addr;
+ void (*b)(void);
+ Exec *ep;
+ Block in;
+ long n;
+ uchar *p;
+
+ if(flash.exec == 0)
+ return -1;
+ p = flash.exec;
+ if(GLLONG(p) == E_MAGIC){
+ /* unsqueezed: copy data and perhaps text, then jump to it */
+ ep = (Exec*)p;
+ entry = PADDR(GLLONG(ep->entry));
+ p += sizeof(Exec);
+ addr = entry;
+ n = GLLONG(ep->text);
+ if(addr != (ulong)p){
+ memmove((void*)addr, p, n);
+ print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+ }
+ p += n;
+ if(entry >= FLASH_BASE)
+ addr = 3*BY2PG; /* kernel text is in Flash, data in RAM */
+ else
+ addr = PGROUND(addr+n);
+ n = GLLONG(ep->data);
+ memmove((void*)addr, p, n);
+ print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+ }else{
+ in.data = p;
+ in.rp = in.data;
+ in.lim = p+BOOTLEN;
+ in.wp = in.lim;
+ n = unsqueezef(&in, &entry);
+ if(n < 0)
+ return -1;
+ }
+ print("entry=0x%lux\n", entry);
+ uartwait();
+ /* scc2stop(); */
+ /*
+ * Go to new code. It's up to the program to get its PC relocated to
+ * the right place.
+ */
+ b = (void (*)(void))KADDR(PADDR(entry));
+ (*b)();
+ return -1;
+}
diff --git a/os/boot/puma/fns.h b/os/boot/puma/fns.h
new file mode 100644
index 00000000..fd3d60d9
--- /dev/null
+++ b/os/boot/puma/fns.h
@@ -0,0 +1,111 @@
+void aamloop(int);
+Alarm* alarm(int, void (*)(Alarm*), void*);
+void alarminit(void);
+int bootp(int, char*);
+void cancel(Alarm*);
+void checkalarms(void);
+void clockinit(void);
+void consinit(void);
+void delay(int);
+uchar* etheraddr(int);
+int etherinit(void);
+int etherrxpkt(int, Etherpkt*, int);
+int ethertxpkt(int, Etherpkt*, int, int);
+int flashboot(int);
+int flashbootable(int);
+char* flashconfig(int);
+int flashinit(void);
+char* getconf(char*);
+int getcfields(char*, char**, int, char*);
+int getstr(char*, char*, int, char*);
+int hardinit(void);
+long hardread(int, void*, long);
+long hardseek(int, long);
+long hardwrite(int, void*, long);
+void* ialloc(ulong, int);
+void idle(void);
+int isaconfig(char*, int, ISAConf*);
+int isgzipped(uchar*);
+int issqueezed(uchar*);
+void kbdinit(void);
+void kbdchar(Queue*, int);
+void machinit(void);
+void meminit(void);
+void microdelay(int);
+void mmuinit(void);
+uchar nvramread(int);
+void outb(int, int);
+void outs(int, ushort);
+void outl(int, ulong);
+void outsb(int, void*, int);
+void outss(int, void*, int);
+void outsl(int, void*, int);
+void panic(char*, ...);
+int optionsw(void);
+int plan9boot(int, long (*)(int, long), long (*)(int, void*, long));
+Partition* setflashpart(int, char*);
+Partition* sethardpart(int, char*);
+Partition* setscsipart(int, char*);
+void setvec(int, void (*)(Ureg*, void*), void*);
+void screeninit(void);
+void screenputs(char*, int);
+void setr13(int, void*);
+int splhi(void);
+int spllo(void);
+void splx(int);
+void trapinit(void);
+void uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int));
+void uartputs(char*, int);
+void uartwait(void);
+long unsqueezef(Block*, ulong*);
+
+#define GSHORT(p) (((p)[1]<<8)|(p)[0])
+#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p))
+#define GLSHORT(p) (((p)[0]<<8)|(p)[1])
+#define GLLONG(p) ((GLSHORT(p)<<16)|GLSHORT(p+2))
+
+#define KADDR(a) ((void*)((ulong)(a)|KZERO))
+#define PADDR(a) ((ulong)(a)&~KZERO)
+
+
+void mapinit(RMap*, Map*, int);
+void mapfree(RMap*, ulong, int);
+ulong mapalloc(RMap*, ulong, int, int);
+
+/* IBM bit field order */
+#define IBFEXT(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1))))
+#define IBIT(b) ((ulong)1<<(31-(b)))
+
+#define SIBIT(n) ((ushort)1<<(15-(n)))
+
+void* malloc(ulong);
+void free(void*);
+
+extern Block* iallocb(int);
+extern void freeb(Block*);
+extern Queue* qopen(int, int, void (*)(void*), void*);
+extern Block* qget(Queue*);
+extern void qbwrite(Queue*, Block*);
+extern long qlen(Queue*);
+#define qpass qbwrite
+extern void qbputc(Queue*, int);
+extern int qbgetc(Queue*);
+
+int sio_inb(int);
+void sio_outb(int, int);
+void led(int);
+
+extern void _virqcall(void);
+extern void _vfiqcall(void);
+extern void _vundcall(void);
+extern void _vsvccall(void);
+extern void _vpabcall(void);
+extern void _vdabcall(void);
+
+void flushIcache(void);
+void writeBackDC(void);
+void flushDcache(void);
+void flushIcache(void);
+void drainWBuffer(void);
+
+void pumainit(void);
diff --git a/os/boot/puma/hard.c b/os/boot/puma/hard.c
new file mode 100644
index 00000000..1f1104c3
--- /dev/null
+++ b/os/boot/puma/hard.c
@@ -0,0 +1,773 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define DPRINT if(0)print
+
+typedef struct Drive Drive;
+typedef struct Ident Ident;
+typedef struct Controller Controller;
+
+enum
+{
+ /* ports */
+ Pbase0= 0x1F0, /* primary */
+ Pbase1= 0x170, /* secondary */
+ Pbase2= 0x1E8, /* tertiary */
+ Pbase3= 0x168, /* quaternary */
+ Pdata= 0, /* data port (16 bits) */
+ Perror= 1, /* error port (read) */
+ Pprecomp= 1, /* buffer mode port (write) */
+ Pcount= 2, /* sector count port */
+ Psector= 3, /* sector number port */
+ Pcyllsb= 4, /* least significant byte cylinder # */
+ Pcylmsb= 5, /* most significant byte cylinder # */
+ Pdh= 6, /* drive/head port */
+ DHmagic= 0xA0,
+ DHslave= 0x10,
+ Pstatus= 7, /* status port (read) */
+ Sbusy= (1<<7),
+ Sready= (1<<6),
+ Sdrq= (1<<3),
+ Serr= (1<<0),
+ Pcmd= 7, /* cmd port (write) */
+
+ /* commands */
+ Crecal= 0x10,
+ Cread= 0x20,
+ Cwrite= 0x30,
+ Cident= 0xEC,
+ Cident2= 0xFF, /* pseudo command for post Cident interrupt */
+ Csetbuf= 0xEF,
+
+ /* file types */
+ Qdir= 0,
+
+ Timeout= 5, /* seconds to wait for things to complete */
+
+ NCtlr= 4,
+ NDrive= NCtlr*2,
+};
+
+/*
+ * ident sector from drive. this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+ ushort config; /* general configuration info */
+ ushort cyls; /* # of cylinders (default) */
+ ushort reserved0;
+ ushort heads; /* # of heads (default) */
+ ushort b2t; /* unformatted bytes/track */
+ ushort b2s; /* unformated bytes/sector */
+ ushort s2t; /* sectors/track (default) */
+ ushort reserved1[3];
+/* 10 */
+ ushort serial[10]; /* serial number */
+ ushort type; /* buffer type */
+ ushort bsize; /* buffer size/512 */
+ ushort ecc; /* ecc bytes returned by read long */
+ ushort firm[4]; /* firmware revision */
+ ushort model[20]; /* model number */
+/* 47 */
+ ushort s2i; /* number of sectors/interrupt */
+ ushort dwtf; /* double word transfer flag */
+ ushort capabilities;
+ ushort reserved2;
+ ushort piomode;
+ ushort dmamode;
+ ushort cvalid; /* (cvald&1) if next 4 words are valid */
+ ushort ccyls; /* current # cylinders */
+ ushort cheads; /* current # heads */
+ ushort cs2t; /* current sectors/track */
+ ushort ccap[2]; /* current capacity in sectors */
+ ushort cs2i; /* current number of sectors/interrupt */
+/* 60 */
+ ushort lbasecs[2]; /* # LBA user addressable sectors */
+ ushort dmasingle;
+ ushort dmadouble;
+/* 64 */
+ ushort reserved3[64];
+ ushort vendor[32]; /* vendor specific */
+ ushort reserved4[96];
+};
+
+/*
+ * a hard drive
+ */
+struct Drive
+{
+ Controller *cp;
+ uchar driveno;
+ uchar dh;
+
+ Disc;
+};
+
+/*
+ * a controller for 2 drives
+ */
+struct Controller
+{
+ int pbase; /* base port */
+ uchar ctlrno;
+
+ /*
+ * current operation
+ */
+ int cmd; /* current command */
+ char *buf; /* xfer buffer */
+ int tcyl; /* target cylinder */
+ int thead; /* target head */
+ int tsec; /* target sector */
+ int tbyte; /* target byte */
+ int nsecs; /* length of transfer (sectors) */
+ int sofar; /* bytes transferred so far */
+ int status;
+ int error;
+ Drive *dp; /* drive being accessed */
+};
+
+static int atactlrmask;
+static Controller *atactlr[NCtlr];
+static int atadrivemask;
+static Drive *atadrive[NDrive];
+static int pbase[NCtlr] = {
+ Pbase0, Pbase1, Pbase2, Pbase3,
+};
+
+static void hardintr(Ureg*, void*);
+static long hardxfer(Drive*, Partition*, int, ulong, long);
+static int hardident(Drive*);
+static void hardsetbuf(Drive*, int);
+static void hardpart(Drive*);
+static int hardparams(Drive*);
+static void hardrecal(Drive*);
+static int hardprobe(Drive*, int, int, int);
+
+static void
+atactlrprobe(int ctlrno, int irq)
+{
+ Controller *ctlr;
+ Drive *drive;
+ int driveno, port;
+
+ if(atactlrmask & (1<<ctlrno))
+ return;
+ atactlrmask |= 1<<ctlrno;
+
+ port = pbase[ctlrno];
+ outb(port+Pdh, DHmagic);
+ delay(1);
+ if((inb(port+Pdh) & 0xFF) != DHmagic){
+ DPRINT("ata%d: DHmagic not ok\n", ctlrno);
+ return;
+ }
+ DPRINT("ata%d: DHmagic ok\n", ctlrno);
+
+ atactlr[ctlrno] = ialloc(sizeof(Controller), 0);
+ ctlr = atactlr[ctlrno];
+ ctlr->pbase = port;
+ ctlr->ctlrno = ctlrno;
+ ctlr->buf = ialloc(Maxxfer, 0);
+ inb(ctlr->pbase+Pstatus);
+ setvec(irq, hardintr, ctlr);
+
+ driveno = ctlrno*2;
+ atadrive[driveno] = ialloc(sizeof(Drive), 0);
+ drive = atadrive[driveno];
+ drive->cp = ctlr;
+ drive->driveno = driveno;
+ drive->dh = DHmagic;
+
+ driveno++;
+ atadrive[driveno] = ialloc(sizeof(Drive), 0);
+ drive = atadrive[driveno];
+ drive->cp = ctlr;
+ drive->driveno = driveno;
+ drive->dh = DHmagic|DHslave;
+}
+
+static Drive*
+atadriveprobe(int driveno)
+{
+ Drive *drive;
+ int ctlrno;
+ ISAConf isa;
+
+ ctlrno = driveno/2;
+ if(atactlr[ctlrno] == 0){
+ if(atactlrmask & (1<<ctlrno))
+ return 0;
+ memset(&isa, 0, sizeof(ISAConf));
+ if(isaconfig("ata", ctlrno, &isa) == 0)
+ return 0;
+ if(ctlrno && isa.irq)
+ atactlrprobe(ctlrno, Int0vec+isa.irq);
+ if(atactlr[ctlrno] == 0)
+ return 0;
+ }
+
+ drive = atadrive[driveno];
+ if(drive->online == 0){
+ if(atadrivemask & (1<<driveno))
+ return 0;
+ atadrivemask |= 1<<driveno;
+ if(hardparams(drive))
+ return 0;
+ if(drive->lba)
+ print("hd%d: LBA %d sectors, %ud bytes\n",
+ drive->driveno, drive->sectors, drive->cap);
+ else
+ print("hd%d: CHS %d/%d/%d %d bytes\n",
+ drive->driveno, drive->cyl, drive->heads,
+ drive->sectors, drive->cap);
+ drive->online = 1;
+ hardpart(drive);
+ hardsetbuf(drive, 1);
+ }
+
+ return drive;
+}
+
+int
+hardinit(void)
+{
+ atactlrprobe(0, ATAvec0);
+ return 0xFF;
+}
+
+long
+hardseek(int driveno, long offset)
+{
+ Drive *drive;
+
+ if((drive = atadriveprobe(driveno)) == 0)
+ return -1;
+ drive->offset = offset;
+ return offset;
+}
+
+/*
+ * did an interrupt happen?
+ */
+static void
+hardwait(Controller *cp)
+{
+ ulong start;
+ int x;
+
+ x = spllo();
+ for(start = m->ticks; TK2SEC(m->ticks - start) < Timeout && cp->cmd;)
+ if(cp->cmd == Cident2 && TK2SEC(m->ticks - start) >= 1)
+ break;
+ if(TK2SEC(m->ticks - start) >= Timeout){
+ DPRINT("hardwait timed out %ux\n", inb(cp->pbase+Pstatus));
+ hardintr(0, cp);
+ }
+ splx(x);
+}
+
+Partition*
+sethardpart(int driveno, char *p)
+{
+ Partition *pp;
+ Drive *dp;
+
+ if((dp = atadriveprobe(driveno)) == 0)
+ return 0;
+
+ for(pp = dp->p; pp < &dp->p[dp->npart]; pp++)
+ if(strcmp(pp->name, p) == 0){
+ dp->current = pp;
+ return pp;
+ }
+ return 0;
+}
+
+long
+hardread(int driveno, void *a, long n)
+{
+ Drive *dp;
+ long rv, i;
+ int skip;
+ uchar *aa = a;
+ Partition *pp;
+ Controller *cp;
+
+ if((dp = atadriveprobe(driveno)) == 0)
+ return 0;
+
+ pp = dp->current;
+ if(pp == 0)
+ return -1;
+ cp = dp->cp;
+
+ skip = dp->offset % dp->bytes;
+ for(rv = 0; rv < n; rv += i){
+ i = hardxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip);
+ if(i == 0)
+ break;
+ if(i < 0)
+ return -1;
+ i -= skip;
+ if(i > n - rv)
+ i = n - rv;
+ memmove(aa+rv, cp->buf + skip, i);
+ skip = 0;
+ }
+ dp->offset += rv;
+
+ return rv;
+}
+
+/*
+ * wait for the controller to be ready to accept a command
+ */
+static int
+cmdreadywait(Drive *drive)
+{
+ ulong end;
+ uchar dh, status;
+ Controller *ctlr;
+
+ ctlr = drive->cp;
+ end = m->ticks+MS2TK(10)+1;
+ dh = (inb(ctlr->pbase+Pdh) & DHslave)^(drive->dh & DHslave);
+
+ status = 0;
+ while(m->ticks < end){
+ status = inb(ctlr->pbase+Pstatus);
+ if(status & Sbusy)
+ continue;
+ if(dh){
+ outb(ctlr->pbase+Pdh, drive->dh);
+ dh = 0;
+ continue;
+ }
+ if(status & Sready)
+ return 0;
+ }
+ USED(status);
+
+ DPRINT("hd%d: cmdreadywait failed %uX\n", drive->driveno, status);
+ outb(ctlr->pbase+Pdh, DHmagic);
+ return -1;
+}
+
+/*
+ * transfer a number of sectors. hardintr will perform all the iterative
+ * parts.
+ */
+static long
+hardxfer(Drive *dp, Partition *pp, int cmd, ulong start, long len)
+{
+ Controller *cp;
+ long lsec;
+
+ if(dp->online == 0){
+ DPRINT("disk not on line\n");
+ return -1;
+ }
+
+ if(cmd == Cwrite)
+ return -1;
+
+ /*
+ * cut transfer size down to disk buffer size
+ */
+ start = start / dp->bytes;
+ if(len > Maxxfer)
+ len = Maxxfer;
+ len = (len + dp->bytes - 1) / dp->bytes;
+
+ /*
+ * calculate physical address
+ */
+ cp = dp->cp;
+ lsec = start + pp->start;
+ if(lsec >= pp->end){
+ DPRINT("read past end of partition\n");
+ return 0;
+ }
+ if(dp->lba){
+ cp->tsec = lsec & 0xff;
+ cp->tcyl = (lsec>>8) & 0xffff;
+ cp->thead = (lsec>>24) & 0xf;
+ } else {
+ cp->tcyl = lsec/(dp->sectors*dp->heads);
+ cp->tsec = (lsec % dp->sectors) + 1;
+ cp->thead = (lsec/dp->sectors) % dp->heads;
+ }
+
+ /*
+ * can't xfer past end of disk
+ */
+ if(lsec+len > pp->end)
+ len = pp->end - lsec;
+ cp->nsecs = len;
+
+ if(cmdreadywait(dp) < 0)
+ return -1;
+
+ /*
+ * start the transfer
+ */
+ cp->cmd = cmd;
+ cp->dp = dp;
+ cp->sofar = 0;
+ cp->status = 0;
+ DPRINT("xfer:\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead);
+ DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+ outb(cp->pbase+Pcount, cp->nsecs);
+ outb(cp->pbase+Psector, cp->tsec);
+ outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | cp->thead);
+ outb(cp->pbase+Pcyllsb, cp->tcyl);
+ outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
+ outb(cp->pbase+Pcmd, cmd);
+
+ hardwait(cp);
+
+ if(cp->status & Serr){
+ DPRINT("hd%d err: status %lux, err %lux\n",
+ dp->driveno, cp->status, cp->error);
+ DPRINT("\ttcyl %d, tsec %d, thead %d\n",
+ cp->tcyl, cp->tsec, cp->thead);
+ DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+ return -1;
+ }
+
+ return cp->nsecs*dp->bytes;
+}
+
+/*
+ * set read ahead mode (1 == on, 0 == off)
+ */
+static void
+hardsetbuf(Drive *dp, int on)
+{
+ Controller *cp = dp->cp;
+
+ if(cmdreadywait(dp) < 0)
+ return;
+
+ cp->cmd = Csetbuf;
+ /* BUG: precomp varies by hard drive...this is safari-specific? */
+ outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);
+ outb(cp->pbase+Pdh, dp->dh);
+ outb(cp->pbase+Pcmd, Csetbuf);
+
+ hardwait(cp);
+}
+
+static int
+isatapi(Drive *drive)
+{
+ Controller *cp;
+
+ cp = drive->cp;
+ outb(cp->pbase+Pdh, drive->dh);
+ microdelay(1);
+ if(inb(cp->pbase+Pstatus))
+ return 0;
+ if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14)
+ return 0;
+ return 1;
+}
+
+/*
+ * get parameters from the drive
+ */
+static int
+hardident(Drive *dp)
+{
+ Controller *cp;
+ Ident *ip;
+
+ dp->bytes = 512;
+ cp = dp->cp;
+
+ if(isatapi(dp) || cmdreadywait(dp) < 0)
+ return -1;
+
+ cp->nsecs = 1;
+ cp->sofar = 0;
+ cp->cmd = Cident;
+ cp->dp = dp;
+ outb(cp->pbase+Pdh, dp->dh);
+ outb(cp->pbase+Pcmd, Cident);
+
+ hardwait(cp);
+
+ if(cp->status & Serr)
+ return -1;
+
+ hardwait(cp);
+
+ ip = (Ident*)cp->buf;
+ DPRINT("LBA%d: %lud\n",
+ ip->capabilities & (1<<9) == 1, (ip->lbasecs[0]) | (ip->lbasecs[1]<<16));
+ DPRINT("DEF: %ud/%ud/%ud\nMAP %ud/%ud/%ud\n",
+ ip->cyls, ip->heads, ip->s2t,
+ ip->ccyls, ip->cheads, ip->cs2t);
+ if(ip->capabilities & (1<<9)){
+ dp->lba = 1;
+ dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+ dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->driveno, id, dp->sectors);/**/
+ } else {
+ dp->lba = 0;
+
+ /* use default (unformatted) settings */
+ dp->cyl = ip->cyls;
+ dp->heads = ip->heads;
+ dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->driveno,
+ id, dp->cyl, dp->heads, dp->sectors);/**/
+
+ if(ip->cvalid&(1<<0)){
+ /* use current settings */
+ dp->cyl = ip->ccyls;
+ dp->heads = ip->cheads;
+ dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+ }
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+ }
+
+ return 0;
+}
+
+/*
+ * probe the given sector to see if it exists
+ */
+static int
+hardprobe(Drive *dp, int cyl, int sec, int head)
+{
+ Controller *cp;
+
+ cp = dp->cp;
+ if(cmdreadywait(dp) < 0)
+ return -1;
+
+ /*
+ * start the transfer
+ */
+ cp->cmd = Cread;
+ cp->dp = dp;
+ cp->sofar = 0;
+ cp->nsecs = 1;
+ cp->status = 0;
+ outb(cp->pbase+Pcount, 1);
+ outb(cp->pbase+Psector, sec+1);
+ outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
+ outb(cp->pbase+Pcyllsb, cyl);
+ outb(cp->pbase+Pcylmsb, cyl>>8);
+ outb(cp->pbase+Pcmd, Cread);
+
+ hardwait(cp);
+
+ if(cp->status & Serr)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * figure out the drive parameters
+ */
+static int
+hardparams(Drive *dp)
+{
+ int i, hi, lo;
+
+ /*
+ * first try the easy way, ask the drive and make sure it
+ * isn't lying.
+ */
+ dp->bytes = 512;
+ if(hardident(dp) < 0)
+ return -1;
+ if(dp->lba){
+ i = dp->sectors - 1;
+ if(hardprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+ return 0;
+ } else {
+ if(hardprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+ return 0;
+ }
+
+ DPRINT("hardparam: cyl %d sectors %d heads %d\n", dp->cyl, dp->sectors, dp->heads);
+ /*
+ * the drive lied, determine parameters by seeing which ones
+ * work to read sectors.
+ */
+ dp->lba = 0;
+ for(i = 0; i < 16; i++)
+ if(hardprobe(dp, 0, 0, i) < 0)
+ break;
+ dp->heads = i;
+ for(i = 0; i < 64; i++)
+ if(hardprobe(dp, 0, i, 0) < 0)
+ break;
+ dp->sectors = i;
+ for(i = 512; ; i += 512)
+ if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ break;
+ lo = i - 512;
+ hi = i;
+ for(; hi-lo > 1;){
+ i = lo + (hi - lo)/2;
+ if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ hi = i;
+ else
+ lo = i;
+ }
+ dp->cyl = lo + 1;
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+
+ if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0 || dp->cap == 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * read partition table. The partition table is just ascii strings.
+ */
+#define MAGIC "plan9 partitions"
+static void
+hardpart(Drive *dp)
+{
+ Partition *pp;
+ Controller *cp;
+ char *field[3], *line[Npart+1], *p, buf[NAMELEN];
+ ulong n;
+ int i;
+
+ cp = dp->cp;
+
+ /*
+ * we always have a partition for the whole disk
+ * and one for the partition table
+ */
+ pp = &dp->p[0];
+ strcpy(pp->name, "disk");
+ pp->start = 0;
+ pp->end = dp->cap / dp->bytes;
+ pp++;
+ strcpy(pp->name, "partition");
+ pp->start = dp->p[0].end - 1;
+ pp->end = dp->p[0].end;
+ dp->npart = 2;
+
+ /*
+ * Check if the partitions are described in plan9.ini.
+ * If not, read the disc.
+ */
+ sprint(buf, "hd%dpartition", dp->driveno);
+ if((p = getconf(buf)) == 0){
+ /*
+ * read last sector from disk, null terminate. This used
+ * to be the sector we used for the partition tables.
+ * However, this sector is special on some PC's so we've
+ * started to use the second last sector as the partition
+ * table instead. To avoid reconfiguring all our old systems
+ * we first look to see if there is a valid partition
+ * table in the last sector. If so, we use it. Otherwise
+ * we switch to the second last.
+ */
+ hardxfer(dp, pp, Cread, 0, dp->bytes);
+ cp->buf[dp->bytes-1] = 0;
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ if(n == 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1)){
+ dp->p[0].end--;
+ dp->p[1].start--;
+ dp->p[1].end--;
+ hardxfer(dp, pp, Cread, 0, dp->bytes);
+ cp->buf[dp->bytes-1] = 0;
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ }
+ }
+ else{
+ strcpy(cp->buf, p);
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ }
+
+ /*
+ * parse partition table.
+ */
+ if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
+ for(i = 1; i < n; i++){
+ pp++;
+ if(getcfields(line[i], field, 3, " ") != 3)
+ break;
+ strncpy(pp->name, field[0], NAMELEN);
+ pp->start = strtoul(field[1], 0, 0);
+ pp->end = strtoul(field[2], 0, 0);
+ if(pp->start > pp->end || pp->start >= dp->p[0].end)
+ break;
+ dp->npart++;
+ }
+ }
+ return;
+}
+
+/*
+ * we get an interrupt for every sector transferred
+ */
+static void
+hardintr(Ureg*, void *arg)
+{
+ Controller *cp;
+ Drive *dp;
+ long loop;
+
+ cp = arg;
+ dp = cp->dp;
+
+ loop = 0;
+ while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy)
+ if(++loop > 100000){
+ print("hardintr 0x%lux\n", cp->status);
+ break;
+ }
+ switch(cp->cmd){
+ case Cread:
+ case Cident:
+ if(cp->status & Serr){
+ cp->cmd = 0;
+ cp->error = inb(cp->pbase+Perror);
+ return;
+ }
+ loop = 0;
+ while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
+ if(++loop > 100000){
+ print("hardintr 2 cmd %ux status %ux",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ cp->cmd = 0;
+ return;
+ }
+ inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
+ dp->bytes/2);
+ cp->sofar++;
+ if(cp->sofar >= cp->nsecs){
+ if(cp->cmd == Cident && (cp->status & Sready) == 0)
+ cp->cmd = Cident2; /* sometimes we get a second intr */
+ else
+ cp->cmd = 0;
+ inb(cp->pbase+Pstatus);
+ }
+ break;
+ case Csetbuf:
+ case Cident2:
+ cp->cmd = 0;
+ break;
+ default:
+ cp->cmd = 0;
+ break;
+ }
+}
diff --git a/os/boot/puma/io.h b/os/boot/puma/io.h
new file mode 100644
index 00000000..f4e66700
--- /dev/null
+++ b/os/boot/puma/io.h
@@ -0,0 +1,3 @@
+#define inb(port) sio_inb(port)
+#define outb(port, data) sio_outb(port, data)
+
diff --git a/os/boot/puma/ip.h b/os/boot/puma/ip.h
new file mode 100644
index 00000000..a39b5b4b
--- /dev/null
+++ b/os/boot/puma/ip.h
@@ -0,0 +1,98 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+ uchar d[6]; /* Ethernet destination */
+ uchar s[6]; /* Ethernet source */
+ uchar type[2]; /* Ethernet packet type */
+
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+
+ /* Udp pseudo ip really starts here */
+ uchar ttl;
+ uchar udpproto; /* Protocol */
+ uchar udpplen[2]; /* Header plus data length */
+ uchar udpsrc[4]; /* Ip source */
+ uchar udpdst[4]; /* Ip destination */
+ uchar udpsport[2]; /* Source port */
+ uchar udpdport[2]; /* Destination port */
+ uchar udplen[2]; /* data length */
+ uchar udpcksum[2]; /* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+ uchar d[6];
+ uchar s[6];
+ uchar type[2];
+
+ /* Now we have the ip fields */
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar cksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
+};
+
+enum
+{
+ IP_VER = 0x40,
+ IP_HLEN = 0x05,
+ UDP_EHSIZE = 22,
+ UDP_PHDRSIZE = 12,
+ UDP_HDRSIZE = 20,
+ ETHER_HDR = 14,
+ IP_UDPPROTO = 17,
+ ET_IP = 0x800,
+ Bcastip = 0xffffffff,
+ BPportsrc = 68,
+ BPportdst = 67,
+ TFTPport = 69,
+ Timeout = 5000, /* milliseconds */
+ Bootrequest = 1,
+ Bootreply = 2,
+ Tftp_READ = 1,
+ Tftp_WRITE = 2,
+ Tftp_DATA = 3,
+ Tftp_ACK = 4,
+ Tftp_ERROR = 5,
+ Segsize = 512,
+ TFTPSZ = Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+ uchar op; /* opcode */
+ uchar htype; /* hardware type */
+ uchar hlen; /* hardware address len */
+ uchar hops; /* hops */
+ uchar xid[4]; /* a random number */
+ uchar secs[2]; /* elapsed snce client started booting */
+ uchar pad[2];
+ uchar ciaddr[4]; /* client IP address (client tells server) */
+ uchar yiaddr[4]; /* client IP address (server tells client) */
+ uchar siaddr[4]; /* server IP address */
+ uchar giaddr[4]; /* gateway IP address */
+ uchar chaddr[16]; /* client hardware address */
+ char sname[64]; /* server host name (optional) */
+ char file[128]; /* boot file name */
+ char vend[128]; /* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+ ulong ip;
+ ushort port;
+ char ea[Eaddrlen];
+};
diff --git a/os/boot/puma/kbd.c b/os/boot/puma/kbd.c
new file mode 100644
index 00000000..55fab0f2
--- /dev/null
+++ b/os/boot/puma/kbd.c
@@ -0,0 +1,482 @@
+#include "boot.h"
+
+
+enum {
+ Data= 0x60, /* data port */
+
+ Status= 0x64, /* status port */
+ Inready= 0x01, /* input character ready */
+ Outbusy= 0x02, /* output busy */
+ Sysflag= 0x04, /* system flag */
+ Cmddata= 0x08, /* cmd==0, data==1 */
+ Inhibit= 0x10, /* keyboard/mouse inhibited */
+ Minready= 0x20, /* mouse character ready */
+ Rtimeout= 0x40, /* general timeout */
+ Parity= 0x80,
+
+ Cmd= 0x64, /* command port (write only) */
+
+ Spec= 0x80,
+
+ PF= Spec|0x20, /* num pad function key */
+ View= Spec|0x00, /* view (shift window up) */
+ KF= Spec|0x40, /* function key */
+ Shift= Spec|0x60,
+ Break= Spec|0x61,
+ Ctrl= Spec|0x62,
+ Latin= Spec|0x63,
+ Caps= Spec|0x64,
+ Num= Spec|0x65,
+ No= Spec|0x7F, /* no mapping */
+
+ Home= KF|13,
+ Up= KF|14,
+ Pgup= KF|15,
+ Print= KF|16,
+ Left= View,
+ Right= View,
+ End= '\r',
+ Down= View,
+ Pgdown= View,
+ Ins= KF|20,
+ Del= 0x7F,
+};
+
+uchar kbtab[] =
+{
+[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
+[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
+[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
+[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
+[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, No,
+[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home,
+[0x48] No, No, No, No, No, No, No, No,
+[0x50] No, No, No, No, No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+};
+
+uchar kbtabshift[] =
+{
+[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
+[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
+[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
+[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
+[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, No,
+[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home,
+[0x48] No, No, No, No, No, No, No, No,
+[0x50] No, No, No, No, No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+};
+
+uchar kbtabesc1[] =
+{
+[0x00] No, No, No, No, No, No, No, No,
+[0x08] No, No, No, No, No, No, No, No,
+[0x10] No, No, No, No, No, No, No, No,
+[0x18] No, No, No, No, No, Ctrl, No, No,
+[0x20] No, No, No, No, No, No, No, No,
+[0x28] No, No, No, No, No, No, No, No,
+[0x30] No, No, No, No, No, No, No, Print,
+[0x38] Latin, No, No, No, No, No, No, No,
+[0x40] No, No, No, No, No, No, Break, Home,
+[0x48] Up, Pgup, No, Down, No, Right, No, End,
+[0x50] Left, Pgdown, Ins, Del, No, No, No, No,
+[0x58] No, No, No, No, No, No, No, No,
+};
+
+struct latin
+{
+ uchar l;
+ char c[2];
+}latintab[] = {
+ L'¡', "!!", /* spanish initial ! */
+ L'¢', "c|", /* cent */
+ L'¢', "c$", /* cent */
+ L'£', "l$", /* pound sterling */
+ L'¤', "g$", /* general currency */
+ L'¥', "y$", /* yen */
+ L'¥', "j$", /* yen */
+ L'¦', "||", /* broken vertical bar */
+ L'§', "SS", /* section symbol */
+ L'¨', "\"\"", /* dieresis */
+ L'©', "cr", /* copyright */
+ L'©', "cO", /* copyright */
+ L'ª', "sa", /* super a, feminine ordinal */
+ L'«', "<<", /* left angle quotation */
+ L'¬', "no", /* not sign, hooked overbar */
+ L'­', "--", /* soft hyphen */
+ L'®', "rg", /* registered trademark */
+ L'¯', "__", /* macron */
+ L'°', "s0", /* degree (sup o) */
+ L'±', "+-", /* plus-minus */
+ L'²', "s2", /* sup 2 */
+ L'³', "s3", /* sup 3 */
+ L'´', "''", /* grave accent */
+ L'µ', "mu", /* mu */
+ L'¶', "pg", /* paragraph (pilcrow) */
+ L'·', "..", /* centered . */
+ L'¸', ",,", /* cedilla */
+ L'¹', "s1", /* sup 1 */
+ L'º', "so", /* sup o */
+ L'»', ">>", /* right angle quotation */
+ L'¼', "14", /* 1/4 */
+ L'½', "12", /* 1/2 */
+ L'¾', "34", /* 3/4 */
+ L'¿', "??", /* spanish initial ? */
+ L'À', "A`", /* A grave */
+ L'Á', "A'", /* A acute */
+ L'Â', "A^", /* A circumflex */
+ L'Ã', "A~", /* A tilde */
+ L'Ä', "A\"", /* A dieresis */
+ L'Ä', "A:", /* A dieresis */
+ L'Å', "Ao", /* A circle */
+ L'Å', "AO", /* A circle */
+ L'Æ', "Ae", /* AE ligature */
+ L'Æ', "AE", /* AE ligature */
+ L'Ç', "C,", /* C cedilla */
+ L'È', "E`", /* E grave */
+ L'É', "E'", /* E acute */
+ L'Ê', "E^", /* E circumflex */
+ L'Ë', "E\"", /* E dieresis */
+ L'Ë', "E:", /* E dieresis */
+ L'Ì', "I`", /* I grave */
+ L'Í', "I'", /* I acute */
+ L'Î', "I^", /* I circumflex */
+ L'Ï', "I\"", /* I dieresis */
+ L'Ï', "I:", /* I dieresis */
+ L'Ð', "D-", /* Eth */
+ L'Ñ', "N~", /* N tilde */
+ L'Ò', "O`", /* O grave */
+ L'Ó', "O'", /* O acute */
+ L'Ô', "O^", /* O circumflex */
+ L'Õ', "O~", /* O tilde */
+ L'Ö', "O\"", /* O dieresis */
+ L'Ö', "O:", /* O dieresis */
+ L'Ö', "OE", /* O dieresis */
+ L'Ö', "Oe", /* O dieresis */
+ L'×', "xx", /* times sign */
+ L'Ø', "O/", /* O slash */
+ L'Ù', "U`", /* U grave */
+ L'Ú', "U'", /* U acute */
+ L'Û', "U^", /* U circumflex */
+ L'Ü', "U\"", /* U dieresis */
+ L'Ü', "U:", /* U dieresis */
+ L'Ü', "UE", /* U dieresis */
+ L'Ü', "Ue", /* U dieresis */
+ L'Ý', "Y'", /* Y acute */
+ L'Þ', "P|", /* Thorn */
+ L'Þ', "Th", /* Thorn */
+ L'Þ', "TH", /* Thorn */
+ L'ß', "ss", /* sharp s */
+ L'à', "a`", /* a grave */
+ L'á', "a'", /* a acute */
+ L'â', "a^", /* a circumflex */
+ L'ã', "a~", /* a tilde */
+ L'ä', "a\"", /* a dieresis */
+ L'ä', "a:", /* a dieresis */
+ L'å', "ao", /* a circle */
+ L'æ', "ae", /* ae ligature */
+ L'ç', "c,", /* c cedilla */
+ L'è', "e`", /* e grave */
+ L'é', "e'", /* e acute */
+ L'ê', "e^", /* e circumflex */
+ L'ë', "e\"", /* e dieresis */
+ L'ë', "e:", /* e dieresis */
+ L'ì', "i`", /* i grave */
+ L'í', "i'", /* i acute */
+ L'î', "i^", /* i circumflex */
+ L'ï', "i\"", /* i dieresis */
+ L'ï', "i:", /* i dieresis */
+ L'ð', "d-", /* eth */
+ L'ñ', "n~", /* n tilde */
+ L'ò', "o`", /* o grave */
+ L'ó', "o'", /* o acute */
+ L'ô', "o^", /* o circumflex */
+ L'õ', "o~", /* o tilde */
+ L'ö', "o\"", /* o dieresis */
+ L'ö', "o:", /* o dieresis */
+ L'ö', "oe", /* o dieresis */
+ L'÷', "-:", /* divide sign */
+ L'ø', "o/", /* o slash */
+ L'ù', "u`", /* u grave */
+ L'ú', "u'", /* u acute */
+ L'û', "u^", /* u circumflex */
+ L'ü', "u\"", /* u dieresis */
+ L'ü', "u:", /* u dieresis */
+ L'ü', "ue", /* u dieresis */
+ L'ý', "y'", /* y acute */
+ L'þ', "th", /* thorn */
+ L'þ', "p|", /* thorn */
+ L'ÿ', "y\"", /* y dieresis */
+ L'ÿ', "y:", /* y dieresis */
+ 0, 0,
+};
+
+enum
+{
+ /* controller command byte */
+ Cscs1= (1<<6), /* scan code set 1 */
+ Cmousedis= (1<<5), /* mouse disable */
+ Ckbddis= (1<<4), /* kbd disable */
+ Csf= (1<<2), /* system flag */
+ Cmouseint= (1<<1), /* mouse interrupt enable */
+ Ckbdint= (1<<0), /* kbd interrupt enable */
+};
+
+static uchar ccc;
+
+int
+latin1(int k1, int k2)
+{
+ struct latin *l;
+
+ for(l=latintab; l->l; l++)
+ if(k1==l->c[0] && k2==l->c[1])
+ return l->l;
+ return 0;
+}
+
+/*
+ * wait for output no longer busy
+ */
+static int
+outready(void)
+{
+ int tries;
+
+ for(tries = 0; (inb(Status) & Outbusy); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * wait for input
+ */
+static int
+inready(void)
+{
+ int tries;
+
+ for(tries = 0; !(inb(Status) & Inready); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * ask 8042 to enable the use of address bit 20
+ */
+void
+i8042a20(void)
+{
+ outready();
+ outb(Cmd, 0xD1);
+ outready();
+ outb(Data, 0xDF);
+ outready();
+}
+
+/*
+ * ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+ ushort *s = (ushort*)(KZERO|0x472);
+ int i, x;
+
+ *s = 0x1234; /* BIOS warm-boot flag */
+
+ outready();
+ outb(Cmd, 0xFE); /* pulse reset line (means resend on AT&T machines) */
+ outready();
+
+ /*
+ * Pulse it by hand (old somewhat reliable)
+ */
+ x = 0xDF;
+ for(i = 0; i < 5; i++){
+ x ^= 1;
+ outready();
+ outb(Cmd, 0xD1);
+ outready();
+ outb(Data, x); /* toggle reset */
+ delay(100);
+ }
+}
+
+/*
+ * keyboard interrupt
+ */
+int
+kbdintr0(void)
+{
+ int s, c;
+ static int esc1, esc2;
+ static int shift;
+ static int caps;
+ static int ctl;
+ static int num;
+ static int lstate, k1, k2;
+ int keyup;
+
+ /*
+ * get status
+ */
+ s = inb(Status);
+ if(!(s&Inready))
+ return -1;
+
+ /*
+ * get the character
+ */
+ c = inb(Data);
+
+ /*
+ * e0's is the first of a 2 character sequence
+ */
+ if(c == 0xe0){
+ esc1 = 1;
+ return 0;
+ } else if(c == 0xe1){
+ esc2 = 2;
+ return 0;
+ }
+
+ keyup = c&0x80;
+ c &= 0x7f;
+ if(c > sizeof kbtab){
+ print("unknown key %ux\n", c|keyup);
+ kbdchar(0, k1);
+ return 0;
+ }
+
+ if(esc1){
+ c = kbtabesc1[c];
+ esc1 = 0;
+ kbdchar(0, c);
+ return 0;
+ } else if(esc2){
+ esc2--;
+ return 0;
+ } else if(shift)
+ c = kbtabshift[c];
+ else
+ c = kbtab[c];
+
+ if(caps && c<='z' && c>='a')
+ c += 'A' - 'a';
+
+ /*
+ * keyup only important for shifts
+ */
+ if(keyup){
+ switch(c){
+ case Shift:
+ shift = 0;
+ break;
+ case Ctrl:
+ ctl = 0;
+ break;
+ }
+ return 0;
+ }
+
+ /*
+ * normal character
+ */
+ if(!(c & Spec)){
+ if(ctl)
+ c &= 0x1f;
+ switch(lstate){
+ case 1:
+ k1 = c;
+ lstate = 2;
+ return 0;
+ case 2:
+ k2 = c;
+ lstate = 0;
+ c = latin1(k1, k2);
+ if(c == 0){
+ kbdchar(0, k1);
+ c = k2;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ } else {
+ switch(c){
+ case Caps:
+ caps ^= 1;
+ return 0;
+ case Num:
+ num ^= 1;
+ return 0;
+ case Shift:
+ shift = 1;
+ return 0;
+ case Latin:
+ lstate = 1;
+ return 0;
+ case Ctrl:
+ ctl = 1;
+ return 0;
+ }
+ }
+ kbdchar(0, c);
+ return 0;
+}
+
+static void
+kbdintr(Ureg*, void*)
+{
+ while(kbdintr0() == 0)
+ ;
+}
+
+static char *initfailed = "kbd init failed\n";
+
+void
+kbdinit(void)
+{
+ int c;
+
+ /* wait for a quiescent controller */
+ while((c = inb(Status)) & (Outbusy | Inready))
+ if(c & Inready)
+ inb(Data);
+
+ /* get current controller command byte */
+ outb(Cmd, 0x20);
+ if(inready() < 0){
+ print("kbdinit: can't read ccc\n");
+ ccc = 0;
+ } else
+ ccc = inb(Data);
+
+ /* enable kbd xfers and interrupts */
+ ccc &= ~Ckbddis;
+ ccc |= Csf | Ckbdint | Cscs1;
+ if(outready() < 0)
+ print(initfailed);
+ outb(Cmd, 0x60);
+ if(outready() < 0)
+ print(initfailed);
+ outb(Data, ccc);
+ if(outready() < 0)
+ print(initfailed);
+
+ setvec(V_KEYBOARD, kbdintr, 0);
+}
diff --git a/os/boot/puma/l.s b/os/boot/puma/l.s
new file mode 100644
index 00000000..a4679fc2
--- /dev/null
+++ b/os/boot/puma/l.s
@@ -0,0 +1,427 @@
+/*
+ * File: l.s
+ * Purpose:
+ * Puma Board StrongARM 110 Architecture Specific Assembly
+ *
+ */
+
+#include "mem.h"
+#include "armv4.h"
+#include "puma.h"
+
+#define DRAMWAIT 100000 /* 3.125μsec per iteration */
+#define TL750R(r) (TL750_BASE+(r)*4)
+
+#define BOOTBASE 0x00200000
+
+TEXT _main(SB),1,$-4
+ MOVW R15, R7 /* save PC on entry */
+
+/*
+ * initialise DRAM controller on the TL750 (SDRAM mode)
+ */
+ MOVW $DRAMWAIT, R0 /* wait 312 ms after reset before touching DRAM */
+dram1:
+ SUB.S $1, R0
+ BNE dram1
+
+ MOVW $TL750R(0x103), R0 /* DMC_DELAY */
+ MOVW $0x03333333, R1 /* DRAM timing parameters */
+ MOVW R1, (R0)
+
+ MOVW $TL750R(0x101), R0 /* DMC_SDRAM */
+ MOVW $0x03133011, R1 /* SDRAM parameters for Puma */
+ MOVW R1, (R0)
+
+ MOVW $DRAMWAIT, R0 /* wait 312 ms for initialisation */
+dram2:
+ SUB.S $1, R0
+ BNE dram2
+
+ MOVW $setR12(SB),R12
+
+/*
+ * copy bootstrap to final location in DRAM
+ */
+ MOVW R7, baddr(SB)
+ MOVW $(BOOTBASE+8), R0
+ CMP R0, R7
+ BEQ inplace
+ MOVW $((128*1024)/4), R6
+copyboot:
+ MOVW.P 4(R7), R5
+ MOVW.P R5, 4(R0)
+ SUB.S $1, R6
+ BNE copyboot
+ MOVW $bootrel(SB), R7
+ MOVW R7, R15
+
+TEXT bootrel(SB), $-4
+
+/*
+ * set C environment and invoke main
+ */
+inplace:
+ MOVW $mach0(SB),R13
+ MOVW R13,m(SB)
+ ADD $(MACHSIZE-12),R13
+
+ /* disable MMU activity */
+ BL mmuctlregr(SB)
+ BIC $(CpCmmu|CpCDcache|CpCwb|CpCIcache), R0
+ BL mmuctlregw(SB)
+
+ BL main(SB)
+loop:
+ B loop
+
+TEXT idle(SB),$0
+ RET
+
+/*
+ * basic timing loop to determine CPU frequency
+ */
+TEXT aamloop(SB), $-4 /* 3 */
+_aamloop:
+ MOVW R0, R0 /* 1 */
+ MOVW R0, R0 /* 1 */
+ MOVW R0, R0 /* 1 */
+ SUB $1, R0 /* 1 */
+ CMP $0, R0 /* 1 */
+ BNE _aamloop /* 3 */
+ RET /* 3 */
+
+/*
+ * Function: setr13( mode, pointer )
+ * Purpose:
+ * Sets the stack pointer for a particular mode
+ */
+
+TEXT setr13(SB), $-4
+ MOVW 4(FP), R1
+
+ MOVW CPSR, R2
+ BIC $PsrMask, R2, R3
+ ORR R0, R3
+ MOVW R3, CPSR
+
+ MOVW R13, R0
+ MOVW R1, R13
+
+ MOVW R2, CPSR
+
+ RET
+
+/*
+ * Function: _vundcall
+ * Purpose:
+ * Undefined Instruction Trap Handler
+ *
+ */
+
+TEXT _vundcall(SB), $-4
+_vund:
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMund, R0
+ B _vswitch
+
+/*
+ * Function: _vsvccall
+ * Purpose:
+ * Reset or SWI Handler
+ *
+ */
+
+TEXT _vsvccall(SB), $-4
+_vsvc:
+ SUB $12, R13
+ MOVW R14, 8(R13)
+ MOVW CPSR, R14
+ MOVW R14, 4(R13)
+ MOVW $PsrMsvc, R14
+ MOVW R14, (R13)
+ B _vsaveu
+
+/*
+ * Function: _pabcall
+ * Purpose:
+ * Prefetch Abort Trap Handler
+ *
+ */
+
+TEXT _vpabcall(SB), $-4
+_vpab:
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMabt, R0
+ B _vswitch
+
+/*
+ * Function: _vdabcall
+ * Purpose:
+ * Data Abort Trap Handler
+ *
+ */
+
+TEXT _vdabcall(SB), $-4
+_vdab:
+ MOVM.DB [R0-R3], (R13)
+ MOVW $(PsrMabt+1), R0
+ B _vswitch
+
+/*
+ * Function: _virqcall
+ * Purpose:
+ * IRQ Trap Handler
+ *
+ */
+
+TEXT _virqcall(SB), $-4 /* IRQ */
+_virq:
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMirq, R0
+ B _vswitch
+
+/*
+ * Function: _vfiqcall
+ * Purpose:
+ * FIQ Trap Handler
+ *
+ */
+
+TEXT _vfiqcall(SB), $-4 /* FIQ */
+_vfiq:
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMfiq, R0
+ /* FALLTHROUGH */
+
+_vswitch: /* switch to svc mode */
+ MOVW SPSR, R1
+ MOVW R14, R2
+ MOVW R13, R3
+
+ MOVW CPSR, R14
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+
+ MOVM.DB.W [R0-R2], (R13)
+ MOVM.DB (R3), [R0-R3]
+
+_vsaveu: /* Save Registers */
+ SUB $4, R13 /* save link */
+ MOVW R14, (R13) /* MOVW.W R14,4(R13)*/
+
+ SUB $8, R13
+
+ MOVW R13, R14 /* ur->sp */
+ ADD $(6*4), R14
+ MOVW R14, 0(R13)
+
+ MOVW 8(SP), R14 /* ur->link */
+ MOVW R14, 4(SP)
+
+ MOVM.DB.W [R0-R12], (R13)
+ MOVW R0, R0 /* gratuitous noop */
+
+ MOVW $setR12(SB), R12 /* static base (SB) */
+ MOVW R13, R0 /* argument is ureg */
+ SUB $8, R13 /* space for arg+lnk*/
+ BL trap(SB)
+
+
+_vrfe: /* Restore Regs */
+ MOVW CPSR, R0 /* splhi on return */
+ ORR $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ ADD $(8+4*15), R13 /* [r0-R14]+argument+link */
+ MOVW (R13), R14 /* restore link */
+ MOVW 8(R13), R0
+ MOVW R0, SPSR
+ MOVM.DB.S (R13), [R0-R14] /* restore user registers */
+ MOVW R0, R0 /* gratuitous nop */
+ ADD $12, R13 /* skip saved link+type+SPSR*/
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+
+/*
+ * Function: splhi
+ * Purpose:
+ * Disable Interrupts
+ * Returns:
+ * Previous interrupt state
+ */
+
+TEXT splhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+/*
+ * Function: spllo
+ * Purpose:
+ * Enable Interrupts
+ * Returns:
+ * Previous interrupt state
+ */
+
+TEXT spllo(SB), $-4
+ MOVW CPSR, R0
+ BIC $(PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+/*
+ * Function: splx(level)
+ * Purpose:
+ * Restore interrupt level
+ */
+
+TEXT splx(SB), $-4
+ MOVW R0, R1
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+/*
+ * Function: islo
+ * Purpose:
+ * Check if interrupts are enabled
+ *
+ */
+
+TEXT islo(SB), $-4
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+/*
+ * Function: cpsrr
+ * Purpose:
+ * Returns current program status register
+ *
+ */
+
+TEXT cpsrr(SB), $-4
+ MOVW CPSR, R0
+ RET
+
+/*
+ * Function: spsrr
+ * Purpose:
+ * Returns saved program status register
+ *
+ */
+
+TEXT spsrr(SB), $-4
+ MOVW SPSR, R0
+ RET
+
+/*
+ * MMU Operations
+ */
+TEXT mmuctlregr(SB), $-4
+ MRC CpMMU, 0, R0, C(CpControl), C(0)
+ RET
+
+TEXT mmuctlregw(SB), $-4
+ MCR CpMMU, 0, R0, C(CpControl), C(0)
+ MOVW R0, R0
+ MOVW R0, R0
+ RET
+
+/*
+ * Cache Routines
+ */
+
+/*
+ * Function: flushIcache
+ * Purpose:
+ * Flushes the *WHOLE* instruction cache
+ */
+
+TEXT flushIcache(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+
+
+/*
+ * Function: flushDentry
+ * Purpose:
+ * Flushes an entry of the data cache
+ */
+
+TEXT flushDentry(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 1
+ RET
+
+/*
+ * Function: drainWBuffer
+ * Purpose:
+ * Drains the Write Buffer
+ */
+
+TEXT drainWBuffer(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4
+ RET
+
+/*
+ * Function: writeBackDC
+ * Purpose:
+ * Drains the dcache prior to flush
+ */
+
+TEXT writeBackDC(SB), $-4
+ MOVW $0xE0000000, R0
+ MOVW $8192, R1
+ ADD R0, R1
+
+wbflush:
+ MOVW (R0), R2
+ ADD $32, R0
+ CMP R1,R0
+ BNE wbflush
+ RET
+
+/*
+ * Function: flushDcache(SB)
+ * Purpose:
+ * Flush the dcache
+ */
+
+TEXT flushDcache(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+ RET
+
+/*
+ * Function: writeBackBDC(SB)
+ * Purpose:
+ * Write back the Baby D-Cache
+ */
+
+TEXT writeBackBDC(SB), $-4
+ MOVW $0xE4000000, R0
+ MOVW $0x200, R1
+ ADD R0, R1
+
+wbbflush:
+ MOVW (R0), R2
+ ADD $32, R0
+ CMP R1,R0
+ BNE wbbflush
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+GLOBL mach0+0(SB), $MACHSIZE
+GLOBL m(SB), $4
+GLOBL baddr(SB), $4
diff --git a/os/boot/puma/lib.h b/os/boot/puma/lib.h
new file mode 100644
index 00000000..0853aa88
--- /dev/null
+++ b/os/boot/puma/lib.h
@@ -0,0 +1,107 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern void* memccpy(void*, void*, int, long);
+extern void* memset(void*, int, long);
+extern int memcmp(void*, void*, long);
+extern void* memmove(void*, void*, long);
+extern void* memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern char* strcat(char*, char*);
+extern char* strchr(char*, char);
+extern int strcmp(char*, char*);
+extern char* strcpy(char*, char*);
+extern char* strncat(char*, char*, long);
+extern char* strncpy(char*, char*, long);
+extern int strncmp(char*, char*, long);
+extern long strlen(char*);
+extern char* strrchr(char*, char);
+extern char* strstr(char*, char*);
+
+/*
+ * print routines
+ * Fconv isn't used but is defined to satisfy prototypes in libg.h
+ * that are never called.
+ */
+typedef struct Fconv Fconv;
+
+extern char* donprint(char*, char*, char*, void*);
+extern int sprint(char*, char*, ...);
+extern int print(char*, ...);
+
+#define PRINTSIZE 256
+
+/*
+ * one-of-a-kind
+ */
+extern int atoi(char*);
+extern long strtol(char*, char**, int);
+extern ulong strtoul(char*, char**, int);
+extern char end[];
+extern char edata[];
+
+/*
+ * Syscall data structures
+ */
+
+#define MORDER 0x0003 /* mask for bits defining order of mounting */
+#define MREPL 0x0000 /* mount replaces object */
+#define MBEFORE 0x0001 /* mount goes before others in union directory */
+#define MAFTER 0x0002 /* mount goes after others in union directory */
+#define MCREATE 0x0004 /* permit creation in mounted directory */
+#define MMASK 0x0007 /* all bits on */
+
+#define OREAD 0 /* open for read */
+#define OWRITE 1 /* write */
+#define ORDWR 2 /* read and write */
+#define OEXEC 3 /* execute, == read but check execute permission */
+#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
+#define OCEXEC 32 /* or'ed in, close on exec */
+#define ORCLOSE 64 /* or'ed in, remove on close */
+
+#define NCONT 0 /* continue after note */
+#define NDFLT 1 /* terminate after note */
+
+typedef struct Qid Qid;
+typedef struct Dir Dir;
+typedef struct Waitmsg Waitmsg;
+
+#define ERRLEN 64
+#define DIRLEN 116
+#define NAMELEN 28
+
+struct Qid
+{
+ ulong path;
+ ulong vers;
+};
+
+struct Dir
+{
+ char name[NAMELEN];
+ char uid[NAMELEN];
+ char gid[NAMELEN];
+ Qid qid;
+ ulong mode;
+ long atime;
+ long mtime;
+ ulong length;
+ short type;
+ short dev;
+};
+
+struct Waitmsg
+{
+ int pid; /* of loved one */
+ int status; /* unused; a placeholder */
+ ulong time[3]; /* of loved one */
+ char msg[ERRLEN];
+};
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
diff --git a/os/boot/puma/main.c b/os/boot/puma/main.c
new file mode 100644
index 00000000..51df6d9c
--- /dev/null
+++ b/os/boot/puma/main.c
@@ -0,0 +1,552 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "dosfs.h"
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Mode Mode;
+
+enum {
+ Dany = -1,
+ Nmedia = 16,
+
+ /* DS1 switch options */
+ Sflashfs = 1<<0, /* take local fs from flash */
+ Snotflash = 1<<1, /* don't boot from flash */
+};
+
+enum { /* type */
+ Tflash,
+ Tether,
+ Thard,
+
+ Tany = -1,
+};
+
+enum { /* flag and name */
+ Fnone = 0x00,
+
+ Fdos = 0x01,
+ Ndos = 0x00,
+ Fboot = 0x02,
+ Nboot = 0x01,
+ Fbootp = 0x04,
+ Nbootp = 0x02,
+ Fflash = 0x08,
+ NName = 0x03,
+
+ Fany = Fbootp|Fboot|Fdos|Fflash,
+
+ Fini = 0x10,
+ Fprobe = 0x80,
+};
+
+enum { /* mode */
+ Mauto = 0x00,
+ Mlocal = 0x01,
+ Manual = 0x02,
+ NMode = 0x03,
+};
+
+typedef struct Type {
+ int type;
+ char *cname;
+ int flag;
+ int (*init)(void);
+ long (*read)(int, void*, long);
+ long (*seek)(int, long);
+ Partition* (*setpart)(int, char*);
+ char* name[NName];
+
+ int mask;
+ Medium* media;
+} Type;
+
+typedef struct Medium {
+ Type* type;
+ int flag;
+ Partition* partition;
+ Dos;
+
+ Medium* next;
+} Medium;
+
+typedef struct Mode {
+ char* name;
+ int mode;
+} Mode;
+
+static Type types[] = {
+ { Tflash, "flash",
+ Fflash,
+ flashinit, 0, 0, 0,
+ { 0, "F", 0, }
+ },
+ { Tether, "ether",
+ Fbootp,
+ etherinit, 0, 0, 0,
+ { 0, 0, "e", },
+ },
+ { Thard, "ata",
+ Fini|Fboot|Fdos,
+ 0, 0, 0, 0, /* not used now, will be later with PCMCIA */
+ { "hd", "h", 0, },
+ },
+ {-1},
+};
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+ [Mauto] { "auto", Mauto, },
+ [Mlocal] { "local", Mlocal, },
+ [Manual] { "manual", Manual, },
+};
+
+static char *inis[] = {
+ "inferno/inferno.ini",
+ "inferno.ini",
+ "plan9/plan9.ini",
+ "plan9.ini",
+ 0,
+};
+char **ini;
+void printversion(void);
+
+static int
+parse(char *line, int *type, int *flag, int *dev, char *file)
+{
+ Type *tp;
+ char buf[2*NAMELEN], *v[4], *p;
+ int i;
+
+ strcpy(buf, line);
+ switch(getcfields(buf, v, 4, "!")){
+
+ case 3:
+ break;
+
+ case 2:
+ v[2] = "";
+ break;
+
+ default:
+ return 0;
+ }
+
+ *flag = 0;
+ for(tp = types; tp->cname; tp++){
+ for(i = 0; i < NName; i++){
+
+ if(tp->name[i] == 0 || strcmp(v[0], tp->name[i]))
+ continue;
+ *type = tp->type;
+ *flag |= 1<<i;
+
+ if((*dev = strtoul(v[1], &p, 0)) == 0 && p == v[1])
+ return 0;
+
+ strcpy(file, v[2]);
+
+ return 1;
+ }
+ }
+
+ return 0;
+
+}
+
+static int
+boot(Medium *mp, int flag, char *file)
+{
+ Dosfile df;
+ char ixdos[128], *p;
+
+ if(flag & Fbootp){
+ sprint(BOOTLINE, "%s!%d", mp->type->name[Nbootp], mp->dev);
+ return bootp(mp->dev, file);
+ }
+
+ if(flag & Fflash){
+ if(mp->flag & Fflash && flashbootable(0))
+ flashboot(mp->dev);
+ }
+
+ if(flag & Fboot){
+
+ if(mp->flag & Fini){
+ (*mp->type->setpart)(mp->dev, "disk");
+ plan9ini(mp, nil);
+ }
+ if(file == 0 || *file == 0)
+ file = mp->partition->name;
+ (*mp->type->setpart)(mp->dev, file);
+ sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file);
+ return plan9boot(mp->dev, mp->seek, mp->read);
+ }
+
+ if(flag & Fdos){
+ if(mp->type->setpart)
+ (*mp->type->setpart)(mp->dev, "disk");
+ if(mp->flag & Fini)
+ plan9ini(mp, nil);
+ if(file == 0 || *file == 0){
+ strcpy(ixdos, *ini);
+ if(p = strrchr(ixdos, '/'))
+ p++;
+ else
+ p = ixdos;
+ strcpy(p, "infernopuma");
+ if(dosstat(mp, ixdos, &df) <= 0)
+ return -1;
+ }
+ else
+ strcpy(ixdos, file);
+ sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos);
+ return dosboot(mp, ixdos);
+ }
+
+ return -1;
+}
+
+static Medium*
+allocm(Type *tp)
+{
+ Medium **l;
+
+ if(curmedium >= &media[Nmedia])
+ return 0;
+
+ for(l = &tp->media; *l; l = &(*l)->next)
+ ;
+ *l = curmedium++;
+ return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+ Type *tp;
+ int dombr, i, start;
+ Medium *mp;
+ Dosfile df;
+ Partition *pp;
+
+ for(tp = types; tp->cname; tp++){
+ if(type != Tany && type != tp->type || tp->init == 0)
+ continue;
+
+ if(flag != Fnone){
+ for(mp = tp->media; mp; mp = mp->next){
+ if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+ return mp;
+ }
+ }
+ if((tp->flag & Fprobe) == 0){
+ tp->flag |= Fprobe;
+ tp->mask = (*tp->init)();
+ }
+
+ for(i = 0; tp->mask; i++){
+ if((tp->mask & (1<<i)) == 0)
+ continue;
+ tp->mask &= ~(1<<i);
+
+ if((mp = allocm(tp)) == 0)
+ continue;
+
+ mp->dev = i;
+ mp->flag = tp->flag;
+ mp->seek = tp->seek;
+ mp->read = tp->read;
+ mp->type = tp;
+
+ if(mp->flag & Fboot){
+ if((mp->partition = (*tp->setpart)(i, "boot")) == 0)
+ mp->flag &= ~Fboot;
+ if((mp->flag & Fflash) == 0)
+ (*tp->setpart)(i, "disk");
+ }
+
+ if(mp->flag & Fdos){
+ start = 0;
+ dombr = 1;
+ if(mp->type->setpart){
+ if(pp = (*mp->type->setpart)(i, "dos")){
+ if(start = pp->start)
+ dombr = 0;
+ }
+ (*tp->setpart)(i, "disk");
+ }
+ if(dosinit(mp, start, dombr) < 0)
+ mp->flag &= ~(Fini|Fdos);
+ else
+ print("dos init failed\n");
+ }
+
+ if(mp->flag & Fini){
+ mp->flag &= ~Fini;
+ for(ini = inis; *ini; ini++){
+ if(dosstat(mp, *ini, &df) <= 0)
+ continue;
+ mp->flag |= Fini;
+ break;
+ }
+ }
+
+ if((flag & mp->flag) && (dev == Dany || dev == i))
+ return mp;
+ }
+ }
+
+ return 0;
+}
+
+static void
+pause(void)
+{
+ long d;
+ for(d=0; d<10000000; d++)
+ ;
+ USED(d);
+}
+
+static void
+flash(int n)
+{
+ int i;
+
+ if(n <= 0)
+ return;
+ for(i=0; i<n-1; i++) {
+ led(1);
+ pause();
+ led(0);
+ pause();
+ }
+ led(1);
+ pause();pause();pause();
+ led(0);
+ pause();pause();pause();
+}
+
+void
+main(void)
+{
+ Medium *mp;
+ int dev, flag, i, mode, tried, type, options;
+ char def[2*NAMELEN], file[2*NAMELEN], line[80], *p;
+ Type *tp;
+
+ memset(edata, 0, end-edata ); /* clear the BSS */
+ pumainit();
+
+ machinit();
+ meminit();
+ trapinit();
+ consinit(); /* screen and keyboard initially */
+// screeninit();
+ alarminit();
+ clockinit();
+ printversion();
+
+ spllo();
+ options = optionsw();
+
+
+ mp = 0;
+ for(tp = types; tp->cname; tp++){
+ if(tp->type == Tether)
+ continue;
+ if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+ plan9ini(mp, nil);
+ break;
+ }
+ }
+
+ if(mp == 0 || (mp->flag & Fini) == 0)
+ plan9ini(nil, flashconfig(0));
+
+ //consinit(); /* establish new console location */
+
+ if((options & Snotflash) == 0 && flashbootable(0)){
+ print("Flash boot\n");
+ flashboot(0);
+ }
+
+ tried = 0;
+ mode = Mauto;
+ p = getconf("bootfile");
+ flag = 0;
+
+ if(p != 0) {
+ mode = Manual;
+ for(i = 0; i < NMode; i++){
+ if(strcmp(p, modes[i].name) == 0){
+ mode = modes[i].mode;
+ goto done;
+ }
+ }
+ if(parse(p, &type, &flag, &dev, file) == 0) {
+ print("Bad bootfile syntax: %s\n", p);
+ goto done;
+ }
+ mp = probe(type, flag, dev);
+ if(mp == 0) {
+ print("Cannot access device: %s\n", p);
+ goto done;
+ }
+ tried = boot(mp, flag, file);
+ }
+done:
+ if(tried == 0 && mode != Manual){
+ flag = Fany;
+ if(mode == Mlocal)
+ flag &= ~Fbootp;
+ if(options & Snotflash)
+ flag &= ~Fflash;
+ if((mp = probe(Tany, flag, Dany)) != 0)
+ boot(mp, flag & mp->flag, 0);
+ }
+
+ def[0] = 0;
+ probe(Tany, Fnone, Dany);
+
+ flag = 0;
+ for(tp = types; tp->cname; tp++){
+ for(mp = tp->media; mp; mp = mp->next){
+ if(flag == 0){
+ flag = 1;
+ print("Boot devices:");
+ }
+
+ if(mp->flag & Fbootp)
+ print(" %s!%d", mp->type->name[Nbootp], mp->dev);
+ if(mp->flag & Fdos)
+ print(" %s!%d", mp->type->name[Ndos], mp->dev);
+ if(mp->flag & Fflash || mp->flag & Fboot)
+ print(" %s!%d", mp->type->name[Nboot], mp->dev);
+ }
+ }
+ if(flag)
+ print("\n");
+
+ for(;;){
+ if(getstr("boot from", line, sizeof(line), def) >= 0){
+ if(parse(line, &type, &flag, &dev, file)){
+ if(mp = probe(type, flag, dev))
+ boot(mp, flag, file);
+ }
+ }
+ def[0] = 0;
+ }
+}
+
+void
+machinit(void)
+{
+ memset(m, 0, sizeof(*m));
+ m->delayloop = 20000;
+}
+
+void
+printversion(void)
+{
+ print("StrongARM SA-110 ");
+ print("%d MHz system\n", m->speed);
+ print("\n");
+{extern long baddr; print("%8.8lux\n", baddr);}
+}
+
+int
+optionsw()
+{
+ return 0;
+}
+
+int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+ int i;
+
+ for(i = 0; lp && *lp && i < n; i++){
+ while(*lp && strchr(sep, *lp) != 0)
+ *lp++ = 0;
+ if(*lp == 0)
+ break;
+ fields[i] = lp;
+ while(*lp && strchr(sep, *lp) == 0){
+ if(*lp == '\\' && *(lp+1) == '\n')
+ *lp++ = ' ';
+ lp++;
+ }
+ }
+
+ return i;
+}
+
+static Map memv[512];
+static RMap rammap = {"physical memory"};
+
+void
+meminit(void)
+{
+ ulong e;
+
+ mapinit(&rammap, memv, sizeof(memv));
+ e = PADDR(end);
+ mapfree(&rammap, e, 4*1024*1024-e);
+}
+
+void*
+ialloc(ulong n, int align)
+{
+ ulong a;
+ int s;
+
+ if(align <= 0)
+ align = 4;
+ s = splhi();
+ a = mapalloc(&rammap, 0, n, align);
+ splx(s);
+ if(a == 0)
+ panic("ialloc");
+ return memset(KADDR(a), 0, n);
+}
+
+void*
+malloc(ulong n)
+{
+ ulong *p;
+
+ n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int);
+ p = ialloc(n, sizeof(int));
+ *p++ = 0xcafebeef;
+ *p++ = n;
+ return p;
+}
+
+void
+free(ulong *p)
+{
+ int s;
+
+ if(p){
+ if(*(p -= 2) != 0xcafebeef)
+ panic("free");
+ s = splhi();
+ mapfree(&rammap, (ulong)p, p[1]);
+ splx(s);
+ }
+}
+
+void
+sched(void)
+{
+}
diff --git a/os/boot/puma/mem.h b/os/boot/puma/mem.h
new file mode 100644
index 00000000..35bca0a6
--- /dev/null
+++ b/os/boot/puma/mem.h
@@ -0,0 +1,38 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define MAXMACH 1 /* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define HZ (20) /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+#define TK2MS(t) ((((ulong)(t))*1000)/HZ) /* ticks to milliseconds */
+#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+
+/*
+ * Address spaces
+ *
+ * Kernel is at 0x00008000
+ */
+#define KZERO 0x00000000 /* base of kernel address space */
+#define KTZERO KZERO /* first address in kernel text */
+#define MACHSIZE 4096
diff --git a/os/boot/puma/mkfile b/os/boot/puma/mkfile
new file mode 100644
index 00000000..383b5442
--- /dev/null
+++ b/os/boot/puma/mkfile
@@ -0,0 +1,71 @@
+<../../../mkconfig
+
+SYSTARG=Inferno
+OBJTYPE=arm
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+TARGET=${O}boot
+OBJ=\
+ l.$O\
+ div.$O\
+ 8250.$O\
+ alarm.$O\
+ bootp.$O\
+ clock.$O\
+ console.$O\
+ conf.$O\
+ dosboot.$O\
+ donprint.$O\
+ ether.$O\
+ ether8900.$O\
+ flash.$O\
+ kbd.$O\
+ main.$O\
+ plan9boot.$O\
+ puma.$O\
+ qio.$O\
+ rmap.$O\
+ trap.$O\
+ zqs.$O
+
+HFILES=\
+ lib.h\
+ mem.h\
+ dat.h\
+ fns.h\
+ io.h\
+ boot.h\
+ armv4.h\
+ puma.h\
+
+CFLAGS=-w -I.
+LFLAGS=-H1 -R4 -T0x00200000 -E_main #-a
+#LFLAGS=-H1 -R4 -T0x00008080 -E_main #-a
+#LFLAGS = -H1 -R4 -T0xa00000c0 -E_main #-a
+
+all:V: $TARGET
+
+$TARGET: $OBJ
+ $LD -o $target $LFLAGS -l $prereq -lc
+ ls -l $target
+
+installall:V: install
+install:V: bb $TARGET
+ cp $TARGET /arm
+
+%.$O: %.s
+ $AS $stem.s
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+%.$O: $HFILES
+
+clock.$O floppy.$O trap.$O: ureg.h
+conf.$O dosboot.$O main.$O: dosfs.h
+ether.$O ether2000.$O ether509.$O ether8003.$O ether8390.$O: ether.h
+bootp.$O: ip.h
+
+clean:
+ rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARGET
+
diff --git a/os/boot/puma/outb.c b/os/boot/puma/outb.c
new file mode 100644
index 00000000..e66c994e
--- /dev/null
+++ b/os/boot/puma/outb.c
@@ -0,0 +1,20 @@
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+
+enum {
+ IsaIOBase = 0xf0000000,
+ IsaMemBase = 0xe0000000,
+
+ IOBase = 0x300,
+ MemBase = 0xc0000,
+
+ TxFrame = 0x0a00,
+};
+
+#define regw(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+
+void
+main(void)
+{
+ regw(TxFrame, 0x1234);
+}
diff --git a/os/boot/puma/plan9boot.c b/os/boot/puma/plan9boot.c
new file mode 100644
index 00000000..5151e1b7
--- /dev/null
+++ b/os/boot/puma/plan9boot.c
@@ -0,0 +1,93 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+char *premature = "premature EOF\n";
+
+/*
+ * read in a segment
+ */
+static long
+readseg(int dev, long (*read)(int, void*, long), long len, long addr)
+{
+ char *a;
+ long n, sofar;
+
+ a = (char *)addr;
+ for(sofar = 0; sofar < len; sofar += n){
+ n = 8*1024;
+ if(len - sofar < n)
+ n = len - sofar;
+ n = (*read)(dev, a + sofar, n);
+ if(n <= 0)
+ break;
+ print(".");
+ }
+ return sofar;
+}
+
+/*
+ * boot
+ */
+int
+plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long))
+{
+ long n;
+ long addr;
+ void (*b)(void);
+ Exec *ep;
+
+ if((*seek)(dev, 0) < 0)
+ return -1;
+
+ /*
+ * read header
+ */
+ ep = (Exec *) ialloc(sizeof(Exec), 0);
+ n = sizeof(Exec);
+ if(readseg(dev, read, n, (ulong) ep) != n){
+ print(premature);
+ return -1;
+ }
+ if(GLLONG(ep->magic) != E_MAGIC){
+ print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic));
+ return -1;
+ }
+
+ /*
+ * read text
+ */
+ addr = PADDR(GLLONG(ep->entry));
+ n = GLLONG(ep->text);
+ print("%d", n);
+ if(readseg(dev, read, n, addr) != n){
+ print(premature);
+ return -1;
+ }
+
+ /*
+ * read data (starts at first page after kernel)
+ */
+ addr = PGROUND(addr+n);
+ n = GLLONG(ep->data);
+ print("+%d", n);
+ if(readseg(dev, read, n, addr) != n){
+ print(premature);
+ return -1;
+ }
+
+ /*
+ * bss and entry point
+ */
+ print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); uartwait();
+
+ /*
+ * Go to new code. It's up to the program to get its PC relocated to
+ * the right place.
+ */
+ b = (void (*)(void))(PADDR(GLLONG(ep->entry)));
+ (*b)();
+ return 0;
+}
diff --git a/os/boot/puma/puma.c b/os/boot/puma/puma.c
new file mode 100644
index 00000000..bd4a822d
--- /dev/null
+++ b/os/boot/puma/puma.c
@@ -0,0 +1,123 @@
+#include "boot.h"
+
+//#define GPIO1_PORT 0x0078
+//#define GPIO2_PORT 0x0079
+
+#define GPIO2_N_LED1_DIAG 0x10 /* diagnostic led mask */
+
+#define HARI2_N_LED1 0x01
+#define HARI2_N_LED2 0x02
+#define HARI2_N_LED3 0x04
+#define HARI2_N_LED4 0x08
+
+/*
+ * National Semiconductor PC87306 Super I/O
+ */
+enum {
+ Index= 0x398, /* sio configuration index register */
+ Data= 0x399, /* sio configuration data register */
+};
+
+
+// byte flip table for Puma SuperIO port permuted as 76543210 -> 01234567
+unsigned char
+byteflip[] = {
+ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+};
+
+int
+sio_inb(int port)
+{
+ unsigned char b = *(uchar *)IOBADDR(port);
+// b = byteflip[b];
+ return b&0xff;
+}
+
+void
+sio_outb(int port, int data)
+{
+// data = byteflip[data];
+ *(uchar *)IOBADDR(port) = data;
+}
+
+static void
+siocfg(int r, int v, int rsv)
+{
+ sio_outb(Index, r);
+ if(rsv)
+ v = (sio_inb(Data)&rsv) | (v&~rsv);
+ sio_outb(Data, v);
+ sio_outb(Data, v); /* ``write data twice'' */
+}
+
+void
+sioinit(void)
+{
+ /* determine which byte flip is required ... */
+
+ siocfg(SIO_CONFIG_FAR, FAR_LPT_LPTA | FAR_UART1_COM1 | FAR_UART2_COM2, 0);
+ siocfg(SIO_CONFIG_FER, FER_LPT_ENABLE | FER_UART1_ENABLE | FER_UART2_ENABLE | FER_IDE_ENABLE, 0);
+ siocfg(SIO_CONFIG_KRR, KRR_KBC_ENABLE | KRR_KBC_MUST_BE_1 | KRR_RTC_ENABLE, 0x50);
+ siocfg(SIO_CONFIG_SCF0,SCF0_GPIO_PORT1_ENABLE | SCF0_GPIO_PORT2_ENABLE, 0xC0);
+
+ /* force UART interrupt pins low, not tristate by setting ienable in MCR (!) */
+ sio_outb(COM1_PORT+UART_MCR, sio_inb(COM1_PORT+UART_MCR)|(1<<3));
+ sio_outb(COM2_PORT+UART_MCR, sio_inb(COM2_PORT+UART_MCR)|(1<<3));
+}
+
+void
+led(int on)
+{
+ int gpio, hari, hbits;
+ int s;
+
+ s = splhi();
+
+ gpio = sio_inb(GPIO2_PORT);
+ if (on)
+ gpio &= ~GPIO2_N_LED1_DIAG;
+ else
+ gpio |= GPIO2_N_LED1_DIAG;
+ sio_outb(GPIO2_PORT, gpio);
+
+ hari = HARI2_INIT;
+ hbits = HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4;
+ if (on)
+ hari &= ~hbits;
+ else
+ hari |= hbits;
+ *(uchar *)HARI2 = hari;
+
+
+ splx(s);
+
+}
+
+void
+pumainit(void)
+{
+ /*
+ * Initialise HARI2 for:
+ * a) Leds off
+ * b) No Timer2 aborts
+ * c) Ethernet on IRQ
+ */
+ *(uchar *)HARI2 = HARI2_INIT|HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4;
+ sioinit();
+}
+
diff --git a/os/boot/puma/puma.h b/os/boot/puma/puma.h
new file mode 100644
index 00000000..a5835cff
--- /dev/null
+++ b/os/boot/puma/puma.h
@@ -0,0 +1,435 @@
+/*
+ * Teralogic TL750 - Puma Evaluation Board
+ */
+
+/*
+ * Puma addresses
+ */
+#define EPROM_BASE 0x80000000 /* EPROM */
+#define FLASH_BASE 0xa0000000 /* Flash */
+#define TL750_BASE 0xc0000000 /* TL750 registers */
+#define ISAMEM_BASE 0xe0000000 /* ISA memory space */
+#define ISAIO_BASE 0xf0000000 /* ISA I/O space */
+
+#define ISAIO_SHIFT 2
+#define IOBADDR(io_port) (ISAIO_BASE + (io_port << ISAIO_SHIFT))
+
+/* Hardware address register for interrupts (HARI) */
+#define HARI1 0xE2000000 /* Interrupt status on read, User interrupt on write */
+#define HARI2 0xE3000000 /* More interrupt status on read, LEDs on write */
+#define HARI1_FIQ_MASK 0x92 /* FIQ indicator bits in HARI1, others are IRQ */
+#define HARI2_INIT 0x20 /* No timer2 aborts, Ethernet on IRQ */
+
+
+
+/*
+ * Interrupt Vectors
+ * corresponding to the HARIx_xxx_IRQ/FIQ bits above.
+ *
+ * HARI1 interrupts
+ */
+#define V_LPT 0 /* Parallel port interrupt */
+#define V_NM0 1 /* MPEG Decode Interrupt */
+#define V_NM1 2 /* MPEG Decode Interrupt */
+#define V_COM2 3 /* Serial Port 2 Interrupt */
+#define V_COM1 4 /* Serial Port 1 Interrupt */
+#define V_MOUSE 5 /* Mouse Interrupt */
+#define V_KEYBOARD 6 /* Keyboard Interrupt */
+#define V_ETHERNET 7 /* Ethernet Interrupt */
+/*
+ * HARI2 interrupts
+ */
+#define V_TIMER0 8 /* 82C54 Timer 0 Interrupt */
+#define V_TIMER1 9 /* 82C54 Timer 1 Interrupt */
+#define V_TIMER2 10 /* 82C54 Timer 2 Interrupt */
+#define V_SOFTWARE 11 /* Software Interrupt */
+#define V_IDE 12 /* IDE Hard Drive Interrupt */
+#define V_SMARTCARD 13 /* Smart Card Interrupt */
+#define V_TL750 14 /* TL750 Interrupt */
+ /* Nothing in vector 15 for now */
+#define V_MAXNUM 15
+
+/*
+ * Definitions for National Semiconductor PC87306 SuperIO configuration
+ */
+#define SIO_CONFIG_INDEX 0x398 /* SuperIO configuration index register */
+#define SIO_CONFIG_DATA 0x399 /* SuperIO configuration data register */
+
+#define SIO_CONFIG_RESET_VAL 0x88 /* Value read from first read of sio_config_index reg after reset */
+/*
+ * PC87306 Configuration Registers (The value listed is the configuration space
+ * index.)
+ */
+#define SIO_CONFIG_FER 0x00 /* Function Enable Register */
+
+#define FER_LPT_ENABLE 0x01 /* Enable Parallel Port */
+#define FER_UART1_ENABLE 0x02 /* Enable Serial Port 1 */
+#define FER_UART2_ENABLE 0x04 /* Enable Serial Port 2 */
+#define FER_FDC_ENABLE 0x08 /* Enable Floppy Controller */
+#define FER_FDC_4DRIVE_ENCODING 0x10 /* Select Floppy 4 Drive Encoding */
+#define FER_FDC_ADDR_ENABLE 0x20 /* Select Floppy Secondary Address */
+ /* 0: [0x3F0..0x3F7] */
+ /* 1: [0x370..0x377] */
+#define FER_IDE_ENABLE 0x40 /* Enable IDE Controller */
+#define FER_IDE_ADDR_SELECT 0x80 /* Select IDE Secondary Address */
+ /* 0: [0x1F0..0x1F7,0x3F6,0x3F7] */
+ /* 1: [0x170..0x177,0x376,0x377] */
+
+#define SIO_CONFIG_FAR 0x01 /* Function Address Register */
+
+#define FAR_LPT_ADDR_MASK 0x03 /* Select LPT Address */
+ /* If (PNP0[4] == 0) then: */
+ /* 0: LPTB [0x378..0x37F] IRQ5/7 */
+ /* 1: LPTA [0x3BC..0x3BE] IRQ7 */
+ /* 2: LPTC [0x278..0x27F] IRQ5 */
+ /* 3: Reserved */
+ /* Else ignored. */
+#define FAR_LPT_LPTB 0 /* 0: LPTB 0x378 irq5/7 */
+#define FAR_LPT_LPTA 1 /* 1: LPTA 0x3BC irq 7 */
+#define FAR_LPT_LPTC 2 /* 2: LPTC 0x278 irq5 */
+
+#define FAR_UART1_ADDR_MASK 0x0C /* Select Serial Port 1 Address */
+ /* 0: COM1 [0x3F8..0x3FF] */
+ /* 1: COM2 [0x2F8..0x2FF] */
+ /* 2: COM3 (See FAR[7:6]) */
+ /* 3: COM4 (See FAR[7:6]) */
+#define FAR_UART1_COM1 0x00
+#define FAR_UART2_ADDR_MASK 0x30 /* Select Serial Port 2 Address */
+ /* 0: COM1 [0x3F8..0x3FF] */
+ /* 1: COM2 [0x2F8..0x2FF] */
+ /* 2: COM3 (See FAR[7:6]) */
+ /* 3: COM4 (See FAR[7:6]) */
+#define FAR_UART2_COM2 0x10
+#define FAR_EXTENDED_UART_ADDR_SELECT 0xC0 /* Extended Address Selects */
+ /* COM3@IRQ4, COM4@IRQ3 */
+ /* 0: COM3@0x3E8, COM4@0x2E8 */
+ /* 1: COM3@0x338, COM4@0x238 */
+ /* 2: COM3@0x2E8, COM4@0x2E0 */
+ /* 3: COM3@0x220, COM4@0x228 */
+
+#define SIO_CONFIG_PTR 0x02 /* Power & Test Register */
+
+#define PTR_POWER_DOWN 0x01 /* Power down all enabled functions */
+#define PTR_LPT_IRQ_SELECT 0x08 /* Select LPT IRQ if (FAR[1:0] == 0) */
+ /* 0: IRQ5 */
+ /* 1: IRQ7 */
+#define PTR_UART1_TEST_MODE 0x10 /* Set serial port 1 test mode */
+#define PTR_UART2_TEST_MODE 0x20 /* Set serial port 2 test mode */
+#define PTR_LOCK_CONFIGURATION 0x40 /* Prevent all further config writes */
+ /* Only a RESET will reenable writes */
+#define PTR_LPT_EXTENDED_MODE_SELECT 0x80 /* Select Mode if not EPP/ECP */
+ /* 0: Compatible Mode */
+ /* 1: Extended Mode */
+
+#define SIO_CONFIG_FCR 0x03 /* Function Control Register */
+ /* WARNING: The FCR register must be written */
+ /* using read-modify-write! */
+#define FCR_TDR_MODE_SELECT 0x01 /* ? (floppy/tape) */
+#define FCR_IDE_DMA_ENABLE 0x02 /* Enable IDE DMA mode */
+#define FCR_EPP_ZERO_WAIT_STATE 0x40 /* Enable EPP zero wait state */
+
+#define SIO_CONFIG_PCR 0x04 /* Printer Control Register */
+
+#define PCR_EPP_ENABLE 0x01 /* Enable parallel port EPP mode */
+#define PCR_EPP_VERSION_SELECT 0x02 /* Select version of EPP mode */
+ /* 0: Version 1.7 */
+ /* 1: Version 1.9 (IEEE 1284) */
+#define PCR_ECP_ENABLE 0x04 /* Enable parallel port ECP mode */
+#define PCR_ECP_POWER_DOWN_CLOCK_ENABLE 0x08 /* Enable clock in power-down state */
+ /* 0: Freeze ECP clock */
+ /* 1: Run ECP clock */
+#define PCR_ECP_INT_POLARITY_CONTROL 0x20 /* Interrupt polarity control */
+ /* 0: Level high or negative pulse */
+ /* 1: Level low or positive pulse */
+#define PCR_ECP_INT_IO_CONTROL 0x40 /* Interrupt I/O control */
+ /* WARNING: Slightly safer to choose */
+ /* open drain if you don't know the */
+ /* exact requirements of the circuit */
+ /* 0: Totem-pole output */
+ /* 1: Open drain output */
+#define PCR_RTC_RAM_WRITE_DISABLE 0x80 /* Disable writes to RTC RAM */
+ /* 0: Enable writes */
+ /* 1: Disable writes */
+
+#define SIO_CONFIG_KRR 0x05 /* Keyboard & RTC Control Register */
+ /* WARNING: The KRR register must be written */
+ /* with a 1 in bit 2, else the KBC will not */
+ /* work! */
+#define KRR_KBC_ENABLE 0x01 /* Enable keyboard controller */
+#define KRR_KBC_SPEED_CONTROL 0x02 /* Select clock divisor if !KRR[7] */
+ /* 0: Divide by 3 */
+ /* 1: Divide by 2 */
+#define KRR_KBC_MUST_BE_1 0x04 /* Reserved: This bit must be 1! */
+#define KRR_RTC_ENABLE 0x08 /* Enable real time clock */
+#define KRR_RTC_RAMSEL 0x20 /* Select RTC RAM bank */
+#define KRR_KBC_CLOCK_SOURCE_SELECT 0x80 /* Select clock source */
+ /* 0: Use X1 clock source */
+ /* 1: Use SYSCLK clock source */
+
+#define SIO_CONFIG_PMC 0x06 /* Power Management Control Register */
+
+#define PMC_IDE_TRISTATE_CONTROL 0x01 /* ? (power management) */
+#define PMC_FDC_TRISTATE_CONTROL 0x02 /* ? (power management) */
+#define PMC_UART1_TRISTATE_CONTROL 0x04 /* ? (power management) */
+#define PMC_SELECTIVE_LOCK 0x20 /* ? (power management) */
+#define PMC_LPT_TRISTATE_CONTROL 0x40 /* ? (power management) */
+
+#define SIO_CONFIG_TUP 0x07 /* Tape, UARTS & Parallel Port Register */
+
+#define TUP_EPP_TIMEOUT_INT_ENABLE 0x04 /* Enable EPP timeout interrupts */
+
+#define SIO_CONFIG_SID 0x08 /* Super I/O Identification Register */
+
+#define SID_ID_MASK 0xF8 /* Super I/O ID field */
+#define SID_ID_PC87306 0x70 /* PC87306 ID value */
+
+#define SIO_CONFIG_ASC 0x09 /* Advanced Super I/O Config Register */
+ /* WARNING: The ASC register must be written */
+ /* with a 0 in bit 3! */
+ /* WARNING: The ASC register resets to 1 in */
+ /* bit 7 (PC/AT mode)! */
+#define ASC_VLD_MASK 0x03 /* ? (floppy/tape) */
+#define ASC_ENHANCED_TDR_SUPPORT 0x04 /* ? (floppy/tape) */
+#define ASC_MUST_BE_0 0x08 /* Reserved: Must be 0 */
+#define ASC_ECP_CNFGA 0x20 /* ? */
+#define ASC_DENSEL_POLARITY_BIT 0x40 /* ? (floppy/tape) */
+#define ASC_SYSTEM_OPERATION_MODE 0x80 /* Select system operation mode */
+ /* 0: PS/2 mode */
+ /* 1: PC/AT mode */
+
+#define SIO_CONFIG_CS0LA 0x0A /* Chip Select 0 Low Address Register */
+
+#define SIO_CONFIG_CS0CF 0x0B /* Chip Select 0 Configuration Register */
+ /* WARNING: The CS0CF register must be */
+ /* written with a 1 in bit 7! */
+#define CS0CF_CS0_DECODE 0x08 /* Select CS0 decode sensitivity */
+ /* 0: Decode full 16-bit address */
+ /* 1: Decode only bits 15 thru 12 */
+#define CS0CF_CS0_WRITE_ENABLE 0x10 /* Enable CS0 on write cycles */
+#define CS0CF_CS0_READ_ENABLE 0x20 /* Enable CS0 on read cycles */
+#define CS0CF_CS0_MUST_BE_1 0x80 /* Reserved: Must be 1 */
+
+#define SIO_CONFIG_CS1LA 0x0C /* Chip Select 1 Low Address Register */
+
+#define SIO_CONFIG_CS1CF 0x0D /* Chip Select 1 Configuration Register */
+
+#define CS1CF_CS1_DECODE 0x08 /* Select CS1 decode sensitivity */
+ /* 0: Decode full 16-bit address */
+ /* 1: Decode only bits 15 thru 12 */
+#define CS1CF_CS1_WRITE_ENABLE 0x10 /* Enable CS1 on write cycles */
+#define CS1CF_CS1_READ_ENABLE 0x20 /* Enable CS1 on read cycles */
+
+#define SIO_CONFIG_IRC 0x0E /* Infrared Configuration Register */
+
+#define IRC_UART2_INTERFACE_MODE 0x01 /* Select UART2 interface mode */
+ /* 0: Normal (modem) mode */
+ /* 1: IR mode */
+#define IRC_IR_FULL_DUPLEX 0x02 /* Select IR duplex mode */
+ /* 0: Full duplex mode */
+ /* 1: Half duplex mode */
+#define IRC_ENCODED_IR_TRANSMITTER_DRIVE 0x10 /* IR transmitter drive control */
+ /* 0: IRTX active for 1.6usec */
+ /* 1: IRTX active for 3/16 baud */
+#define IRC_ENCODED_IR_MODE 0x20 /* IR encode mode */
+ /* 0: Encoded mode */
+ /* 1: Non-encoded mode */
+
+#define SIO_CONFIG_GPBA 0x0F /* GP I/O Port Base Address Config Register */
+
+#define SIO_CONFIG_CS0HA 0x10 /* Chip Select 0 High Address Register */
+
+#define SIO_CONFIG_CS1HA 0x11 /* Chip Select 1 High Address Register */
+
+#define SIO_CONFIG_SCF0 0x12 /* Super I/O Configuration Register 0 */
+
+#define SCF0_RTC_RAM_LOCK 0x01 /* Lock (1) will prevent all further */
+ /* accesses to RTC RAM. Only RESET */
+ /* return this bit to a 0. */
+#define SCF0_IRQ1_12_LATCH_ENABLE 0x02 /* Enable IRQ1/IRQ12 latching */
+#define SCF0_IRQ12_TRISTATE 0x04 /* IRQ12 tri-state control */
+ /* 0: Use quasi-bidirectional buffer */
+ /* 1: Tri-state IRQ12 */
+#define SCF0_UART2_TRISTATE 0x08 /* Force UART2/IR outputs to */
+ /* tri-state when disabled */
+#define SCF0_GPIO_PORT1_ENABLE 0x10 /* Enable GPIO port 1 */
+#define SCF0_GPIO_PORT2_ENABLE 0x20 /* Enable GPIO port 2 */
+
+#define SIO_CONFIG_SCF1 0x18 /* Super I/O Configuration Register 1 */
+
+#define SCF1_REPORTED_ECP_DMA 0x06 /* Reported ECP DMA number */
+ /* 0: Jumpered 8-bit DMA */
+ /* 1: DMA channel 1 */
+ /* 2: DMA channel 2 */
+ /* 3: DMA channel 3 */
+#define SCF1_SELECTED_ECP_DMA 0x08 /* Selected ECP DMA pins */
+ /* 0: PDRQ0 & PDACK0 */
+ /* 1: PDRQ1 & PDACK1 */
+#define SCF1_SCRATCH_BITS 0xC0 /* ? */
+
+#define SIO_CONFIG_LPTBA 0x19 /* LPT Base Address */
+
+#define SIO_CONFIG_PNP0 0x1B /* Plug & Play Configuration Register 0 */
+
+#define PNP0_LPT_INT_SELECT_CONTROL 0x10 /* LPT IRQ select control */
+ /* 0: IRQ selected by FAR[1:0] */
+ /* 1: IRQ selected by PNP0[5] */
+#define PNP0_LPT_INT_MAPPING 0x20 /* LPT IRQ mapping */
+ /* 0: IRQ5 */
+ /* 1: IRQ7 */
+#define PNP0_LPTA_BASE_ADDR_SELECT 0x40 /* LPTA base address */
+ /* 0: Always 0x3BC */
+ /* 1: Selected by LPTBA[7:0] */
+
+#define SIO_CONFIG_PNP1 0x1C /* Plug & Play Configuration Register 1 */
+
+#define PNP1_UARTS_INT_SELECT_CONTROL 0x01 /* UART interrupt select control */
+ /* 0: Use FAR[3:2] & FAR[5:4] */
+ /* 1: Use PNP1[2] & PNP1[6] */
+#define PNP1_UART1_INT_MAPPING 0x04 /* UART1 interrupt mapping */
+ /* 0: IRQ3 */
+ /* 1: IRQ4 */
+#define PNP1_UART2_INT_MAPPING 0x40 /* UART2 interrupt mapping */
+ /* 0: IRQ3 */
+ /* 1: IRQ4 */
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Definitions for the SuperIO UART.
+ */
+#define COM1_PORT 0x3f8
+#define COM2_PORT 0x2f8
+
+/*
+ * Register offsets.
+ */
+#define UART_RX 0 /* Receive port, read only */
+#define UART_TX 0 /* transmit port, write only */
+#define UART_IER 1 /* Interrupt enable, read/write */
+#define UART_IIR 2 /* Interrupt id, read only */
+#define UART_FIFO_CONTROL 2 /* FIFO control, write only */
+#define UART_LCR 3 /* Line control register */
+#define UART_MCR 4 /* Modem control register */
+#define UART_LSR 5 /* Line Status register */
+#define UART_MSR 6 /* Modem Status register */
+
+/* with the DLAB bit set, the first two registers contain the baud rate */
+#define UART_DLLSB 0
+#define UART_DLMSB 1
+
+/*
+ * Line control register
+ */
+#define LCR_DB 3 /* Data bits in transmission (0 = 5, 1 = 6, 2 = 7, 3 = 8) */
+#define LCR_SB 4 /* Stop bit */
+#define LCR_PE 8 /* Parity enable */
+#define LCR_EP 16 /* Even parity */
+#define LCR_SP 32 /* Stick parity */
+#define LCR_BC 64 /* break control */
+#define LCR_DLAB 128 /* Divisor latch access bit */
+
+
+/*
+ * Modem Control register
+ */
+#define MCR_DTR 1 /* Data Terminal Ready */
+#define MCR_RTS 2 /* Request To Send */
+#define MCR_OUT1 4 /* Out1 (not used) */
+#define MCR_IRQ_ENABLE 8 /* Enable IRQ */
+#define MCR_LOOP 16 /* Loopback mode */
+
+/*
+ * Line status bits.
+ */
+#define LSR_DR 0x01 /* Data ready */
+#define LSR_OE 0x02 /* Overrun error */
+#define LSR_PE 0x04 /* Parity error */
+#define LSR_FE 0x08 /* Framing error */
+#define LSR_BI 0x10 /* Break interrupt */
+#define LSR_THRE 0x20 /* Transmitter holding register empty */
+#define LSR_TEMT 0x40 /* Transmitter empty */
+#define LSR_FFE 0x80 /* Receiver FIFO error */
+
+#define LSR_ERROR (LSR_OE | LSR_PE | LSR_FE)
+
+/*
+ * Interrupt Identification register (IIR)
+ */
+#define IIR_IP 1 /* No Interrupt pending */
+#define IIR_RECEIVE_LINE_STATUS 6 /* Overrun, Parity, Framing erros, Break */
+#define IIR_RDA 4 /* Receive data available */
+#define IIR_FIFO_FLAG 8 /* FIFO flag */
+#define IIR_FIFO_TIMEOUT (IIR_RDA+IIR_FIFO_FLAG) /* Got data some time ago, but FIFO time out */
+#define IIR_THRE 2 /* Transmitter holding register empty. */
+#define IIR_MS 0 /* CTS, DSR, RING, DCD changed */
+#define IIR_HPIP 6 /* Highest priority interrupt pending */
+
+/*
+ * Interrupt enable register (IER)
+ */
+#define IER_RDA 1 /* Received data available */
+#define IER_THRE 2 /* Transmitter holding register empty */
+#define IER_RLS 4 /* Receiver line status */
+#define IER_MS 8 /* Modem status */
+
+/*
+ * PC87306 Parallel I/O Port
+ */
+#define LPT1_PORT 0x03BC
+
+/*
+ * PC87306 General Purpose I/O Ports
+ */
+#define GPIO1_PORT 0x0078
+#define GPIO2_PORT 0x0079
+
+/*
+ * PC87306 IDE Port
+ */
+#define IDE_PORT_1 0x01F0
+#define IDE_PORT_2 0x03F6
+#define IDE_PORT_3 0x03F7
+
+/*
+ * PC87306 Floppy Port
+ */
+#define FDC_PORT 0x03F0
+
+/*
+ * PC87306 Real Time Clock/battery backed up RAM port
+ */
+#define RTC_INDEX_PORT 0x0070
+#define RTC_DATA_PORT 0x0071
+
+/*
+ * Offsets in RTC memory (RAMSEL = 0)
+ */
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+#define RTC_CONTROL_A 0xA
+#define RTC_CONTROL_B 0xB
+#define RTC_CONTROL_C 0xC
+#define RTC_CONTROL_D 0xD
+
+#define RTC_NVRAM0_START 0xE
+#define RTC_NVRAM0_SIZE 114
+#define RTC_NVRAM1_START 0
+#define RTC_NVRAM1_SIZE 128
+#define RTC_NVRAM_SIZE (RTC_NVRAM0_SIZE+RTC_NVRAM1_SIZE)
+
+#define RTC_PWNVRAM_START 0x38 /* Start of protected NVRAM */
+#define RTC_PWNVRAM_SIZE 8 /* Size of protected NVRAM */
+
+
+/*
+ * PC87306 Keyboard controller ports
+ */
+#define KEYBD_DATA_PORT 0x0060
+#define KEYBD_CTRL_PORT 0x0064
diff --git a/os/boot/puma/qio.c b/os/boot/puma/qio.c
new file mode 100644
index 00000000..e014433e
--- /dev/null
+++ b/os/boot/puma/qio.c
@@ -0,0 +1,128 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+struct Queue {
+ Block* first;
+ Block* last;
+ void (*kick)(void*);
+ void* arg;
+ long len;
+};
+
+Block *
+iallocb(int n)
+{
+ Block *b;
+
+ b = (Block*)malloc(sizeof(Block)+n);
+ b->data = (uchar*)b + sizeof(Block);
+ b->rp = b->wp = b->data;
+ b->lim = b->data + n;
+ b->next = 0;
+ b->magic = 0xcafebee0;
+ return b;
+}
+
+void
+freeb(Block *b)
+{
+ if(b){
+ if(b->magic != 0xcafebee0)
+ panic("freeb");
+ b->magic = 0;
+ b->next = (Block*)0xdeadbabe;
+ free(b);
+ }
+}
+
+Queue *
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+ Queue *q;
+
+ USED(limit, msg);
+ q = (Queue*)malloc(sizeof(Queue));
+ q->first = q->last = 0;
+ q->kick = kick;
+ q->arg = arg;
+ q->len = 0;
+ return q;
+}
+
+Block *
+qget(Queue *q)
+{
+ int s;
+ Block *b;
+
+ s = splhi();
+ if((b = q->first) != 0){
+ q->first = b->next;
+ b->next = 0;
+ q->len -= BLEN(b);
+ if(q->len < 0)
+ panic("qget");
+ }
+ splx(s);
+ return b;
+}
+
+void
+qbwrite(Queue *q, Block *b)
+{
+ int s;
+
+ s = splhi();
+ b->next = 0;
+ if(q->first == 0)
+ q->first = b;
+ else
+ q->last->next = b;
+ q->last = b;
+ q->len += BLEN(b);
+ splx(s);
+ if(q->kick)
+ q->kick(q->arg);
+}
+
+long
+qlen(Queue *q)
+{
+ return q->len;
+}
+
+int
+qbgetc(Queue *q)
+{
+ Block *b;
+ int s, c;
+
+ c = -1;
+ s = splhi();
+ while(c < 0 && (b = q->first) != nil){
+ if(b->rp < b->wp){
+ c = *b->rp++;
+ q->len--;
+ }
+ if(b->rp >= b->wp){
+ q->first = b->next;
+ b->next = nil;
+ }
+ }
+ splx(s);
+ return c;
+}
+
+void
+qbputc(Queue *q, int c)
+{
+ Block *b;
+
+ b = iallocb(1);
+ *b->wp++ = c;
+ qbwrite(q, b);
+}
diff --git a/os/boot/puma/rmap.c b/os/boot/puma/rmap.c
new file mode 100644
index 00000000..8b8a0bef
--- /dev/null
+++ b/os/boot/puma/rmap.c
@@ -0,0 +1,104 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+ lock(rmap);
+ rmap->map = map;
+ rmap->mapend = map+(size/sizeof(Map));
+ unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+ Map *mp;
+ ulong t;
+
+ if(size <= 0)
+ return;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+ ;
+
+ if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+ (mp-1)->size += size;
+ if(addr+size == mp->addr){
+ (mp-1)->size += mp->size;
+ while(mp->size){
+ mp++;
+ (mp-1)->addr = mp->addr;
+ (mp-1)->size = mp->size;
+ }
+ }
+ }
+ else{
+ if(addr+size == mp->addr && mp->size){
+ mp->addr -= size;
+ mp->size += size;
+ }
+ else do{
+ if(mp >= rmap->mapend){
+ print("mapfree: %s: losing 0x%uX, %d\n",
+ rmap->name, addr, size);
+ break;
+ }
+ t = mp->addr;
+ mp->addr = addr;
+ addr = t;
+ t = mp->size;
+ mp->size = size;
+ mp++;
+ }while(size = t);
+ }
+ unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+ Map *mp;
+ ulong maddr, oaddr;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->size; mp++){
+ maddr = mp->addr;
+
+ if(addr){
+ if(maddr > addr)
+ continue;
+ if(addr+size > maddr+mp->size)
+ break;
+ maddr = addr;
+ }
+
+ if(align > 0)
+ maddr = ((maddr+align-1)/align)*align;
+ if(mp->addr+mp->size-maddr < size)
+ continue;
+
+ oaddr = mp->addr;
+ mp->addr = maddr+size;
+ mp->size -= maddr-oaddr+size;
+ if(mp->size == 0){
+ do{
+ mp++;
+ (mp-1)->addr = mp->addr;
+ }while((mp-1)->size = mp->size);
+ }
+
+ unlock(rmap);
+ if(oaddr != maddr)
+ mapfree(rmap, oaddr, maddr-oaddr);
+
+ return maddr;
+ }
+ unlock(rmap);
+
+ return 0;
+}
diff --git a/os/boot/puma/squeeze.h b/os/boot/puma/squeeze.h
new file mode 100644
index 00000000..b06c1b79
--- /dev/null
+++ b/os/boot/puma/squeeze.h
@@ -0,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ * Sqhdr
+ * original Exec header
+ * two Squeeze tables
+ * squeezed segment
+ * unsqueezed segment, if any
+ */
+#define SQMAGIC (ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+ uchar magic[4]; /* SQMAGIC */
+ uchar text[4]; /* squeezed length of text (excluding tables) */
+ uchar data[4]; /* squeezed length of data (excluding tables) */
+ uchar asis[4]; /* length of unsqueezed segment */
+ uchar toptxt[4]; /* value for 0 encoding in text */
+ uchar topdat[4]; /* value for 0 encoding in data */
+ uchar sum[4]; /* simple checksum of unsqueezed data */
+ uchar flags[4];
+};
+#define SQHDRLEN (8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits. note that the mapping is its own inverse.
+ */
+#define QREMAP(X)\
+ switch((X)>>26){\
+ case 19: case 31: case 59: case 63:\
+ (X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+ }
diff --git a/os/boot/puma/sum.c b/os/boot/puma/sum.c
new file mode 100644
index 00000000..ce7e33fd
--- /dev/null
+++ b/os/boot/puma/sum.c
@@ -0,0 +1,13 @@
+int
+sum(int a, int b, int c)
+{
+ return a+b+c;
+}
+
+void
+main(void)
+{
+ int s;
+
+ s = sum(1, 2, 3);
+}
diff --git a/os/boot/puma/trap.c b/os/boot/puma/trap.c
new file mode 100644
index 00000000..6322e5c7
--- /dev/null
+++ b/os/boot/puma/trap.c
@@ -0,0 +1,190 @@
+#include "boot.h"
+
+typedef struct IrqEntry {
+ void (*r)(Ureg*, void*);
+ void *a;
+} IrqEntry;
+
+IrqEntry Irq[V_MAXNUM+1];
+
+static void dumpstk(ulong *);
+void dumpregs(Ureg* ureg);
+
+void
+setvec(int v, void (*f)(Ureg*, void*), void* a)
+{
+ if(v < 0 || v >= V_MAXNUM)
+ panic("setvec: interrupt source %d out of range\n", v);
+ Irq[v].r = f;
+ Irq[v].a = a;
+}
+
+ulong irqstack[64];
+ulong fiqstack[64];
+ulong abtstack[64];
+ulong undstack[64];
+
+static void
+safeintr(Ureg*, void *a)
+{
+ int v = (int)a;
+// print("spurious interrupt %d\n", v);
+ USED(v);
+}
+
+void
+trapinit(void)
+{
+ int offset;
+ ulong op;
+ int v;
+ int s;
+
+ s = splhi();
+
+ /* set up stacks for various exceptions */
+ setr13(PsrMirq, irqstack+nelem(irqstack)-1);
+ setr13(PsrMfiq, fiqstack+nelem(fiqstack)-1);
+ setr13(PsrMabt, abtstack+nelem(abtstack)-1);
+ setr13(PsrMund, undstack+nelem(undstack)-1);
+
+ for(v = 0; v <= V_MAXNUM; v++) {
+ Irq[v].r = safeintr;
+ Irq[v].a = (void *)v;
+ }
+
+ /* Reset Exception */
+ offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x0) = op;
+
+ /* Undefined Instruction Exception */
+ offset = ((((ulong) _vundcall) - 0x4)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x4) = op;
+
+ /* SWI Exception */
+ offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x8) = op;
+
+ /* Prefetch Abort Exception */
+ offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0xc) = op;
+
+ /* Data Abort Exception */
+ offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x10) = op;
+
+ /* IRQ Exception */
+ offset = ((((ulong) _virqcall) - 0x18)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x18) = op;
+
+ /* FIQ Exception */
+ offset = ((((ulong) _vfiqcall) - 0x1c)-8) >> 2;
+ op = ( 0xea << 24 ) | offset;
+ *((ulong *) 0x1c) = op;
+
+
+ flushIcache();
+ writeBackDC();
+ flushDcache();
+ flushIcache();
+ drainWBuffer();
+
+ splx(s);
+}
+
+/*
+ * trap is called splhi().
+ */
+
+void
+trap(Ureg* ureg)
+{
+ ushort mask;
+ IrqEntry *ip;
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ ureg->pc -= 4;
+
+ switch(ureg->type) {
+ case PsrMirq: /* Interrupt Request */
+ mask = *(uchar*)HARI1 | ((*(uchar*)HARI2) << 8);
+ ip = Irq;
+ while (mask != 0) {
+ if(mask&1)
+ ip->r(ureg, ip->a);
+ ip++;
+ mask >>= 1;
+ }
+ break;
+
+ case PsrMfiq: /* FIQ */
+ mask = *(uchar*)HARI1 & HARI1_FIQ_MASK;
+ ip = Irq;
+ while (mask != 0) {
+ if(mask&1)
+ ip->r(ureg, ip->a);
+ ip++;
+ mask >>= 1;
+ }
+ break;
+
+ case PsrMund: /* Undefined instruction */
+ dumpregs(ureg);
+ panic("Undefined Instruction Exception\n");
+ break;
+
+ case PsrMsvc: /* Jump through 0 or SWI */
+ dumpregs(ureg);
+ panic("SVC/SWI Exception\n");
+ break;
+
+ case PsrMabt: /* Prefetch abort */
+ ureg->pc -= 4;
+ /* FALLTHROUGH */
+
+ case PsrMabt+1: { /* Data abort */
+ uint far =0;
+ uint fsr =0;
+
+ USED(far,fsr);
+ fsr = 0; /*mmuregr(CpFSR);*/
+ far = 0; /*mmuregr(CpFAR); */
+ if (ureg->type == PsrMabt)
+ print("Prefetch Abort/");
+ print("Data Abort\n");
+
+ print("Data Abort: FSR %8.8uX FAR %8.8uX\n", fsr, far);
+ /* FALLTHROUGH */
+ }
+ default:
+ dumpregs(ureg);
+ panic("exception %uX\n", ureg->type);
+ break;
+ }
+
+ splhi();
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ print("Stack is at: %8.8uX\n", ureg);
+/* print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr());*/
+}
diff --git a/os/boot/puma/ureg.h b/os/boot/puma/ureg.h
new file mode 100644
index 00000000..6f853744
--- /dev/null
+++ b/os/boot/puma/ureg.h
@@ -0,0 +1,22 @@
+typedef struct Ureg {
+ uint r0;
+ uint r1;
+ uint r2;
+ uint r3;
+ uint r4;
+ uint r5;
+ uint r6;
+ uint r7;
+ uint r8;
+ uint r9;
+ uint r10;
+ uint r11;
+ uint r12;
+ uint r13;
+ uint r14;
+ uint link;
+ uint type;
+ uint psr;
+// uint sp;
+ uint pc;
+} Ureg;
diff --git a/os/boot/puma/zqs.c b/os/boot/puma/zqs.c
new file mode 100644
index 00000000..7c000c68
--- /dev/null
+++ b/os/boot/puma/zqs.c
@@ -0,0 +1,216 @@
+#include "boot.h"
+#include "squeeze.h"
+
+#define EXECHDRLEN (8*4)
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+ int n;
+ ulong tab[7*256];
+};
+
+#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+/*
+ * for speed of unsqueezing from Flash, certain checks are
+ * not done inside the loop (as they would be in the unsqueeze program zqs),
+ * but instead the checksum is expected to catch corrupted files.
+ * in fact the Squeeze array bounds can't be exceeded in practice
+ * because the tables are always full for a squeezed kernel.
+ */
+enum {
+ QFLAG = 1, /* invert powerpc-specific code transformation */
+ CHECK = 0, /* check precise bounds in Squeeze array (otherwise checksum detects error) */
+};
+
+static ulong chksum;
+static int rdtab(Block*, Squeeze*, int);
+static ulong* unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static uchar* unsqzseg(uchar*, Block*, long, long, char*);
+static Alarm* unsqzal;
+
+int
+issqueezed(uchar *b)
+{
+ return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0;
+}
+
+static void
+unsqzdot(void*)
+{
+ unsqzal = alarm(500, unsqzdot, nil);
+ print(".");
+}
+
+long
+unsqueezef(Block *b, ulong *entryp)
+{
+ uchar *loada, *wp;
+ ulong toptxt, topdat, oldsum;
+ long asis, nst, nsd;
+ Sqhdr *sqh;
+ Exec *ex;
+
+ if(BLEN(b) < SQHDRLEN+EXECHDRLEN)
+ return -1;
+ sqh = (Sqhdr*)b->rp;
+ if(GET4(sqh->magic) != SQMAGIC)
+ return -1;
+ chksum = 0;
+ toptxt = GET4(sqh->toptxt);
+ topdat = GET4(sqh->topdat);
+ oldsum = GET4(sqh->sum);
+ asis = GET4(sqh->asis);
+ nst = GET4(sqh->text);
+ nsd = GET4(sqh->data);
+ b->rp += SQHDRLEN;
+ ex = (Exec*)b->rp;
+ if(GET4(ex->magic) != E_MAGIC){
+ print("zqs: not StrongARM executable\n");
+ return -1;
+ }
+ *entryp = GET4(ex->entry);
+ b->rp += EXECHDRLEN;
+ loada = KADDR(PADDR(*entryp));
+ wp = unsqzseg(loada, b, nst, toptxt, "text");
+ if(wp == nil){
+ print("zqs: format error\n");
+ return -1;
+ }
+ if(nsd){
+ wp = (uchar*)PGROUND((ulong)wp);
+ wp = unsqzseg(wp, b, nsd, topdat, "data");
+ if(wp == nil){
+ print("zqs: format error\n");
+ return -1;
+ }
+ }
+ if(asis){
+ memmove(wp, b->rp, asis);
+ wp += asis;
+ b->rp += asis;
+ }
+ if(chksum != oldsum){
+ print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum);
+ return -1;
+ }
+ return wp-loada;
+}
+
+static uchar *
+unsqzseg(uchar *wp, Block *b, long ns, long top, char *what)
+{
+ static Squeeze sq3, sq4;
+
+ print("unpack %s %8.8lux %lud:", what, wp, ns);
+ if(ns == 0)
+ return wp;
+ if(rdtab(b, &sq3, 0) < 0)
+ return nil;
+ if(rdtab(b, &sq4, 8) < 0)
+ return nil;
+ if(BLEN(b) < ns){
+ print(" **size error\n");
+ return nil;
+ }
+ unsqzal = alarm(500, unsqzdot, nil);
+ wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top);
+ cancel(unsqzal);
+ unsqzal = nil;
+ print("\n");
+ if(wp == nil){
+ print("zqs: corrupt squeezed data stream\n");
+ return nil;
+ }
+ b->rp += ns;
+ return wp;
+}
+
+static ulong*
+unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+ ulong nx, csum;
+ int code, n;
+
+ if(QFLAG){
+ QREMAP(top); /* adjust top just once, outside the loop */
+ }
+ csum = chksum;
+ while(rp < ep){
+ /* no function calls within this loop for speed */
+ code = *rp;
+ rp++;
+ n = 0;
+ nx = code>>4;
+ do{
+ if(nx == 0){
+ nx = top;
+ }else{
+ if(nx==1){
+ nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+ rp += 4;
+ }else if(nx <= 8){ /* 2 to 8 */
+ nx = ((nx-2)<<8) | rp[0];
+ if(CHECK && nx >= sq4->n)
+ return nil; /* corrupted file */
+ nx = sq4->tab[nx] | rp[1];
+ rp += 2;
+ }else{ /* 9 to 15 */
+ nx = ((nx-9)<<8) | rp[0];
+ if(CHECK && nx >= sq3->n)
+ return nil; /* corrupted file */
+ nx = sq3->tab[nx];
+ rp++;
+ }
+ if(rp > ep)
+ return nil; /* corrupted file */
+ if(QFLAG){
+ QREMAP(nx);
+ }
+ }
+ *wp = nx;
+ wp++;
+ csum += nx;
+ nx = code & 0xF;
+ }while(++n == 1);
+ }
+ chksum = csum;
+ return wp;
+}
+
+static int
+rdtab(Block *b, Squeeze *sq, int shift)
+{
+ uchar *p, *ep;
+ ulong v, w;
+ int i;
+
+ if(BLEN(b) < 2)
+ return -1;
+ i = (b->rp[0]<<8) | b->rp[1];
+ if(1)
+ print(" T%d", i);
+ b->rp += 2;
+ if((i -= 2) > 0){
+ if(BLEN(b) < i)
+ return -1;
+ }
+ sq->n = 0;
+ p = b->rp;
+ ep = b->rp+i;
+ b->rp += i;
+ v = 0;
+ while(p < ep){
+ w = 0;
+ do{
+ if(p >= ep)
+ return -1;
+ w = (w<<7) | (*p & 0x7F);
+ }while(*p++ & 0x80);
+ v += w;
+ if(0)
+ print("%d %8.8lux %8.8lux\n", sq->n, v, w);
+ sq->tab[sq->n++] = v<<shift;
+ }
+ return 0;
+}