diff options
Diffstat (limited to 'os/boot/puma')
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 = ðer[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 = ðer[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; +} |
