diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ppc |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ppc')
35 files changed, 12118 insertions, 0 deletions
diff --git a/sys/src/9/ppc/blast b/sys/src/9/ppc/blast new file mode 100755 index 000000000..5581d7a40 --- /dev/null +++ b/sys/src/9/ppc/blast @@ -0,0 +1,59 @@ +dev + root + cons + env + flash + pipe + proc + mnt + srv + dup + ssl + cap + kprof + uart + irq + + ether netif + ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno + +link + etherfcc ethermii + ethermedium + netdevmedium + loopbackmedium + +misc + uartsmc + m8260 + +ip + tcp + udp + ipifc + icmp + icmp6 + +port + int cpuserver = 1; + +boot cpu + tcp + +bootdir + /power/bin/rc + /rc/lib/rcmain + /power/bin/bind + /power/bin/sed + /power/bin/srv + /power/bin/cat + /power/bin/cp + /power/bin/rm + /power/bin/echo + /power/bin/mount + /power/bin/sleep + /power/bin/ip/ipconfig + /power/bin/auth/factotum + /power/bin/ls + /power/bin/auth/wrkey + /sys/lib/sysconfig/blast/boot diff --git a/sys/src/9/ppc/blast.h b/sys/src/9/ppc/blast.h new file mode 100755 index 000000000..4b4904842 --- /dev/null +++ b/sys/src/9/ppc/blast.h @@ -0,0 +1,99 @@ +/* + * Here, we define everything that is specific for the blast board from Crawford Hill + */ + + +/* Clock speed of the blast board */ +#define CLKIN 72000000 + +/* + * Blast memory layout: + * CS0: FE000000 -> FFFFFFFF (Flash) + * CS1: FC000000 -> FCFFFFFF (DSP hpi) + * CS2: 00000000 -> 03FFFFFF (60x sdram) + * CS3: 04000000 -> 04FFFFFF (FPGA) + * CS4: 05000000 -> 06FFFFFF (local bus sdram) + * CS5: 07000000 -> 070FFFFF (eeprom - not populated) + * CS6: E0000000 -> E0FFFFFF (FPGA) + * + * Main Board memory lay out: + * CS0: FE000000 -> FEFFFFFF (16 M FLASH) + * CS1: FC000000 -> FCFFFFFF (16 M DSP1) + * CS2: 00000000 -> 03FFFFFF (64 M SDRAM) + * CS3: 04000000 -> 04FFFFFF (16M DSP2) + * CS4: 05000000 -> 06FFFFFF (32 M Local SDRAM) + * CS5: 07000000 -> 070FFFFF (eeprom - not populated) + * CS6: E0000000 -> E0FFFFFF (16 M FPGA) + * + * CS2, CS3, CS4, (and CS5) are covered by DBAT 0, CS0 and CS1 by DBAT 3, CS6 by DBAT 2 + */ +#define FLASHMEM 0xfe000000 +#define FLASHSIZE 0x01000000 +#define DSP1BASE 0xfc000000 +#define DSP1SIZE 0x01000000 +#define MEM1BASE 0x00000000 +#define MEM1SIZE 0x04000000 +#define DSP2BASE 0x04000000 +#define DSP2SIZE 0x01000000 +#define MEM2BASE 0x05000000 +/* #define MEM2SIZE 0x02000000 */ +#define MEM2SIZE 0 +#define FPGABASE 0xe0000000 +#define FPGASIZE 0x01000000 + +#define PLAN9INI 0x00460000 + +#define TLBENTRIES 32 +/* + * PTE bits for fault.c. These belong to the second PTE word. Validity is + * implied for putmmu(), and we always set PTE0_V. PTEVALID is used + * here to set cache policy bits on a global basis. + */ +#define PTEVALID PTE1_M +#define PTEWRITE (PTE1_RW|PTE1_C) +#define PTERONLY PTE1_RO +#define PTEUNCACHED PTE1_I + +/* SMC Uart configuration */ +#define SMC1PORT 3 /* Port D */ +#define SMTXD1 BIT(9) +#define SMRXD1 BIT(8) + +/* Ethernet FCC configuration */ +#define A1txer 0x00000004 +#define A1rxdv 0x00000010 +#define A1txen 0x00000008 +#define A1rxer 0x00000020 +#define A1col 0x00000001 +#define A1crs 0x00000002 +#define A1txdat 0x00003c00 +#define A1rxdat 0x0003c000 +#define B2txer 0x00000001 +#define B2rxdv 0x00000002 +#define B2txen 0x00000004 +#define B2rxer 0x00000008 +#define B2col 0x00000010 +#define B2crs 0x00000020 +#define B2txdat 0x000003c0 +#define B2rxdat 0x00003c00 +#define B3rxdv 0x00004000 +#define B3rxer 0x00008000 +#define B3txer 0x00010000 +#define B3txen 0x00020000 +#define B3col 0x00040000 +#define B3crs 0x00080000 +#define B3txdat 0x0f000000 +#define B3rxdat 0x00f00000 + +#define A1psor0 (A1rxdat | A1txdat) +#define A1psor1 (A1col | A1crs | A1txer | A1txen | A1rxdv | A1rxer) +#define A1dir0 (A1rxdat | A1crs | A1col | A1rxer | A1rxdv) +#define A1dir1 (A1txdat | A1txen | A1txer) +#define B2psor0 (B2rxdat | B2txdat | B2crs | B2col | B2rxer | B2rxdv | B2txer) +#define B2psor1 (B2txen) +#define B2dir0 (B2rxdat | B2crs | B2col | B2rxer | B2rxdv) +#define B2dir1 (B2txdat | B2txen | B2txer) +#define B3psor0 (B3rxdat | B3txdat | B3crs | B3col | B3rxer | B3rxdv | B3txer | B3txen) +#define B3psor1 0 +#define B3dir0 (B3rxdat | B3crs | B3col | B3rxer | B3rxdv) +#define B3dir1 (B3txdat | B3txen | B3txer) diff --git a/sys/src/9/ppc/clock.c b/sys/src/9/ppc/clock.c new file mode 100755 index 000000000..30d350a2d --- /dev/null +++ b/sys/src/9/ppc/clock.c @@ -0,0 +1,88 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "io.h" +#include "fns.h" +#include "ureg.h" + +static ulong clkreload; + +void +delayloopinit(void) +{ + ulong v; + uvlong x; + + /* initial value for loopconst set in machinit */ + m->loopconst = 1000; + v = getdec(); + delay(1000); + v -= getdec(); + + x = m->loopconst; + x *= m->dechz; + x /= v; + m->loopconst = x; +} + +void +clockinit(void) +{ + m->dechz = m->bushz/4; /* true for all 604e */ + m->tbhz = m->dechz; /* conjecture; manual doesn't say */ + + delayloopinit(); + + clkreload = m->dechz/HZ-1; + putdec(clkreload); +} + +void +clockintr(Ureg *) +{ + long v; + + v = -getdec(); + if(v > (clkreload >> 1)){ + if(v > clkreload) + m->ticks += v/clkreload; + v = 0; + } + putdec(clkreload-v); +} + +void +delay(int l) +{ + ulong i, j; + + j = 0; + if(m) + j = m->loopconst; + if(j == 0) + j = 1096; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->loopconst; + l += 500; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +ulong +perfticks(void) +{ + return (ulong)fastticks(nil); +} diff --git a/sys/src/9/ppc/dat.h b/sys/src/9/ppc/dat.h new file mode 100755 index 000000000..f92367764 --- /dev/null +++ b/sys/src/9/ppc/dat.h @@ -0,0 +1,231 @@ +typedef struct Conf Conf; +typedef struct FPsave FPsave; +typedef struct ISAConf ISAConf; +typedef struct Imap Imap; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Notsave Notsave; +typedef struct PCArch PCArch; +typedef struct PMMU PMMU; +typedef struct Page Page; +typedef struct Pcidev Pcidev; +typedef struct Proc Proc; +typedef struct Sys Sys; +typedef struct Ureg Ureg; +typedef struct Vctl Vctl; + +#pragma incomplete Ureg +#pragma incomplete Imap + +#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */ + +/* + * parameters for sysproc.c + */ +#define AOUT_MAGIC Q_MAGIC + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Lock +{ + ulong key; /* semaphore (non-zero = locked) */ + ulong sr; + ulong pc; + Proc *p; + ulong pid; + ushort isilock; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + /* Floating point states */ + FPinit = 0, + FPactive = 1, + FPinactive = 2, + /* Bit that's or-ed in during note handling (FP is illegal in note handlers) */ + FPillegal = 0x100, +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPsave +{ + double fpreg[32]; + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + int monitor; /* has display? */ + ulong ialloc; /* bytes available for interrupt time allocation */ + ulong pipeqsize; /* size in bytes of pipe queues */ +}; + +/* + * mmu goo in the Proc structure + */ +#define NCOLOR 1 +struct PMMU +{ + int mmupid; + Ureg *mmureg; /* pointer to ureg structure */ +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong UNUSED; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ +/* + * Fake kmap + */ +typedef void KMap; +#define VA(k) ((ulong)(k)) +#define kmap(p) (KMap*)((p)->pa|KZERO) +#define kunmap(k) + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ +/*0x00*/ int machno; /* physical id of processor */ +/*0x04*/ ulong splpc; /* pc that called splhi() */ +/*0x08*/ Proc *proc; /* current process on this processor */ + /* Debugging/statistics for software TLB in l.s (therefore, also known by l.s) */ +/*0x0c*/ ulong tlbfault; /* type of last miss */ +/*0x10*/ ulong imiss; /* number of instruction misses */ +/*0x14*/ ulong dmiss; /* number of data misses */ + + /* ordering from here on irrelevant */ + + Imap* imap; + + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int inclockintr; + int cputype; + ulong loopconst; + Perf perf; /* performance counters */ + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + ulong clkin; /* basic clock frequency */ + ulong vco_out; + vlong cpuhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + ulong bushz; + ulong dechz; + ulong tbhz; + ulong cpmhz; /* communications processor module frequency */ + ulong brghz; /* baud rate generator frequency */ + + ulong pcclast; + uvlong fastclock; + + int tlbpurge; /* # of tlb purges */ + int pfault; /* # of page faults */ + int cs; + int syscall; + int load; + int intr; + int flushmmu; /* make current proc flush it's mmu state */ + int ilockdepth; + + ulong ptabbase; /* start of page table in kernel virtual space */ + int slotgen; /* next pte (byte offset) when pteg is full */ + int mmupid; /* next mmu pid to use */ + int sweepcolor; + int trigcolor; + Rendez sweepr; + + ulong spuriousintr; + int lastintr; + + /* MUST BE LAST */ + int stack[1]; +}; + +struct +{ + Lock; + short machs; + short exiting; + short ispanic; +}active; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +struct Vctl { + Vctl* next; /* handlers on this vector */ + + char name[KNAMELEN]; /* of driver */ + int isintr; /* interrupt or fault/trap */ + int irq; + + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +}; + +extern Mach mach0; + +extern register Mach *m; +extern register Proc *up; + +extern FPsave initfp; diff --git a/sys/src/9/ppc/devether.c b/sys/src/9/ppc/devether.c new file mode 100755 index 000000000..6d58e70dd --- /dev/null +++ b/sys/src/9/ppc/devether.c @@ -0,0 +1,470 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; +extern uchar etheraddr[]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->dev], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->dev], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->dev], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + qpass(f->in, xbp); + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){ + qnoblock(ether->oq, 1); + return n; + } + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + 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; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[32], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + if(isaconfig("ether", ctlrno, ether) == 0) + continue; + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + memmove(ether->ea, etheraddr, 6); + for(i = 0; i < ether->nopt; i++){ + if(strncmp(ether->opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + } + if(cards[n].reset(ether)) + break; + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI) + ether->irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + /* + * If ether->irq is <0, it is a hack to indicate no interrupt + * used by ethersink. + */ + if(ether->irq >= 0) + intrenable(ether->irq, ether->interrupt, ether, name); + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps >= 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 65*1024); + if(ether->oq == 0) + ether->oq = qopen(65*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + devshutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/sys/src/9/ppc/devflash.c b/sys/src/9/ppc/devflash.c new file mode 100755 index 000000000..b6fe365c0 --- /dev/null +++ b/sys/src/9/ppc/devflash.c @@ -0,0 +1,963 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + Nflash = 2, + Maxwchunk= 1024, /* maximum chunk written by one call to falg->write */ +}; + + +/* + * Flashes are either 8 or 16 bits wide. On some installations (e.g., the + * bitsy, they are interleaved: address 0 is in the first chip, address 2 + * on the second, address 4 on the first, etc. + * We define Funit as the unit that matches the width of a single flash chip, + * so Funit is either `uchar' or `ushort' (I haven't seen 32-bit wide flashes), + * and we define Fword as the unit that matches a set of interleaved Funits. + * We access interleaved flashes simultaneously, by doing single reads and + * writes to both. The macro `mirror' takes a command and replicates it for + * this purpose. + * The Blast board has a non-interleaved 16-bit wide flash. When doing + * writes to it, we must swap bytes. + */ + +typedef struct FlashAlg FlashAlg; +typedef struct Flash Flash; +typedef struct FlashRegion FlashRegion; + +#ifdef WIDTH8 + typedef uchar Funit; /* Width of the flash (uchar or ushort) */ +# define toendian(x) (x) /* Little or big endianness */ +# define fromendian(x) (x) +# define reg(x) ((x)<<1) +# ifdef INTERLEAVED +# define mirror(x) ((x)<<8|(x)) /* Double query for interleaved flashes */ + typedef ushort Fword; /* Width after interleaving */ +# define Wshift 1 +# else +# define mirror(x) (x) + typedef uchar Fword; +# define Wshift 0 +# endif +#else + typedef ushort Funit; +# define toendian(x) ((x)<<8) +# define fromendian(x) ((x)>>8) +# define reg(x) (x) +# ifdef INTERLEAVED +# define mirror(x) (toendian(x)<<16|toendian(x)) + typedef ulong Fword; +# define Wshift 2 +# else +# define mirror(x) toendian(x) + typedef ushort Fword; +# define Wshift 1 +# endif +#endif + +/* this defines a contiguous set of erase blocks of one size */ +struct FlashRegion +{ + ulong addr; /* start of region */ + ulong end; /* end of region + 1 */ + ulong n; /* number of blocks */ + ulong size; /* size of each block */ +}; + +struct Flash +{ + ISAConf; /* contains size */ + RWlock; + Fword *p; + ushort algid; /* access algorithm */ + FlashAlg *alg; + ushort manid; /* manufacturer id */ + ushort devid; /* device id */ + int wbsize; /* size of write buffer */ + ulong nr; /* number of regions */ + uchar bootprotect; + ulong offset; /* beginning offset of this flash */ + FlashRegion r[32]; +}; + +/* this defines a particular access algorithm */ +struct FlashAlg +{ + int id; + char *name; + void (*identify)(Flash*); /* identify device */ + void (*erase)(Flash*, ulong); /* erase a region */ + void (*write)(Flash*, void*, long, ulong); /* write a region */ +}; + +static void ise_id(Flash*); +static void ise_erase(Flash*, ulong); +static void ise_write(Flash*, void*, long, ulong); + +static void afs_id(Flash*); +static void afs_erase(Flash*, ulong); +static void afs_write(Flash*, void*, long, ulong); + +static ulong blockstart(Flash*, ulong); +static ulong blockend(Flash*, ulong); + +FlashAlg falg[] = +{ + { 1, "Intel/Sharp Extended", ise_id, ise_erase, ise_write }, + { 2, "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write }, +}; + +Flash flashes[Nflash]; + +/* + * common flash interface + */ +static uchar +cfigetc(Flash *flash, int off) +{ + uchar rv; + + flash->p[reg(0x55)] = mirror(0x98); + rv = fromendian(flash->p[reg(off)]); + flash->p[reg(0x55)] = mirror(0xFF); + return rv; +} + +static ushort +cfigets(Flash *flash, int off) +{ + return (cfigetc(flash, off+1)<<8)|cfigetc(flash, off); +} + +static ulong +cfigetl(Flash *flash, int off) +{ + return (cfigetc(flash, off+3)<<24)|(cfigetc(flash, off+2)<<16)| + (cfigetc(flash, off+1)<<8)|cfigetc(flash, off); +} + +static void +cfiquery(Flash *flash) +{ + uchar q, r, y; + ulong x, addr; + + q = cfigetc(flash, 0x10); + r = cfigetc(flash, 0x11); + y = cfigetc(flash, 0x12); + if(q != 'Q' || r != 'R' || y != 'Y'){ + print("cfi query failed: %ux %ux %ux\n", q, r, y); + return; + } + flash->algid = cfigetc(flash, 0x13); + flash->size = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x27))); + flash->wbsize = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x2a))); + flash->nr = cfigetc(flash, 0x2c); + if(flash->nr > nelem(flash->r)){ + print("cfi reports > %d regions\n", nelem(flash->r)); + flash->nr = nelem(flash->r); + } + addr = 0; + for(q = 0; q < flash->nr; q++){ + x = cfigetl(flash, q+0x2d); + flash->r[q].size = (sizeof(Fword)/sizeof(Funit)) * 256 * (x>>16); + flash->r[q].n = (x&0xffff)+1; + flash->r[q].addr = addr; + addr += flash->r[q].size*flash->r[q].n; + flash->r[q].end = addr; + } +} + +/* + * flash device interface + */ + +enum +{ + Qtopdir, + Q2nddir, + Qfctl, + Qfdata, + + Maxpart= 8, +}; + + +typedef struct FPart FPart; +struct FPart +{ + Flash *flash; + char *name; + char *ctlname; + ulong start; + ulong end; +}; +static FPart part[Maxpart]; + +#define FQID(p,q) ((p)<<8|(q)) +#define FTYPE(q) ((q) & 0xff) +#define FPART(q) (&part[(q) >>8]) + +static int +gen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + Qid q; + FPart *fp; + + q.vers = 0; + + /* top level directory contains the name of the network */ + if(c->qid.path == Qtopdir){ + switch(i){ + case DEVDOTDOT: + q.path = Qtopdir; + q.type = QTDIR; + devdir(c, q, "#F", 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = Q2nddir; + q.type = QTDIR; + devdir(c, q, "flash", 0, eve, DMDIR|0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* second level contains all partitions and their control files */ + switch(i) { + case DEVDOTDOT: + q.path = Qtopdir; + q.type = QTDIR; + devdir(c, q, "#F", 0, eve, DMDIR|0555, dp); + break; + default: + if(i >= 2*Maxpart) + return -1; + fp = &part[i>>1]; + if(fp->name == nil) + return 0; + if(i & 1){ + q.path = FQID(i>>1, Qfdata); + q.type = QTFILE; + devdir(c, q, fp->name, fp->end-fp->start, eve, 0660, dp); + } else { + q.path = FQID(i>>1, Qfctl); + q.type = QTFILE; + devdir(c, q, fp->ctlname, 0, eve, 0660, dp); + } + break; + } + return 1; +} + +static Flash * +findflash(ulong addr) +{ + Flash *flash; + + for (flash = flashes; flash < flashes + Nflash; flash++) + if(addr >= flash->offset && addr < flash->offset + flash->size) + return flash; + return nil; +} + +static FPart* +findpart(char *name) +{ + int i; + + for(i = 0; i < Maxpart; i++) + if(part[i].name != nil && strcmp(name, part[i].name) == 0) + break; + if(i >= Maxpart) + return nil; + return &part[i]; +} + +static void +addpart(FPart *fp, char *name, ulong start, ulong end) +{ + int i; + char ctlname[64]; + Flash *flash; + if (start > end) + error(Ebadarg); + if(fp == nil){ + flash = findflash(start); + if (flash == nil || end > flash->offset + flash->size) + error(Ebadarg); + start -= flash->offset; + end -= flash->offset; + } else { + start += fp->start; + end += fp->start; + if(start >= fp->end || end > fp->end){ + error(Ebadarg); + } + flash = fp->flash; + } + if(blockstart(flash, start) != start) + error("must start on erase boundary"); + if(blockstart(flash, end) != end && end != flash->size) + error("must end on erase boundary"); + + fp = findpart(name); + if(fp != nil) + error(Eexist); + for(i = 0; i < Maxpart; i++) + if(part[i].name == nil) + break; + if(i == Maxpart) + error("no more partitions"); + fp = &part[i]; + kstrdup(&fp->name, name); + snprint(ctlname, sizeof ctlname, "%sctl", name); + kstrdup(&fp->ctlname, ctlname); + fp->flash = flash; + fp->start = start; + fp->end = end; +} + +static void +rempart(FPart *fp) +{ + char *p, *cp; + + p = fp->name; + fp->name = nil; + cp = fp->ctlname; + fp->ctlname = nil; + free(p); + free(cp); +} + +void +flashinit(void) +{ + int i, ctlrno; + char *fname; + ulong offset; + Flash *flash; + + offset = 0; + for (ctlrno = 0; ctlrno < Nflash; ctlrno++){ + flash = flashes + ctlrno; + if(isaconfig("flash", ctlrno, flash) == 0) + continue; + flash->p = (Fword*)flash->mem; + cfiquery(flash); + for(i = 0; i < nelem(falg); i++) + if(flash->algid == falg[i].id){ + flash->alg = &falg[i]; + (*flash->alg->identify)(flash); + break; + } + flash->bootprotect = 1; + flash->offset = offset; + fname = malloc(8); + sprint(fname, "flash%d", ctlrno); + addpart(nil, fname, offset, offset + flash->size); + offset += flash->size; + } +} + +static Chan* +flashattach(char* spec) +{ + return devattach('F', spec); +} + +static Walkqid* +flashwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, gen); +} + +static int +flashstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, gen); +} + +static Chan* +flashopen(Chan* c, int omode) +{ + omode = openmode(omode); + if(strcmp(up->user, eve)!=0) + error(Eperm); + return devopen(c, omode, nil, 0, gen); +} + +static void +flashclose(Chan*) +{ +} + +static long +flashctlread(FPart *fp, void* a, long n, vlong off) +{ + char *buf, *p, *e; + int i; + ulong addr, end; + Flash *flash; + + flash = fp->flash; + buf = smalloc(1024); + e = buf + 1024; + p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9x 0x%-9ux 0x%-9ux\n", + flash->offset, fp->start, fp->end-fp->start, flash->wbsize, flash->manid, flash->devid); + addr = fp->start; + for(i = 0; i < flash->nr && addr < fp->end; i++) + if(flash->r[i].addr <= addr && flash->r[i].end > addr){ + if(fp->end <= flash->r[i].end) + end = fp->end; + else + end = flash->r[i].end; + p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", addr, + (end-addr)/flash->r[i].size, flash->r[i].size); + addr = end; + } + n = readstr(off, a, n, buf); + free(buf); + return n; +} + +static long +flashdataread(FPart *fp, void* a, long n, vlong off) +{ + Flash *flash; + + flash = fp->flash; + rlock(flash); + if(waserror()){ + runlock(flash); + nexterror(); + } + if(fp->name == nil) + error("partition vanished"); + if(!iseve()) + error(Eperm); + off += fp->start; + if(off >= fp->end) + n = 0; + if(off+n >= fp->end) + n = fp->end - off; + if(n > 0) + memmove(a, ((uchar*)flash->mem)+off, n); + runlock(flash); + poperror(); + + return n; +} + +static long +flashread(Chan* c, void* a, long n, vlong off) +{ + int t; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, gen); + t = FTYPE(c->qid.path); + switch(t){ + default: + error(Eperm); + case Qfctl: + n = flashctlread(FPART(c->qid.path), a, n, off); + break; + case Qfdata: + n = flashdataread(FPART(c->qid.path), a, n, off); + break; + } + return n; +} + +static void +bootprotect(ulong addr) +{ + FlashRegion *r; + Flash *flash; + + flash = findflash(addr); + if (flash == nil) + error(Ebadarg); + if(flash->bootprotect == 0) + return; + if(flash->nr == 0) + error("writing over boot loader disallowed"); + r = flash->r; + if(addr >= r->addr && addr < r->addr + r->size) + error("writing over boot loader disallowed"); +} + +static ulong +blockstart(Flash *flash, ulong addr) +{ + FlashRegion *r, *e; + ulong x; + + r = flash->r; + for(e = &flash->r[flash->nr]; r < e; r++){ + if(addr >= r->addr && addr < r->end){ + x = addr - r->addr; + x /= r->size; + return r->addr + x*r->size; + } + } + + return (ulong)-1; +} + +static ulong +blockend(Flash *flash, ulong addr) +{ + FlashRegion *r, *e; + ulong x; + + r = flash->r; + for(e = &flash->r[flash->nr]; r < e; r++) + if(addr >= r->addr && addr < r->end){ + x = addr - r->addr; + x /= r->size; + return r->addr + (x+1)*r->size; + } + + return (ulong)-1; +} + +static long +flashctlwrite(FPart *fp, char *p, long n) +{ + Cmdbuf *cmd; + ulong off; + Flash *flash; + + if(fp == nil) + panic("flashctlwrite"); + + flash = fp->flash; + cmd = parsecmd(p, n); + wlock(flash); + if(waserror()){ + wunlock(flash); + nexterror(); + } + if(strcmp(cmd->f[0], "erase") == 0){ + switch(cmd->nf){ + case 2: + /* erase a single block in the partition */ + off = atoi(cmd->f[1]); + off += fp->start; + if(off >= fp->end) + error("region not in partition"); + if(off != blockstart(flash, off)) + error("erase must be a block boundary"); + bootprotect(off); + (*flash->alg->erase)(flash, off); + break; + case 1: + /* erase the whole partition */ + bootprotect(fp->start); + for(off = fp->start; off < fp->end; off = blockend(flash, off)) + (*flash->alg->erase)(flash, off); + break; + default: + error(Ebadarg); + } + } else if(strcmp(cmd->f[0], "add") == 0){ + if(cmd->nf != 4) + error(Ebadarg); + addpart(fp, cmd->f[1], strtoul(cmd->f[2], nil, 0), strtoul(cmd->f[3], nil, 0)); + } else if(strcmp(cmd->f[0], "remove") == 0){ + rempart(fp); + } else if(strcmp(cmd->f[0], "protectboot") == 0){ + if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0) + flash->bootprotect = 1; + else + flash->bootprotect = 0; + } else + error(Ebadarg); + poperror(); + wunlock(flash); + free(cmd); + + return n; +} + +static long +flashdatawrite(FPart *fp, uchar *p, long n, long off) +{ + uchar *end; + int m; + int on; + long ooff; + uchar *buf; + Flash *flash; + + if(fp == nil) + panic("flashdatawrite"); + + flash = fp->flash; + buf = nil; + wlock(flash); + if(waserror()){ + wunlock(flash); + if(buf != nil) + free(buf); + nexterror(); + } + + if(fp->name == nil) + error("partition vanished"); + if(!iseve()) + error(Eperm); + + /* can't cross partition boundaries */ + off += fp->start; + if(off >= fp->end || off+n > fp->end || n <= 0) + error(Ebadarg); + + /* make sure we're not writing the boot sector */ + bootprotect(off); + + on = n; + + /* + * get the data into kernel memory to avoid faults during writing. + * if write is not on a quad boundary or not a multiple of 4 bytes, + * extend with data already in flash. + */ + buf = smalloc(n+8); + m = off & 3; + if(m){ + *(ulong*)buf = flash->p[off>>Wshift]; + n += m; + off -= m; + } + if(n & 3){ + n -= n & 3; + *(ulong*)(&buf[n]) = flash->p[(off+n)>>Wshift]; + n += 4; + } + memmove(&buf[m], p, on); + + /* (*flash->alg->write) can't cross blocks */ + ooff = off; + p = buf; + for(end = p + n; p < end; p += m){ + m = blockend(flash, off) - off; + if(m > end - p) + m = end - p; + if(m > Maxwchunk) + m = Maxwchunk; + (*flash->alg->write)(flash, p, m, off); + off += m; + } + + /* make sure write succeeded */ + if(memcmp(buf, &flash->p[ooff>>Wshift], n) != 0) + error("written bytes don't match"); + + wunlock(flash); + free(buf); + poperror(); + + return on; +} + +static long +flashwrite(Chan* c, void* a, long n, vlong off) +{ + int t; + + if(c->qid.type == QTDIR) + error(Eperm); + + if(!iseve()) + error(Eperm); + + t = FTYPE(c->qid.path); + switch(t){ + default: + panic("flashwrite"); + case Qfctl: + n = flashctlwrite(FPART(c->qid.path), a, n); + break; + case Qfdata: + n = flashdatawrite(FPART(c->qid.path), a, n, off); + break; + } + return n; +} + +Dev flashdevtab = { + 'F', + "flash", + + devreset, + flashinit, + devshutdown, + flashattach, + flashwalk, + flashstat, + flashopen, + devcreate, + flashclose, + flashread, + devbread, + flashwrite, + devbwrite, + devremove, + devwstat, +}; + +enum +{ + /* status register */ + ISEs_lockerr= 1<<1, + ISEs_powererr= 1<<3, + ISEs_progerr= 1<<4, + ISEs_eraseerr= 1<<5, + ISEs_ready= 1<<7, + ISEs_err= (ISEs_lockerr|ISEs_powererr|ISEs_progerr|ISEs_eraseerr), + + /* extended status register */ + ISExs_bufavail= 1<<7, +}; + +/* intel/sharp extended command set */ +static void +ise_reset(Flash* flash) +{ + flash->p[reg(0xaa)] = mirror(0xff); /* reset */ +} + +static void +ise_id(Flash* flash) +{ + ise_reset(flash); + flash->p[reg(0xaaa)] = mirror(0x90); /* uncover vendor info */ + flash->manid = fromendian(flash->p[reg(0x0)]); + flash->devid = fromendian(flash->p[reg(0x1)]); + ise_reset(flash); +} + +static void +ise_clearerror(Flash* flash) +{ + flash->p[reg(0x200)] = mirror(0x50); + +} + +static void +ise_error(int bank, ulong status) +{ + char err[64]; + + if(status & (ISEs_lockerr)){ + sprint(err, "flash%d: block locked %lux", bank, status); + error(err); + } + if(status & (ISEs_powererr)){ + sprint(err, "flash%d: low prog voltage %lux", bank, status); + error(err); + } + if(status & (ISEs_progerr|ISEs_eraseerr)){ + sprint(err, "flash%d: i/o error %lux", bank, status); + error(err); + } +} +static void +ise_erase(Flash *flash, ulong addr) +{ + ulong start; + ulong x; + + addr >>= Wshift; + + flashprogpower(1); + flash->p[addr] = mirror(0x20); + flash->p[addr] = mirror(0xd0); + start = m->ticks; + do { + x = fromendian(flash->p[addr]); + if((x & mirror(ISEs_ready)) == mirror(ISEs_ready)) + break; + } while(TK2MS(m->ticks-start) < 1500); + flashprogpower(0); + + ise_clearerror(flash); + ise_error(0, x); + ise_error(1, x>>16); + + ise_reset(flash); +} +/* + * the flash spec claimes writing goes faster if we use + * the write buffer. We fill the write buffer and then + * issue the write request. After the write request, + * subsequent reads will yield the status register. + * + * returns the status, even on timeouts. + * + * NOTE: I tried starting back to back buffered writes + * without reading the status in between, as the + * flowchart in the intel data sheet suggests. + * However, it always responded with an illegal + * command sequence, so I must be missing something. + * If someone learns better, please email me, though + * I doubt it will be much faster. - presotto@bell-labs.com + */ +static long +ise_wbwrite(Flash *flash, Fword *p, int n, ulong off, ulong baddr, ulong *status) +{ + Fword x; + ulong start; + int i; + int s; + + /* put flash into write buffer mode */ + start = m->ticks; + for(;;) { + s = splhi(); + /* request write buffer mode */ + flash->p[baddr] = mirror(0xe8); + + /* look at extended status reg for status */ + if((flash->p[baddr] & mirror(1<<7)) == mirror(1<<7)) + break; + splx(s); + + /* didn't work, keep trying for 2 secs */ + if(TK2MS(m->ticks-start) > 2000){ + /* set up to read status */ + flash->p[baddr] = mirror(0x70); + *status = fromendian(flash->p[baddr]); + pprint("write buffered cmd timed out\n"); + return -1; + } + } + + /* fill write buffer */ + flash->p[baddr] = mirror(n-1); + for(i = 0; i < n; i++) + flash->p[off+i] = *p++; + + /* program from buffer */ + flash->p[baddr] = mirror(0xd0); + splx(s); + + /* wait till the programming is done */ + start = m->ticks; + for(;;) { + x = flash->p[baddr]; /* read status register */ + *status = fromendian(x); + if((x & mirror(ISEs_ready)) == mirror(ISEs_ready)) + break; + if(TK2MS(m->ticks-start) > 2000){ + pprint("read status timed out\n"); + return -1; + } + } + if(x & mirror(ISEs_err)) + return -1; + + return n; +} + +static void +ise_write(Flash *flash, void *a, long n, ulong off) +{ + Fword *p, *end; + int i, wbsize; + ulong x, baddr; + + /* everything in terms of Fwords */ + wbsize = flash->wbsize >> Wshift; + baddr = blockstart(flash, off) >> Wshift; + off >>= Wshift; + n >>= Wshift; + p = a; + + /* first see if write will succeed */ + for(i = 0; i < n; i++) + if((p[i] & flash->p[off+i]) != p[i]) + error("flash needs erase"); + + if(waserror()){ + ise_reset(flash); + flashprogpower(0); + nexterror(); + } + flashprogpower(1); + + /* + * use the first write to reach + * a write buffer boundary. the intel maunal + * says writes starting at wb boundaries + * maximize speed. + */ + i = wbsize - (off & (wbsize-1)); + for(end = p + n; p < end;){ + if(i > end - p) + i = end - p; + + if(ise_wbwrite(flash, p, i, off, baddr, &x) < 0) + break; + + off += i; + p += i; + i = wbsize; + } + + ise_clearerror(flash); + ise_error(0, x); + ise_error(1, x>>16); + + ise_reset(flash); + flashprogpower(0); + poperror(); +} + +/* amd/fujitsu standard command set + * I don't have an amd chipset to work with + * so I'm loathe to write this yet. If someone + * else does, please send it to me and I'll + * incorporate it -- presotto@bell-labs.com + */ +static void +afs_reset(Flash *flash) +{ + flash->p[reg(0xaa)] = mirror(0xf0); /* reset */ +} +static void +afs_id(Flash *flash) +{ + afs_reset(flash); + flash->p[reg(0xaa)] = mirror(0xf0); /* reset */ + flash->p[reg(0xaaa)] = mirror(0xaa); /* query vendor block */ + flash->p[reg(0x554)] = mirror(0x55); + flash->p[reg(0xaaa)] = mirror(0x90); + flash->manid = fromendian(flash->p[reg(0x00)]); + afs_reset(flash); + flash->p[reg(0xaaa)] = mirror(0xaa); /* query vendor block */ + flash->p[reg(0x554)] = mirror(0x55); + flash->p[reg(0xaaa)] = mirror(0x90); + flash->devid = fromendian(flash->p[reg(0x02)]); + afs_reset(flash); +} +static void +afs_erase(Flash*, ulong) +{ + error("amd/fujistsu erase not implemented"); +} +static void +afs_write(Flash*, void*, long, ulong) +{ + error("amd/fujistsu write not implemented"); +} diff --git a/sys/src/9/ppc/devirq.c b/sys/src/9/ppc/devirq.c new file mode 100755 index 000000000..dab8ceac2 --- /dev/null +++ b/sys/src/9/ppc/devirq.c @@ -0,0 +1,355 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "m8260.h" +#include "../port/error.h" + +enum{ + IRQ0 = 18, + Level = 0, + Edge = 1, +}; + +enum{ + Qdir, + Qirq1, + Qirq2, + Qirq3, + Qirq4, + Qirq5, + Qirq6, + Qirq7, + Qmstimer, + Qfpgareset, + NIRQ, +}; + +static Dirtab irqdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "irq1", {Qirq1}, 0, 0666, + "irq2", {Qirq2}, 0, 0666, + "irq3", {Qirq1}, 0, 0666, + "irq4", {Qirq1}, 0, 0666, + "irq5", {Qirq1}, 0, 0666, + "irq6", {Qirq1}, 0, 0666, + "irq7", {Qirq1}, 0, 0666, + "mstimer", {Qmstimer}, 0, 0666, + "fpgareset", {Qfpgareset}, 0, 0222, +}; + +enum +{ + CMinterrupt, + CMmode, + CMreset, + CMwait, + CMdebug, +}; + +Cmdtab irqmsg[] = +{ + CMinterrupt, "interrupt", 2, + CMmode, "mode", 2, + CMreset, "reset", 1, + CMwait, "wait", 1, + CMdebug, "debug", 1, +}; + +typedef struct Irqconfig Irqconfig; +struct Irqconfig { + int intenable; /* Interrupts are enabled */ + int mode; /* level == 0; edge == 1 */ + ulong interrupts; /* Count interrupts */ + ulong sleepints; /* interrupt count when waiting */ + Rendez r; /* Rendez-vous point for interrupt waiting */ + Irqconfig *next; + Timer; +}; + +Irqconfig *irqconfig[NIRQ]; /* irqconfig[0] is not used */ +Lock irqlock; + +static void interrupt(Ureg*, void*); +void dumpvno(void); + +static void +ticmstimer(Ureg*, Timer *t) +{ + Irqconfig *ic; + + ic = t->ta; + ic->interrupts++; + wakeup(&ic->r); +} + +void +irqenable(Irqconfig *ic, int irq) +{ + /* call with ilock(&irqlock) held */ + + if (ic->intenable) + return; + if (irq == Qmstimer){ + if (ic->tnext == nil) + ic->tns = MS2NS(ic->mode); + ic->tmode = Tperiodic; + timeradd(&ic->Timer); + }else{ + if (irqconfig[irq]){ + ic->next = irqconfig[irq]; + irqconfig[irq] = ic; + }else{ + ic->next = nil; + irqconfig[irq] = ic; + intrenable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name); + } + } + ic->intenable = 1; +} + +void +irqdisable(Irqconfig *ic, int irq) +{ + Irqconfig **pic; + + /* call with ilock(&irqlock) held */ + + if (ic->intenable == 0) + return; + if (irq == Qmstimer){ + timerdel(&ic->Timer); + }else{ + for(pic = &irqconfig[irq]; *pic != ic; pic = &(*pic)->next) + assert(*pic); + *pic = (*pic)->next; + if (irqconfig[irq] == nil) + intrdisable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name); + } + ic->intenable = 0; +} + +static Chan* +irqattach(char *spec) +{ + return devattach('b', spec); +} + +static Walkqid* +irqwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, irqdir, nelem(irqdir), devgen); +} + +static int +irqstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, irqdir, nelem(irqdir), devgen); +} + +static Chan* +irqopen(Chan *c, int omode) +{ + Irqconfig *ic; + int irq; + + irq = (ulong)c->qid.path; + if(irq != Qdir){ + ic = mallocz(sizeof(Irqconfig), 1); + ic->tf = ticmstimer; + ic->ta = ic; + if (irq == Qmstimer) + ic->mode = 1000; + c->aux = ic; + } + return devopen(c, omode, irqdir, nelem(irqdir), devgen); +} + +static void +irqclose(Chan *c) +{ + int irq; + Irqconfig *ic; + + irq = (ulong)c->qid.path; + if(irq == Qdir) + return; + ic = c->aux; + if (irq > Qmstimer) + return; + ilock(&irqlock); + irqdisable(ic, irq); + iunlock(&irqlock); + free(ic); +} + +static int +irqtfn(void *arg) +{ + Irqconfig *ic; + + ic = arg; + return ic->sleepints != ic->interrupts; +} + +static long +irqread(Chan *c, void *buf, long n, vlong) +{ + int irq; + Irqconfig *ic; + char tmp[24]; + + if(n <= 0) + return n; + irq = (ulong)c->qid.path; + if(irq == Qdir) + return devdirread(c, buf, n, irqdir, nelem(irqdir), devgen); + if(irq > Qmstimer){ + print("irqread 0x%llux\n", c->qid.path); + error(Egreg); + } + ic = c->aux; + if (ic->intenable == 0) + error("disabled"); + ic->sleepints = ic->interrupts; + sleep(&ic->r, irqtfn, ic); + if (irq == Qmstimer) + snprint(tmp, sizeof tmp, "%11lud %d", ic->interrupts, ic->mode); + else + snprint(tmp, sizeof tmp, "%11lud %s", ic->interrupts, ic->mode ?"edge":"level"); + n = readstr(0, buf, n, tmp); + return n; +} + +static long +irqwrite(Chan *c, void *a, long n, vlong) +{ + int irq; + Irqconfig *ic; + Cmdbuf *cb; + Cmdtab *ct; + + if(n <= 0) + return n; + + irq = (ulong)c->qid.path; + if(irq <= 0 || irq >= nelem(irqdir)){ + print("irqwrite 0x%llux\n", c->qid.path); + error(Egreg); + } + if (irq == Qfpgareset){ + if (strncmp(a, "reset", 5) == 0) + fpgareset(); + else + error(Egreg); + return n; + } + ic = c->aux; + + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, irqmsg, nelem(irqmsg)); + switch(ct->index) { + case CMinterrupt: + /* Turn interrupts on or off */ + if (strcmp(cb->f[1], "on") == 0){ + ilock(&irqlock); + irqenable(ic, irq); + iomem->siprr = 0x65009770; + iunlock(&irqlock); + }else if (strcmp(cb->f[1], "off") == 0){ + ilock(&irqlock); + irqdisable(ic, irq); + iunlock(&irqlock); + }else + error(Ebadarg); + break; + case CMmode: + /* Set mode */ + if (irq == Qmstimer){ + ic->mode = strtol(cb->f[1], nil, 0); + if (ic->mode <= 0){ + ic->tns = MS2NS(1000); + ic->mode = 1000; + error(Ebadarg); + } + ic->tns = MS2NS(ic->mode); + }else if (strcmp(cb->f[1], "level") == 0){ + ic->mode = Level; + iomem->siexr &= ~(0x8000 >> irq); + }else if (strcmp(cb->f[1], "edge") == 0){ + ic->mode = Edge; + iomem->siexr |= 0x8000 >> irq; + }else + error(Ebadarg); + break; + case CMreset: + ic->interrupts = 0; + break; + case CMwait: + if (ic->intenable == 0) + error("interrupts are off"); + ic->sleepints = ic->interrupts; + sleep(&ic->r, irqtfn, ic); + break; + case CMdebug: + print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux, siprr 0x%lux\n", + iomem->simr_h, iomem->simr_l, + iomem->sipnr_h, iomem->sipnr_l, + iomem->siexr, iomem->siprr); + dumpvno(); + } + poperror(); + free(cb); + + /* Irqi */ + return n; +} + +static void +interrupt(Ureg*, void *arg) +{ + Irqconfig **pic, *ic; + int irq; + + pic = arg; + irq = pic - irqconfig; + if (irq <= 0 || irq > nelem(irqdir)){ + print("Unexpected interrupt: %d\n", irq); + return; + } + ilock(&irqlock); + if (irq <= Qirq7) + iomem->sipnr_h |= 0x8000 >> irq; /* Clear the interrupt */ + for(ic = *pic; ic; ic = ic->next){ + ic->interrupts++; + wakeup(&ic->r); + } + iunlock(&irqlock); +} + +Dev irqdevtab = { + 'b', + "irq", + + devreset, + devinit, + devshutdown, + irqattach, + irqwalk, + irqstat, + irqopen, + devcreate, + irqclose, + irqread, + devbread, + irqwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/ppc/devtls.c b/sys/src/9/ppc/devtls.c new file mode 100755 index 000000000..d333b10b9 --- /dev/null +++ b/sys/src/9/ppc/devtls.c @@ -0,0 +1,2113 @@ +/* + * devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0 + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include <libsec.h> + +typedef struct OneWay OneWay; +typedef struct Secret Secret; +typedef struct TlsRec TlsRec; +typedef struct TlsErrs TlsErrs; + +enum { + Statlen= 1024, /* max. length of status or stats message */ + /* buffer limits */ + MaxRecLen = 1<<14, /* max payload length of a record layer message */ + MaxCipherRecLen = MaxRecLen + 2048, + RecHdrLen = 5, + MaxMacLen = SHA1dlen, + + /* protocol versions we can accept */ + TLSVersion = 0x0301, + SSL3Version = 0x0300, + ProtocolVersion = 0x0301, /* maximum version we speak */ + MinProtoVersion = 0x0300, /* limits on version we accept */ + MaxProtoVersion = 0x03ff, + + /* connection states */ + SHandshake = 1 << 0, /* doing handshake */ + SOpen = 1 << 1, /* application data can be sent */ + SRClose = 1 << 2, /* remote side has closed down */ + SLClose = 1 << 3, /* sent a close notify alert */ + SAlert = 1 << 5, /* sending or sent a fatal alert */ + SError = 1 << 6, /* some sort of error has occured */ + SClosed = 1 << 7, /* it is all over */ + + /* record types */ + RChangeCipherSpec = 20, + RAlert, + RHandshake, + RApplication, + + SSL2ClientHello = 1, + HSSL2ClientHello = 9, /* local convention; see tlshand.c */ + + /* alerts */ + ECloseNotify = 0, + EUnexpectedMessage = 10, + EBadRecordMac = 20, + EDecryptionFailed = 21, + ERecordOverflow = 22, + EDecompressionFailure = 30, + EHandshakeFailure = 40, + ENoCertificate = 41, + EBadCertificate = 42, + EUnsupportedCertificate = 43, + ECertificateRevoked = 44, + ECertificateExpired = 45, + ECertificateUnknown = 46, + EIllegalParameter = 47, + EUnknownCa = 48, + EAccessDenied = 49, + EDecodeError = 50, + EDecryptError = 51, + EExportRestriction = 60, + EProtocolVersion = 70, + EInsufficientSecurity = 71, + EInternalError = 80, + EUserCanceled = 90, + ENoRenegotiation = 100, + + EMAX = 256 +}; + +struct Secret +{ + char *encalg; /* name of encryption alg */ + char *hashalg; /* name of hash alg */ + int (*enc)(Secret*, uchar*, int); + int (*dec)(Secret*, uchar*, int); + int (*unpad)(uchar*, int, int); + DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); + int block; /* encryption block len, 0 if none */ + int maclen; + void *enckey; + uchar mackey[MaxMacLen]; +}; + +struct OneWay +{ + QLock io; /* locks io access */ + QLock seclock; /* locks secret paramaters */ + ulong seq; + Secret *sec; /* cipher in use */ + Secret *new; /* cipher waiting for enable */ +}; + +struct TlsRec +{ + Chan *c; /* io channel */ + int ref; /* serialized by tdlock for atomic destroy */ + int version; /* version of the protocol we are speaking */ + char verset; /* version has been set */ + char opened; /* opened command every issued? */ + char err[ERRMAX]; /* error message to return to handshake requests */ + vlong handin; /* bytes communicated by the record layer */ + vlong handout; + vlong datain; + vlong dataout; + + Lock statelk; + int state; + + /* record layer mac functions for different protocol versions */ + void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); + + /* input side -- protected by in.io */ + OneWay in; + Block *processed; /* next bunch of application data */ + Block *unprocessed; /* data read from c but not parsed into records */ + + /* handshake queue */ + Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */ + int hqref; + Queue *handq; /* queue of handshake messages */ + Block *hprocessed; /* remainder of last block read from handq */ + QLock hqread; /* protects reads for hprocessed, handq */ + + /* output side */ + OneWay out; + + /* protections */ + char *user; + int perm; +}; + +struct TlsErrs{ + int err; + int sslerr; + int tlserr; + int fatal; + char *msg; +}; + +static TlsErrs tlserrs[] = { + {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"}, + {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"}, + {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"}, + {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"}, + {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"}, + {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"}, + {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security paramters"}, + {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"}, + {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"}, + {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"}, + {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"}, + {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"}, + {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"}, + {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"}, + {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"}, + {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"}, + {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"}, + {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"}, + {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"}, + {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, + {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"}, + {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"}, + {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"}, + {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"}, +}; + +enum +{ + /* max. open tls connections */ + MaxTlsDevs = 1024 +}; + +static Lock tdlock; +static int tdhiwat; +static int maxtlsdevs = 128; +static TlsRec **tlsdevs; +static char **trnames; +static char *encalgs; +static char *hashalgs; + +enum{ + Qtopdir = 1, /* top level directory */ + Qprotodir, + Qclonus, + Qencalgs, + Qhashalgs, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qhand, + Qstatus, + Qstats, +}; + +#define TYPE(x) ((x).path & 0xf) +#define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1)) +#define QID(c, y) (((c)<<5) | (y)) + +static void checkstate(TlsRec *, int, int); +static void ensure(TlsRec*, Block**, int); +static void consume(Block**, uchar*, int); +static Chan* buftochan(char*); +static void tlshangup(TlsRec*); +static void tlsError(TlsRec*, char *); +static void alertHand(TlsRec*, char *); +static TlsRec *newtls(Chan *c); +static TlsRec *mktlsrec(void); +static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); +static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); +static void put64(uchar *p, vlong x); +static void put32(uchar *p, u32int); +static void put24(uchar *p, int); +static void put16(uchar *p, int); +static u32int get32(uchar *p); +static int get16(uchar *p); +static void tlsSetState(TlsRec *tr, int new, int old); +static void rcvAlert(TlsRec *tr, int err); +static void sendAlert(TlsRec *tr, int err); +static void rcvError(TlsRec *tr, int err, char *msg, ...); +static int rc4enc(Secret *sec, uchar *buf, int n); +static int des3enc(Secret *sec, uchar *buf, int n); +static int des3dec(Secret *sec, uchar *buf, int n); +static int noenc(Secret *sec, uchar *buf, int n); +static int sslunpad(uchar *buf, int n, int block); +static int tlsunpad(uchar *buf, int n, int block); +static void freeSec(Secret *sec); +static char *tlsstate(int s); + +#pragma varargck argpos rcvError 3 + +static char *tlsnames[] = { +[Qclonus] "clone", +[Qencalgs] "encalgs", +[Qhashalgs] "hashalgs", +[Qdata] "data", +[Qctl] "ctl", +[Qhand] "hand", +[Qstatus] "status", +[Qstats] "stats", +}; + +static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats }; + +static int +tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp) +{ + Qid q; + TlsRec *tr; + char *name, *nm; + int perm, t; + + q.vers = 0; + q.type = QTFILE; + + t = TYPE(c->qid); + switch(t) { + case Qtopdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#a", 0, eve, 0555, dp); + return 1; + } + if(s > 0) + return -1; + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "tls", 0, eve, 0555, dp); + return 1; + case Qprotodir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + return 1; + } + if(s < 3){ + switch(s) { + default: + return -1; + case 0: + q.path = QID(0, Qclonus); + break; + case 1: + q.path = QID(0, Qencalgs); + break; + case 2: + q.path = QID(0, Qhashalgs); + break; + } + perm = 0444; + if(TYPE(q) == Qclonus) + perm = 0555; + devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp); + return 1; + } + s -= 3; + if(s >= tdhiwat) + return -1; + q.path = QID(s, Qconvdir); + q.type = QTDIR; + lock(&tdlock); + tr = tlsdevs[s]; + if(tr != nil) + nm = tr->user; + else + nm = eve; + if((name = trnames[s]) == nil) { + name = trnames[s] = smalloc(16); + sprint(name, "%d", s); + } + devdir(c, q, name, 0, nm, 0555, dp); + unlock(&tdlock); + return 1; + case Qconvdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "tls", 0, eve, 0555, dp); + return 1; + } + if(s < 0 || s >= nelem(convdir)) + return -1; + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr != nil){ + nm = tr->user; + perm = tr->perm; + }else{ + perm = 0; + nm = eve; + } + t = convdir[s]; + if(t == Qstatus || t == Qstats) + perm &= 0444; + q.path = QID(CONV(c->qid), t); + devdir(c, q, tlsnames[t], 0, nm, perm, dp); + unlock(&tdlock); + return 1; + case Qclonus: + case Qencalgs: + case Qhashalgs: + perm = 0444; + if(t == Qclonus) + perm = 0555; + devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp); + return 1; + default: + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr != nil){ + nm = tr->user; + perm = tr->perm; + }else{ + perm = 0; + nm = eve; + } + if(t == Qstatus || t == Qstats) + perm &= 0444; + devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp); + unlock(&tdlock); + return 1; + } + return -1; +} + +static Chan* +tlsattach(char *spec) +{ + Chan *c; + + c = devattach('a', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.type = QTDIR; + c->qid.vers = 0; + return c; +} + +static Walkqid* +tlswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, tlsgen); +} + +static int +tlsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, tlsgen); +} + +static Chan* +tlsopen(Chan *c, int omode) +{ + TlsRec *tr, **pp; + int t, perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + t = TYPE(c->qid); + switch(t) { + default: + panic("tlsopen"); + case Qtopdir: + case Qprotodir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + tr = newtls(c); + if(tr == nil) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qhand: + case Qstatus: + case Qstats: + if((t == Qstatus || t == Qstats) && omode != OREAD) + error(Eperm); + if(waserror()) { + unlock(&tdlock); + nexterror(); + } + lock(&tdlock); + pp = &tlsdevs[CONV(c->qid)]; + tr = *pp; + if(tr == nil) + error("must open connection using clone"); + if((perm & (tr->perm>>6)) != perm + && (strcmp(up->user, tr->user) != 0 + || (perm & tr->perm) != perm)) + error(Eperm); + if(t == Qhand){ + if(waserror()){ + unlock(&tr->hqlock); + nexterror(); + } + lock(&tr->hqlock); + if(tr->handq != nil) + error(Einuse); + tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil); + if(tr->handq == nil) + error("can't allocate handshake queue"); + tr->hqref = 1; + unlock(&tr->hqlock); + poperror(); + } + tr->ref++; + unlock(&tdlock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static int +tlswstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + TlsRec *tr; + int rv; + + d = nil; + if(waserror()){ + free(d); + unlock(&tdlock); + nexterror(); + } + + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + error(Ebadusefd); + if(strcmp(tr->user, up->user) != 0) + error(Eperm); + + d = smalloc(n + sizeof *d); + rv = convM2D(dp, n, &d[0], (char*) &d[1]); + if(rv == 0) + error(Eshortstat); + if(!emptystr(d->uid)) + kstrdup(&tr->user, d->uid); + if(d->mode != ~0UL) + tr->perm = d->mode; + + free(d); + poperror(); + unlock(&tdlock); + + return rv; +} + +static void +dechandq(TlsRec *tr) +{ + lock(&tr->hqlock); + if(--tr->hqref == 0){ + if(tr->handq != nil){ + qfree(tr->handq); + tr->handq = nil; + } + if(tr->hprocessed != nil){ + freeb(tr->hprocessed); + tr->hprocessed = nil; + } + } + unlock(&tr->hqlock); +} + +static void +tlsclose(Chan *c) +{ + TlsRec *tr; + int t; + + t = TYPE(c->qid); + switch(t) { + case Qctl: + case Qdata: + case Qhand: + case Qstatus: + case Qstats: + if((c->flag & COPEN) == 0) + break; + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + break; + + if(t == Qhand) + dechandq(tr); + + lock(&tdlock); + if(--tr->ref > 0) { + unlock(&tdlock); + return; + } + tlsdevs[CONV(c->qid)] = nil; + unlock(&tdlock); + + if(tr->c != nil && !waserror()){ + checkstate(tr, 0, SOpen|SHandshake|SRClose); + sendAlert(tr, ECloseNotify); + poperror(); + } + tlshangup(tr); + if(tr->c != nil) + cclose(tr->c); + freeSec(tr->in.sec); + freeSec(tr->in.new); + freeSec(tr->out.sec); + freeSec(tr->out.new); + free(tr->user); + free(tr); + break; + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(TlsRec *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0); + if(bl == 0) + error(Ehungup); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * give back n bytes + */ +static void +regurgitate(TlsRec *s, uchar *p, int n) +{ + Block *b; + + if(n <= 0) + return; + b = s->unprocessed; + if(s->unprocessed == nil || b->rp - b->base < n) { + b = allocb(n); + memmove(b->wp, p, n); + b->wp += n; + b->next = s->unprocessed; + s->unprocessed = b; + } else { + b->rp -= n; + memmove(b->rp, p, n); + } +} + +/* + * remove at most n bytes from the queue + */ +static Block* +qgrab(Block **l, int n) +{ + Block *bb, *b; + int i; + + b = *l; + if(BLEN(b) == n){ + *l = b->next; + b->next = nil; + return b; + } + + i = 0; + for(bb = b; bb != nil && i < n; bb = bb->next) + i += BLEN(bb); + if(i > n) + i = n; + + bb = allocb(i); + consume(l, bb->wp, i); + bb->wp += i; + return bb; +} + +static void +tlsclosed(TlsRec *tr, int new) +{ + lock(&tr->statelk); + if(tr->state == SOpen || tr->state == SHandshake) + tr->state = new; + else if((new | tr->state) == (SRClose|SLClose)) + tr->state = SClosed; + unlock(&tr->statelk); + alertHand(tr, "close notify"); +} + +/* + * read and process one tls record layer message + * must be called with tr->in.io held + * We can't let Eintrs lose data, since doing so will get + * us out of sync with the sender and break the reliablity + * of the channel. Eintr only happens during the reads in + * consume. Therefore we put back any bytes consumed before + * the last call to ensure. + */ +static void +tlsrecread(TlsRec *tr) +{ + OneWay *volatile in; + Block *volatile b; + uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen]; + int volatile nconsumed; + int len, type, ver, unpad_len; + + nconsumed = 0; + if(waserror()){ + if(strcmp(up->errstr, Eintr) == 0 && !waserror()){ + regurgitate(tr, header, nconsumed); + poperror(); + }else + tlsError(tr, "channel error"); + nexterror(); + } + ensure(tr, &tr->unprocessed, RecHdrLen); + consume(&tr->unprocessed, header, RecHdrLen); + nconsumed = RecHdrLen; + + if((tr->handin == 0) && (header[0] & 0x80)){ + /* Cope with an SSL3 ClientHello expressed in SSL2 record format. + This is sent by some clients that we must interoperate + with, such as Java's JSSE and Microsoft's Internet Explorer. */ + len = (get16(header) & ~0x8000) - 3; + type = header[2]; + ver = get16(header + 3); + if(type != SSL2ClientHello || len < 22) + rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message"); + }else{ /* normal SSL3 record format */ + type = header[0]; + ver = get16(header+1); + len = get16(header+3); + } + if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion)) + rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'", + tr->version, tr->verset?"/set":"", len, type, ver, (char*)header); + if(len > MaxCipherRecLen || len < 0) + rcvError(tr, ERecordOverflow, "record message too long %d", len); + ensure(tr, &tr->unprocessed, len); + nconsumed = 0; + poperror(); + + /* + * If an Eintr happens after this, we'll get out of sync. + * Make sure nothing we call can sleep. + * Errors are ok, as they kill the connection. + * Luckily, allocb won't sleep, it'll just error out. + */ + b = nil; + if(waserror()){ + if(b != nil) + freeb(b); + tlsError(tr, "channel error"); + nexterror(); + } + b = qgrab(&tr->unprocessed, len); + + in = &tr->in; + if(waserror()){ + qunlock(&in->seclock); + nexterror(); + } + qlock(&in->seclock); + p = b->rp; + if(in->sec != nil) { + /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here + should look alike, including timing of the response. */ + unpad_len = (*in->sec->dec)(in->sec, p, len); + if(unpad_len > in->sec->maclen) + len = unpad_len - in->sec->maclen; + + /* update length */ + put16(header+3, len); + put64(seq, in->seq); + in->seq++; + (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac); + if(unpad_len <= in->sec->maclen || memcmp(hmac, p+len, in->sec->maclen) != 0) + rcvError(tr, EBadRecordMac, "record mac mismatch"); + b->wp = b->rp + len; + } + qunlock(&in->seclock); + poperror(); + if(len <= 0) + rcvError(tr, EDecodeError, "runt record message"); + + switch(type) { + default: + rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type); + break; + case RChangeCipherSpec: + if(len != 1 || p[0] != 1) + rcvError(tr, EDecodeError, "invalid change cipher spec"); + qlock(&in->seclock); + if(in->new == nil){ + qunlock(&in->seclock); + rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec"); + } + freeSec(in->sec); + in->sec = in->new; + in->new = nil; + in->seq = 0; + qunlock(&in->seclock); + break; + case RAlert: + if(len != 2) + rcvError(tr, EDecodeError, "invalid alert"); + if(p[0] == 2) + rcvAlert(tr, p[1]); + if(p[0] != 1) + rcvError(tr, EIllegalParameter, "invalid alert fatal code"); + + /* + * propate non-fatal alerts to handshaker + */ + if(p[1] == ECloseNotify) { + tlsclosed(tr, SRClose); + if(tr->opened) + error("tls hungup"); + error("close notify"); + } + if(p[1] == ENoRenegotiation) + alertHand(tr, "no renegotiation"); + else if(p[1] == EUserCanceled) + alertHand(tr, "handshake canceled by user"); + else + rcvError(tr, EIllegalParameter, "invalid alert code"); + break; + case RHandshake: + /* + * don't worry about dropping the block + * qbwrite always queues even if flow controlled and interrupted. + * + * if there isn't any handshaker, ignore the request, + * but notify the other side we are doing so. + */ + lock(&tr->hqlock); + if(tr->handq != nil){ + tr->hqref++; + unlock(&tr->hqlock); + if(waserror()){ + dechandq(tr); + nexterror(); + } + b = padblock(b, 1); + *b->rp = RHandshake; + qbwrite(tr->handq, b); + b = nil; + poperror(); + dechandq(tr); + }else{ + unlock(&tr->hqlock); + if(tr->verset && tr->version != SSL3Version && !waserror()){ + sendAlert(tr, ENoRenegotiation); + poperror(); + } + } + break; + case SSL2ClientHello: + lock(&tr->hqlock); + if(tr->handq != nil){ + tr->hqref++; + unlock(&tr->hqlock); + if(waserror()){ + dechandq(tr); + nexterror(); + } + /* Pass the SSL2 format data, so that the handshake code can compute + the correct checksums. HSSL2ClientHello = HandshakeType 9 is + unused in RFC2246. */ + b = padblock(b, 8); + b->rp[0] = RHandshake; + b->rp[1] = HSSL2ClientHello; + put24(&b->rp[2], len+3); + b->rp[5] = SSL2ClientHello; + put16(&b->rp[6], ver); + qbwrite(tr->handq, b); + b = nil; + poperror(); + dechandq(tr); + }else{ + unlock(&tr->hqlock); + if(tr->verset && tr->version != SSL3Version && !waserror()){ + sendAlert(tr, ENoRenegotiation); + poperror(); + } + } + break; + case RApplication: + if(!tr->opened) + rcvError(tr, EUnexpectedMessage, "application message received before handshake completed"); + tr->processed = b; + b = nil; + break; + } + if(b != nil) + freeb(b); + poperror(); +} + +/* + * got a fatal alert message + */ +static void +rcvAlert(TlsRec *tr, int err) +{ + char *s; + int i; + + s = "unknown error"; + for(i=0; i < nelem(tlserrs); i++){ + if(tlserrs[i].err == err){ + s = tlserrs[i].msg; + break; + } + } + + tlsError(tr, s); + if(!tr->opened) + error(s); + error("tls error"); +} + +/* + * found an error while decoding the input stream + */ +static void +rcvError(TlsRec *tr, int err, char *fmt, ...) +{ + char msg[ERRMAX]; + va_list arg; + + va_start(arg, fmt); + vseprint(msg, msg+sizeof(msg), fmt, arg); + va_end(arg); + + sendAlert(tr, err); + + if(!tr->opened) + error(msg); + error("tls error"); +} + +/* + * make sure the next hand operation returns with a 'msg' error + */ +static void +alertHand(TlsRec *tr, char *msg) +{ + Block *b; + int n; + + lock(&tr->hqlock); + if(tr->handq == nil){ + unlock(&tr->hqlock); + return; + } + tr->hqref++; + unlock(&tr->hqlock); + + n = strlen(msg); + if(waserror()){ + dechandq(tr); + nexterror(); + } + b = allocb(n + 2); + *b->wp++ = RAlert; + memmove(b->wp, msg, n + 1); + b->wp += n + 1; + + qbwrite(tr->handq, b); + + poperror(); + dechandq(tr); +} + +static void +checkstate(TlsRec *tr, int ishand, int ok) +{ + int state; + + lock(&tr->statelk); + state = tr->state; + unlock(&tr->statelk); + if(state & ok) + return; + switch(state){ + case SHandshake: + case SOpen: + break; + case SError: + case SAlert: + if(ishand) + error(tr->err); + error("tls error"); + case SRClose: + case SLClose: + case SClosed: + error("tls hungup"); + } + error("tls improperly configured"); +} + +static Block* +tlsbread(Chan *c, long n, ulong offset) +{ + int ty; + Block *b; + TlsRec *volatile tr; + + ty = TYPE(c->qid); + switch(ty) { + default: + return devbread(c, n, offset); + case Qhand: + case Qdata: + break; + } + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlsbread"); + + if(waserror()){ + qunlock(&tr->in.io); + nexterror(); + } + qlock(&tr->in.io); + if(ty == Qdata){ + checkstate(tr, 0, SOpen); + while(tr->processed == nil) + tlsrecread(tr); + + /* return at most what was asked for */ + b = qgrab(&tr->processed, n); + qunlock(&tr->in.io); + poperror(); + tr->datain += BLEN(b); + }else{ + checkstate(tr, 1, SOpen|SHandshake|SLClose); + + /* + * it's ok to look at state without the lock + * since it only protects reading records, + * and we have that tr->in.io held. + */ + while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq)) + tlsrecread(tr); + + qunlock(&tr->in.io); + poperror(); + + if(waserror()){ + qunlock(&tr->hqread); + nexterror(); + } + qlock(&tr->hqread); + if(tr->hprocessed == nil){ + b = qbread(tr->handq, MaxRecLen + 1); + if(*b->rp++ == RAlert){ + strecpy(up->errstr, up->errstr+ERRMAX, (char*)b->rp); + freeb(b); + nexterror(); + } + tr->hprocessed = b; + } + b = qgrab(&tr->hprocessed, n); + poperror(); + qunlock(&tr->hqread); + tr->handin += BLEN(b); + } + + return b; +} + +static long +tlsread(Chan *c, void *a, long n, vlong off) +{ + Block *volatile b; + Block *nb; + uchar *va; + int i, ty; + char *buf, *s, *e; + ulong offset = off; + TlsRec * tr; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tlsgen); + + tr = tlsdevs[CONV(c->qid)]; + ty = TYPE(c->qid); + switch(ty) { + default: + error(Ebadusefd); + case Qstatus: + buf = smalloc(Statlen); + qlock(&tr->in.seclock); + qlock(&tr->out.seclock); + s = buf; + e = buf + Statlen; + s = seprint(s, e, "State: %s\n", tlsstate(tr->state)); + s = seprint(s, e, "Version: 0x%x\n", tr->version); + if(tr->in.sec != nil) + s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg); + if(tr->in.new != nil) + s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg); + if(tr->out.sec != nil) + s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg); + if(tr->out.new != nil) + seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg); + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qstats: + buf = smalloc(Statlen); + s = buf; + e = buf + Statlen; + s = seprint(s, e, "DataIn: %lld\n", tr->datain); + s = seprint(s, e, "DataOut: %lld\n", tr->dataout); + s = seprint(s, e, "HandIn: %lld\n", tr->handin); + seprint(s, e, "HandOut: %lld\n", tr->handout); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qctl: + buf = smalloc(Statlen); + snprint(buf, Statlen, "%llud", CONV(c->qid)); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qdata: + case Qhand: + b = tlsbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + if(waserror()){ + freeblist(b); + nexterror(); + } + + n = 0; + va = a; + for(nb = b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b); + poperror(); + + return n; +} + +/* + * write a block in tls records + */ +static void +tlsrecwrite(TlsRec *tr, int type, Block *b) +{ + Block *volatile bb; + Block *nb; + uchar *p, seq[8]; + OneWay *volatile out; + int n, maclen, pad, ok; + + out = &tr->out; + bb = b; + if(waserror()){ + qunlock(&out->io); + if(bb != nil) + freeb(bb); + nexterror(); + } + qlock(&out->io); + + ok = SHandshake|SOpen|SRClose; + if(type == RAlert) + ok |= SAlert; + while(bb != nil){ + checkstate(tr, type != RApplication, ok); + + /* + * get at most one maximal record's input, + * with padding on the front for header and + * back for mac and maximal block padding. + */ + if(waserror()){ + qunlock(&out->seclock); + nexterror(); + } + qlock(&out->seclock); + maclen = 0; + pad = 0; + if(out->sec != nil){ + maclen = out->sec->maclen; + pad = maclen + out->sec->block; + } + n = BLEN(bb); + if(n > MaxRecLen){ + n = MaxRecLen; + nb = allocb(n + pad + RecHdrLen); + memmove(nb->wp + RecHdrLen, bb->rp, n); + bb->rp += n; + }else{ + /* + * carefully reuse bb so it will get freed if we're out of memory + */ + bb = padblock(bb, RecHdrLen); + if(pad) + nb = padblock(bb, -pad); + else + nb = bb; + bb = nil; + } + + p = nb->rp; + p[0] = type; + put16(p+1, tr->version); + put16(p+3, n); + + if(out->sec != nil){ + put64(seq, out->seq); + out->seq++; + (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n); + n += maclen; + + /* encrypt */ + n = (*out->sec->enc)(out->sec, p + RecHdrLen, n); + nb->wp = p + RecHdrLen + n; + + /* update length */ + put16(p+3, n); + } + if(type == RChangeCipherSpec){ + if(out->new == nil) + error("change cipher without a new cipher"); + freeSec(out->sec); + out->sec = out->new; + out->new = nil; + out->seq = 0; + } + qunlock(&out->seclock); + poperror(); + + /* + * if bwrite error's, we assume the block is queued. + * if not, we're out of sync with the receiver and will not recover. + */ + if(waserror()){ + if(strcmp(up->errstr, "interrupted") != 0) + tlsError(tr, "channel error"); + nexterror(); + } + devtab[tr->c->type]->bwrite(tr->c, nb, 0); + poperror(); + } + qunlock(&out->io); + poperror(); +} + +static long +tlsbwrite(Chan *c, Block *b, ulong offset) +{ + int ty; + ulong n; + TlsRec *tr; + + n = BLEN(b); + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlsbread"); + + ty = TYPE(c->qid); + switch(ty) { + default: + return devbwrite(c, b, offset); + case Qhand: + tlsrecwrite(tr, RHandshake, b); + tr->handout += n; + break; + case Qdata: + checkstate(tr, 0, SOpen); + tlsrecwrite(tr, RApplication, b); + tr->dataout += n; + break; + } + + return n; +} + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int maclen; + void (*initkey)(Hashalg *, int, Secret *, uchar*); +}; + +static void +initmd5key(Hashalg *ha, int version, Secret *s, uchar *p) +{ + s->maclen = ha->maclen; + if(version == SSL3Version) + s->mac = sslmac_md5; + else + s->mac = hmac_md5; + memmove(s->mackey, p, ha->maclen); +} + +static void +initclearmac(Hashalg *, int, Secret *s, uchar *) +{ + s->maclen = 0; + s->mac = nomac; +} + +static void +initsha1key(Hashalg *ha, int version, Secret *s, uchar *p) +{ + s->maclen = ha->maclen; + if(version == SSL3Version) + s->mac = sslmac_sha1; + else + s->mac = hmac_sha1; + memmove(s->mackey, p, ha->maclen); +} + +static Hashalg hashtab[] = +{ + { "clear", 0, initclearmac, }, + { "md5", MD5dlen, initmd5key, }, + { "sha1", SHA1dlen, initsha1key, }, + { 0 } +}; + +static Hashalg* +parsehashalg(char *p) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++) + if(strcmp(p, ha->name) == 0) + return ha; + error("unsupported hash algorithm"); + return nil; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int keylen; + int ivlen; + void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*); +}; + +static void +initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *) +{ + s->enckey = smalloc(sizeof(RC4state)); + s->enc = rc4enc; + s->dec = rc4enc; + s->block = 0; + setupRC4state(s->enckey, p, ea->keylen); +} + +static void +initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv) +{ + s->enckey = smalloc(sizeof(DES3state)); + s->enc = des3enc; + s->dec = des3dec; + s->block = 8; + setupDES3state(s->enckey, (uchar(*)[8])p, iv); +} + +static void +initclearenc(Encalg *, Secret *s, uchar *, uchar *) +{ + s->enc = noenc; + s->dec = noenc; + s->block = 0; +} + +static Encalg encrypttab[] = +{ + { "clear", 0, 0, initclearenc }, + { "rc4_128", 128/8, 0, initRC4key }, + { "3des_ede_cbc", 3 * 8, 8, initDES3key }, + { 0 } +}; + +static Encalg* +parseencalg(char *p) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++) + if(strcmp(p, ea->name) == 0) + return ea; + error("unsupported encryption algorithm"); + return nil; +} + +static long +tlswrite(Chan *c, void *a, long n, vlong off) +{ + Encalg *ea; + Hashalg *ha; + TlsRec *volatile tr; + Secret *volatile tos, *volatile toc; + Block *volatile b; + Cmdbuf *volatile cb; + int m, ty; + char *p, *e; + uchar *volatile x; + ulong offset = off; + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlswrite"); + + ty = TYPE(c->qid); + switch(ty){ + case Qdata: + case Qhand: + p = a; + e = p + n; + do{ + m = e - p; + if(m > MaxRecLen) + m = MaxRecLen; + + b = allocb(m); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p, m); + poperror(); + b->wp += m; + + tlsbwrite(c, b, offset); + + p += m; + }while(p < e); + return n; + case Qctl: + break; + default: + error(Ebadusefd); + return -1; + } + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + nexterror(); + } + qlock(&tr->in.seclock); + qlock(&tr->out.seclock); + + if(strcmp(cb->f[0], "fd") == 0){ + if(cb->nf != 3) + error("usage: fd open-fd version"); + if(tr->c != nil) + error(Einuse); + m = strtol(cb->f[2], nil, 0); + if(m < MinProtoVersion || m > MaxProtoVersion) + error("unsupported version"); + tr->c = buftochan(cb->f[1]); + tr->version = m; + tlsSetState(tr, SHandshake, SClosed); + }else if(strcmp(cb->f[0], "version") == 0){ + if(cb->nf != 2) + error("usage: version vers"); + if(tr->c == nil) + error("must set fd before version"); + if(tr->verset) + error("version already set"); + m = strtol(cb->f[1], nil, 0); + if(m == SSL3Version) + tr->packMac = sslPackMac; + else if(m == TLSVersion) + tr->packMac = tlsPackMac; + else + error("unsupported version"); + tr->verset = 1; + tr->version = m; + }else if(strcmp(cb->f[0], "secret") == 0){ + if(cb->nf != 5) + error("usage: secret hashalg encalg isclient secretdata"); + if(tr->c == nil || !tr->verset) + error("must set fd and version before secrets"); + + if(tr->in.new != nil){ + freeSec(tr->in.new); + tr->in.new = nil; + } + if(tr->out.new != nil){ + freeSec(tr->out.new); + tr->out.new = nil; + } + + ha = parsehashalg(cb->f[1]); + ea = parseencalg(cb->f[2]); + + p = cb->f[4]; + m = (strlen(p)*3)/2; + x = smalloc(m); + tos = nil; + toc = nil; + if(waserror()){ + freeSec(tos); + freeSec(toc); + free(x); + nexterror(); + } + m = dec64(x, m, p, strlen(p)); + if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen) + error("not enough secret data provided"); + + tos = smalloc(sizeof(Secret)); + toc = smalloc(sizeof(Secret)); + if(!ha->initkey || !ea->initkey) + error("misimplemented secret algorithm"); + (*ha->initkey)(ha, tr->version, tos, &x[0]); + (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]); + (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); + (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); + + if(!tos->mac || !tos->enc || !tos->dec + || !toc->mac || !toc->enc || !toc->dec) + error("missing algorithm implementations"); + if(strtol(cb->f[3], nil, 0) == 0){ + tr->in.new = tos; + tr->out.new = toc; + }else{ + tr->in.new = toc; + tr->out.new = tos; + } + if(tr->version == SSL3Version){ + toc->unpad = sslunpad; + tos->unpad = sslunpad; + }else{ + toc->unpad = tlsunpad; + tos->unpad = tlsunpad; + } + toc->encalg = ea->name; + toc->hashalg = ha->name; + tos->encalg = ea->name; + tos->hashalg = ha->name; + + free(x); + poperror(); + }else if(strcmp(cb->f[0], "changecipher") == 0){ + if(cb->nf != 1) + error("usage: changecipher"); + if(tr->out.new == nil) + error("can't change cipher spec without setting secret"); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + /* + * the real work is done as the message is written + * so the stream is encrypted in sync. + */ + b = allocb(1); + *b->wp++ = 1; + tlsrecwrite(tr, RChangeCipherSpec, b); + return n; + }else if(strcmp(cb->f[0], "opened") == 0){ + if(cb->nf != 1) + error("usage: opened"); + if(tr->in.sec == nil || tr->out.sec == nil) + error("cipher must be configured before enabling data messages"); + lock(&tr->statelk); + if(tr->state != SHandshake && tr->state != SOpen){ + unlock(&tr->statelk); + error("can't enable data messages"); + } + tr->state = SOpen; + unlock(&tr->statelk); + tr->opened = 1; + }else if(strcmp(cb->f[0], "alert") == 0){ + if(cb->nf != 2) + error("usage: alert n"); + if(tr->c == nil) + error("must set fd before sending alerts"); + m = strtol(cb->f[1], nil, 0); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + sendAlert(tr, m); + + if(m == ECloseNotify) + tlsclosed(tr, SLClose); + + return n; + } else + error(Ebadarg); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + return n; +} + +static void +tlsinit(void) +{ + struct Encalg *e; + struct Hashalg *h; + int n; + char *cp; + + tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs); + trnames = smalloc((sizeof *trnames) * maxtlsdevs); + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + cp = encalgs = smalloc(n); + for(e = encrypttab;;){ + strcpy(cp, e->name); + cp += strlen(e->name); + e++; + if(e->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + cp = hashalgs = smalloc(n); + for(h = hashtab;;){ + strcpy(cp, h->name); + cp += strlen(h->name); + h++; + if(h->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; +} + +Dev tlsdevtab = { + 'a', + "tls", + + devreset, + tlsinit, + devshutdown, + tlsattach, + tlswalk, + tlsstat, + tlsopen, + devcreate, + tlsclose, + tlsread, + tlsbread, + tlswrite, + tlsbwrite, + devremove, + tlswstat, +}; + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +static void +sendAlert(TlsRec *tr, int err) +{ + Block *b; + int i, fatal; + char *msg; + + fatal = 1; + msg = "tls unknown alert"; + for(i=0; i < nelem(tlserrs); i++) { + if(tlserrs[i].err == err) { + msg = tlserrs[i].msg; + if(tr->version == SSL3Version) + err = tlserrs[i].sslerr; + else + err = tlserrs[i].tlserr; + fatal = tlserrs[i].fatal; + break; + } + } + + if(!waserror()){ + b = allocb(2); + *b->wp++ = fatal + 1; + *b->wp++ = err; + if(fatal) + tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose); + tlsrecwrite(tr, RAlert, b); + poperror(); + } + if(fatal) + tlsError(tr, msg); +} + +static void +tlsError(TlsRec *tr, char *msg) +{ + int s; + + lock(&tr->statelk); + s = tr->state; + tr->state = SError; + if(s != SError){ + strncpy(tr->err, msg, ERRMAX - 1); + tr->err[ERRMAX - 1] = '\0'; + } + unlock(&tr->statelk); + if(s != SError) + alertHand(tr, msg); +} + +static void +tlsSetState(TlsRec *tr, int new, int old) +{ + lock(&tr->statelk); + if(tr->state & old) + tr->state = new; + unlock(&tr->statelk); +} + +/* hand up a digest connection */ +static void +tlshangup(TlsRec *tr) +{ + Block *b; + + qlock(&tr->in.io); + for(b = tr->processed; b; b = tr->processed){ + tr->processed = b->next; + freeb(b); + } + if(tr->unprocessed != nil){ + freeb(tr->unprocessed); + tr->unprocessed = nil; + } + qunlock(&tr->in.io); + + tlsSetState(tr, SClosed, ~0); +} + +static TlsRec* +newtls(Chan *ch) +{ + TlsRec **pp, **ep, **np; + char **nmp; + int t, newmax; + + if(waserror()) { + unlock(&tdlock); + nexterror(); + } + lock(&tdlock); + ep = &tlsdevs[maxtlsdevs]; + for(pp = tlsdevs; pp < ep; pp++) + if(*pp == nil) + break; + if(pp >= ep) { + if(maxtlsdevs >= MaxTlsDevs) { + unlock(&tdlock); + poperror(); + return nil; + } + newmax = 2 * maxtlsdevs; + if(newmax > MaxTlsDevs) + newmax = MaxTlsDevs; + np = smalloc(sizeof(TlsRec*) * newmax); + memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs); + tlsdevs = np; + pp = &tlsdevs[maxtlsdevs]; + memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs)); + + nmp = smalloc(sizeof *nmp * newmax); + memmove(nmp, trnames, sizeof *nmp * maxtlsdevs); + trnames = nmp; + + maxtlsdevs = newmax; + } + *pp = mktlsrec(); + if(pp - tlsdevs >= tdhiwat) + tdhiwat++; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - tlsdevs, t); + ch->qid.vers = 0; + unlock(&tdlock); + poperror(); + return *pp; +} + +static TlsRec * +mktlsrec(void) +{ + TlsRec *tr; + + tr = mallocz(sizeof(*tr), 1); + if(tr == nil) + error(Enomem); + tr->state = SClosed; + tr->ref = 1; + kstrdup(&tr->user, up->user); + tr->perm = 0660; + return tr; +} + +static char* +tlsstate(int s) +{ + switch(s){ + case SHandshake: + return "Handshaking"; + case SOpen: + return "Established"; + case SRClose: + return "RemoteClosed"; + case SLClose: + return "LocalClosed"; + case SAlert: + return "Alerting"; + case SError: + return "Errored"; + case SClosed: + return "Closed"; + } + return "Unknown"; +} + +static void +freeSec(Secret *s) +{ + if(s != nil){ + free(s->enckey); + free(s); + } +} + +static int +noenc(Secret *, uchar *, int n) +{ + return n; +} + +static int +rc4enc(Secret *sec, uchar *buf, int n) +{ + rc4(sec->enckey, buf, n); + return n; +} + +static int +tlsunpad(uchar *buf, int n, int block) +{ + int pad, nn; + + pad = buf[n - 1]; + nn = n - 1 - pad; + if(nn <= 0 || n % block) + return -1; + while(--n > nn) + if(pad != buf[n - 1]) + return -1; + return nn; +} + +static int +sslunpad(uchar *buf, int n, int block) +{ + int pad, nn; + + pad = buf[n - 1]; + nn = n - 1 - pad; + if(nn <= 0 || n % block) + return -1; + return nn; +} + +static int +blockpad(uchar *buf, int n, int block) +{ + int pad, nn; + + nn = n + block; + nn -= nn % block; + pad = nn - (n + 1); + while(n < nn) + buf[n++] = pad; + return nn; +} + +static int +des3enc(Secret *sec, uchar *buf, int n) +{ + n = blockpad(buf, n, 8); + des3CBCencrypt(buf, n, sec->enckey); + return n; +} + +static int +des3dec(Secret *sec, uchar *buf, int n) +{ + des3CBCdecrypt(buf, n, sec->enckey); + return (*sec->unpad)(buf, n, 8); +} +static DigestState* +nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *) +{ + return nil; +} + +/* + * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac. + */ +static DigestState* +sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s, + DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen) +{ + int i; + uchar pad[48], innerdigest[20]; + + if(xlen > sizeof(innerdigest) + || padlen > sizeof(pad)) + return nil; + + if(klen>64) + return nil; + + /* first time through */ + if(s == nil){ + for(i=0; i<padlen; i++) + pad[i] = 0x36; + s = (*x)(key, klen, nil, nil); + s = (*x)(pad, padlen, nil, s); + if(s == nil) + return nil; + } + + s = (*x)(p, len, nil, s); + if(digest == nil) + return s; + + /* last time through */ + for(i=0; i<padlen; i++) + pad[i] = 0x5c; + (*x)(nil, 0, innerdigest, s); + s = (*x)(key, klen, nil, nil); + s = (*x)(pad, padlen, nil, s); + (*x)(innerdigest, xlen, digest, s); + return nil; +} + +static DigestState* +sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s) +{ + return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40); +} + +static DigestState* +sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s) +{ + return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48); +} + +static void +sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) +{ + DigestState *s; + uchar buf[11]; + + memmove(buf, seq, 8); + buf[8] = header[0]; + buf[9] = header[3]; + buf[10] = header[4]; + + s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + +static void +tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) +{ + DigestState *s; + uchar buf[13]; + + memmove(buf, seq, 8); + memmove(&buf[8], header, 5); + + s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + +static void +put32(uchar *p, u32int x) +{ + p[0] = x>>24; + p[1] = x>>16; + p[2] = x>>8; + p[3] = x; +} + +static void +put64(uchar *p, vlong x) +{ + put32(p, (u32int)(x >> 32)); + put32(p+4, (u32int)x); +} + +static void +put24(uchar *p, int x) +{ + p[0] = x>>16; + p[1] = x>>8; + p[2] = x; +} + +static void +put16(uchar *p, int x) +{ + p[0] = x>>8; + p[1] = x; +} + +static u32int +get32(uchar *p) +{ + return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; +} + +static int +get16(uchar *p) +{ + return (p[0]<<8)|p[1]; +} diff --git a/sys/src/9/ppc/etherfcc.c b/sys/src/9/ppc/etherfcc.c new file mode 100755 index 000000000..0b2e1f21e --- /dev/null +++ b/sys/src/9/ppc/etherfcc.c @@ -0,0 +1,891 @@ +/* + * FCCn ethernet + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "imm.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "../ppc/ethermii.h" + +#define DBG 1 + +enum { + Nrdre = 128, /* receive descriptor ring entries */ + Ntdre = 128, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = Rbsize+CACHELINESZ, /* extra room for alignment */ +}; + +enum { + + /* ether-specific Rx BD bits */ + RxMiss= SBIT(7), + RxeLG= SBIT(10), + RxeNO= SBIT(11), + RxeSH= SBIT(12), + RxeCR= SBIT(13), + RxeOV= SBIT(14), + RxeCL= SBIT(15), + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= SBIT(1), /* pad short frames */ + TxTC= SBIT(5), /* transmit CRC */ + TxeDEF= SBIT(6), + TxeHB= SBIT(7), + TxeLC= SBIT(8), + TxeRL= SBIT(9), + TxeUN= SBIT(14), + TxeCSL= SBIT(15), + + /* psmr */ + CRCE= BIT(24), /* Ethernet CRC */ + FCE= BIT(10), /* flow control */ + PRO= BIT(9), /* promiscuous mode */ + FDE= BIT(5), /* full duplex ethernet */ + LPB= BIT(3), /* local protect bit */ + + /* gfmr */ + ENET= 0xc, /* ethernet mode */ + ENT= BIT(27), + ENR= BIT(26), + TCI= BIT(2), + + /* FCC function code register */ + GBL= 0x20, + BO= 0x18, + EB= 0x10, /* Motorola byte order */ + TC2= 0x04, + DTB= 0x02, + BDB= 0x01, + + /* FCC Event/Mask bits */ + GRA= SBIT(8), + RXC= SBIT(9), + TXC= SBIT(10), + TXE= SBIT(11), + RXF= SBIT(12), + BSY= SBIT(13), + TXB= SBIT(14), + RXB= SBIT(15), +}; + +enum { /* Mcr */ + MDIread = 0x60020000, /* read opcode */ + MDIwrite = 0x50020000, /* write opcode */ +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { +/*0x00*/ FCCparam; +/*0x3c*/ ulong stat_buf; +/*0x40*/ ulong cam_ptr; +/*0x44*/ ulong cmask; +/*0x48*/ ulong cpres; +/*0x4c*/ ulong crcec; +/*0x50*/ ulong alec; +/*0x54*/ ulong disfc; +/*0x58*/ ushort retlim; +/*0x5a*/ ushort retcnt; +/*0x5c*/ ushort p_per; +/*0x5e*/ ushort boff_cnt; +/*0x60*/ ulong gaddr[2]; +/*0x68*/ ushort tfcstat; +/*0x6a*/ ushort tfclen; +/*0x6c*/ ulong tfcptr; +/*0x70*/ ushort mflr; +/*0x72*/ ushort paddr[3]; +/*0x78*/ ushort ibd_cnt; +/*0x7a*/ ushort ibd_start; +/*0x7c*/ ushort ibd_end; +/*0x7e*/ ushort tx_len; +/*0x80*/ uchar ibd_base[32]; +/*0xa0*/ ulong iaddr[2]; +/*0xa8*/ ushort minflr; +/*0xaa*/ ushort taddr[3]; +/*0xb0*/ ushort padptr; +/*0xb2*/ ushort Rsvdb2; +/*0xb4*/ ushort cf_range; +/*0xb6*/ ushort max_b; +/*0xb8*/ ushort maxd1; +/*0xba*/ ushort maxd2; +/*0xbc*/ ushort maxd; +/*0xbe*/ ushort dma_cnt; +/*0xc0*/ ulong octc; +/*0xc4*/ ulong colc; +/*0xc8*/ ulong broc; +/*0xcc*/ ulong mulc; +/*0xd0*/ ulong uspc; +/*0xd4*/ ulong frgc; +/*0xd8*/ ulong ospc; +/*0xdc*/ ulong jbrc; +/*0xe0*/ ulong p64c; +/*0xe4*/ ulong p65c; +/*0xe8*/ ulong p128c; +/*0xec*/ ulong p256c; +/*0xf0*/ ulong p512c; +/*0xf4*/ ulong p1024c; +/*0xf8*/ ulong cam_buf; +/*0xfc*/ ulong Rsvdfc; +/*0x100*/ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + Lock; + int fccid; + int port; + ulong pmdio; + ulong pmdck; + int init; + int active; + int duplex; /* 1 == full */ + FCC* fcc; + + Ring; + Block* rcvbufs[Nrdre]; + Mii* mii; + Timer; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +}; + +static int fccirq[] = {0x20, 0x21, 0x22}; +static int fccid[] = {FCC1ID, FCC2ID, FCC3ID}; + +#ifdef DBG +ulong fccrhisto[16]; +ulong fccthisto[16]; +ulong fccrthisto[16]; +ulong fcctrhisto[16]; +ulong ehisto[0x80]; +#endif + +static int fccmiimir(Mii*, int, int); +static int fccmiimiw(Mii*, int, int, int); +static void fccltimer(Ureg*, Timer*); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + ctlr->active = 1; + ctlr->fcc->gfmr |= ENR|ENT; + iunlock(ctlr); + ctlr->tmode = Tperiodic; + ctlr->tf = fccltimer; + ctlr->ta = ether; + ctlr->tns = 5000000000LL; /* 5 seconds */ + timeradd(ctlr); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + ctlr->active = 0; + ctlr->fcc->gfmr &= ~(ENR|ENT); + iunlock(ctlr); + print("Ether closed\n"); +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->fcc->fpsmr |= PRO; + else + ctlr->fcc->fpsmr &= ~PRO; + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(ether->prom || ether->nmaddr) + ctlr->fcc->fpsmr |= PRO; + else + ctlr->fcc->fpsmr &= ~PRO; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + if(ctlr->init) + return; + while(ctlr->ntq < Ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + dczap(dre, sizeof(BD)); + if(dre->status & BDReady) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("fcc/ether: txstart"); + ctlr->txb[ctlr->tdrh] = b; + if((ulong)b->rp&1) + panic("fcc/ether: txstart align"); /* TO DO: ensure alignment */ + dre->addr = PADDR(b->rp); + dre->length = len; + dcflush(b->rp, len); + dcflush(dre, sizeof(BD)); + dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + dcflush(dre, sizeof(BD)); +/* ctlr->fcc->ftodr = 1<<15; /* transmit now; Don't do this according to errata */ + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + int len, status, rcvd, xmtd, restart; + ushort events; + Ctlr *ctlr; + BD *dre; + Block *b, *nb; + Ether *ether = arg; + + ctlr = ether->ctlr; + if(!ctlr->active) + return; /* not ours */ + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = ctlr->fcc->fcce; + ctlr->fcc->fcce = events; /* clear events */ + +#ifdef DBG + ehisto[events & 0x7f]++; +#endif + + ctlr->interrupts++; + + if(events & BSY) + ctlr->overrun++; + if(events & TXE) + ether->oerrs++; + +#ifdef DBG + rcvd = xmtd = 0; +#endif + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(events & RXF){ + dre = &ctlr->rdr[ctlr->rdrx]; + dczap(dre, sizeof(BD)); + while(((status = dre->status) & BDEmpty) == 0){ + rcvd++; + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxeLG|RxeSH)) + ether->buffs++; + if(status & RxeNO) + ether->frames++; + if(status & RxeCR) + ether->crcs++; + if(status & RxeOV) + ether->overflows++; + print("eth rx: %ux\n", status); + }else{ + /* + * We have a packet. Read it in. + */ + len = dre->length-4; + b = ctlr->rcvbufs[ctlr->rdrx]; + assert(dre->addr == PADDR(b->rp)); + dczap(b->rp, len); + if(nb = iallocb(Bufsize)){ + b->wp += len; + etheriq(ether, b, 1); + b = nb; + b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1)); + b->wp = b->rp; + ctlr->rcvbufs[ctlr->rdrx] = b; + ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->length = 0; + dre->status = (status & BDWrap) | BDEmpty | BDInt; + dcflush(dre, sizeof(BD)); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + dczap(dre, sizeof(BD)); + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & (TXB|TXE)){ + ilock(ctlr); + restart = 0; + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + dczap(dre, sizeof(BD)); + status = dre->status; + if(status & BDReady) + break; + if(status & TxeDEF) + ctlr->deferred++; + if(status & TxeHB) + ctlr->heartbeat++; + if(status & TxeLC) + ctlr->latecoll++; + if(status & TxeRL) + ctlr->retrylim++; + if(status & TxeUN) + ctlr->underrun++; + if(status & TxeCSL) + ctlr->carrierlost++; + if(status & (TxeLC|TxeRL|TxeUN)) + restart = 1; + ctlr->retrycount += (status>>2)&0xF; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("fcce/interrupt: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + xmtd++; + } + + if(restart){ + ctlr->fcc->gfmr &= ~ENT; + delay(10); + ctlr->fcc->gfmr |= ENT; + cpmop(RestartTx, ctlr->fccid, 0xc); + } + txstart(ether); + iunlock(ctlr); + } +#ifdef DBG + if(rcvd >= nelem(fccrhisto)) + rcvd = nelem(fccrhisto) - 1; + if(xmtd >= nelem(fccthisto)) + xmtd = nelem(fccthisto) - 1; + if(rcvd) + fcctrhisto[xmtd]++; + else + fccthisto[xmtd]++; + if(xmtd) + fccrthisto[rcvd]++; + else + fccrhisto[rcvd]++; +#endif +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len, i, r; + Ctlr *ctlr; + MiiPhy *phy; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); + len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + miistatus(ctlr->mii); + phy = ctlr->mii->curphy; + len += snprint(p+len, READSTR-len, "phy: link=%d, tfc=%d, rfc=%d, speed=%d, fd=%d\n", + phy->link, phy->tfc, phy->rfc, phy->speed, phy->fd); + +#ifdef DBG + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + len += snprint(p+len, READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + len += snprint(p+len, READSTR-len, "\n "); + r = miimir(ctlr->mii, i); + len += snprint(p+len, READSTR-len, " %4.4uX", r); + } + snprint(p+len, READSTR-len, "\n"); + } +#endif + snprint(p+len, READSTR-len, "\n"); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +/* + * This follows the MPC8260 user guide: section28.9's initialisation sequence. + */ +static int +fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea) +{ + int i; + Etherparam *p; + MiiPhy *phy; + + /* Turn Ethernet off */ + fcc->gfmr &= ~(ENR | ENT); + + ioplock(); + switch(ctlr->port) { + default: + iopunlock(); + return -1; + case 0: + /* Step 1 (Section 28.9), write the parallel ports */ + ctlr->pmdio = 0x01000000; + ctlr->pmdck = 0x08000000; + imm->port[0].pdir &= ~A1dir0; + imm->port[0].pdir |= A1dir1; + imm->port[0].psor &= ~A1psor0; + imm->port[0].psor |= A1psor1; + imm->port[0].ppar |= (A1dir0 | A1dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x00000c00; + imm->port[2].pdir &= ~0x00000c00; + imm->port[2].ppar |= 0x00000c00; + imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].podr |= ctlr->pmdio; + imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0xff000000; /* Clock mask */ + imm->cmxfcr |= 0x37000000; /* Clock route */ + break; + + case 1: + /* Step 1 (Section 28.9), write the parallel ports */ + ctlr->pmdio = 0x00400000; + ctlr->pmdck = 0x00200000; + imm->port[1].pdir &= ~B2dir0; + imm->port[1].pdir |= B2dir1; + imm->port[1].psor &= ~B2psor0; + imm->port[1].psor |= B2psor1; + imm->port[1].ppar |= (B2dir0 | B2dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x00003000; + imm->port[2].pdir &= ~0x00003000; + imm->port[2].ppar |= 0x00003000; + + imm->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[2].podr |= ctlr->pmdio; + imm->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0x00ff0000; + imm->cmxfcr |= 0x00250000; + break; + + case 2: + /* Step 1 (Section 28.9), write the parallel ports */ + imm->port[1].pdir &= ~B3dir0; + imm->port[1].pdir |= B3dir1; + imm->port[1].psor &= ~B3psor0; + imm->port[1].psor |= B3psor1; + imm->port[1].ppar |= (B3dir0 | B3dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x0000c000; + imm->port[2].pdir &= ~0x0000c000; + imm->port[2].ppar |= 0x0000c000; + imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].podr |= ctlr->pmdio; + imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0x0000ff00; + imm->cmxfcr |= 0x00003700; + break; + } + iopunlock(); + + p = (Etherparam*)(m->immr->prmfcc + ctlr->port); + memset(p, 0, sizeof(Etherparam)); + + /* Step 4 */ + fcc->gfmr |= ENET; + + /* Step 5 */ + fcc->fpsmr = CRCE | FDE | LPB; /* full duplex operation */ + ctlr->duplex = ~0; + + /* Step 6 */ + fcc->fdsr = 0xd555; + + /* Step 7, initialize parameter ram */ + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + p->rstate = (GBL | EB) << 24; + p->tstate = (GBL | EB) << 24; + + p->cmask = 0xdebb20e3; + p->cpres = 0xffffffff; + + p->retlim = 15; /* retry limit */ + + p->mrblr = (Rbsize+0x1f)&~0x1f; /* multiple of 32 */ + p->mflr = Rbsize; + p->minflr = ETHERMINTU; + p->maxd1 = (Rbsize+7) & ~7; + p->maxd2 = (Rbsize+7) & ~7; + + for(i=0; i<Eaddrlen; i+=2) + p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i]; + + /* Step 7, initialize parameter ram, configuration-dependent values */ + p->riptr = m->immr->fccextra[ctlr->port].ri - (uchar*)IMMR; + p->tiptr = m->immr->fccextra[ctlr->port].ti - (uchar*)IMMR; + p->padptr = m->immr->fccextra[ctlr->port].pad - (uchar*)IMMR; + memset(m->immr->fccextra[ctlr->port].pad, 0x88, 0x20); + + /* Step 8, clear out events */ + fcc->fcce = ~0; + + /* Step 9, Interrupt enable */ + fcc->fccm = TXE | RXF | TXB; + + /* Step 10, Configure interrupt priority (not done here) */ + /* Step 11, Clear out current events */ + /* Step 12, Enable interrupts to the CP interrupt controller */ + + /* Step 13, Issue the Init Tx and Rx command, specifying 0xc for ethernet*/ + cpmop(InitRxTx, fccid[ctlr->port], 0xc); + + /* Step 14, Link management */ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = fccmiimir; + ctlr->mii->miw = fccmiimiw; + ctlr->mii->ctlr = ctlr; + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + miiane(ctlr->mii, ~0, ~0, ~0); +#ifdef DBG + print("oui=%X, phyno=%d, ", phy->oui, phy->phyno); + print("anar=%ux, ", phy->anar); + print("fc=%ux, ", phy->fc); + print("mscr=%ux, ", phy->mscr); + + print("link=%ux, ", phy->link); + print("speed=%ux, ", phy->speed); + print("fd=%ux, ", phy->fd); + print("rfc=%ux, ", phy->rfc); + print("tfc=%ux\n", phy->tfc); +#endif + /* Step 15, Enable ethernet: done at attach time */ + return 0; +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + Ctlr *ctlr; + FCC *fcc; + Block *b; + int i; + + if(m->cpuhz < 24000000){ + print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type); + return -1; + } + + if(ether->port > 3){ + print("%s ether: no FCC port %ld\n", ether->type, ether->port); + return -1; + } + ether->irq = fccirq[ether->port]; + ether->tbdf = BusPPC; + fcc = imm->fcc + ether->port; + + ctlr = malloc(sizeof(*ctlr)); + ether->ctlr = ctlr; + memset(ctlr, 0, sizeof(*ctlr)); + ctlr->fcc = fcc; + ctlr->port = ether->port; + ctlr->fccid = fccid[ether->port]; + + /* Ioringinit will allocate the buffer descriptors in normal memory + * and NOT in Dual-Ported Ram, as prescribed by the MPC8260 + * PowerQUICC II manual (Section 28.6). When they are allocated + * in DPram and the Dcache is enabled, the processor will hang + */ + if(ioringinit(ctlr, Nrdre, Ntdre, 0) < 0) + panic("etherfcc init"); + for(i = 0; i < Nrdre; i++){ + b = iallocb(Bufsize); + b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1)); + b->wp = b->rp; + ctlr->rcvbufs[i] = b; + ctlr->rdr[i].addr = PADDR(b->wp); + } + + fccsetup(ctlr, fcc, ether->ea); + + ether->mbps = 100; /* TO DO: could be 10mbps */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + /* + * Until we know where to find it, insist that the plan9.ini + * entry holds the Ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + return 0; +} + +void +etherfcclink(void) +{ + addethercard("fcc", reset); +} + +static void +nanodelay(void) +{ + static int count; + int i; + + for(i = 0; i < 500; i++) + count++; + return; +} + +static +void miiwriteloop(Ctlr *ctlr, Port *port, int cnt, ulong cmd) +{ + int i; + + for(i = 0; i < cnt; i++){ + port->pdat &= ~ctlr->pmdck; + if(cmd & BIT(i)) + port->pdat |= ctlr->pmdio; + else + port->pdat &= ~ctlr->pmdio; + nanodelay(); + port->pdat |= ctlr->pmdck; + nanodelay(); + } +} + +static int +fccmiimiw(Mii *mii, int pa, int ra, int data) +{ + int x; + Port *port; + ulong cmd; + Ctlr *ctlr; + + /* + * MII Management Interface Write. + */ + + ctlr = mii->ctlr; + port = imm->port + 3; + cmd = MDIwrite | (pa<<(5+2+16))| (ra<<(2+16)) | (data & 0xffff); + + x = splhi(); + + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + miiwriteloop(ctlr, port, 32, cmd); + + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + + splx(x); + return 1; +} + +static int +fccmiimir(Mii *mii, int pa, int ra) +{ + int data, i, x; + Port *port; + ulong cmd; + Ctlr *ctlr; + + ctlr = mii->ctlr; + port = imm->port + 3; + + cmd = MDIread | pa<<(5+2+16) | ra<<(2+16); + + x = splhi(); + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + + /* Clock out the first 14 MS bits of the command */ + miiwriteloop(ctlr, port, 14, cmd); + + /* Turn-around */ + port->pdat &= ~ctlr->pmdck; + port->pdir &= ~ctlr->pmdio; + nanodelay(); + + /* For read, clock in 18 bits, use 16 */ + data = 0; + for(i=0; i<18; i++){ + data <<= 1; + if(port->pdat & ctlr->pmdio) + data |= 1; + port->pdat |= ctlr->pmdck; + nanodelay(); + port->pdat &= ~ctlr->pmdck; + nanodelay(); + } + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + miiwriteloop(ctlr, port, 32, ~0); + splx(x); + return data & 0xffff; +} + +static void +fccltimer(Ureg*, Timer *t) +{ + Ether *ether; + Ctlr *ctlr; + MiiPhy *phy; + ulong gfmr; + + ether = t->ta; + ctlr = ether->ctlr; + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + return; + phy = ctlr->mii->curphy; + if(miistatus(ctlr->mii) < 0){ + print("miistatus failed\n"); + return; + } + if(phy->link == 0){ + print("link lost\n"); + return; + } + ether->mbps = phy->speed; + + if(phy->fd != ctlr->duplex) + print("set duplex\n"); + ilock(ctlr); + gfmr = ctlr->fcc->gfmr; + if(phy->fd != ctlr->duplex){ + ctlr->fcc->gfmr &= ~(ENR|ENT); + if(phy->fd) + ctlr->fcc->fpsmr |= FDE | LPB; /* full duplex operation */ + else + ctlr->fcc->fpsmr &= ~(FDE | LPB); /* half duplex operation */ + ctlr->duplex = phy->fd; + } + ctlr->fcc->gfmr = gfmr; + iunlock(ctlr); +} diff --git a/sys/src/9/ppc/etherif.h b/sys/src/9/ppc/etherif.h new file mode 100755 index 000000000..34fa2cc02 --- /dev/null +++ b/sys/src/9/ppc/etherif.h @@ -0,0 +1,35 @@ +enum { + MaxEther = 24, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void *ctlr; + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/sys/src/9/ppc/ethersaturn.c b/sys/src/9/ppc/ethersaturn.c new file mode 100755 index 000000000..893835f87 --- /dev/null +++ b/sys/src/9/ppc/ethersaturn.c @@ -0,0 +1,229 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "msaturn.h" + +#include "etherif.h" + +enum{ + Etcr = Saturn + 0x0c00, + Etsr = Saturn + 0x0c02, + Ercr = Saturn + 0x0c04, + Ersr = Saturn + 0x0c06, + Eisr = Saturn + 0x0d04, + Eimr = Saturn + 0x0d06, + Emacaddr0 = Saturn + 0x0e02, + Miicr = Saturn + 0x0f02, + Miiwdr = Saturn + 0x0f04, + Miirdr = Saturn + 0x0f06, + + Ethermem = 0xf2c00000, + Etherfsize = 0x2000, + Nrx = 14, + Ntx = 2, // Nrx + Ntx must be 16 + + Ersr_rxfpmask = 0xf, + Ersr_rxevent = RBIT(0, ushort), + Etcr_txfpmask = 0xf, + Ercr_rxenab = RBIT(0, ushort), + Ercr_auienab = RBIT(2, ushort), + Etcr_txstart = RBIT(1, ushort), + Etcr_retries = 0xf<<8, + Ei_txecall = RBIT(0, ushort), + Ei_txretry = RBIT(2, ushort), + Ei_txdefer = RBIT(3, ushort), + Ei_txcrs = RBIT(4, ushort), + Ei_txdone = RBIT(5, ushort), + Ei_rxcrcerr = RBIT(8, ushort), + Ei_rxdrib = RBIT(9, ushort), + Ei_rxdone = RBIT(10, ushort), + Ei_rxshort = RBIT(11, ushort), + Ei_rxlong = RBIT(12, ushort), + + Miicr_regshift = 6, + Miicr_read = RBIT(10, ushort), + Miicr_preambledis = RBIT(12, ushort), + Miicr_accack = RBIT(14, ushort), + Miicr_accbsy = RBIT(15, ushort), +}; + +typedef struct { + Lock; + int txbusy; + int txempty; + int txfull; + int ntx; /* number of entries in transmit ring */ + int rxlast; + + int active; + ulong interrupts; /* statistics */ + ulong overflows; +} Ctlr; + +static ushort*etcr=(ushort*)Etcr; +static ushort*etsr=(ushort*)Etsr; +static ushort*ercr=(ushort*)Ercr; +static ushort*ersr=(ushort*)Ersr; +static ushort*eimr=(ushort*)Eimr; +static ushort*eisr=(ushort*)Eisr; +static ushort*miicr=(ushort*)Miicr; +static ushort*miirdr=(ushort*)Miirdr; + +static void +txfill(Ether*ether, Ctlr*ctlr) +{ + int len; + Block *b; + ushort*dst; + + while(ctlr->ntx<Ntx){ + if((b=qget(ether->oq)) == nil) + break; + + len = BLEN(b); + dst = (ushort*)(Ethermem+(ctlr->txempty+Nrx)*Etherfsize); + *dst = len; + memmove(&dst[1], b->rp, len); + ctlr->ntx++; + ctlr->txempty++; + if(ctlr->txempty==Ntx) + ctlr->txempty = 0; + freeb(b); + } +} + +static void +txrestart(Ctlr*ctlr) +{ + if(ctlr->ntx==0 || ctlr->txbusy) + return; + ctlr->txbusy = 1; + *etcr = Etcr_txstart|Etcr_retries|(ctlr->txfull+Nrx); +} + +static void interrupt(Ureg*, void*); + +static void +transmit(Ether*ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txfill(ether, ctlr); + txrestart(ctlr); + iunlock(ctlr); + +} + +static void +interrupt(Ureg*, void*arg) +{ + Ctlr*ctlr; + Ether*ether = arg; + Etherpkt*pkt; + ushort ie; + int rx, len; + Block *b; + + ctlr = ether->ctlr; + if(!ctlr->active) + return; /* not ours */ + ctlr->interrupts++; + + ilock(ctlr); + ie = *eisr; + *eisr = ie; + intack(); + + if(ie==0) + iprint("interrupt: no interrupt source?\n"); + + if(ie&Ei_txdone){ + if((*etcr&Etcr_txstart)==0){ + if(ctlr->txbusy){ + ctlr->txbusy = 0; + ctlr->ntx--; + ctlr->txfull++; + if(ctlr->txfull==Ntx) + ctlr->txfull = 0; + } + txrestart(ctlr); + txfill(ether, ctlr); + txrestart(ctlr); + } + else + iprint("interrupt: bogus tx interrupt\n"); + ie &= ~Ei_txdone; + } + + if(ie&Ei_rxdone){ + rx=*ersr&Ersr_rxfpmask; + while(ctlr->rxlast!=rx){ + + ctlr->rxlast++; + if(ctlr->rxlast >= Nrx) + ctlr->rxlast = 0; + + pkt = (Etherpkt*)(Ethermem+ctlr->rxlast*Etherfsize); + len = *(ushort*)pkt; + if((b = iallocb(len+sizeof(ushort))) != nil){ + memmove(b->wp, pkt, len+sizeof(ushort)); + b->rp += sizeof(ushort); + b->wp = b->rp + len; + etheriq(ether, b, 1); + }else + ether->soverflows++; + rx=*ersr&Ersr_rxfpmask; + } + ie &= ~Ei_rxdone; + } + + if(ie&Ei_txretry){ + iprint("ethersaturn: txretry!\n"); + ie &= ~Ei_txretry; + ctlr->txbusy = 0; + txrestart(ctlr); + } + + ie &= ~Ei_txcrs; + if(ie) + iprint("interrupt: unhandled interrupts %.4uX\n", ie); + iunlock(ctlr); +} + +static int +reset(Ether* ether) +{ + Ctlr*ctlr; + + *ercr = 0; + ctlr = malloc(sizeof(*ctlr)); + memset(ctlr, 0, sizeof(*ctlr)); + ctlr->active = 1; + + ether->ctlr = ctlr; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->irq = Vecether; + ether->arg = ether; + memmove(ether->ea, (ushort*)Emacaddr0, Eaddrlen); + + *ercr = Ercr_rxenab|Ercr_auienab|(Nrx-1); + *eimr = Ei_rxdone|Ei_txretry|Ei_txdone; + + iprint("reset: ercr %.4uX\n", *ercr); + return 0; +} + +void +ethersaturnlink(void) +{ + addethercard("saturn", reset); +} + diff --git a/sys/src/9/ppc/fns.h b/sys/src/9/ppc/fns.h new file mode 100755 index 000000000..56b6aceab --- /dev/null +++ b/sys/src/9/ppc/fns.h @@ -0,0 +1,124 @@ +#include "../port/portfns.h" + +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockinit(void); +void clockintr(Ureg*); +void cpuidprint(void); +void cycles(uvlong*); +void dbgputc(int c); +void dcacheenb(void); +void dcflush(void*, ulong); +void dczap(void*, ulong); +void delay(int); +void delayloopinit(void); +void dmiss(void); +void dumpregs(Ureg*); +void eieio(void); +void evenaddr(ulong); +void faultpower(Ureg*, ulong addr, int read); +void flashprogpower(int); +void fpgareset(void); +void fprestore(FPsave*); +void fpsave(FPsave*); +void fptrap(Ureg*); +char* getconf(char*); +ulong getdar(void); +ulong getdcmp(void); +ulong getdec(void); +ulong getdmiss(void); +ulong getdsisr(void); +ulong gethash1(void); +ulong gethash2(void); +ulong gethid0(void); +ulong gethid1(void); +ulong geticmp(void); +ulong geticmp(void); +ulong getimiss(void); +ulong getmsr(void); +ulong getpvr(void); +ulong getrpa(void); +ulong getsdr1(void); +ulong getsr(int); +ulong getsrr1(void); +void gotopc(ulong); +void hwintrinit(void); +void icacheenb(void); +void icflush(void*, ulong); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +void imiss(void); +int inb(int); +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, char*); +int ioalloc(int, int, int, char*); +void iofree(int); +int iprint(char*, ...); +int isaconfig(char*, int, ISAConf*); +void kfpinit(void); +#define kmapinval() +void links(void); +void vectordisable(Vctl *); +int vectorenable(Vctl *); +void intack(void); +void intend(int); +int intvec(void); +void l2disable(void); +void mmuinit(void); +void mmusweep(void*); +int newmmupid(void); +void outb(int, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +int pcicfgr8(Pcidev*, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pcicfgw8(Pcidev*, int, int); +void procsave(Proc*); +void procsetup(Proc*); +void putdcmp(ulong); +void putdec(ulong); +void puthash1(ulong); +void puthash2(ulong); +void puthid0(ulong); +void puthid2(ulong); +void puticmp(ulong); +void puticmp(ulong); +void putmsr(ulong); +void putrpa(ulong); +void putsdr1(ulong); +void putsdr1(ulong); +void putsr(int, ulong); +void putsrr1(ulong); +void sethvec(int, void (*)(void)); +void setmvec(int, void (*)(void), void (*)(void)); +void sharedseginit(void); +void console(void); +void sync(void); +int tas(void*); +void timeradd(Timer *); +void timerdel(Timer *); +void timerinit(void); +void tlbflush(ulong); +void tlbflushall(void); +void tlbld(ulong); +void tlbli(ulong); +void tlbvec(void); +void touser(void*); +void trapinit(void); +void trapvec(void); +#define userureg(ur) (((ur)->status & MSR_PR) != 0) +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((((ulong)(a)&0xf0000000)==0xf0000000)?(ulong)(a):((ulong)(a)&~KZERO)) +#define coherence() eieio() +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void procrestore(Proc*); + +#ifdef ucuconf +extern ulong getpll(void); +extern ulong getl2cr(void); +extern void putl2cr(ulong); +#endif diff --git a/sys/src/9/ppc/init9.s b/sys/src/9/ppc/init9.s new file mode 100755 index 000000000..3445f26f8 --- /dev/null +++ b/sys/src/9/ppc/init9.s @@ -0,0 +1,29 @@ +/* this is the same as a c program: + * main(char *argv0){ + * startboot(argv0, &argv0); + * } + * + * it is in asm because we need to set the SB before + * doing it and the only way to do this in c drags in + * too many other routines. + */ + +TEXT _main(SB),$8 + + MOVW $setSB(SB), R2 + + /* make a frame */ + SUB $16,R1 + + /* argv0 is already passed to us in R3 so it is already the first arg */ + + /* copy argv0 into the stack and push its address as the second arg */ + MOVW R3,0x14(R1) + ADD $0x14,R1,R6 + MOVW R6,0x8(R1) + + BL startboot(SB) + + /* should never get here */ +loop: + BR loop diff --git a/sys/src/9/ppc/initcode b/sys/src/9/ppc/initcode new file mode 100755 index 000000000..1d846abe5 --- /dev/null +++ b/sys/src/9/ppc/initcode @@ -0,0 +1,25 @@ +#include "/sys/src/libc/9syscall/sys.h" + +/* + * we pass in the argument of the exec parameters as 0(FP) + */ + +TEXT main(SB),$8 + + MOVW $setSB(SB), R2 + MOVW $boot(SB), R3 + ADD $12, R1, R4 /* get a pointer to 0(FP) */ + MOVW R3, 4(R1) + MOVW R4, 8(R1) + MOVW $EXEC, R3 + SYSCALL + + /* should never get here */ +loop: + BR loop + +DATA boot+0(SB)/5,$"/boot" +DATA boot+5(SB)/5,$"/boot" +DATA bootv+0(SB)/4,$boot+6(SB) +GLOBL boot+0(SB),$11 +GLOBL bootv+0(SB),$8 diff --git a/sys/src/9/ppc/io.h b/sys/src/9/ppc/io.h new file mode 100755 index 000000000..c14051b2e --- /dev/null +++ b/sys/src/9/ppc/io.h @@ -0,0 +1,30 @@ + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ + BusPPC /* Power PC internal bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) diff --git a/sys/src/9/ppc/l.s b/sys/src/9/ppc/l.s new file mode 100755 index 000000000..878aa457d --- /dev/null +++ b/sys/src/9/ppc/l.s @@ -0,0 +1,1037 @@ +#include "mem.h" + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +#ifdef ucuconf +/* These only exist on the PPC 755: */ +#define SAVER4 SPRG4 +#define SAVER5 SPRG5 +#define SAVER6 SPRG6 +#define SAVER7 SPRG7 +#endif /* ucuconf */ + +/* special instruction definitions */ +#define BDNZ BC 16, 0, +#define BDNE BC 0, 2, +#define MTCRF(r, crm) WORD $((31<<26)|((r)<<21)|(crm<<12)|(144<<1)) + +/* #define TLBIA WORD $((31<<26)|(370<<1)) Not implemented on the 603e */ +#define TLBSYNC WORD $((31<<26)|(566<<1)) +#define TLBLI(n) WORD $((31<<26)|((n)<<11)|(1010<<1)) +#define TLBLD(n) WORD $((31<<26)|((n)<<11)|(978<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC + +#define UREGSPACE (UREGSIZE+8) + +TEXT start(SB), $-4 + + /* + * setup MSR + * turn off interrupts + * use 0x000 as exception prefix + * enable machine check + */ + MOVW MSR, R3 + MOVW $(MSR_ME|MSR_EE|MSR_IP), R4 + ANDN R4, R3 + SYNC + MOVW R3, MSR + MSRSYNC + + /* except during trap handling, R0 is zero from now on */ + MOVW $0, R0 + + /* setup SB for pre mmu */ + MOVW $setSB(SB), R2 + MOVW $KZERO, R3 + ANDN R3, R2 + + /* before this we're not running above KZERO */ + BL mmuinit0(SB) + /* after this we are */ + +#ifdef ucuconf + MOVW $0x2000000, R4 /* size */ + MOVW $0, R3 /* base address */ + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE _dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +_dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ _dcf0 +_dcf1: + SYNC + + /* BAT0, 3 unused, copy of BAT2 */ + MOVW SPR(IBATL(2)), R3 + MOVW R3, SPR(IBATL(0)) + MOVW SPR(IBATU(2)), R3 + MOVW R3, SPR(IBATU(0)) + MOVW SPR(DBATL(2)), R3 + MOVW R3, SPR(DBATL(0)) + MOVW SPR(DBATU(2)), R3 + MOVW R3, SPR(DBATU(0)) + + MOVW SPR(IBATL(2)), R3 + MOVW R3, SPR(IBATL(3)) + MOVW SPR(IBATU(2)), R3 + MOVW R3, SPR(IBATU(3)) + MOVW SPR(DBATL(2)), R3 + MOVW R3, SPR(DBATL(3)) + MOVW SPR(DBATU(2)), R3 + MOVW R3, SPR(DBATU(3)) +#endif /* ucuconf */ + + /* running with MMU on!! */ + + /* set R2 to correct value */ + MOVW $setSB(SB), R2 + + /* set up Mach */ + MOVW $MACHADDR, R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 /* set stack */ + + MOVW R0, R(USER) /* up-> set to zero */ + MOVW R0, 0(R(MACH)) /* machno set to zero */ + + BL main(SB) + + RETURN /* not reached */ + +/* + * on return from this function we will be running in virtual mode. + * We set up the Block Address Translation (BAT) registers thus: + * 1) first 3 BATs are 256M blocks, starting from KZERO->0 + * 2) remaining BAT maps last 256M directly + */ +TEXT mmuinit0(SB), $0 + /* reset all the tlbs */ + MOVW $64, R3 + MOVW R3, CTR + MOVW $0, R4 + +tlbloop: + TLBIE R4 + SYNC + ADD $BIT(19), R4 + BDNZ tlbloop + TLBSYNC + +#ifndef ucuconf + /* BATs 0 and 1 cover memory from 0x00000000 to 0x20000000 */ + + /* KZERO -> 0, IBAT and DBAT, 256 MB */ + MOVW $(KZERO|(0x7ff<<2)|2), R3 + MOVW $(PTEVALID|PTEWRITE), R4 /* PTEVALID => Cache coherency on */ + MOVW R3, SPR(IBATU(0)) + MOVW R4, SPR(IBATL(0)) + MOVW R3, SPR(DBATU(0)) + MOVW R4, SPR(DBATL(0)) + + /* KZERO+256M -> 256M, IBAT and DBAT, 256 MB */ + ADD $(1<<28), R3 + ADD $(1<<28), R4 + MOVW R3, SPR(IBATU(1)) + MOVW R4, SPR(IBATL(1)) + MOVW R3, SPR(DBATU(1)) + MOVW R4, SPR(DBATL(1)) + + /* FPGABASE -> FPGABASE, DBAT, 16 MB */ + MOVW $(FPGABASE|(0x7f<<2)|2), R3 + MOVW $(FPGABASE|PTEWRITE|PTEUNCACHED), R4 /* FPGA memory, don't cache */ + MOVW R3, SPR(DBATU(2)) + MOVW R4, SPR(DBATL(2)) + + /* IBAT 2 unused */ + MOVW R0, SPR(IBATU(2)) + MOVW R0, SPR(IBATL(2)) + + /* direct map last block, uncached, (not guarded, doesn't work for BAT), DBAT only */ + MOVW $(INTMEM|(0x7ff<<2)|2), R3 + MOVW $(INTMEM|PTEWRITE|PTEUNCACHED), R4 /* Don't set PTEVALID here */ + MOVW R3, SPR(DBATU(3)) + MOVW R4, SPR(DBATL(3)) + + /* IBAT 3 unused */ + MOVW R0, SPR(IBATU(3)) + MOVW R0, SPR(IBATL(3)) +#else /* ucuconf */ + /* BAT 2 covers memory from 0x00000000 to 0x10000000 */ + + /* KZERO -> 0, IBAT2 and DBAT2, 256 MB */ + MOVW $(KZERO|(0x7ff<<2)|2), R3 + MOVW $(PTEVALID|PTEWRITE), R4 /* PTEVALID => Cache coherency on */ + MOVW R3, SPR(DBATU(2)) + MOVW R4, SPR(DBATL(2)) + MOVW R3, SPR(IBATU(2)) + MOVW R4, SPR(IBATL(2)) +#endif /* ucuconf */ + + /* enable MMU */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) /* Stored PC for RFI instruction */ + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R4 + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + + RETURN + +TEXT kfpinit(SB), $0 + MOVFL $0, FPSCR(7) + MOVFL $0xD, FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FMOVD $4503601774854144.0, F27 + FMOVD $0.5, F29 + FSUB F29, F29, F28 + FADD F29, F29, F30 + FADD F30, F30, F31 + FMOVD F28, F0 + FMOVD F28, F1 + FMOVD F28, F2 + FMOVD F28, F3 + FMOVD F28, F4 + FMOVD F28, F5 + FMOVD F28, F6 + FMOVD F28, F7 + FMOVD F28, F8 + FMOVD F28, F9 + FMOVD F28, F10 + FMOVD F28, F11 + FMOVD F28, F12 + FMOVD F28, F13 + FMOVD F28, F14 + FMOVD F28, F15 + FMOVD F28, F16 + FMOVD F28, F17 + FMOVD F28, F18 + FMOVD F28, F19 + FMOVD F28, F20 + FMOVD F28, F21 + FMOVD F28, F22 + FMOVD F28, F23 + FMOVD F28, F24 + FMOVD F28, F25 + FMOVD F28, F26 + RETURN + +TEXT splhi(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splx(SB), $0 + /* fall though */ + +TEXT splxpc(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MOVW MSR, R3 + OR $MSR_EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +TEXT touser(SB), $-4 + MOVW $(UTZERO+32), R5 /* header appears in text */ + MOVW $(MSR_EE|MSR_PR|MSR_IR|MSR_DR|MSR_RI), R4 + MOVW R4, SPR(SRR1) + MOVW R3, R1 + MOVW R5, SPR(SRR0) + RFI + +TEXT dczap(SB), $-4 /* dczap(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcz1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcz0: + DCBI (R5) + ADD $CACHELINESZ, R5 + BDNZ dcz0 +dcz1: + SYNC + RETURN + +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBST (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 +dcf1: + SYNC + RETURN + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE icf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) /* invalidate the instruction cache */ + ADD $CACHELINESZ, R5 + BDNZ icf0 + ISYNC +icf1: + RETURN + +TEXT tas(SB), $0 + MOVW R3, R4 + MOVW $0xdead, R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + SYNC + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 + EIEIO +tas0: + SYNC + RETURN + +TEXT _xinc(SB), $0 /* void _xinc(long *); */ + MOVW R3, R4 +xincloop: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $1, R3 + STWCCC R3, (R4) + BNE xincloop + RETURN + +TEXT _xdec(SB), $0 /* long _xdec(long *); */ + MOVW R3, R4 +xdecloop: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $-1, R3 + STWCCC R3, (R4) + BNE xdecloop + RETURN + +TEXT tlbflushall(SB), $0 + MOVW $TLBENTRIES, R3 + MOVW R3, CTR + MOVW $0, R4 + ISYNC +tlbflushall0: + TLBIE R4 + SYNC + ADD $BIT(19), R4 + BDNZ tlbflushall0 + TLBSYNC + RETURN + +TEXT tlbflush(SB), $0 + ISYNC + TLBIE R3 + SYNC + TLBSYNC + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +/* On an imiss, we get here. If we can resolve it, we do. + * Otherwise take the real trap. The code at the vector is + * MOVW R0, SPR(SAVER0) No point to this, of course + * MOVW LR, R0 + * MOVW R0, SPR(SAVELR) + * BL imiss(SB) or dmiss, as the case may be + * BL tlbvec(SB) + */ +TEXT imiss(SB), $-4 + /* Statistics */ + MOVW $MACHPADDR, R1 + MOVW 0xc(R1), R3 /* count m->tlbfault */ + ADD $1, R3 + MOVW R3, 0xc(R1) + MOVW 0x10(R1), R3 /* count m->imiss */ + ADD $1, R3 + MOVW R3, 0x10(R1) + + /* Real work */ + MOVW SPR(HASH1), R1 /* (phys) pointer into the hash table */ + ADD $BY2PTEG, R1, R2 /* end pointer */ + MOVW SPR(iCMP), R3 /* pattern to look for */ +imiss1: + MOVW (R1), R0 + CMP R3, R0 + BEQ imiss2 /* found the entry */ + ADD $8, R1 + CMP R1, R2 /* test end of loop */ + BNE imiss1 /* Loop */ + /* Failed to find an entry; take the full trap */ + MOVW SPR(SRR1), R1 + MTCRF(1, 0x80) /* restore CR0 bits (they're auto saved in SRR1) */ + RETURN +imiss2: + /* Found the entry */ + MOVW 4(R1), R2 /* Phys addr */ + MOVW R2, SPR(RPA) + MOVW SPR(IMISS), R3 + TLBLI(3) + + /* Restore Registers */ + MOVW SPR(SRR1), R1 /* Restore the CR0 field of the CR register from SRR1 */ + MTCRF(1, 0x80) + MOVW SPR(SAVELR), R0 + MOVW R0, LR + RFI + +/* On a data load or store miss, we get here. If we can resolve it, we do. + * Otherwise take the real trap + */ +TEXT dmiss(SB), $-4 + /* Statistics */ + MOVW $MACHPADDR, R1 + MOVW 0xc(R1), R3 /* count m->tlbfault */ + ADD $1, R3 + MOVW R3, 0xc(R1) + MOVW 0x14(R1), R3 /* count m->dmiss */ + ADD $1, R3 + MOVW R3, 0x14(R1) + /* Real work */ + MOVW SPR(HASH1), R1 /* (phys) pointer into the hash table */ + ADD $BY2PTEG, R1, R2 /* end pointer */ + MOVW SPR(DCMP), R3 /* pattern to look for */ +dmiss1: + MOVW (R1), R0 + CMP R3, R0 + BEQ dmiss2 /* found the entry */ + ADD $8, R1 + CMP R1, R2 /* test end of loop */ + BNE dmiss1 /* Loop */ + /* Failed to find an entry; take the full trap */ + MOVW SPR(SRR1), R1 + MTCRF(1, 0x80) /* restore CR0 bits (they're auto saved in SRR1) */ + RETURN +dmiss2: + /* Found the entry */ + MOVW 4(R1), R2 /* Phys addr */ + MOVW R2, SPR(RPA) + MOVW SPR(DMISS), R3 + TLBLD(3) + /* Restore Registers */ + MOVW SPR(SRR1), R1 /* Restore the CR0 field of the CR register from SRR1 */ + MTCRF(1, 0x80) + MOVW SPR(SAVELR), R0 + MOVW R0, LR + RFI + +/* + * When a trap sets the TGPR bit (TLB miss traps do this), + * registers get remapped: R0-R31 are temporarily inaccessible, + * and Temporary Registers TR0-TR3 are mapped onto R0-R3. + * While this bit is set, R4-R31 cannot be used. + * The code at the vector has executed this code before + * coming to tlbvec: + * MOVW R0, SPR(SAVER0) No point to this, of course + * MOVW LR, R0 + * MOVW R0, SPR(SAVELR) + * BL tlbvec(SB) + * SAVER0 can be reused. We're not interested in the value of TR0 + */ +TEXT tlbvec(SB), $-4 + MOVW MSR, R1 + RLWNM $0, R1, $~MSR_TGPR, R1 /* Clear the dreaded TGPR bit in the MSR */ + SYNC + MOVW R1, MSR + MSRSYNC + /* Now the GPRs are what they're supposed to be, save R0 again */ + MOVW R0, SPR(SAVER0) + /* Fall through to trapvec */ + +/* + * traps force memory mapping off. + * the following code has been executed at the exception + * vector location + * MOVW R0, SPR(SAVER0) + * MOVW LR, R0 + * MOVW R0, SPR(SAVELR) + * bl trapvec(SB) + * + */ +TEXT trapvec(SB), $-4 + MOVW LR, R0 + MOVW R1, SPR(SAVER1) + MOVW R0, SPR(SAVEXX) /* vector */ + + /* did we come from user space */ + MOVW SPR(SRR1), R0 + MOVW CR, R1 + MOVW R0, CR + BC 4, 17, ktrap + + /* switch to kernel stack */ + MOVW R1, CR + MOVW $MACHPADDR, R1 /* PADDR(m->) */ + MOVW 8(R1), R1 /* m->proc */ + RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc) */ + MOVW 8(R1), R1 /* m->proc->kstack */ + RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc->kstack) */ + ADD $(KSTACK-UREGSIZE), R1 /* make room on stack */ + + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +ktrap: + MOVW R1, CR + MOVW SPR(SAVER1), R1 + RLWNM $0, R1, $~KZERO, R1 /* set stack pointer */ + SUB $UREGSPACE, R1 + + BL saveureg(SB) /* addressed relative to PC */ + BL trap(SB) + BR restoreureg + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* save r2 .. r31 in 48(R1) .. 164(R1) */ + MOVW $MACHPADDR, R(MACH) /* PADDR(m->) */ + MOVW 8(R(MACH)), R(USER) /* up-> */ + MOVW $MACHADDR, R(MACH) /* m-> */ + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + MOVW SPR(SRR0), R0 + MOVW R0, 16(R1) /* old PC */ + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) /* old status */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + MOVW SPR(DCMP), R0 + MOVW R0, (160+8)(R1) + MOVW SPR(iCMP), R0 + MOVW R0, (164+8)(R1) + MOVW SPR(DMISS), R0 + MOVW R0, (168+8)(R1) + MOVW SPR(IMISS), R0 + MOVW R0, (172+8)(R1) + MOVW SPR(HASH1), R0 + MOVW R0, (176+8)(R1) + MOVW SPR(HASH2), R0 + MOVW R0, (180+8)(R1) + MOVW SPR(DAR), R0 + MOVW R0, (184+8)(R1) + MOVW SPR(DSISR), R0 + MOVW R0, (188+8)(R1) + ADD $8, R1, R3 /* Ureg* */ + OR $KZERO, R3 /* fix ureg */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + MOVW $setSB(SB), R2 /* SB register */ + + MOVW MSR, R5 + OR $(MSR_IR|MSR_DR|MSR_FP|MSR_RI), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + OR $KZERO, R1 /* fix stack pointer */ + RFI /* returns to trap handler */ + +/* + * restore state from Ureg and return from trap/interrupt + */ +TEXT forkret(SB), $0 + BR restoreureg + +restoreureg: + MOVMW 48(R1), R2 /* restore r2 through r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) /* resave saved R0 */ + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* Condition register*/ + MOVW 24(R1), R0 + MOVW R0, LR + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVER0), R0 + RFI + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +TEXT getdar(SB), $0 + MOVW SPR(DAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT putsdr1(SB), $0 + SYNC + MOVW R3, SPR(SDR1) + ISYNC + RETURN + +TEXT putsr(SB), $0 + MOVW 4(FP), R4 + SYNC + MOVW R4, SEG(R3) + MSRSYNC + RETURN + +TEXT getsr(SB), $0 + MOVW SEG(R3), R3 + RETURN + +TEXT gethid0(SB), $0 + MOVW SPR(HID0), R3 + RETURN + +TEXT puthid0(SB), $0 + SYNC + ISYNC + MOVW R3, SPR(HID0) + SYNC + RETURN + +TEXT gethid1(SB), $0 + MOVW SPR(HID1), R3 + RETURN + +TEXT gethid2(SB), $0 + MOVW SPR(HID2), R3 + RETURN + +TEXT puthid2(SB), $0 + MOVW R3, SPR(HID2) + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT sync(SB), $0 + SYNC + RETURN + +/* Power PC 603e specials */ +TEXT getimiss(SB), $0 + MOVW SPR(IMISS), R3 + RETURN + +TEXT geticmp(SB), $0 + MOVW SPR(iCMP), R3 + RETURN + +TEXT puticmp(SB), $0 + MOVW R3, SPR(iCMP) + RETURN + +TEXT getdmiss(SB), $0 + MOVW SPR(DMISS), R3 + RETURN + +TEXT getdcmp(SB), $0 + MOVW SPR(DCMP), R3 + RETURN + +TEXT putdcmp(SB), $0 + MOVW R3, SPR(DCMP) + RETURN + +TEXT getsdr1(SB), $0 + MOVW SPR(SDR1), R3 + RETURN + +TEXT gethash1(SB), $0 + MOVW SPR(HASH1), R3 + RETURN + +TEXT puthash1(SB), $0 + MOVW R3, SPR(HASH1) + RETURN + +TEXT gethash2(SB), $0 + MOVW SPR(HASH2), R3 + RETURN + +TEXT puthash2(SB), $0 + MOVW R3, SPR(HASH2) + RETURN + +TEXT getrpa(SB), $0 + MOVW SPR(RPA), R3 + RETURN + +TEXT putrpa(SB), $0 + MOVW R3, SPR(RPA) + RETURN + +TEXT tlbli(SB), $0 + TLBLI(3) + ISYNC + RETURN + +TEXT tlbld(SB), $0 + SYNC + TLBLD(3) + ISYNC + RETURN + +TEXT getsrr1(SB), $0 + MOVW SPR(SRR1), R3 + RETURN + +TEXT putsrr1(SB), $0 + MOVW R3, SPR(SRR1) + RETURN + +TEXT fpsave(SB), $0 + FMOVD F0, (0*8)(R3) + FMOVD F1, (1*8)(R3) + FMOVD F2, (2*8)(R3) + FMOVD F3, (3*8)(R3) + FMOVD F4, (4*8)(R3) + FMOVD F5, (5*8)(R3) + FMOVD F6, (6*8)(R3) + FMOVD F7, (7*8)(R3) + FMOVD F8, (8*8)(R3) + FMOVD F9, (9*8)(R3) + FMOVD F10, (10*8)(R3) + FMOVD F11, (11*8)(R3) + FMOVD F12, (12*8)(R3) + FMOVD F13, (13*8)(R3) + FMOVD F14, (14*8)(R3) + FMOVD F15, (15*8)(R3) + FMOVD F16, (16*8)(R3) + FMOVD F17, (17*8)(R3) + FMOVD F18, (18*8)(R3) + FMOVD F19, (19*8)(R3) + FMOVD F20, (20*8)(R3) + FMOVD F21, (21*8)(R3) + FMOVD F22, (22*8)(R3) + FMOVD F23, (23*8)(R3) + FMOVD F24, (24*8)(R3) + FMOVD F25, (25*8)(R3) + FMOVD F26, (26*8)(R3) + FMOVD F27, (27*8)(R3) + FMOVD F28, (28*8)(R3) + FMOVD F29, (29*8)(R3) + FMOVD F30, (30*8)(R3) + FMOVD F31, (31*8)(R3) + MOVFL FPSCR, F0 + FMOVD F0, (32*8)(R3) + RETURN + +TEXT fprestore(SB), $0 + FMOVD (32*8)(R3), F0 + MOVFL F0, FPSCR + FMOVD (0*8)(R3), F0 + FMOVD (1*8)(R3), F1 + FMOVD (2*8)(R3), F2 + FMOVD (3*8)(R3), F3 + FMOVD (4*8)(R3), F4 + FMOVD (5*8)(R3), F5 + FMOVD (6*8)(R3), F6 + FMOVD (7*8)(R3), F7 + FMOVD (8*8)(R3), F8 + FMOVD (9*8)(R3), F9 + FMOVD (10*8)(R3), F10 + FMOVD (11*8)(R3), F11 + FMOVD (12*8)(R3), F12 + FMOVD (13*8)(R3), F13 + FMOVD (14*8)(R3), F14 + FMOVD (15*8)(R3), F15 + FMOVD (16*8)(R3), F16 + FMOVD (17*8)(R3), F17 + FMOVD (18*8)(R3), F18 + FMOVD (19*8)(R3), F19 + FMOVD (20*8)(R3), F20 + FMOVD (21*8)(R3), F21 + FMOVD (22*8)(R3), F22 + FMOVD (23*8)(R3), F23 + FMOVD (24*8)(R3), F24 + FMOVD (25*8)(R3), F25 + FMOVD (26*8)(R3), F26 + FMOVD (27*8)(R3), F27 + FMOVD (28*8)(R3), F28 + FMOVD (29*8)(R3), F29 + FMOVD (30*8)(R3), F30 + FMOVD (31*8)(R3), F31 + RETURN + +TEXT dcacheenb(SB), $0 + SYNC + MOVW SPR(HID0), R4 /* Get HID0 and clear unwanted bits */ + RLWNM $0, R4, $~(HID_DLOCK), R4 + MOVW $(HID_DCFI|HID_DCE), R5 + OR R4, R5 + MOVW $HID_DCE, R3 + OR R4, R3 + SYNC +// MOVW R5, SPR(HID0) /* Cache enable and flash invalidate */ + MOVW R3, SPR(HID0) /* Cache enable */ + SYNC + RETURN + +TEXT icacheenb(SB), $0 + SYNC + MOVW SPR(HID0), R4 /* Get HID0 and clear unwanted bits */ + RLWNM $0, R4, $~(HID_ILOCK), R4 + MOVW $(HID_ICFI|HID_ICE), R5 + OR R4, R5 + MOVW $HID_ICE, R3 + OR R4, R3 + SYNC + MOVW R5, SPR(HID0) /* Cache enable and flash invalidate */ + MOVW R3, SPR(HID0) /* Cache enable */ + SYNC + RETURN + +#ifdef ucuconf +TEXT getpll(SB), $0 + MOVW SPR(1009), R3 + ISYNC + RETURN + +TEXT getl2pm(SB), $0 + MOVW SPR(1016), R3 + RETURN + +TEXT getl2cr(SB), $0 + MOVW SPR(1017), R3 + RETURN + +TEXT putl2cr(SB), $0 + MOVW R3, SPR(1017) + RETURN + +TEXT dcachedis(SB), $0 + SYNC +/* MOVW SPR(HID0), R4 + RLWNM $0, R4, $~(HID_DCE), R4 + MOVW R4, SPR(HID0) /* L1 Cache disable */ + + MOVW SPR(1017), R4 + RLWNM $0, R4, $~(0x80000000), R4 + MOVW R4, SPR(1017) /* L2 Cache disable */ + + SYNC + RETURN + +TEXT l2disable(SB), $0 + SYNC + MOVW SPR(1017), R4 + RLWNM $0, R4, $~(0x80000000), R4 + MOVW R4, SPR(1017) /* L2 Cache disable */ + SYNC + RETURN + +TEXT getbats(SB), $0 + MOVW SPR(DBATU(0)), R4 + MOVW R4, 0(R3) + MOVW SPR(DBATL(0)), R4 + MOVW R4, 4(R3) + MOVW SPR(IBATU(0)), R4 + MOVW R4, 8(R3) + MOVW SPR(IBATL(0)), R4 + MOVW R4, 12(R3) + MOVW SPR(DBATU(1)), R4 + MOVW R4, 16(R3) + MOVW SPR(DBATL(1)), R4 + MOVW R4, 20(R3) + MOVW SPR(IBATU(1)), R4 + MOVW R4, 24(R3) + MOVW SPR(IBATL(1)), R4 + MOVW R4, 28(R3) + MOVW SPR(DBATU(2)), R4 + MOVW R4, 32(R3) + MOVW SPR(DBATL(2)), R4 + MOVW R4, 36(R3) + MOVW SPR(IBATU(2)), R4 + MOVW R4, 40(R3) + MOVW SPR(IBATL(2)), R4 + MOVW R4, 44(R3) + MOVW SPR(DBATU(3)), R4 + MOVW R4, 48(R3) + MOVW SPR(DBATL(3)), R4 + MOVW R4, 52(R3) + MOVW SPR(IBATU(3)), R4 + MOVW R4, 56(R3) + MOVW SPR(IBATL(3)), R4 + MOVW R4, 60(R3) + RETURN + +TEXT setdbat0(SB), $0 + MOVW 0(R3), R4 + MOVW R4, SPR(DBATU(0)) + MOVW 4(R3), R4 + MOVW R4, SPR(DBATL(0)) + RETURN +#endif /* ucuconf */ + +TEXT mmudisable(SB), $0 + /* disable MMU */ + MOVW LR, R4 + MOVW $KZERO, R5 + ANDN R5, R4 + MOVW R4, SPR(SRR0) /* Stored PC for RFI instruction */ + + MOVW MSR, R4 + MOVW $(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R5 + ANDN R5, R4 + MOVW R4, SPR(SRR1) + + MOVW SPR(HID0), R4 /* Get HID0 and clear unwanted bits */ + MOVW $(HID_ICE|HID_DCE), R5 + ANDN R5, R4 + MOVW R4, SPR(HID0) /* Cache disable */ + RFI /* resume caller with MMU off */ + RETURN + +TEXT kreboot(SB), $0 + BL mmudisable(SB) + MOVW R3, LR + RETURN + +TEXT mul64fract(SB), $0 + MOVW a0+8(FP), R9 + MOVW a1+4(FP), R10 + MOVW b0+16(FP), R4 + MOVW b1+12(FP), R5 + + MULLW R10, R5, R13 /* c2 = lo(a1*b1) */ + + MULLW R10, R4, R12 /* c1 = lo(a1*b0) */ + MULHWU R10, R4, R7 /* hi(a1*b0) */ + ADD R7, R13 /* c2 += hi(a1*b0) */ + + MULLW R9, R5, R6 /* lo(a0*b1) */ + MULHWU R9, R5, R7 /* hi(a0*b1) */ + ADDC R6, R12 /* c1 += lo(a0*b1) */ + ADDE R7, R13 /* c2 += hi(a0*b1) + carry */ + + MULHWU R9, R4, R7 /* hi(a0*b0) */ + ADDC R7, R12 /* c1 += hi(a0*b0) */ + ADDE R0, R13 /* c2 += carry */ + + MOVW R12, 4(R3) + MOVW R13, 0(R3) + RETURN diff --git a/sys/src/9/ppc/lblast.h b/sys/src/9/ppc/lblast.h new file mode 100755 index 000000000..2b3bf4833 --- /dev/null +++ b/sys/src/9/ppc/lblast.h @@ -0,0 +1,66 @@ +/* + * on return from this function we will be running in virtual mode. + * We set up the Block Address Translation (BAT) registers thus: + * 1) first 3 BATs are 256M blocks, starting from KZERO->0 + * 2) remaining BAT maps last 256M directly + */ +TEXT mmuinit0(SB), $0 + /* reset all the tlbs */ + MOVW $64, R3 + MOVW R3, CTR + MOVW $0, R4 +tlbloop: + TLBIE R4 + SYNC + ADD $BIT(19), R4 + BDNZ tlbloop + TLBSYNC + + /* BATs 0 and 1 cover memory from 0x00000000 to 0x20000000 */ + + /* KZERO -> 0, IBAT and DBAT, 256 MB */ + MOVW $(KZERO|(0x7ff<<2)|2), R3 + MOVW $(PTEVALID|PTEWRITE), R4 /* PTEVALID => Cache coherency on */ + MOVW R3, SPR(IBATU(0)) + MOVW R4, SPR(IBATL(0)) + MOVW R3, SPR(DBATU(0)) + MOVW R4, SPR(DBATL(0)) + + /* KZERO+256M -> 256M, IBAT and DBAT, 256 MB */ + ADD $(1<<28), R3 + ADD $(1<<28), R4 + MOVW R3, SPR(IBATU(1)) + MOVW R4, SPR(IBATL(1)) + MOVW R3, SPR(DBATU(1)) + MOVW R4, SPR(DBATL(1)) + + /* FPGABASE -> FPGABASE, DBAT, 16 MB */ + MOVW $(FPGABASE|(0x7f<<2)|2), R3 + MOVW $(FPGABASE|PTEWRITE|PTEUNCACHED), R4 /* FPGA memory, don't cache */ + MOVW R3, SPR(DBATU(2)) + MOVW R4, SPR(DBATL(2)) + + /* IBAT 2 unused */ + MOVW R0, SPR(IBATU(2)) + MOVW R0, SPR(IBATL(2)) + + /* direct map last block, uncached, (not guarded, doesn't work for BAT), DBAT only */ + MOVW $(INTMEM|(0x7ff<<2)|2), R3 + MOVW $(INTMEM|PTEWRITE|PTEUNCACHED), R4 /* Don't set PTEVALID here */ + MOVW R3, SPR(DBATU(3)) + MOVW R4, SPR(DBATL(3)) + + /* IBAT 3 unused */ + MOVW R0, SPR(IBATU(3)) + MOVW R0, SPR(IBATL(3)) + + /* enable MMU */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) /* Stored PC for RFI instruction */ + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R4 + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + + RETURN diff --git a/sys/src/9/ppc/lucu.h b/sys/src/9/ppc/lucu.h new file mode 100755 index 000000000..e8b040e7e --- /dev/null +++ b/sys/src/9/ppc/lucu.h @@ -0,0 +1,39 @@ +/* + * on return from this function we will be running in virtual mode. + * We set up the Block Address Translation (BAT) registers thus: + * 1) first 3 BATs are 256M blocks, starting from KZERO->0 + * 2) remaining BAT maps last 256M directly + */ +TEXT mmuinit0(SB), $0 + /* reset all the tlbs */ + MOVW $64, R3 + MOVW R3, CTR + MOVW $0, R4 + +tlbloop: + TLBIE R4 + SYNC + ADD $BIT(19), R4 + BDNZ tlbloop + TLBSYNC + + /* BATs 0 and 1 cover memory from 0x00000000 to 0x20000000 */ + + /* KZERO -> 0, IBAT2 and DBAT2, 256 MB */ + MOVW $(KZERO|(0x7ff<<2)|2), R3 + MOVW $(PTEVALID|PTEWRITE), R4 /* PTEVALID => Cache coherency on */ + MOVW R3, SPR(DBATU(2)) + MOVW R4, SPR(DBATL(2)) + MOVW R3, SPR(IBATU(2)) + MOVW R4, SPR(IBATL(2)) + + /* enable MMU */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) /* Stored PC for RFI instruction */ + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R4 + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + + RETURN diff --git a/sys/src/9/ppc/m8260.c b/sys/src/9/ppc/m8260.c new file mode 100755 index 000000000..4f84ec135 --- /dev/null +++ b/sys/src/9/ppc/m8260.c @@ -0,0 +1,661 @@ +/* + * 8260 specific stuff: + * Interrupt handling + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "io.h" +#include "fns.h" +#include "m8260.h" + +enum { + Pin4 = BIT(4), +}; + +static union { + struct { + ulong hi; + ulong lo; + }; + uvlong val; +} ticks; + +struct { + ulong hi; + ulong lo; +} vec2mask[64] = { +[0] = {0, 0 }, /* Error, No interrupt */ +[1] = {0, BIT(16) }, /* I2C */ +[2] = {0, BIT(17) }, /* SPI */ +[3] = {0, BIT(18) }, /* Risc Timers */ +[4] = {0, BIT(19) }, /* SMC1 */ +[5] = {0, BIT(20) }, /* SMC2 */ +[6] = {0, BIT(21) }, /* IDMA1 */ +[7] = {0, BIT(22) }, /* IDMA2 */ +[8] = {0, BIT(23) }, /* IDMA3 */ +[9] = {0, BIT(24) }, /* IDMA4 */ +[10] = {0, BIT(25) }, /* SDMA */ +[11] = {0, 0 }, /* Reserved */ +[12] = {0, BIT(27) }, /* Timer1 */ +[13] = {0, BIT(28) }, /* Timer2 */ +[14] = {0, BIT(29) }, /* Timer3 */ +[15] = {0, BIT(30) }, /* Timer4 */ + +[16] = {BIT(29), 0 }, /* TMCNT */ +[17] = {BIT(30), 0 }, /* PIT */ +[18] = {0, 0 }, /* Reserved */ +[19] = {BIT(17), 0 }, /* IRQ1 */ +[20] = {BIT(18), 0 }, /* IRQ2 */ +[21] = {BIT(19), 0 }, /* IRQ3 */ +[22] = {BIT(20), 0 }, /* IRQ4 */ +[23] = {BIT(21), 0 }, /* IRQ5 */ +[24] = {BIT(22), 0 }, /* IRQ6 */ +[25] = {BIT(23), 0 }, /* IRQ7 */ +[26] = {0, 0 }, /* Reserved */ +[27] = {0, 0 }, /* Reserved */ +[28] = {0, 0 }, /* Reserved */ +[29] = {0, 0 }, /* Reserved */ +[30] = {0, 0 }, /* Reserved */ +[31] = {0, 0 }, /* Reserved */ + +[32] = {0, BIT(0) }, /* FCC1 */ +[33] = {0, BIT(1) }, /* FCC2 */ +[34] = {0, BIT(2) }, /* FCC3 */ +[35] = {0, 0 }, /* Reserved */ +[36] = {0, BIT(4) }, /* MCC1 */ +[37] = {0, BIT(5) }, /* MCC2 */ +[38] = {0, 0 }, /* Reserved */ +[39] = {0, 0 }, /* Reserved */ +[40] = {0, BIT(8) }, /* SCC1 */ +[41] = {0, BIT(9) }, /* SCC2 */ +[42] = {0, BIT(10) }, /* SCC3 */ +[43] = {0, BIT(11) }, /* SCC4 */ +[44] = {0, 0 }, /* Reserved */ +[45] = {0, 0 }, /* Reserved */ +[46] = {0, 0 }, /* Reserved */ +[47] = {0, 0 }, /* Reserved */ + +[48] = {BIT(15), 0 }, /* PC15 */ +[49] = {BIT(14), 0 }, /* PC14 */ +[50] = {BIT(13), 0 }, /* PC13 */ +[51] = {BIT(12), 0 }, /* PC12 */ +[52] = {BIT(11), 0 }, /* PC11 */ +[53] = {BIT(10), 0 }, /* PC10 */ +[54] = {BIT(9), 0 }, /* PC9 */ +[55] = {BIT(8), 0 }, /* PC8 */ +[56] = {BIT(7), 0 }, /* PC7 */ +[57] = {BIT(6), 0 }, /* PC6 */ +[58] = {BIT(5), 0 }, /* PC5 */ +[59] = {BIT(4), 0 }, /* PC4 */ +[60] = {BIT(3), 0 }, /* PC3 */ +[61] = {BIT(2), 0 }, /* PC2 */ +[62] = {BIT(1), 0 }, /* PC1 */ +[63] = {BIT(0), 0 }, /* PC0 */ +}; + +/* Blast memory layout: + * CS0: FE000000 -> FFFFFFFF (Flash) + * CS1: FC000000 -> FCFFFFFF (DSP hpi) + * CS2: 00000000 -> 03FFFFFF (60x sdram) + * CS3: 04000000 -> 04FFFFFF (FPGA) + * CS4: 05000000 -> 06FFFFFF (local bus sdram) + * CS5: 07000000 -> 0700FFFF (eeprom - not populated) + * CS6: E0000000 -> E0FFFFFF (FPGA - 64bits) + * + * Main Board memory layout: + * CS0: FE000000 -> FEFFFFFF (16 M FLASH) + * CS1: FC000000 -> FCFFFFFF (16 M DSP1) + * CS2: 00000000 -> 03FFFFFF (64 M SDRAM) + * CS3: 04000000 -> 04FFFFFF (16M DSP2) + * CS4: 05000000 -> 06FFFFFF (32 M Local SDRAM) + * CS5: 07000000 -> 0700FFFF (eeprom - not populated) + * CS6: unused + * CS7: E0000000 -> E0FFFFFF (16 M FPGA) + */ + +IMM* iomem = (IMM*)IOMEM; + +static Lock cpmlock; + +void +machinit(void) +{ + ulong scmr; + int pllmf; + extern char* plan9inistr; + + memset(m, 0, sizeof(*m)); + m->cputype = getpvr()>>16; /* pvr = 0x00810101 for the 8260 */ + m->imap = (Imap*)INTMEM; + + m->loopconst = 1096; + + /* Make sure Ethernet is disabled (boot code may have buffers allocated anywhere in memory) */ + iomem->fcc[0].gfmr &= ~(BIT(27)|BIT(26)); + iomem->fcc[1].gfmr &= ~(BIT(27)|BIT(26)); + iomem->fcc[2].gfmr &= ~(BIT(27)|BIT(26)); + + /* Flashed CS configuration is wrong for DSP2. It's set to 64 bits, should be 16 */ + iomem->bank[3].br = 0x04001001; /* Set 16-bit port */ + + /* + * FPGA is capable of doing 64-bit transfers. To use these, set br to 0xe0000001. + * Currently we use 32-bit transfers, because the 8260 does not easily do 64-bit operations. + */ + iomem->bank[6].br = 0xe0001801; + iomem->bank[6].or = 0xff000830; /* Was 0xff000816 */ + +/* + * All systems with rev. A.1 (0K26N) silicon had serious problems when doing + * DMA transfers with data cache enabled (usually this shows when using + * one of the FCC's with some traffic on the ethernet). Allocating FCC buffer + * descriptors in main memory instead of DP ram solves this problem. + */ + + /* Guess at clocks based upon the PLL configuration from the + * power-on reset. + */ + scmr = iomem->scmr; + + /* The EST8260 is typically run using either 33 or 66 MHz + * external clock. The configuration byte in the Flash will + * tell us which is configured. The blast appears to be slightly + * overclocked at 72 MHz (if set to 66 MHz, the uart runs too fast) + */ + + m->clkin = CLKIN; + + pllmf = scmr & 0xfff; + + /* This is arithmetic from the 8260 manual, section 9.4.1. */ + + /* Collect the bits from the scmr. + */ + m->vco_out = m->clkin * (pllmf + 1); + if (scmr & BIT(19)) /* plldf (division factor is 1 or 2) */ + m->vco_out >>= 1; + + m->cpmhz = m->vco_out >> 1; /* cpm hz is half of vco_out */ + m->brghz = m->vco_out >> (2 * ((iomem->sccr & 0x3) + 1)); + m->bushz = m->vco_out / (((scmr & 0x00f00000) >> 20) + 1); + + /* Serial init sets BRG clock....I don't know how to compute + * core clock from core configuration, but I think I know the + * mapping.... + */ + switch(scmr >> (31-7)){ + case 0x0a: + m->cpuhz = m->clkin * 2; + break; + case 0x0b: + m->cpuhz = (m->clkin >> 1) * 5; + break; + default: + case 0x0d: + m->cpuhz = m->clkin * 3; + break; + case 0x14: + m->cpuhz = (m->clkin >> 1) * 7; + break; + case 0x1c: + m->cpuhz = m->clkin * 4; + break; + } + + m->cyclefreq = m->bushz / 4; + +/* Expect: + intfreq 133 m->cpuhz + busfreq 33 m->bushz + cpmfreq 99 m->cpmhz + brgfreq 49.5 m->brghz + vco 198 +*/ + + active.machs = 1; + active.exiting = 0; + + putmsr(getmsr() | MSR_ME); + + /* + * turn on data cache before instruction cache; + * for some reason which I don't understand, + * you can't turn on both caches at once + */ + icacheenb(); + dcacheenb(); + + kfpinit(); + + /* Plan9.ini location in flash is FLASHMEM+PLAN9INI + * if PLAN9INI == ~0, it's not stored in flash or there is no flash + * if *cp == 0xff, flash memory is not initialized + */ + if (PLAN9INI == ~0 || *(plan9inistr = (char*)(FLASHMEM+PLAN9INI)) == 0xff){ + /* No plan9.ini in flash */ + plan9inistr = + "console=0\n" + "ether0=type=fcc port=0 ea=00601d051dd8\n" + "flash0=mem=0xfe000000\n" + "fs=135.104.9.42\n" + "auth=135.104.9.7\n" + "authdom=cs.bell-labs.com\n" + "sys=blast\n" + "ntp=135.104.9.52\n"; + } +} + +void +fpgareset(void) +{ + print("fpga reset\n"); + + ioplock(); + + iomem->port[1].pdat &= ~Pin4; /* force reset signal to 0 */ + delay(100); + iomem->port[1].pdat |= Pin4; /* force reset signal back to one */ + + iopunlock(); +} + +void +hwintrinit(void) +{ + iomem->sicr = 2 << 8; + /* Write ones into most bits of the interrupt pending registers to clear interrupts */ + iomem->sipnr_h = ~7; + iomem->sipnr_h = ~1; + /* Clear the interrupt masks, thereby disabling all interrupts */ + iomem->simr_h = 0; + iomem->simr_l = 0; + + iomem->sypcr &= ~2; /* cause a machine check interrupt on memory timeout */ + + /* Initialize fpga reset pin */ + iomem->port[1].pdir |= Pin4; /* 1 is an output */ + iomem->port[1].ppar &= ~Pin4; + iomem->port[1].pdat |= Pin4; /* force reset signal back to one */ +} + +int +vectorenable(Vctl *v) +{ + ulong hi, lo; + + if (v->irq & ~0x3f){ + print("m8260enable: interrupt vector %d out of range\n", v->irq); + return -1; + } + hi = vec2mask[v->irq].hi; + lo = vec2mask[v->irq].lo; + if (hi == 0 && lo == 0){ + print("m8260enable: nonexistent vector %d\n", v->irq); + return -1; + } + ioplock(); + /* Clear the interrupt before enabling */ + iomem->sipnr_h |= hi; + iomem->sipnr_l |= lo; + /* Enable */ + iomem->simr_h |= hi; + iomem->simr_l |= lo; + iopunlock(); + return v->irq; +} + +void +vectordisable(Vctl *v) +{ + ulong hi, lo; + + if (v->irq & ~0x3f){ + print("m8260disable: interrupt vector %d out of range\n", v->irq); + return; + } + hi = vec2mask[v->irq].hi; + lo = vec2mask[v->irq].lo; + if (hi == 0 && lo == 0){ + print("m8260disable: nonexistent vector %d\n", v->irq); + return; + } + ioplock(); + iomem->simr_h &= ~hi; + iomem->simr_l &= ~lo; + iopunlock(); +} + +int +intvec(void) +{ + return iomem->sivec >> 26; +} + +void +intend(int vno) +{ + /* Clear interrupt */ + ioplock(); + iomem->sipnr_h |= vec2mask[vno].hi; + iomem->sipnr_l |= vec2mask[vno].lo; + iopunlock(); +} + +int +m8260eoi(int) +{ + return 0; +} + +int +m8260isr(int) +{ + return 0; +} + +void +flashprogpower(int) +{ +} + +enum { + TgcrCas = 0x80, + TgcrGm = 0x08, + TgcrStp = 0x2, /* There are two of these, timer-2 bits are bits << 4 */ + TgcrRst = 0x1, + + TmrIclkCasc = 0x00<<1, + TmrIclkIntclock = 0x01<<1, + TmrIclkIntclock16 = 0x02<<1, + TmrIclkTin = 0x03<<1, + TmrCERising = 0x1 << 6, + TmrCEFalling = 0x2 << 6, + TmrCEAny = 0x3 << 6, + TmrFrr = SBIT(12), + TmrOri = SBIT(11), + + TerRef = SBIT(14), + TerCap = SBIT(15), +}; + +uvlong +fastticks(uvlong *hz) +{ + ulong count; + static Lock fasttickslock; + + if (hz) + *hz = m->clkin>>1; + ilock(&fasttickslock); + count = iomem->tcnl1; + if (count < ticks.lo) + ticks.hi += 1; + ticks.lo = count; + iunlock(&fasttickslock); + return ticks.val; +} + +void +timerset(uvlong next) +{ + long offset; + uvlong now; + static int cnt; + + now = fastticks(nil); + offset = next - now; + if (offset < 2500) + next = now + 2500; /* 10000 instructions */ + else if (offset > m->clkin / HZ){ + print("too far in the future: offset %llux, now %llux\n", next, now); + next = now + m->clkin / HZ; + } + iomem->trrl1 = next; +} + +void +m8260timerintr(Ureg *u, void*) +{ + iomem->ter2 |= TerRef | TerCap; /* Clear interrupt */ + timerintr(u, 0); +} + +void +timerinit(void) +{ + + iomem->tgcr1 = TgcrCas | TgcrGm; /* cascade timers 1 & 2, normal gate mode */ + iomem->tcnl1 = 0; + iomem->trrl1 = m->clkin / HZ; /* first capture in 1/HZ seconds */ + iomem->tmr1 = TmrIclkCasc; + iomem->tmr2 = TmrIclkIntclock | TmrOri; + intrenable(13, m8260timerintr, nil, "timer"); /* Timer 2 interrupt is on 13 */ + iomem->tgcr1 |= TgcrRst << 4; +} + +static void +addseg(char *name, ulong start, ulong length) +{ + Physseg segbuf; + + memset(&segbuf, 0, sizeof(segbuf)); + segbuf.attr = SG_PHYSICAL; + kstrdup(&segbuf.name, name); + segbuf.pa = start; + segbuf.size = length; + if (addphysseg(&segbuf) == -1) { + print("addphysseg: %s\n", name); + return; + } +} + +void +sharedseginit(void) +{ + int i, j; + ulong base, size; + char name[16], *a, *b, *s; + static char *segnames[] = { + "fpga", + "dsp", + }; + + for (j = 0; j < nelem(segnames); j++){ + for (i = 0; i < 8; i++){ + snprint(name, sizeof name, "%s%d", segnames[j], i); + if ((a = getconf(name)) == nil) + continue; + if ((b = strstr(a, "mem=")) == nil){ + print("blastseginit: %s: no base\n", name); + continue; + } + b += 4; + base = strtoul(b, nil, 0); + if (base == 0){ + print("blastseginit: %s: bad base: %s\n", name, b); + continue; + } + if ((s = strstr(a, "size=")) == nil){ + print("blastseginit: %s: no size\n", name); + continue; + } + s += 5; + size = strtoul(s, nil, 0); + if (size == 0){ + print("blastseginit: %s: bad size: %s\n", name, s); + continue; + } + addseg(name, base, size); + } + } +} + +void +cpmop(int op, int dev, int mcn) +{ + ioplock(); + eieio(); + while(iomem->cpcr & 0x10000) + eieio(); + iomem->cpcr = dev<<(31-10) | mcn<<(31-25) | op | 0x10000; + eieio(); + while(iomem->cpcr & 0x10000) + eieio(); + iopunlock(); +} + +/* + * connect SCCx clocks in NSMI mode (x=1 for USB) + */ +void +sccnmsi(int x, int rcs, int tcs) +{ + ulong v; + int sh; + + sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */ + v = (((rcs&7)<<3) | (tcs&7)) << sh; + iomem->sicr = (iomem->sicr & ~(0xFF<<sh)) | v; +} + +/* + * lock the shared IO memory and return a reference to it + */ +void +ioplock(void) +{ + ilock(&cpmlock); +} + +/* + * release the lock on the shared IO memory + */ +void +iopunlock(void) +{ + eieio(); + iunlock(&cpmlock); +} + +BD* +bdalloc(int n) +{ + static BD *palloc = ((Imap*)INTMEM)->bd; + BD *p; + + p = palloc; + if (palloc > ((Imap*)INTMEM)->bd + nelem(((Imap*)INTMEM)->bd)){ + print("bdalloc: out of BDs\n"); + return nil; + } + palloc += n; + return p; +} + +/* + * Initialise receive and transmit buffer rings. Only used for FCC + * Ethernet now. + * + * Ioringinit will allocate the buffer descriptors in normal memory + * and NOT in Dual-Ported Ram, as prescribed by the MPC8260 + * PowerQUICC II manual (Section 28.6). When they are allocated + * in DPram and the Dcache is enabled, the processor will hang. + * This has been observed for the FCCs, it may or may not be true + * for SCCs or DMA. + * The SMC Uart buffer descriptors are not allocated here; (1) they + * can ONLY be in DPram and (2) they are not configured as a ring. + */ +int +ioringinit(Ring* r, int nrdre, int ntdre, int bufsize) +{ + int i, x; + static uchar *dpmallocaddr; + static uchar *dpmallocend; + + if (dpmallocaddr == nil){ + dpmallocaddr = m->imap->dpram1; + dpmallocend = dpmallocaddr + sizeof(m->imap->dpram1); + } + /* the ring entries must be aligned on sizeof(BD) boundaries */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = xspanalloc(nrdre*sizeof(BD), 0, 8); + if(r->rdr == nil) + return -1; + if(r->rrb == nil && bufsize){ + r->rrb = xspanalloc(nrdre*bufsize, 0, CACHELINESZ); + if(r->rrb == nil) + return -1; + } + x = bufsize ? PADDR(r->rrb) : 0; + for(i = 0; i < nrdre; i++){ + r->rdr[i].length = 0; + r->rdr[i].addr = x; + r->rdr[i].status = BDEmpty|BDInt; + x += bufsize; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = xspanalloc(ntdre*sizeof(BD), 0, 8); + if(r->txb == nil) + r->txb = xspanalloc(ntdre*sizeof(Block*), 0, CACHELINESZ); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} + +void +trapinit(void) +{ + int i; + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x2000; i += 0x100) + sethvec(i, trapvec); + + setmvec(0x1000, imiss, tlbvec); + setmvec(0x1100, dmiss, tlbvec); + setmvec(0x1200, dmiss, tlbvec); + +/* Useful for avoiding assembler miss handling: + sethvec(0x1000, tlbvec); + sethvec(0x1100, tlbvec); + sethvec(0x1200, tlbvec); +/* */ + dcflush(KADDR(0), 0x2000); + icflush(KADDR(0), 0x2000); + + putmsr(getmsr() & ~MSR_IP); +} + +void +reboot(void*, void*, ulong) +{ + ulong *p; + int x; + + p = (ulong*)0x90000000; + x = splhi(); + iomem->sypcr |= 0xc0; + print("iomem->sypcr = 0x%lux\n", iomem->sypcr); + *p = 0; + print("still alive\n"); + splx(x); +} diff --git a/sys/src/9/ppc/m8260.h b/sys/src/9/ppc/m8260.h new file mode 100755 index 000000000..9aa0ffb82 --- /dev/null +++ b/sys/src/9/ppc/m8260.h @@ -0,0 +1,658 @@ + +typedef struct BD BD; +struct BD { + ushort status; + ushort length; + ulong addr; +}; + +enum{ + BDEmpty= SBIT(0), + BDReady= SBIT(0), + BDWrap= SBIT(2), + BDInt= SBIT(3), + BDLast= SBIT(4), + BDFirst= SBIT(5), +}; + +typedef struct Ring Ring; + +struct Ring { + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + void** txb; /* corresponding transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +int ioringinit(Ring*, int, int, int); + +/* + * MCC parameters + */ +typedef struct MCCparam MCCparam; +struct MCCparam { +/*0x00*/ ulong mccbase; +/*0x04*/ ushort mccstate; +/*0x06*/ ushort mrblr; +/*0x08*/ ushort grfthr; +/*0x0a*/ ushort grfcnt; +/*0x0c*/ ulong rinttmp; +/*0x10*/ ulong data0; +/*0x14*/ ulong data1; +/*0x18*/ ulong tintbase; +/*0x1c*/ ulong tintptr; +/*0x20*/ ulong tinttmp; +/*0x24*/ ushort sctpbase; +/*0x26*/ ushort Rsvd26; +/*0x28*/ ulong cmask32; +/*0x2c*/ ushort xtrabase; +/*0x2e*/ ushort cmask16; +/*0x30*/ ulong rinttmp[4]; +/*0x40*/ struct { + ulong base; + ulong ptr; + } rint[4]; +/*0x60*/ ulong tstmp; +/*0x64*/ +}; +/* + * IO controller parameters + */ +typedef struct IOCparam IOCparam; +struct IOCparam { +/*0x00*/ ushort rbase; +/*0x02*/ ushort tbase; +/*0x04*/ uchar rfcr; +/*0x05*/ uchar tfcr; +/*0x06*/ ushort mrblr; +/*0x08*/ ulong rstate; +/*0x0c*/ ulong rxidp; +/*0x10*/ ushort rbptr; +/*0x12*/ ushort rxibc; +/*0x14*/ ulong rxtemp; +/*0x18*/ ulong tstate; +/*0x1c*/ ulong txidp; +/*0x20*/ ushort tbptr; +/*0x22*/ ushort txibc; +/*0x24*/ ulong txtemp; +/*0x28*/ +}; + +typedef struct SCCparam SCCparam; +struct SCCparam { + IOCparam; + ulong rcrc; + ulong tcrc; +}; + +typedef struct FCCparam FCCparam; +struct FCCparam { +/*0x00*/ ushort riptr; +/*0x02*/ ushort tiptr; +/*0x04*/ ushort Rsvd04; +/*0x06*/ ushort mrblr; +/*0x08*/ ulong rstate; +/*0x0c*/ ulong rbase; +/*0x10*/ ushort rbdstat; +/*0x12*/ ushort rbdlen; +/*0x14*/ char* rdptr; +/*0x18*/ ulong tstate; +/*0x1c*/ ulong tbase; +/*0x20*/ ushort tbdstat; +/*0x22*/ ushort tbdlen; +/*0x24*/ ulong tdptr; +/*0x28*/ ulong rbptr; +/*0x2c*/ ulong tbptr; +/*0x30*/ ulong rcrc; +/*0x34*/ ulong Rsvd34; +/*0x38*/ ulong tcrc; +/*0x3c*/ +}; + +typedef struct SCC SCC; +struct SCC { + ulong gsmrl; + ulong gsmrh; + ushort psmr; + uchar rsvscc0[2]; + ushort todr; + ushort dsr; + ushort scce; + uchar rsvscc1[2]; + ushort sccm; + uchar rsvscc2; + uchar sccs; + ushort irmode; + ushort irsip; + uchar rsvscc3[4]; /* BUG */ +}; + +typedef struct FCC FCC; +struct FCC { +/*0x00*/ ulong gfmr; /* general mode register 28.2/28-3 */ +/*0x04*/ ulong fpsmr; /* protocol-specific mode reg. 29.13.2(ATM) 30.18.1(Ether) */ +/*0x08*/ ushort ftodr; /* transmit on demand register 28.5/28-7 */ +/*0x0A*/ ushort Rsvd0A; +/*0x0C*/ ushort fdsr; /* data synchronization register 28.4/28-7 */ +/*0x0E*/ ushort Rsvd0E; +/*0x10*/ ushort fcce; /* event register 29.13.3 (ATM), 30.18.2 (Ethernet) */ +/*0x12*/ ushort Rsvd12; +/*0x14*/ ushort fccm; /* mask register */ +/*0x16*/ ushort Rsvd16; +/*0x18*/ uchar fccs; /* status register 8 bits 31.10 (HDLC) */ +/*0x19*/ uchar Rsvd19[3]; +/*0x1C*/ uchar ftirrphy[4]; /* transmit internal rate registers for PHY0DH3 29.13.4/29-88 (ATM) */ +/*0x20*/ +}; + +typedef struct SMC SMC; +struct SMC { +/*0x0*/ ushort pad1; +/*0x2*/ ushort smcmr; +/*0x4*/ ushort pad2; +/*0x6*/ uchar smce; +/*0x7*/ uchar pad3[3]; +/*0xa*/ uchar smcm; +/*0xb*/ uchar pad4[5]; +/*0x10*/ +}; + +typedef struct SPI SPI; +struct SPI { + ushort spmode; + uchar res1[4]; + uchar spie; + uchar res2[3]; + uchar spim; + uchar res3[2]; + uchar spcom; + uchar res4[2]; +}; + +typedef struct Bankmap Bankmap; +struct Bankmap { +/*0*/ ulong br; /* Base register bank 32 bits 10.3.1/10-14 */ +/*4*/ ulong or; /* Option register bank 32 bits 10.3.2/10-16 */ +/*8*/ +}; + +typedef struct Port Port; +struct Port { +/*0x00*/ ulong pdir; /* Port A data direction register 32 bits 35.2.3/35-3 */ +/*0x04*/ ulong ppar; /* Port Apin assignment register 32 bits 35.2.4/35-4 */ +/*0x08*/ ulong psor; /* Port A special options register 32 bits 35.2.5/35-4 */ +/*0x0C*/ ulong podr; /* Port Aopen drain register 32 bits 35.2.1/35-2 */ +/*0x10*/ ulong pdat; /* Port A data register 32 bits 35.2.2/35-2 */ +/*0x14*/ uchar Rsvd14[12]; +/*0x20*/ +}; + +typedef struct IDMA IDMA; +struct IDMA { +/*0x0*/ uchar idsr; /* IDMA event register 8 bits 18.8.4/18-22 */ +/*0x1*/ uchar Rsvd1[3]; +/*0x4*/ uchar idmr; /* IDMA mask register 8 bits 18.8.4/18-22 */ +/*0x5*/ uchar Rsvd5[3]; +/*0x8*/ +}; + +typedef struct PrmSCC PrmSCC; +struct PrmSCC { + uchar sccbytes[0x100]; +}; + +typedef struct PrmFCC PrmFCC; +struct PrmFCC { + uchar fccbytes[0x100]; +}; + +typedef struct Bases Bases; +struct Bases { +/*0x00*/ uchar mcc[0x80]; +/*0x80*/ uchar Rsvd80[0x60]; +/*0xe0*/ uchar risctimers[0x10]; +/*0xf0*/ ushort revnum; +/*0xf2*/ uchar Rsvdf2[6]; +/*0xf8*/ ulong rand; +/*0xfc*/ ushort smcbase; +#define i2cbase smcbase +/*0xfe*/ ushort idmabase; +/*0x100*/ +}; + +typedef struct Uartsmc Uartsmc; +struct Uartsmc { +/*0x00*/ IOCparam; +/*0x28*/ ushort maxidl; +/*0x2a*/ ushort idlc; +/*0x2c*/ ushort brkln; +/*0x2e*/ ushort brkec; +/*0x30*/ ushort brkcr; +/*0x32*/ ushort r_mask; +/*0x34*/ ulong sdminternal; +/*0x38*/ uchar Rsvd38[8]; +/*0x40*/ +}; + +typedef struct SI SI; +struct SI { +/*0x11B20*/ ushort siamr; /* SI TDMA1 mode register 16 bits 14.5.2/14-17 */ +/*0x11B22*/ ushort sibmr; /* SI TDMB1 mode register 16 bits */ +/*0x11B24*/ ushort sicmr; /* SI TDMC1 mode register 16 bits */ +/*0x11B26*/ ushort sidmr; /* SI TDMD1 mode register 16 bits */ +/*0x11B28*/ uchar sigmr; /* SI global mode register 8 bits 14.5.1/14-17 */ +/*0x11B29*/ uchar Rsvd11B29; +/*0x11B2A*/ ushort sicmdr; /* SI command register 8 bits 14.5.4/14-24 */ +/*0x11B2C*/ ushort sistr; /* SI status register 8 bits 14.5.5/14-25 */ +/*0x11B2E*/ ushort sirsr; /* SI RAM shadow address register 16 bits 14.5.3/14-23 */ +}; + +typedef struct IMM IMM; +struct IMM { +/* General SIU */ +/*0x10000*/ ulong siumcr; /* SIU module configuration register 32 bits 4.3.2.6/4-31 */ +/*0x10004*/ ulong sypcr; /* System protection control register 32 bits 4.3.2.8/4-35 */ +/*0x10008*/ uchar Rsvd10008[0xe-0x8]; +/*0x1000E*/ ushort swsr; /* Softwareservice register 16 bits 4.3.2.9/4-36 */ +/*0x10010*/ uchar Rsvd10010[0x14]; +/*0x10024*/ ulong bcr; /* Bus configuration register 32 bits 4.3.2.1/4-25 */ +/*0x10028*/ ulong PPC_ACR; /* 60x bus arbiter configuration register 8 bits 4.3.2.2/4-28 */ +/*0x1002C*/ ulong PPCALRH; /* 60x bus arbitration-level register high (first 8 clients) 32 bits 4.3.2.3/4-28 */ +/*0x10030*/ ulong PPC_ALRL; /* 60x bus arbitration-level register low (next 8 clients) 32 bits 4.3.2.3/4-28 */ +/*0x10034*/ ulong LCL_ACR; /* Local arbiter configuration register 8 bits 4.3.2.4/4-29 */ +/*0x10038*/ ulong LCL_ALRH; /* Local arbitration-level register (first 8 clients) 32 bits 4.3.2.5/4-30 */ + +/*0x1003C*/ ulong LCL_ALRL; /* Local arbitration-level register (next 8 clients) 32 bits 4.3.2.3/4-28 */ +/*0x10040*/ ulong TESCR1; /* 60x bus transfer error status control register1 32 bits 4.3.2.10/4-36 */ +/*0x10044*/ ulong TESCR2; /* 60x bus transfer error status control register2 32 bits 4.3.2.11/4-37 */ +/*0x10048*/ ulong L_TESCR1; /* Local bus transfer error status control register1 32 bits 4.3.2.12/4-38 */ +/*0x1004C*/ ulong L_TESCR2; /* Local bus transfer error status control register2 32 bits 4.3.2.13/4-39 */ +/*0x10050*/ ulong pdtea; /* 60x bus DMAtransfer error address 32 bits 18.2.3/18-4 */ +/*0x10054*/ uchar pdtem; /* 60x bus DMAtransfer error MSNUM 8 bits 18.2.4/18-4 */ +/*0x10055*/ uchar Rsvd10055[3]; +/*0x10058*/ void* ldtea; /* Local bus DMA transfer error address 32 bits 18.2.3/18-4 */ +/*0x1005C*/ uchar ldtem; /* Local bus DMA transfer error MSNUM 8 bits 18.2.4/18-4 */ +/*0x1005D*/ uchar Rsvd1005D[163]; + +/* Memory Controller */ +/*0x10100*/ Bankmap bank[12]; + +/*0x10160*/ uchar Rsvd10160[8]; +/*0x10168*/ void* MAR; /* Memory address register 32 bits 10.3.7/10-29 */ +/*0x1016C*/ ulong Rsvd1016C; +/*0x10170*/ ulong MAMR; /* Machine A mode register 32 bits 10.3.5/10-26 */ +/*0x10174*/ ulong MBMR; /* Machine B mode register 32 bits */ +/*0x10178*/ ulong MCMR; /* Machine C mode register 32 bits */ +/*0x1017C*/ uchar Rsvd1017C[6]; +/*0x10184*/ ulong mptpr; /* Memory periodic timer prescaler 16 bits 10.3.12/10-32 */ +/*0x10188*/ ulong mdr; /* Memorydata register 32 bits 10.3.6/10-28 */ +/*0x1018C*/ ulong Rsvd1018C; +/*0x10190*/ ulong psdmr; /* 60x bus SDRAM mode register 32 bits 10.3.3/10-21 */ +/*0x10194*/ ulong lsdmr; /* Local bus SDRAM mode register 32 bits 10.3.4/10-24 */ +/*0x10198*/ ulong PURT; /* 60x bus-assigned UPM refresh timer 8 bits 10.3.8/10-30 */ +/*0x1019C*/ ulong PSRT; /* 60x bus-assigned SDRAM refresh timer 8 bits 10.3.10/10-31 */ + +/*0x101A0*/ ulong LURT; /* Local bus-assigned UPM refresh timer8 bits 10.3.9/10-30 */ +/*0x101A4*/ ulong LSRT; /* Local bus-assigned SDRAM refresh timer 8 bits 10.3.11/10-32 */ + +/*0x101A8*/ ulong immr; /* Internal memory map register 32 bits 4.3.2.7/4-34 */ +/*0x101AC*/ uchar Rsvd101AC[84]; +/* System Integration Timers */ +/*0x10200*/ uchar Rsvd10200[32]; +/*0x10220*/ ulong TMCNTSC; /* Time counter statusand control register 16 bits 4.3.2.14/4-40 */ + +/*0x10224*/ ulong TMCNT; /* Time counter register 32 bits 4.3.2.15/4-41 */ +/*0x10228*/ ulong Rsvd10228; +/*0x1022C*/ ulong TMCNTAL; /* Time counter alarm register 32 bits 4.3.2.16/4-41 */ +/*0x10230*/ uchar Rsvd10230[0x10]; +/*0x10240*/ ulong PISCR; /* Periodic interrupt statusand control register 16 bits 4.3.3.1/4-42 */ + +/*0x10244*/ ulong PITC; /* Periodic interrupt count register 32 bits 4.3.3.2/4-43 */ +/*0x10248*/ ulong PITR; /* Periodic interrupt timer register 32 bits 4.3.3.3/4-44 */ +/*0x1024C*/ uchar Rsvd1024C[94]; +/*0x102AA*/ uchar Rsvd102AA[2390]; + +/* Interrupt Controller */ +/*0x10C00*/ ushort sicr; /* SIU interrupt configuration register 16 bits 4.3.1.1/4-17 */ +/*0x10C02*/ ushort Rsvd10C02; +/*0x10C04*/ ulong sivec; /* SIU interrupt vector register 32 bits 4.3.1.6/4-23 */ +/*0x10C08*/ ulong sipnr_h; /* SIU interrupt pending register(high) 32 bits 4.3.1.4/4-21 */ +/*0x10C0C*/ ulong sipnr_l; /* SIU interrupt pending register(low) 32 bits 4.3.1.4/4-21 */ +/*0x10C10*/ ulong siprr; /* SIU interrupt priority register 32 bits 4.3.1.2/4-18 */ +/*0x10C14*/ ulong scprr_h; /* CPM interrupt priority register(high) 32 bits 4.3.1.3/4-19 */ +/*0x10C18*/ ulong scprr_l; /* CPM interrupt priority register(low) 32 bits 4.3.1.3/4-19 */ +/*0x10C1C*/ ulong simr_h; /* SIU interrupt mask register(high) 32 bits 4.3.1.5/4-22 */ +/*0x10C20*/ ulong simr_l; /* SIU interrupt mask register(low) 32 bits 4.3.1.5/4-22 */ +/*0x10C24*/ ulong siexr; /* SIUexternal interrupt control register 32 bits 4.3.1.7/4-24 */ +/*0x10C28*/ uchar Rsvd10C28[88]; + +/* Clocksand Reset */ +/*0x10C80*/ ulong sccr; /* Systemclock control register 32 bits 9.8/9-8 */ +/*0x10C84*/ uchar Rsvd10C84[4]; +/*0x10C88*/ ulong scmr; /* Systemclock mode register 32 bits 9.9/9-9 */ +/*0x10C8C*/ uchar Rsvd10C8C[4]; +/*0x10C90*/ ulong rsr; /* Reset status register 32 bits 5.2/5-4 */ +/*0x10C94*/ ulong rmr; /* Reset mode register 32 bits 5.3/5-5 */ +/*0x10C98*/ uchar Rsvd10C98[104]; + +/* Part I.Overview Input/Output Port */ +/*0x10D00*/ Port port[4]; + +/* CPMTimers */ +/*0x10D80*/ uchar tgcr1; /* Timer1 and timer2 global configuration register 8 bits 17.2.2/17-4 */ + +/*0x10D81*/ uchar Rsvd10D81[3]; +/*0x10D84*/ uchar tgcr2; /* Timer3 and timer4 global configuration register 8 bits 17.2.2/17-4 */ +/*0x10D85*/ uchar Rsvd10D85[3]; + +/*0x10D88*/ uchar Rsvd10D88[8]; +/*0x10D90*/ ushort tmr1; /* Timer1 mode register 16 bits 17.2.3/17-6 */ +/*0x10D92*/ ushort tmr2; /* Timer2 mode register 16 bits 17.2.3/17-6 */ + union{ + struct { +/*0x10D94*/ ushort trr1; /* Timer1 reference register 16 bits 17.2.4/17-7 */ +/*0x10D96*/ ushort trr2; /* Timer2 reference register 16 bits 17.2.4/17-7 */ + }; +/*0x10D94*/ ulong trrl1; /* Combined Timer 1/2 trr register */ + }; + union{ + struct { +/*0x10D98*/ ushort tcr1; /* Timer1 capture register 16 bits 17.2.5/17-8 */ +/*0x10D9A*/ ushort tcr2; /* Timer2 capture register 16 bits 17.2.5/17-8 */ + }; +/*0x10D98*/ ulong tcrl1; /* Combined timer1/2 capture register */ + }; + union{ + struct { +/*0x10D9C*/ ushort tcn1; /* Timer1 counter 16 bits 17.2.6/17-8 */ +/*0x10D9E*/ ushort tcn2; /* Timer2 counter 16 bits 17.2.6/17-8 */ + }; +/*0x10D9C*/ ulong tcnl1; /* Combined timer1/2 counter */ + }; +/*0x10DA0*/ ushort tmr3; /* Timer3 mode register 16 bits 17.2.3/17-6 */ +/*0x10DA2*/ ushort tmr4; /* Timer4 mode register 16 bits 17.2.3/17-6 */ + union{ + struct { +/*0x10DA4*/ ushort trr3; /* Timer3 reference register 16 bits 17.2.4/17-7 */ +/*0x10DA6*/ ushort trr4; /* Timer4 reference register 16 bits 17.2.4/17-7 */ + }; +/*0x10DA4*/ ulong trrl3; + }; + union{ + struct { +/*0x10DA8*/ ushort tcr3; /* Timer3 capture register 16 bits 17.2.5/17-8 */ +/*0x10DAA*/ ushort tcr4; /* Timer4 capture register 16 bits 17.2.5/17-8 */ + }; +/*0x10DA8*/ ulong tcrl3; + }; + union{ + struct { +/*0x10DAC*/ ushort tcn3; /* Timer3 counter 16 bits 17.2.6/17-8 */ +/*0x10DAE*/ ushort tcn4; /* Timer4 counter 16 bits 17.2.6/17-8 */ + }; +/*0x10DAC*/ ulong tcnl3; + }; +/*0x10DB0*/ ushort ter1; /* Timer1 event register 16 bits 17.2.7/17-8 */ +/*0x10DB2*/ ushort ter2; /* Timer2 event register 16 bits 17.2.7/17-8 */ +/*0x10DB4*/ ushort ter3; /* Timer3 event register 16 bits 17.2.7/17-8 */ +/*0x10DB6*/ ushort ter4; /* Timer4 event register 16 bits 17.2.7/17-8 */ +/*0x10DB8*/ uchar Rsvd10DB8[608]; + +/* SDMADHGeneral */ +/*0x11018*/ uchar sdsr; /* SDMA status register 8 bits 18.2.1/18-3 */ +/*0x11019*/ uchar Rsvd11019[3]; +/*0x1101C*/ uchar sdmr; /* SDMA mask register 8 bits 18.2.2/18-4 */ +/*0x1101D*/ uchar Rsvd1101D[3]; + +/* IDMA */ +/*0x11020*/ IDMA idma[4]; + +/*0x11040*/ uchar Rsvd11040[704]; + +/*0x11300*/ FCC fcc[3]; + +/*0x11360*/ uchar Rsvd11360[0x290]; + +/* BRGs5DH8 */ +/*0x115F0*/ ulong BRGC5; /* BRG5 configuration register 32 bits 16.1/16-2 */ +/*0x115F4*/ ulong BRGC6; /* BRG6configuration register 32 bits */ +/*0x115F8*/ ulong BRGC7; /* BRG7configuration register 32 bits */ +/*0x115FC*/ ulong BRGC8; /* BRG8configuration register 32 bits */ +/*0x11600*/ uchar Rsvd11600[0x260]; +/*0x11860*/ uchar I2MOD; /* I2C mode register 8 bits 34.4.1/34-6 */ +/*0x11861*/ uchar Rsvd11861[3]; +/*0x11864*/ uchar I2ADD; /* I2C address register 8 bits 34.4.2/34-7 */ +/*0x11865*/ uchar Rsvd11865[3]; +/*0x11868*/ uchar I2BRG; /* I2C BRG register 8 bits 34.4.3/34-7 */ +/*0x11869*/ uchar Rsvd11869[3]; +/*0x1186C*/ uchar I2COM; /* I2C command register 8 bits 34.4.5/34-8 */ +/*0x1186D*/ uchar Rsvd1186D[3]; +/*0x11870*/ uchar I2CER; /* I2C event register 8 bits 34.4.4/34-8 */ +/*0x11871*/ uchar Rsvd11871[3]; +/*0x11874*/ uchar I2CMR; /* I2C mask register 8 bits 34.4.4/34-8 */ +/*0x11875*/ uchar Rsvd11875[331]; + +/* Communications Processor */ +/*0x119C0*/ ulong cpcr; /* Communications processor command register 32 bits 13.4.1/13-11 */ + +/*0x119C4*/ ulong rccr; /* CP configuration register 32 bits 13.3.6/13-7 */ +/*0x119C8*/ uchar Rsvd119C8[14]; +/*0x119D6*/ ushort rter; /* CP timers event register 16 bits 13.6.4/13-21 */ +/*0x119D8*/ ushort Rsvd119D8; +/*0x119DA*/ ushort rtmr; /* CP timers mask register 16 bits */ +/*0x119DC*/ ushort rtscr; /* CPtime-stamp timer control register 16 bits 13.3.7/13-9 */ +/*0x119DE*/ ushort Rsvd119DE; +/*0x119E0*/ ulong rtsr; /* CPtime-stamp register 32 bits 13.3.8/13-10 */ +/*0x119E4*/ uchar Rsvd119E4[12]; + +/*0x119F0*/ ulong brgc[4]; /* BRG configuration registers 32 bits 16.1/16-2 */ + +/*0x11A00*/ SCC scc[4]; + +/*0x11A80*/ SMC smc[2]; + + SPI spi; + +/*0x11AB0*/ uchar Rsvd11AB0[80]; + +/* CPMMux */ +/*0x11B00*/ uchar cmxsi1cr; /* CPM mux SI1clock route register 8 bits 15.4.2/15-10 */ +/*0x11B01*/ uchar Rsvd11B01; +/*0x11B02*/ uchar cmxsi2cr; /* CPM mux SI2clock route register 8 bits 15.4.3/15-11 */ +/*0x11B03*/ uchar Rsvd11B03; +/*0x11B04*/ ulong cmxfcr; /* CPM mux FCC clock route register 32 bits 15.4.4/15-12 */ +/*0x11B08*/ ulong cmxscr; /* CPM mux SCC clock route register 32 bits 15.4.5/15-14 */ +/*0x11B0C*/ uchar cmxsmr; /* CPM mux SMC clock route register 8 bits 15.4.6/15-17 */ +/*0x11B0D*/ uchar Rsvd11B0D; +/*0x11B0E*/ ushort cmxuar; /* CPM mux UTOPIA address register 16 bits 15.4.1/15-7 */ +/*0x11B10*/ uchar Rsvd11B10[16]; + + SI si1; /* SI 1 Registers */ + +/* MCC1Registers */ +/*0x11B30*/ ushort MCCE1; /* MCC1 event register 16 bits 27.10.1/27-18 */ +/*0x11B32*/ ushort Rsvd11B32; +/*0x11B34*/ ushort MCCM1; /* MCC1 mask register 16 bits */ +/*0x11B36*/ ushort Rsvd11B36; +/*0x11B38*/ uchar MCCF1; /* MCC1 configuration register 8 bits 27.8/27-15 */ +/*0x11B39*/ uchar Rsvd11B39[7]; + + SI si2; /* SI 2 Registers */ + +/* MCC2Registers */ +/*0x11B50*/ ushort MCCE2; /* MCC2 event register 16 bits 27.10.1/27-18 */ +/*0x11B52*/ ushort Rsvd11B52; +/*0x11B54*/ ushort MCCM2; /* MCC2 mask register 16 bits */ +/*0x11B56*/ ushort Rsvd11B56; +/*0x11B58*/ uchar MCCF2; /* MCC2 configuration register 8 bits 27.8/27-15 */ +/*0x11B59*/ uchar Rsvd11B59[1191]; + +/* SI1RAM */ +/*0x12000*/ uchar SI1TxRAM[0x200];/* SI1 transmit routing RAM 512 14.4.3/14-10 */ +/*0x12200*/ uchar Rsvd12200[0x200]; +/*0x12400*/ uchar SI1RxRAM[0x200];/* SI1 receive routing RAM 512 14.4.3/14-10 */ +/*0x12600*/ uchar Rsvd12600[0x200]; + +/* SI2RAM */ +/*0x12800*/ uchar SI2TxRAM[0x200];/* SI2 transmit routing RAM 512 14.4.3/14-10 */ +/*0x12A00*/ uchar Rsvd12A00[0x200]; +/*0x12C00*/ uchar SI2RxRAM[0x200];/* SI2 receive routing RAM 512 14.4.3/14-10 */ +/*0x12E00*/ uchar Rsvd12E00[0x200]; +/*0x13000*/ uchar Rsvd13000[0x800]; +/*0x13800*/ uchar Rsvd13800[0x800]; +}; + +typedef struct FCCextra FCCextra; +struct FCCextra { +/*0x00*/ uchar ri[0x20]; +/*0x20*/ uchar ti[0x20]; +/*0x40*/ uchar pad[0x20]; +}; + +typedef struct Imap Imap; +struct Imap { +/* CPMDual-Port RAM */ +/*0x00000*/ uchar dpram1[0x3800]; /* Dual-port RAM 16Kbytes 13.5/13-15 */ +/*0x03800*/ FCCextra fccextra[4]; +/*0x03980*/ Uartsmc uartsmc[2]; +/*0x03a00*/ uchar dsp1p[0x40]; +/*0x03a40*/ uchar dsp2p[0x40]; +/*0x03a80*/ BD bd[(0x04000-0x03a80)/sizeof(BD)]; /* Buffer descriptors */ +/*0x04000*/ uchar Rsvd4000[0x04000]; + +/* Dual port RAM bank 2 -- Parameter Ram, Section 13.5 */ +/*0x08000*/ PrmSCC prmscc[4]; +/*0x08400*/ PrmFCC prmfcc[3]; +/*0x08700*/ Bases param[4]; +/*0x08b00*/ uchar dpram2[0x500]; + +/*0x09000*/ uchar Rsvd9000[0x2000]; + +/* Dual port RAM bank 3 -- Section 13.5 */ +/*0x0B000*/ uchar dpram3[0x1000]; /* Dual-port RAM 4Kbytes 13.5/13-15 */ +/*0x0C000*/ uchar Rsvdc000[0x4000]; + +/*0x10000*/ IMM; +}; + +enum { +/* CPM Command register. */ + cpm_rst = 0x80000000, + cpm_page = 0x7c000000, + cpm_sblock = 0x03e00000, + cpm_flg = 0x00010000, + cpm_mcn = 0x00003fc0, + cpm_opcode = 0x0000000f, + +/* Device sub-block and page codes. */ + cpm_fcc1_sblock = 0x10, + cpm_fcc2_sblock = 0x11, + cpm_fcc3_sblock = 0x12, + cpm_scc1_sblock = 0x04, + cpm_scc2_sblock = 0x05, + cpm_scc3_sblock = 0x06, + cpm_scc4_sblock = 0x07, + cpm_smc1_sblock = 0x08, + cpm_smc2_sblock = 0x09, + cpm_rand_sblock = 0x0e, + cpm_spi_sblock = 0x0a, + cpm_i2c_sblock = 0x0b, + cpm_timer_sblock = 0x0f, + cpm_mcc1_sblock = 0x1c, + cpm_mcc2_sblock = 0x1d, + cpm_idma1_sblock = 0x14, + cpm_idma2_sblock = 0x15, + cpm_idma3_sblock = 0x16, + cpm_idma4_sblock = 0x17, + + cpm_scc1_page = 0x00, + cpm_scc2_page = 0x01, + cpm_scc3_page = 0x02, + cpm_scc4_page = 0x03, + cpm_smc1_page = 0x07, + cpm_smc2_page = 0x08, + cpm_spi_page = 0x09, + cpm_i2c_page = 0x0a, + cpm_timer_page = 0x0a, + cpm_rand_page = 0x0a, + cpm_fcc1_page = 0x04, + cpm_fcc2_page = 0x05, + cpm_fcc3_page = 0x06, + cpm_idma1_page = 0x07, + cpm_idma2_page = 0x08, + cpm_idma3_page = 0x09, + cpm_idma4_page = 0x0a, + cpm_mcc1_page = 0x07, + cpm_mcc2_page = 0x08, + +}; + +/* + * CPM + */ +enum { + /* commands */ + InitRxTx = 0, + InitRx = 1, + InitTx = 2, + EnterHunt= 3, + StopTx= 4, + GracefulStopTx = 5, + InitIDMA = 5, + RestartTx = 6, + CloseRxBD = 7, + SetGroupAddr = 8, + SetTimer = 8, + GCITimeout = 9, + GCIAbort = 10, + StopIDMA = 11, + StartDSP = 12, + ArmIDMA = 13, + InitDSP = 13, + USBCmd = 15, + + /* channel IDs */ + SCC1ID= cpm_scc1_page << 5 | cpm_scc1_sblock, + SCC2ID= cpm_scc2_page << 5 | cpm_scc2_sblock, + SCC3ID= cpm_scc3_page << 5 | cpm_scc3_sblock, + SMC1ID= cpm_smc1_page << 5 | cpm_smc1_sblock, + SMC2ID= cpm_smc2_page << 5 | cpm_smc2_sblock, + FCC1ID= cpm_fcc1_page << 5 | cpm_fcc1_sblock, + FCC2ID= cpm_fcc2_page << 5 | cpm_fcc2_sblock, + FCC3ID= cpm_fcc3_page << 5 | cpm_fcc3_sblock, +// USBID= 0, These are wrong +// I2CID= 1, +// IDMA1ID= 1, +// SPIID= 5, +// IDMA2ID= 5, +// TIMERID= 5, +// DSP1ID=9, +// SCC4ID= 10, +// DSP2ID= 13, + + /* sicr */ + BRG1 = 0, + BRG2 = 1, + BRG3 = 2, + BRG4 = 4, + CLK1 = 4, + CLK2 = 5, + CLK3 = 6, + CLK4 = 7, + +}; + +extern IMM* iomem; + +BD* bdalloc(int); +void cpmop(int, int, int); +void ioplock(void); +void iopunlock(void); +void kreboot(ulong); diff --git a/sys/src/9/ppc/main.c b/sys/src/9/ppc/main.c new file mode 100755 index 000000000..380d6fd6f --- /dev/null +++ b/sys/src/9/ppc/main.c @@ -0,0 +1,511 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "init.h" +#include "pool.h" +#include "tos.h" + +#define MAXCONF 64 + +typedef struct Plan9ini Plan9ini; +struct Plan9ini +{ + char *name; + char *val; +}; + +char *plan9inistr; +Plan9ini plan9ini[MAXCONF]; +int nconf; + +Conf conf; +FPsave initfp; +Lock testlock; + +static void plan9iniinit(void); + +char * +cpuid(void) +{ + char *id; + + id = "unknown PowerPC"; + switch(m->cputype) { + case 8: + id = "PowerPC 750"; + break; + case 9: + id = "PowerPC 604e"; + break; + case 0x81: + id = "PowerPC 8260"; + break; + case 0x8081: + id = "PowerPC 826xA"; + break; + default: + break; + } + return id; +} + +void +cpuidprint(void) +{ + print("cpu0: %s, rev 0x%lux, cpu hz %lld, bus hz %ld\n", + cpuid(), getpvr()&0xffff, m->cpuhz, m->bushz); +} + +void +main(void) +{ + memset(edata, 0, (ulong)end-(ulong)edata); + conf.nmach = 1; + machinit(); + confinit(); + xinit(); + trapinit(); + mmuinit(); + plan9iniinit(); + hwintrinit(); + clockinit(); + timerinit(); + console(); + quotefmtinstall(); + printinit(); + cpuidprint(); + print("\nPlan 9 from Bell Labs\n"); + procinit0(); + initseg(); + timersinit(); + links(); + chandevreset(); + pageinit(); + swapinit(); + sharedseginit(); + fpsave(&initfp); + initfp.fpscr = 0; + userinit(); + schedinit(); +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(name, plan9ini[i].name) == 0) + return plan9ini[i].val; + return nil; +} + +static void +plan9iniinit(void) +{ + long i; + int c; + char *cp, line[MAXCONF], *p, *q; + + /* + * parse configuration args from dos file plan9.ini + */ + + cp = plan9inistr; + for(i = 0; i < MAXCONF; i++){ + /* + * Strip out '\r', change '\t' -> ' ', test for 0xff which is end of file + */ + p = line; + for(q = cp; c = (uchar)*q; q++){ + if(c == '\r') + continue; + if(c == '\t') + c = ' '; + if(c == 0xff || c == '\n') + break; + *p++ = c; + } + *p = 0; + if (*line == 0) + break; + if(*line != '#' && (cp = strchr(line, '='))){ + *cp++ = '\0'; + kstrdup(&plan9ini[nconf].name, line); + kstrdup(&plan9ini[nconf].val, cp); + nconf++; + } + if (c == 0xff) + break; + + cp = q + 1; + } +} + +void +init0(void) +{ +// char **p, *q, name[KNAMELEN]; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + chandevinit(); + + if(!waserror()){ + snprint(buf, sizeof(buf), "power %s mtx", conffile); + ksetenv("terminal", buf, 0); + ksetenv("cputype", "power", 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + + for(i = 0; i < nconf; i++){ + if(plan9ini[i].name[0] != '*') + ksetenv(plan9ini[i].name, plan9ini[i].val, 0); + ksetenv(plan9ini[i].name, plan9ini[i].val, 1); + } + poperror(); + } + kproc("alarm", alarmkproc, 0); + kproc("mmusweep", mmusweep, 0); + touser((void*)(USTKTOP-sizeof(Tos))); +} + +void +userinit(void) +{ + Proc *p; + Segment *s; + KMap *k; + Page *pg; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + p->fpstate = FPinit; + + /* + * Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); + + /* + * User Stack + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + segpage(s, pg); + + /* + * Text + */ + s = newseg(SG_TEXT, UTZERO, 1); + s->flushme++; + p->seg[TSEG] = s; + pg = newpage(1, 0, UTZERO); + memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); + segpage(s, pg); + k = kmap(s->map[0]->pages[0]); + memmove((ulong*)VA(k), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +void +exit(int ispanic) +{ + int ms, once; + + lock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<<m->machno); + active.machs &= ~(1<<m->machno); + active.exiting = 1; + unlock(&active); + + if(once) + print("cpu%d: exiting\n", m->machno); + spllo(); + for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ + delay(TK2MS(2)); + if(active.machs == 0 && consactive() == 0) + break; + } + + if(active.ispanic && m->machno == 0){ + if(cpuserver) + delay(10000); + else if(conf.monitor) + for(;;); + } + else + delay(1000); + +} + +/* + * set up floating point for a new process + */ +void +procsetup(Proc *p) +{ + p->fpstate = FPinit; +} + +void +procrestore(Proc *p) +{ + uvlong t; + + if(p->kp) + return; + cycles(&t); + p->pcycles -= t; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + uvlong t; + + cycles(&t); + p->pcycles += t; + if(p->fpstate == FPactive){ + if(p->state != Moribund) + fpsave(&up->fpsave); + p->fpstate = FPinactive; + } +} + +void +confinit(void) +{ + char *p; + int userpcnt; + ulong pa, kpages; + /* passed in from ROM monitor: */ + + if(p = getconf("*kernelpercent")) + userpcnt = 100 - strtol(p, 0, 0); + else + userpcnt = 0; + + pa = PGROUND(PADDR(end)); + + /* Blast Board specific */ + conf.mem[0].npage = (MEM1SIZE - pa)/BY2PG; + conf.mem[0].base = pa; + + conf.mem[1].npage = MEM2SIZE/BY2PG; + conf.mem[1].base = MEM2BASE; + + conf.npage = conf.mem[0].npage + conf.mem[1].npage; + + conf.nmach = 1; + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + if(cpuserver) + conf.nproc *= 3; + if(conf.nproc > 2000) + conf.nproc = 2000; + conf.nimage = 200; + conf.nswap = conf.nproc*80; + conf.nswppo = 4096; + conf.copymode = 0; /* copy on write */ + + if(cpuserver) { + if(userpcnt < 10) + userpcnt = 70; + kpages = conf.npage - (conf.npage*userpcnt)/100; + + /* + * Hack for the big boys. Only good while physmem < 4GB. + * Give the kernel a max. of 16MB + enough to allocate the + * page pool. + * This is an overestimate as conf.upages < conf.npages. + * The patch of nimage is a band-aid, scanning the whole + * page list in imagereclaim just takes too long. + */ + if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){ + kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG; + conf.nimage = 2000; + kpages += (conf.nproc*KSTACK)/BY2PG; + } + } else { + if(userpcnt < 10) { + if(conf.npage*BY2PG < 16*MB) + userpcnt = 40; + else + userpcnt = 60; + } + kpages = conf.npage - (conf.npage*userpcnt)/100; + + /* + * Make sure terminals with low memory get at least + * 4MB on the first Image chunk allocation. + */ + if(conf.npage*BY2PG < 16*MB) + imagmem->minarena = 4*1024*1024; + } + conf.upages = conf.npage - kpages; + conf.ialloc = (kpages/2)*BY2PG; + + /* + * Guess how much is taken by the large permanent + * datastructures. Mntcache and Mntrpc are not accounted for + * (probably ~300KB). + */ + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page); + mainmem->maxsize = kpages; + if(!cpuserver){ + /* + * give terminals lots of image memory, too; the dynamic + * allocation will balance the load properly, hopefully. + * be careful with 32-bit overflow. + */ + imagmem->maxsize = kpages; + } + +// conf.monitor = 1; /* BUG */ +} + +static 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; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + int i; + char cc[KNAMELEN], *p; + + sprint(cc, "%s%d", class, ctlrno); + + p = getconf(cc); + if(p == 0) + return 0; + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + } + return 1; +} + +int +cistrcmp(char *a, char *b) +{ + int ac, bc; + + for(;;){ + ac = *a++; + bc = *b++; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + return 0; +} + +int +cistrncmp(char *a, char *b, int n) +{ + unsigned ac, bc; + + while(n > 0){ + ac = *a++; + bc = *b++; + n--; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + + return 0; +} diff --git a/sys/src/9/ppc/mcc.c b/sys/src/9/ppc/mcc.c new file mode 100755 index 000000000..8d89f2d69 --- /dev/null +++ b/sys/src/9/ppc/mcc.c @@ -0,0 +1,351 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * + * mcc.c + * + * This is the driver for the Multi-Channel Communications Controller + * of the MPC8260. This version is constructed for the EST SBC8260 to + * handle data from an interface to an offboard T1 framer. The driver + * supports MCC2 + TDM A:2 (channel 128) which is connected to the + * external interface. + * + * Neville Chandler + * Lucent Technologies - Bell Labs + * March 2001 + * + */ + +#define PKT_LEN 40 +#define MPC82XX_INIT_DELAY 0x10000 + +#define HPIC 0xFC000000 +#define HPIA 0xFC000010 +#define HPID_A 0xFC000020 +#define HPID 0xFC000030 + + +#include "mcc2.h" + +static ssize_t mcc2_read( struct file *, char *, size_t, loff_t * ); +static ssize_t mcc2_write( struct file *, const char *, size_t, loff_t *); +static loff_t mcc2_lseek( struct file *, loff_t, int ); +static int mcc2_release( struct inode *, struct file * ); +static ssize_t mcc2_ioctl( struct inode *, struct file *, unsigned int, unsigned long ); +//static ssize_t mcc2_ioctl( struct inode *, struct file *, unsigned int, char * ); + +void MPC82xxCpmInit( void ); +void PortInit( void ); +void PortSelectPin( unsigned short ); + +void InitMemAlloc( void ); +void HeapCreate( U32, U32, U32, U32, char *); +void HeapCreate( U32, U32, U32, U32, char *); +void *HeapSearchMem( U32, U32); +void *HeapAllocMem( U32, U32); +void HeapFreeMem( U32, void *); + +void InitLinkedList( void ); +boolean DwCreateList( ListDB * ); +void *DwMalloc( U32 ); +void DwFree( U32, void * ); + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; +extern unsigned int ppc_local_bh_count[NR_CPUS]; +extern unsigned int ppc_local_irq_count[NR_CPUS]; +extern unsigned int ppc_cached_irq_mask[NR_MASK_WORDS]; +extern unsigned int ppc_lost_interrupts[NR_MASK_WORDS]; +extern atomic_t ppc_n_lost_interrupts; + +//static void disp_led( unsigned char ); + +void Mcc2Init( void ); +void MccDisable( unsigned char ); +void MccEnable( unsigned char, unsigned char, unsigned char ); +void MccRiscCmd( unsigned char, dwv_RISC_OPCODE, unsigned char ); +boolean MccTest( void ); +int MccTxBuffer( unsigned char, unsigned char, char *, unsigned short, unsigned short ); +extern U32 PpcDisable( void ); +extern void PpcMsrRestore( U32 ); + +static int mcc2_major = MCC_MAJOR; + +static BOOLEAN insertBD_T( BD_PFIFO *, BD_P ); +static BOOLEAN removBD_T( BD_PFIFO *, BD_P * ); +BOOLEAN empty(volatile register FIFO *); +int insert( FIFO *, char * ); +int remove( FIFO *, char ** ); +void AppInit( void ); + +#define physaddr(ADDR) (0x60020000 | ((ADDR) << 23) | (2 << 18)) + +mcc_iorw_t mcc_iorw; + +#if 0 +typedef struct mcc_io { + unsigned int cmd; + unsigned int address; + unsigned int *buf; + int ind; + int nbytes; + siramctl_t SiRam; + cpmux_t CpMux; + mcc_t Mcc_T; + iop8260_t Io_Ports; +} mcc_iorw_t; +#endif + +static void +ioctl_parm( unsigned int loop_mode ) +{ + + /* Setup the SIMODE Register */ + Si2Regs->SiAmr = SIxMR_SAD_BANK0_FIRST_HALF | /* SADx */ + loop_mode | /* SDMx */ + SIxMR_NO_BIT_RX_SYNC_DELAY | /* RFSDx */ + SIxMR_DSC_CH_DATA_CLK_EQU | /* DSCx */ + SIxMR_CRT_SPEPARATE_PINS | /* CRTx */ + SIxMR_SLx_NORMAL_OPERATION | /* SLx */ + SIxMR_CE_TX_RISING_RX_FALLING | /* CEx */ + SIxMR_FE_FALLING_EDGE | /* FEx */ + SIxMR_GM_GCI_SCIT_MODE | /* GMx */ + SIxMR_NO_BIT_TX_SYNC_DELAY; /* TFSDx */ +} + +#if 0 +static void +disp_led( unsigned char byte ) +{ + //int i; + + *leds = byte; + //for(i=0; i<1000; i++); + +} +#endif + + + +static ssize_t +mcc2_ioctl( struct inode *inode, struct file *file, + unsigned int ioctl_cmd, // IOCTL number + unsigned long param ) +// char *param ) // IOCTL parameter +{ + static unsigned char mode; + char cp, *cptr; + void *vptr; + unsigned long *lptr; + int i, j; + unsigned int ld; + unsigned long lng; + volatile immap_t *Mmap; + + cptr = (char *)param; + mode = (unsigned char)*cptr; + Mmap = ((volatile immap_t *)IMAP_ADDR); + switch(ioctl_cmd) + { + case IOCTL_SET_MODE: + //mode = (unsigned char)*param; + mode = ((mcc_iorw_t *)param)->cmd; + switch( mode ) + { + case NORMAL_OPERATION: + /* Setup the SIMODE Register */ + D( printk("mcc2_ioctl: ioctl set NORMAL_OPERATION mode\n"); ) + ioctl_parm( (unsigned int)SIxMR_SDM_NORMAL_OPERATION ); /* SDMx */ + break; + + case AUTOMATIC_ECHO: + /* Setup the SIMODE Register */ + D( printk("mcc2_ioctl: ioctl set AUTOMATIC_ECHO mode\n"); ) + ioctl_parm( (unsigned int)SIxMR_SDM_AUTOMATIC_ECHO ); /* SDMx */ + break; + + case INTERNAL_LOOPBACK: + /* Setup the SIMODE Register */ + D( printk("mcc2_ioctl: ioctl set INTERNAL_LOOPBACK mode\n"); ) + ioctl_parm( (unsigned int)SIxMR_SDM_INTERNAL_LOOPBACK ); /* SDMx */ + break; + + case LOOPBACK_CONTROL: + /* Setup the SIMODE Register */ + D( printk("mcc2_ioctl: ioctl set LOOPBACK_CONTROL mode\n"); ) + ioctl_parm( (unsigned int)SIxMR_SDM_LOOPBACK_CONTROL ); /* SDMx */ + break; + + + default: + printk("mcc2_ioctl: Error, unrecognized ioctl parameter, device operation unchanged.\n"); + break; + + } + break; + + case IOCTL_RWX_MODE: + mode = ((mcc_iorw_t *)param)->cmd; + switch(mode) + { + case HPI_RD: + lng = (long)(((mcc_iorw_t *)param)->address); + lptr = ((unsigned long *)lng); + vptr = (void *)lptr; + if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)vptr, (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during read from hpi.\n"); + return -EFAULT; + } + break; + + + case HPI_WR: + lng = (long)(((mcc_iorw_t *)param)->address); + lptr = ((unsigned long *)lng); + vptr = (void *)lptr; + if (copy_from_user( (void *)vptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during write to hpi\n"); + return -EFAULT; + } + break; + + + + case FPGA_RD: + lng = (long)(((mcc_iorw_t *)param)->address); + lptr = ((unsigned long *)lng); + vptr = (void *)lptr; + if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)vptr, (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during read from FPGA.\n"); + return -EFAULT; + } + break; + + + case FPGA_WR: + lng = (long)(((mcc_iorw_t *)param)->address); + lptr = ((unsigned long *)lng); + vptr = (void *)lptr; + if (copy_from_user( (void *)vptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during write to FPGA\n"); + return -EFAULT; + } + break; + + + + + + case MEM_MODR: + cptr = (char *)Mmap; + cptr += ((mcc_iorw_t *)param)->address; + if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)cptr, (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during read of read-modify memory\n"); + return -EFAULT; + } + break; + + case MEM_MODW: + cptr = (char *)Mmap; + cptr += ((mcc_iorw_t *)param)->address; + if (copy_from_user( (void *)cptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) { + printk("mcc2_ioctl: Failed during modify of read-modify memory\n"); + return -EFAULT; + } + break; + + case IO_PORTS: + break; + case SI_RAM_CTL1: + break; + case SI_RAM_CTL2: + if (copy_to_user( (void *)param, (siramctl_t *)&(Mmap->im_siramctl2), sizeof(siramctl_t))) { + printk("mcc2_ioctl: Failed to copy SI_RAM_CTL2 struct\n"); + return -EFAULT; + } + break; + + + + default: + break; + } + break; + + default: + //if (copy_to_user((void *)param, &mode, sizeof(mode))) + printk("We are at the end ...\n"); + return -EFAULT; + break; + } + break; + + default: + break; + } + + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +static ssize_t +mcc2_open( struct inode *inode, struct file *file ) +{ + MOD_INC_USE_COUNT; + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +static int +mcc2_release( struct inode *inode, struct file *file ) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + + + + + +#ifndef MODULE + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +long +mcc2_init( long mem_start, long mem_end ) +{ + + if ((mcc2_major = register_chrdev(MCC_MAJOR, MCC_NAME, &mcc2_fops))) + printk("mcc2_init: Unable to get major for mcc2 device %d\n", MCC_MAJOR); + else { + //MPC82xxSiuInit(); + MPC82xxCpmInit(); + } + return mem_start; +} + +#else + diff --git a/sys/src/9/ppc/mem.h b/sys/src/9/ppc/mem.h new file mode 100755 index 000000000..dcaa0ad68 --- /dev/null +++ b/sys/src/9/ppc/mem.h @@ -0,0 +1,229 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +#ifdef ucuconf +#include "ucu.h" +#else +#include "blast.h" +#endif + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 5 +#define CACHELINESZ (1<<CACHELINELOG) +#define BLOCKALIGN CACHELINESZ + +#define MHz 1000000 + +#define BY2PTE 8 /* bytes per pte entry */ +#define BY2PTEG 64 /* bytes per pte group */ + +#define MAXMACH 1 /* max # cpus system can run */ +#define MACHSIZE BY2PG +#define KSTACK 4096 /* Size of kernel stack */ + +/* + * Time + */ +#define HZ 1000 /* clock frequency */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Standard PPC Special Purpose Registers (OEA and VEA) + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SDR1 25 +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define TBRL 268 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define SPRG4 276 +#define SPRG5 277 +#define SPRG6 278 +#define SPRG7 279 +#define ASR 280 /* Address Space Register */ +#define EAR 282 /* External Access Register (optional) */ +#define TBWU 284 /* Time base Upper/Lower (Writing) */ +#define TBWL 285 +#define PVR 287 /* Processor Version */ +#define IABR 1010 /* Instruction Address Breakpoint Register (optional) */ +#define DABR 1013 /* Data Address Breakpoint Register (optional) */ +#define FPECR 1022 /* Floating-Point Exception Cause Register (optional) */ +#define PIR 1023 /* Processor Identification Register (optional) */ + +#define IBATU(i) (528+2*(i)) /* Instruction BAT register (upper) */ +#define IBATL(i) (529+2*(i)) /* Instruction BAT register (lower) */ +#define DBATU(i) (536+2*(i)) /* Data BAT register (upper) */ +#define DBATL(i) (537+2*(i)) /* Data BAT register (lower) */ + +/* + * PPC604e-specific Special Purpose Registers (OEA) + */ +#define MMCR0 952 /* Monitor Control Register 0 */ +#define PMC1 953 /* Performance Monitor Counter 1 */ +#define PMC2 954 /* Performance Monitor Counter 2 */ +#define SIA 955 /* Sampled Instruction Address */ +#define MMCR1 956 /* Monitor Control Register 0 */ +#define PMC3 957 /* Performance Monitor Counter 3 */ +#define PMC4 958 /* Performance Monitor Counter 4 */ +#define SDA 959 /* Sampled Data Address */ +/* + * PPC603e-specific Special Purpose Registers + */ +#define DMISS 976 /* Data Miss Address Register */ +#define DCMP 977 /* Data Miss Address Register */ +#define HASH1 978 +#define HASH2 979 +#define IMISS 980 /* Instruction Miss Address Register */ +#define iCMP 981 /* Instruction Miss Address Register */ +#define RPA 982 +#define HID0 1008 /* Hardware Implementation Dependent Register 0 */ +#define HID1 1009 /* Hardware Implementation Dependent Register 1 */ +/* + * PowerQUICC II (MPC 8260) Special Purpose Registers + */ +#define HID2 1011 /* Hardware Implementation Dependent Register 2 */ + +#define BIT(i) (1<<(31-(i))) /* Silly backwards register bit numbering scheme */ +#define SBIT(n) ((ushort)1<<(15-(n))) +#define RBIT(b,n) (1<<(8*sizeof(n)-1-(b))) + +/* + * Bit encodings for Machine State Register (MSR) + */ +#define MSR_POW BIT(13) /* Enable Power Management */ +#define MSR_TGPR BIT(14) /* Temporary GPR Registers in use (603e) */ +#define MSR_ILE BIT(15) /* Interrupt Little-Endian enable */ +#define MSR_EE BIT(16) /* External Interrupt enable */ +#define MSR_PR BIT(17) /* Supervisor/User privilege */ +#define MSR_FP BIT(18) /* Floating Point enable */ +#define MSR_ME BIT(19) /* Machine Check enable */ +#define MSR_FE0 BIT(20) /* Floating Exception mode 0 */ +#define MSR_SE BIT(21) /* Single Step (optional) */ +#define MSR_BE BIT(22) /* Branch Trace (optional) */ +#define MSR_FE1 BIT(23) /* Floating Exception mode 1 */ +#define MSR_IP BIT(25) /* Exception prefix 0x000/0xFFF */ +#define MSR_IR BIT(26) /* Instruction MMU enable */ +#define MSR_DR BIT(27) /* Data MMU enable */ +#define MSR_PM BIT(29) /* Performance Monitor marked mode (604e specific) */ +#define MSR_RI BIT(30) /* Recoverable Exception */ +#define MSR_LE BIT(31) /* Little-Endian enable */ +/* SRR1 bits for TLB operations */ +#define MSR_SR0 0xf0000000 /* Saved bits from CR register */ +#define MSR_KEY BIT(12) /* Copy of Ks or Kp bit */ +#define MSR_IMISS BIT(13) /* It was an I miss */ +#define MSR_WAY BIT(14) /* TLB set to be replaced */ +#define MSR_STORE BIT(15) /* Miss caused by a store */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +#define CFPU 0x08 +#define CDEC 0x09 +#define CSYSCALL 0x0C +#define CTRACE 0x0D /* optional */ +#define CFPA 0x0E /* not implemented in 603e */ + +/* PPC603e-specific: */ +#define CIMISS 0x10 /* Instruction TLB miss */ +#define CLMISS 0x11 /* Data load TLB miss */ +#define CSMISS 0x12 /* Data store TLB miss */ +#define CIBREAK 0x13 +#define CSMI 0x14 + +/* + * Magic registers + */ + +#define MACH 30 /* R30 is m-> */ +#define USER 29 /* R29 is up-> */ + + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 +#define SSEGMAPSIZE 16 +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * First pte word + */ +#define PTE0(v, vsid, h, va) (((v)<<31)|((vsid)<<7)|((h)<<6)|(((va)>>22)&0x3f)) + +/* + * Second pte word; WIMG & PP(RW/RO) common to page table and BATs + */ +#define PTE1_R BIT(23) +#define PTE1_C BIT(24) + +#define PTE1_W BIT(25) +#define PTE1_I BIT(26) +#define PTE1_M BIT(27) +#define PTE1_G BIT(28) + +#define PTE1_RW BIT(30) +#define PTE1_RO BIT(31) + +/* HID0 register bits */ +#define HID_ICE BIT(16) +#define HID_DCE BIT(17) +#define HID_ILOCK BIT(18) +#define HID_DLOCK BIT(19) +#define HID_ICFI BIT(20) +#define HID_DCFI BIT(21) +#define HID_IFEM BIT(24) + +/* + * Address spaces + */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */ +#define TSTKTOP KZERO /* top of temporary stack */ +#define TSTKSIZ 100 +#define USTKSIZE (4*1024*1024) /* size of user stack */ +#define UREGSIZE ((8+40)*4) +#define MACHADDR (KTZERO-MAXMACH*MACHSIZE) +#define MACHPADDR (MACHADDR&~KZERO) +#define MACHP(n) ((Mach *)(MACHADDR+(n)*MACHSIZE)) + +#define isphys(x) (((ulong)x&KZERO)!=0) + +/* + * MPC8xx addresses + */ +#define INTMEM 0xf0000000 +#define IOMEM (INTMEM+0x10000) + +#define getpgcolor(a) 0 diff --git a/sys/src/9/ppc/mkfile b/sys/src/9/ppc/mkfile new file mode 100755 index 000000000..37b2486c9 --- /dev/null +++ b/sys/src/9/ppc/mkfile @@ -0,0 +1,108 @@ +CONF=ucu +CONFLIST=blast ucu + +loadaddr = 0x80100000 + +objtype=power +</$objtype/mkfile +p=9 + +DEVS=`{rc ../port/mkdevlist $CONF} + +PORT=\ + alarm.$O\ + alloc.$O\ + allocb.$O\ + auth.$O\ + cache.$O\ + chan.$O\ + dev.$O\ + edf.$O\ + fault.$O\ + latin1.$O\ + log.$O\ + rebootcmd.$O\ + page.$O\ + parse.$O\ + pgrp.$O\ + portclock.$O\ + print.$O\ + proc.$O\ + qio.$O\ + qlock.$O\ + rdb.$O\ + segment.$O\ + swap.$O\ + sysfile.$O\ + sysproc.$O\ + taslock.$O\ + tod.$O\ + xalloc.$O\ + +OBJ=\ + l.$O\ + clock.$O\ + main.$O\ + mmu.$O\ + random.$O\ + trap.$O\ + $CONF.root.$O\ + $CONF.rootc.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + dat.h\ + errstr.h\ + etherif.h\ + fns.h\ + init.h\ + io.h\ + mem.h\ + +LIB=\ + /$objtype/lib/libmemlayer.a\ + /$objtype/lib/libmemdraw.a\ + /$objtype/lib/libdraw.a\ + /$objtype/lib/libc.a\ + /$objtype/lib/libsec.a\ + +ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'} +VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'} +SDEV=`{echo devsd.c sd*.c | sed 's/\.c/.'$O'/g'} + +CFLAGS=$CFLAGS -D$CONF'conf='$CONF +AFLAGS=$AFLAGS -D$CONF'conf='$CONF + +it:V: $p$CONF + +9blast: $CONF.c $OBJ $LIB + $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c + $LD -o $target -T$loadaddr -R4096 -l $OBJ $CONF.$O $LIB + size $p$CONF + +9ucu: $CONF.c $OBJ $LIB + $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c + $LD -R0x1000 -H5 -o $target -T$loadaddr -l $OBJ $CONF.$O $LIB + +install:V: $p$CONF + cp $p$CONF /$objtype/$p$CONF + +<../boot/bootmkfile +<../port/portmkfile +<|../port/mkbootrules $CONF + +clock.$O devether.$O main.$O trap.$O: /$objtype/include/ureg.h + +%.$O: $HFILES + +$ETHER: etherif.h ../port/netif.h + +init.h: ../port/initcode.c init9.s + $CC ../port/initcode.c + $AS init9.s + $LD -l -s -R4 -o init.out init9.$O initcode.$O /power/lib/libc.a + {echo 'uchar initcode[]={' + strip < init.out | xd -1x | + sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' + echo '};'} > init.h diff --git a/sys/src/9/ppc/mmu.c b/sys/src/9/ppc/mmu.c new file mode 100755 index 000000000..73155e448 --- /dev/null +++ b/sys/src/9/ppc/mmu.c @@ -0,0 +1,282 @@ +#include <u.h> +#include <ureg.h> +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * We have one page table per processor. + * + * Different processes are distinguished via the VSID field in + * the segment registers. As flushing the entire page table is an + * expensive operation, we implement an aging algorithm for + * mmu pids, with a background kproc to purge stale pids en mass. + * + * This needs modifications to run on a multiprocessor. + */ + +static ulong ptabsize; /* number of bytes in page table */ +static ulong ptabmask; /* hash mask */ + +/* + * VSID is 24 bits. 3 are required to distinguish segments in user + * space (kernel space only uses the BATs). pid 0 is reserved. + * The top 2 bits of the pid are used as a `color' for the background + * pid reclamation algorithm. + */ + +enum { + PIDBASE = 1, + PIDBITS = 21, + COLBITS = 2, + PIDMAX = ((1<<PIDBITS)-1), + COLMASK = ((1<<COLBITS)-1), +}; + +#define VSID(pid, i) (((pid)<<3)|i) +#define PIDCOLOR(pid) ((pid)>>(PIDBITS-COLBITS)) +#define PTECOL(color) PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0) + +void +mmuinit(void) +{ + int lhash, mem, i; + ulong memsize; + + memsize = conf.npage * BY2PG; + if(ptabsize == 0) { + /* heuristically size the hash table */ + lhash = 10; + mem = (1<<23); + while(mem < memsize) { + lhash++; + mem <<= 1; + } + ptabsize = (1<<(lhash+6)); + ptabmask = (1<<lhash)-1; + } + m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize); + /* set page table base address */ + putsdr1(PADDR(m->ptabbase) | (ptabmask>>10)); + m->mmupid = PIDBASE; + m->sweepcolor = 0; + m->trigcolor = COLMASK; + + for(i = 0; i < 16; i++) + putsr(i<<28, 0); +} + +static int +work(void*) +{ + return PIDCOLOR(m->mmupid) == m->trigcolor; +} + +void +mmusweep(void*) +{ + Proc *p; + int i, x, sweepcolor; + ulong *ptab, *ptabend, ptecol; + + for(;;) { + if(PIDCOLOR(m->mmupid) != m->trigcolor) + sleep(&m->sweepr, work, nil); + + sweepcolor = m->sweepcolor; + x = splhi(); + p = proctab(0); + for(i = 0; i < conf.nproc; i++, p++) + if(PIDCOLOR(p->mmupid) == sweepcolor) + p->mmupid = 0; + splx(x); + + ptab = (ulong*)m->ptabbase; + ptabend = (ulong*)(m->ptabbase+ptabsize); + ptecol = PTECOL(sweepcolor); + while(ptab < ptabend) { + if((*ptab & PTECOL(3)) == ptecol){ + *ptab = 0; + } + ptab += 2; + } + + m->sweepcolor = (sweepcolor+1) & COLMASK; + m->trigcolor = (m->trigcolor+1) & COLMASK; + } +} + +int +newmmupid(void) +{ + int pid, newcolor, i, x; + Proc *p; + + pid = m->mmupid++; + if(m->mmupid > PIDMAX){ + /* Used up all mmupids, start again from first. Flush the tlb + * to delete any entries with old pids remaining, then reassign + * all pids. + */ + m->mmupid = PIDBASE; + x = splhi(); + tlbflushall(); + p = proctab(0); + for(i = 0; i < conf.nproc; i++, p++) + p->mmupid = 0; + splx(x); + wakeup(&m->sweepr); + } + newcolor = PIDCOLOR(m->mmupid); + if(newcolor != PIDCOLOR(pid)) { + if(newcolor == m->sweepcolor) { + /* desperation time. can't block here. punt to fault/putmmu */ + print("newmmupid: %uld: no free mmu pids\n", up->pid); + if(m->mmupid == PIDBASE) + m->mmupid = PIDMAX; + else + m->mmupid--; + pid = 0; + } + else if(newcolor == m->trigcolor) + wakeup(&m->sweepr); + } + up->mmupid = pid; + return pid; +} + +void +flushmmu(void) +{ + int x; + + x = splhi(); + up->newtlb = 1; + mmuswitch(up); + splx(x); +} + +/* + * called with splhi + */ +void +mmuswitch(Proc *p) +{ + int i, mp; + ulong r; + + if(p->kp) { + for(i = 0; i < 8; i++) + putsr(i<<28, 0); + return; + } + + if(p->newtlb) { + p->mmupid = 0; + p->newtlb = 0; + } + mp = p->mmupid; + if(mp == 0) + mp = newmmupid(); + + for(i = 0; i < 8; i++){ + r = VSID(mp, i)|BIT(1)|BIT(2); + putsr(i<<28, r); + } +} + +void +mmurelease(Proc* p) +{ + p->mmupid = 0; +} + +void +putmmu(ulong va, ulong pa, Page *pg) +{ + int mp; + char *ctl; + ulong *p, *ep, *q, pteg; + ulong vsid, hash; + ulong ptehi, x; + static ulong pva; + + /* + * If mmupid is 0, mmuswitch/newmmupid was unable to assign us + * a pid, hence we faulted. Keep calling sched() until the mmusweep + * proc catches up, and we are able to get a pid. + */ + while((mp = up->mmupid) == 0) + sched(); + + vsid = VSID(mp, va>>28); + hash = (vsid ^ ((va>>12)&0xffff)) & ptabmask; + ptehi = PTE0(1, vsid, 0, va); + pteg = m->ptabbase + BY2PTEG*hash; + + p = (ulong*)pteg; + ep = (ulong*)(pteg+BY2PTEG); + q = nil; + + while(p < ep) { + x = p[0]; + if(x == ptehi) { + q = p; + break; + } + if(q == nil && (x & BIT(0)) == 0) + q = p; + p += 2; + } + if(q == nil) { + q = (ulong*)(pteg+m->slotgen); + m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1); + } + + if (q[0] != ptehi || q[1] != pa){ + tlbflush(va); + m->tlbpurge++; + } + q[0] = ptehi; + q[1] = pa; + + ctl = &pg->cachectl[m->machno]; + switch(*ctl) { + case PG_NEWCOL: + default: + panic("putmmu: %d\n", *ctl); + break; + case PG_TXTFLUSH: + dcflush((void*)pg->va, BY2PG); + icflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_NOFLUSH: + break; + } + +} + +void +checkmmu(ulong, ulong) +{ +} + +void +countpagerefs(ulong*, int) +{ +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +ulong +cankaddr(ulong pa) +{ + if(pa >= -KZERO) + return 0; + return -KZERO - pa; +} + diff --git a/sys/src/9/ppc/msaturn.c b/sys/src/9/ppc/msaturn.c new file mode 100755 index 000000000..94aa1fb17 --- /dev/null +++ b/sys/src/9/ppc/msaturn.c @@ -0,0 +1,190 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "msaturn.h" + +enum { + Isr = Saturn + 0x0400, + Ipol = Saturn + 0x0404, + Issr = Saturn + 0x0500, + Ier = Saturn + 0x0504, + Ipri = Saturn + 0x0600, + Ithresh = Saturn + 0x0706, +#define Iar Ithresh +}; + +enum{ + Syscfg = Saturn + 0x0100, +}; + +static uchar intprio[] = { + Vecuart0, // uart 0 + Vecunused, // uart 1 + Vecunused, // sint + Vectimer0, // timer 0 + Vecunused, // timer 1 + Vecether, // ethernet + Vecunused, // tea + Vecunused, // irq0 + Vecunused, // irq1 + Vecunused, // irq2 + Vecunused, // irq3 + Vecunused, // irq4 + Vecunused, // irq5 + Vecunused, // irq6 + Vecunused, // irq7 + Vecunused, // irq8 +}; + +void +intend(int) +{ +} + +void +hwintrinit(void) +{ + int i; + + *(ulong*)Ier=0; + + for(i=0; i<nelem(intprio)/2; i++) + ((uchar*)Ipri)[i] = (intprio[2*i]<<4)|intprio[2*i+1]; +} + +int +vectorenable(Vctl*v) +{ + int i; + + for(i=0; i<nelem(intprio); i++) + if(v->irq==intprio[i]){ + *(ulong*)Ier |= 1<<(31-i); + return v->irq; + } + print("intrenable: cannot enable intr %d\n", v->irq); + return -1; +} + +void +vectordisable(Vctl*v) +{ + int i; + + for(i=0; i<nelem(intprio); i++) + if(v->irq==intprio[i]){ + *(ulong*)Ier &= ~(1<<(31-i)); + return; + } +} + +int +intvec(void) +{ + ushort iar; + int i; + + iar = *(ushort*)Iar; // push(prio) onto stack + for(i=0; i<nelem(intprio); i++) + if(iar==intprio[i]) + return iar; + + iprint("saturnint: no vector %d\n", iar); + intack(); + return -1; +} + +void +intack(void) +{ + *(ushort*)Ithresh = 0; // pop(prio) stack +} + +void +machinit(void) +{ + int rrate; + ulong hid; + extern char* plan9inistr; + ulong l2cr; + + memset(m, 0, sizeof(*m)); + m->cputype = getpvr()>>16; + m->imap = (Imap*)INTMEM; + + m->loopconst = 1096; + + rrate = (*(ushort*)Syscfg >> 6) & 3; + switch(rrate){ + case 0: + m->bushz = 66666666; + break; + case 1: + m->bushz = 83333333; + break; + case 2: + m->bushz = 100000000; + break; + case 3: + m->bushz = 133333333; + break; + } + + if(getpll() == 0x80000000) + m->cpuhz = 300000000; + else + m->cpuhz = 200000000; /* 750FX? */ + m->cyclefreq = m->bushz / 4; + + active.machs = 1; + active.exiting = 0; + + putmsr(getmsr() | MSR_ME); + + dcflush((void*)KZERO, 0x2000000); + l2cr = getl2cr(); + putl2cr(l2cr|BIT(10)); + + kfpinit(); + + hid=gethid0(); + hid |= BIT(28)|BIT(26)|BIT(24); + puthid0(hid); + + plan9inistr = + "console=0\n" + "ether0=type=saturn\n" + "fs=135.104.9.42\n" + "auth=135.104.9.7\n" + "authdom=cs.bell-labs.com\n" + "sys=ucu\n" + "ntp=135.104.9.52\n"; +} + +void +sharedseginit(void) +{ +} + +void +trapinit(void) +{ + int i; + + for(i = 0x0; i < 0x2000; i += 0x100) + sethvec(i, trapvec); + + dcflush(KADDR(0), 0x2000); + icflush(KADDR(0), 0x2000); + + putmsr(getmsr() & ~MSR_IP); +} + +void +reboot(void*, void*, ulong) +{ +} diff --git a/sys/src/9/ppc/msaturn.h b/sys/src/9/ppc/msaturn.h new file mode 100755 index 000000000..0beab872b --- /dev/null +++ b/sys/src/9/ppc/msaturn.h @@ -0,0 +1,7 @@ +enum { + Vecuart0 = 1, + Vecuart1 = Vecuart0 + 1, + Vectimer0 = 0, + Vecether = 3, + Vecunused = 15, +}; diff --git a/sys/src/9/ppc/mtx.c b/sys/src/9/ppc/mtx.c new file mode 100755 index 000000000..aaf8ebd7b --- /dev/null +++ b/sys/src/9/ppc/mtx.c @@ -0,0 +1,11 @@ +/* + * mtx specific stuff: + * Interrupt handling + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "io.h" +#include "mtx.h" +#include "fns.h" diff --git a/sys/src/9/ppc/random.c b/sys/src/9/ppc/random.c new file mode 100755 index 000000000..3c204b8d1 --- /dev/null +++ b/sys/src/9/ppc/random.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +struct Rb +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar wakeme; + ushort bits; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.rp - rb.wp; + return i != 1 && i != (1 - sizeof(rb.buf)); +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + up->basepri = PriNormal; + up->priority = up->basepri; + + for(;;){ + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + if(rb.randomcount == 0 || !rbnotfull(0)) + return; + + rb.bits = (rb.bits<<2) ^ rb.randomcount; + rb.randomcount = 0; + + rb.next++; + if(rb.next != 8/2) + return; + rb.next = 0; + + *rb.wp ^= rb.bits; + if(rb.wp+1 == rb.ep) + rb.wp = rb.buf; + else + rb.wp = rb.wp+1; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + addclock0link(randomclock, 1000/HZ); + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; + kproc("genrandom", genrandom, 0); +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + uchar *e, *p; + ulong x; + + p = xp; + + if(waserror()){ + qunlock(&rb); + nexterror(); + } + + qlock(&rb); + for(e = p + n; p < e; ){ + if(rb.wp == rb.rp){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + + /* + * beating clocks will be precictable if + * they are synchronized. Use a cheap pseudo + * random number generator to obscure any cycles. + */ + x = rb.randn*1103515245 ^ *rb.rp; + *p++ = rb.randn = x; + + if(rb.rp+1 == rb.ep) + rb.rp = rb.buf; + else + rb.rp = rb.rp+1; + } + qunlock(&rb); + poperror(); + + wakeup(&rb.producer); + + return n; +} diff --git a/sys/src/9/ppc/saturntimer.c b/sys/src/9/ppc/saturntimer.c new file mode 100755 index 000000000..2b9ef66cc --- /dev/null +++ b/sys/src/9/ppc/saturntimer.c @@ -0,0 +1,95 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "msaturn.h" + +enum { + Timer_ctrl = Saturn + 0x0106, + Timer0_load = Saturn + 0x0200, + Timer0_cnt = Saturn + 0x0204, + Timer1_load = Saturn + 0x0300, + Timer1_cnt = Saturn + 0x0304, + + T0_event = RBIT(13, ushort), + T0_ie = RBIT(14, ushort), + T0_cen = RBIT(15, ushort), + T1_event = RBIT(5, ushort), + T1_ie = RBIT(6, ushort), + T1_cen = RBIT(7, ushort), +}; + +static ulong ticks; +static Lock tlock; +static ushort timer_ctl; + +void +saturntimerintr(Ureg *u, void*) +{ + ushort ctl = *(ushort*)Timer_ctrl, v = 0; + + if(ctl&T1_event){ + v = T1_event; + ticks++; + } + + *(ushort*)Timer_ctrl = timer_ctl|T0_event|v; + intack(); + timerintr(u, 0); +} + +void +timerinit(void) +{ + *(ushort*)Timer_ctrl = 0; + *(ulong*)Timer0_load = m->bushz / HZ; + *(ulong*)Timer0_cnt = m->bushz / HZ; + *(ulong*)Timer1_load = m->bushz; + *(ulong*)Timer1_cnt = m->bushz; + + intrenable(Vectimer0, saturntimerintr, nil, "timer"); + + timer_ctl = T0_cen|T0_ie|T1_cen; + *(ushort*)Timer_ctrl = timer_ctl; +} + +uvlong +fastticks(uvlong *hz) +{ + assert(*(ushort*)Timer_ctrl&T1_cen); + if(*(ushort*)Timer_ctrl&T1_event){ + *(ushort*)Timer_ctrl = timer_ctl|T1_event; + ticks++; + } + + if (hz) + *hz = m->bushz; + + return (uvlong)ticks*m->bushz+(uvlong)(m->bushz-*(ulong*)Timer1_cnt); +} + +void +timerset(uvlong next) +{ + ulong offset; + uvlong now; + + ilock(&tlock); + *(ushort*)Timer_ctrl = T1_cen; + + now = fastticks(nil); + offset = next - now; + if((long)offset < 10000) + offset = 10000; + else if(offset > m->bushz) + offset = m->bushz; + + *(ulong*)Timer0_cnt = offset; + *(ushort*)Timer_ctrl = timer_ctl; + assert(*(ushort*)Timer_ctrl & T1_cen); + iunlock(&tlock); +} + diff --git a/sys/src/9/ppc/trap.c b/sys/src/9/ppc/trap.c new file mode 100755 index 000000000..ceacce8a8 --- /dev/null +++ b/sys/src/9/ppc/trap.c @@ -0,0 +1,857 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "tos.h" +#include <trace.h> + +static Lock vctllock; +Vctl *vctl[256]; + +void +intrenable(int irq, void (*f)(Ureg*, void*), void* a, char *name) +{ + int vno; + Vctl *v; + + if(f == nil){ + print("intrenable: nil handler for %d for %s\n", + irq, name); + return; + } + + v = xalloc(sizeof(Vctl)); + v->isintr = 1; + v->irq = irq; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN-1); + v->name[KNAMELEN-1] = 0; + + ilock(&vctllock); + vno = vectorenable(v); + if(vno == -1){ + iunlock(&vctllock); + print("intrenable: couldn't enable irq %d for %s\n", + irq, v->name); + xfree(v); + return; + } + v->next = vctl[vno]; + vctl[vno] = v; + iunlock(&vctllock); +} + +void +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, char *name) +{ + Vctl **pv, *v; + + ilock(&vctllock); + pv = &vctl[irq]; + while (*pv && + ((*pv)->irq != irq || (*pv)->f != f || (*pv)->a != a || + strcmp((*pv)->name, name))) + pv = &((*pv)->next); + + if(*pv == nil){ + print("intrdisable: irq %d not found\n", irq); + iunlock(&vctllock); + return; + } + + v = *pv; + *pv = (*pv)->next; /* Link out the entry */ + + if(vctl[irq] == nil) + vectordisable(v); + iunlock(&vctllock); + xfree(v); +} + +void syscall(Ureg*); +void noted(Ureg*, ulong); +static void _dumpstack(Ureg*); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "reserved A", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "reserved 10", + "data load translation miss", + "data store translation miss", + "instruction address breakpoint", + "system management interrupt", +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", + "DCMP", "ICMP", + "DMISS", "IMISS", + "HASH1", "HASH2", + "DAR", "DSISR", +}; + +void +kexit(Ureg*) +{ + uvlong t; + Tos *tos; + + /* precise time accounting, kernel exit */ + tos = (Tos*)(USTKTOP-sizeof(Tos)); + cycles(&t); + tos->kcycles += t - up->kentry; + tos->pcycles = up->pcycles; + tos->pid = up->pid; +} + +void +trap(Ureg *ureg) +{ + int ecode, user; + char buf[ERRMAX], *s; + extern FPsave initfp; + + ecode = (ureg->cause >> 8) & 0xff; + user = (ureg->srr1 & MSR_PR) != 0; + if(user){ + cycles(&up->kentry); + up->dbgreg = ureg; + } + if(ureg->status & MSR_RI == 0) + print("double fault?: ecode = %d\n", ecode); + + switch(ecode) { + case CEI: + m->intr++; + intr(ureg); + break; + case CDEC: + clockintr(ureg); + break; + case CDSI: + m->pfault++; + if (up == nil){ + dumpregs(ureg); + panic("kernel fault"); + } + up->mmureg = ureg; + faultpower(ureg, ureg->dar, (ureg->dsisr & BIT(6)) == 0); + break; + case CISI: + m->pfault++; + if (up == nil){ + dumpregs(ureg); + panic("kernel fault"); + } + up->mmureg = ureg; + faultpower(ureg, ureg->pc, 1); + break; + case CIMISS: /* instruction miss */ + if (up == nil){ + dumpregs(ureg); + panic("kernel fault"); + } + up->mmureg = ureg; + faultpower(ureg, ureg->imiss, 1); + break; + case CLMISS: /* data load miss */ + if (up == nil){ + dumpregs(ureg); + panic("kernel fault"); + } + up->mmureg = ureg; + faultpower(ureg, ureg->dmiss, 1); + break; + case CSMISS: /* data store miss */ + if (up == nil){ + dumpregs(ureg); + panic("kernel fault"); + } + up->mmureg = ureg; + faultpower(ureg, ureg->dmiss, 0); + break; + + case CSYSCALL: + if(!user) + panic("syscall in kernel: srr1 0x%4.4luX\n", ureg->srr1); + syscall(ureg); + if (up->delaysched){ + sched(); + splhi(); + } + kexit(ureg); + return; /* syscall() calls notify itself, don't do it again */ + + case CFPU: + if(!user || up == nil) { + dumpregs(ureg); + panic("floating point in kernel"); + } + switch(up->fpstate){ + case FPinit: + fprestore(&initfp); + up->fpstate = FPactive; + break; + case FPinactive: + fprestore(&up->fpsave); + up->fpstate = FPactive; + break; + case FPactive: + print("up->fpstate %d\n", up->fpstate); + delay(100); + dumpregs(ureg); + delay(200); + panic("fpstate"); + break; + default: + if(user){ + spllo(); + sprint(buf, "sys: floating point in note handler:"); + postnote(up, 1, buf, NDebug); + break; + } + panic("kernel fpstate illegal"); + } + ureg->srr1 |= MSR_FP; + break; + case CPROG: + if(ureg->status & (1<<19)) + s = "floating point exception"; + else if(ureg->status & (1<<18)) + s = "illegal instruction"; + else if(ureg->status & (1<<17)) + s = "privileged instruction"; + else + s = "undefined program exception"; + if(user){ + spllo(); + sprint(buf, "sys: trap: %s", s); + postnote(up, 1, buf, NDebug); + break; + } + dumpregs(ureg); + panic(s); + break; + default: + if(ecode < nelem(excname) && user){ + spllo(); + sprint(buf, "sys: trap: %s", excname[ecode]); + postnote(up, 1, buf, NDebug); + break; + } + dumpregs(ureg); + if(ecode < nelem(excname)) + panic("%s", excname[ecode]); + panic("unknown trap/intr: %d\n", ecode); + } + + /* restoreureg must execute at high IPL */ + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && ecode == CDEC){ + sched(); + splhi(); + } + + if(user) { + if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){ + postnote(up, 1, buf, NDebug); + } + notify(ureg); + if(up->fpstate != FPactive) + ureg->srr1 &= ~MSR_FP; + kexit(ureg); + } +} + +void +faultpower(Ureg *ureg, ulong addr, int read) +{ + int user, insyscall, n; + char buf[ERRMAX]; + + user = (ureg->srr1 & MSR_PR) != 0; + insyscall = up->insyscall; + up->insyscall = 1; + n = fault(addr, read); + if(n < 0){ + if(!user){ + dumpregs(ureg); + panic("fault: 0x%lux", addr); + } + sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr); + postnote(up, 1, buf, NDebug); + } + up->insyscall = insyscall; +} + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ +} + +void +setmvec(int v, void (*r)(void), void (*t)(void)) +{ + ulong *vp, pa, o, n; + + vp = KADDR(v); + n = 0; + vp[n++] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[n++] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[n++] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far */ + vp[n++] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[n++] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[n++] = 0x7c0803a6; /* MOVW R0, LR */ + vp[n++] = 0x4e800021; /* BL (LR) */ + }else + vp[n++] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + pa = PADDR(t); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far */ + vp[n++] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[n++] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[n++] = 0x7c0803a6; /* MOVW R0, LR */ + vp[n] = 0x4e800021; /* BL (LR) */ + }else + vp[n] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<<i)) + s = fpcause[i]; + if(s == 0) + return "no floating point exception"; + sprint(buf, "%s fppc=0x%lux", s, fppc); + return buf; +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ + +static void +getpcsp(ulong *pc, ulong *sp) +{ + *pc = getcallerpc(&pc); + *sp = (ulong)&pc-4; +} + +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp); + ureg.lr = getcallerpc(&fn); + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong l, sl, el, v; + int i; + + l = (ulong)&l; + if(up == 0){ + el = (ulong)m+BY2PG; + sl = el-KSTACK; + } + else{ + sl = (ulong)up->kstack; + el = sl + KSTACK; + } + if(l > el || l < sl){ + el = (ulong)m+BY2PG; + sl = el-KSTACK; + } + if(l > el || l < sl) + return; + print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr); + i = 0; + for(; l < el; l += 4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%.8lux=%.8lux ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->srr1 & MSR_PR == 0) + if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + for(i=0; i<16; i+=2) + print("sr[%x]\t0x%.8lux\tsr[%x]\t0x%.8lux\n", i, getsr(i<<28), i+1, getsr((i+1)<<28)); + l = &ur->cause; + for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2) + print("%s\t0x%.8lux\t%s\t0x%.8lux\n", regname[i], l[0], regname[i+1], l[1]); + delay(500); +} + +static void +linkproc(void) +{ + spllo(); + (*up->kpfun)(up->kparg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +/* + * called in sysfile.c + */ +void +evenaddr(ulong addr) +{ + if(addr & 3){ + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} + +long +execregs(ulong entry, ulong ssize, ulong nargs) +{ + ulong *sp; + Ureg *ureg; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ureg = up->dbgreg; + ureg->usp = (ulong)sp; + ureg->pc = entry; + ureg->srr1 &= ~MSR_FP; /* disable floating point */ + up->fpstate = FPinit; + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (ulong)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + cur->r3 = 0; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +ulong +userpc(void) +{ + Ureg *ureg; + + ureg = (Ureg*)up->dbgreg; + return ureg->pc; +} + + +/* This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg* ureg, Proc* p) +{ + ureg->pc = p->sched.pc; + ureg->sp = p->sched.sp+4; +} + +ulong +dbgpc(Proc *p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} + +/* + * system calls + */ +#include "../port/systab.h" + +/* TODO: make this trap a separate asm entry point, like on other RISC architectures */ +void +syscall(Ureg* ureg) +{ + int i; + char *e; + long ret; + ulong sp, scallnr; + + m->syscall++; + up->insyscall = 1; + up->pc = ureg->pc; + up->dbgreg = ureg; + + if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){ + print("fpstate check, entry syscall\n"); + delay(200); + dumpregs(ureg); + print("fpstate check, entry syscall\n"); + } + + scallnr = ureg->r3; + up->scallnr = ureg->r3; + if(scallnr == RFORK && up->fpstate == FPactive){ + fpsave(&up->fpsave); + up->fpstate = FPinactive; + } + spllo(); + + sp = ureg->usp; + up->nerrlab = 0; + ret = -1; + if(!waserror()){ + if(scallnr >= nsyscall || systab[scallnr] == nil){ + pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + up->s = *((Sargs*)(sp+BY2WD)); + up->psstate = sysctab[scallnr]; + + ret = systab[scallnr](up->s.args); + poperror(); + }else{ + /* failure: save the error buffer for errstr */ + e = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = e; + } + if(up->nerrlab){ + print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab); + print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr); + for(i = 0; i < NERR; i++) + print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + + up->insyscall = 0; + up->psstate = 0; + + /* + * Put return value in frame. On the x86 the syscall is + * just another trap and the return value from syscall is + * ignored. On other machines the return value is put into + * the results register by caller of syscall. + */ + ureg->r3 = ret; + + if(scallnr == NOTED) + noted(ureg, *(ulong*)(sp+BY2WD)); + + /* restoreureg must execute at high IPL */ + splhi(); + if(scallnr!=RFORK) + notify(ureg); + + if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){ + print("fpstate check, exit syscall nr %lud, pid %lud\n", scallnr, up->pid); + dumpregs(ureg); + } + if(up->fpstate != FPactive) + ureg->srr1 &= ~MSR_FP; +} + +/* + * Call user, if necessary, with note. + * Pass user the Ureg struct and the note on his stack. + */ +int +notify(Ureg* ur) +{ + int l; + ulong s, sp; + Note *n; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + s = spllo(); + qlock(&up->debug); + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0){ + l = strlen(n->msg); + if(l > ERRMAX-15) /* " pc=0x12345678\0" */ + l = ERRMAX-15; + sprint(n->msg+l, " pc=0x%.8lux", ur->pc); + } + + if(n->flag!=NUser && (up->notified || up->notify==0)){ + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splhi(); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->fpstate == FPactive){ + fpsave(&up->fpsave); + up->fpstate = FPinactive; + } + up->fpstate |= FPillegal; + + sp = ur->usp & ~(BY2V-1); + sp -= sizeof(Ureg); + + if(!okaddr((ulong)up->notify, BY2WD, 0) || + !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + sp -= BY2WD+ERRMAX; + memmove((char*)sp, up->note[0].msg, ERRMAX); + sp -= 3*BY2WD; + *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + ur->pc = (ulong)up->notify; + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + + +/* + * Return user to state before notify() + */ +void +noted(Ureg* ureg, ulong arg0) +{ + Ureg *nureg; + ulong oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + nureg = up->ureg; /* pointer to user returned Ureg struct */ + + /* sanity clause */ + oureg = (ulong)nureg; + if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad ureg in noted or call to noted when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove(ureg, nureg, sizeof(Ureg)); + + switch(arg0){ + case NCONT: + case NRSTR: + if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); + qunlock(&up->debug); + break; + + case NSAVE: + if(!okaddr(nureg->pc, BY2WD, 0) + || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRMAX; + splhi(); + ureg->sp = sp; + ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + break; + + default: + pprint("unknown noted arg 0x%lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } + up->fpstate &= ~FPillegal; + if (up->fpstate == FPactive) + ureg->srr1 |= MSR_FP; + else + ureg->srr1 &= ~MSR_FP; +} diff --git a/sys/src/9/ppc/uartsaturn.c b/sys/src/9/ppc/uartsaturn.c new file mode 100755 index 000000000..51f91f94c --- /dev/null +++ b/sys/src/9/ppc/uartsaturn.c @@ -0,0 +1,472 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "msaturn.h" + +enum{ + UartAoffs = Saturn + 0x0a00, + UartBoffs = Saturn + 0x0b00, + Nuart = 2, + + Baudfreq = 14745600 / 16, + Lcr_div = RBIT(1, uchar), + Lcr_peven = RBIT(3, uchar), + Lcr_pen = RBIT(4, uchar), + Lcr_stop = RBIT(5, uchar), + Lcr_wrdlenmask = RBIT(6, uchar) | RBIT(7, uchar), + Lcr_wrdlenshift = 0, + Lsr_tbre = RBIT(2, uchar), + Fcr_txreset = RBIT(5, uchar), + Fcr_rxreset = RBIT(6, uchar), + Iir_txempty = RBIT(5, uchar), + Iir_rxfull = RBIT(6, uchar), + Iir_rxerr = RBIT(7, uchar), + Ier_rxerr = RBIT(5, uchar), + Ier_txempty = RBIT(6, uchar), + Ier_rxfull = RBIT(7, uchar), + Lsr_rxavail = RBIT(7, uchar), + Txsize = 16, + Rxsize = 16, +}; + +typedef struct Saturnuart Saturnuart; +struct Saturnuart { + uchar rxb; +#define txb rxb +#define dll rxb + uchar ier; // Interrupt enable, divisor latch +#define dlm ier + uchar iir; // Interrupt identification, fifo control +#define fcr iir + uchar lcr; // Line control register + uchar f1; + uchar lsr; // Line status register + ushort f2; +}; + +typedef struct UartData UartData; +struct UartData { + int suno; /* saturn uart number: 0 or 1 */ + Saturnuart *su; + char *rxbuf; + char *txbuf; + int initialized; + int enabled; +} uartdata[Nuart]; + +extern PhysUart saturnphysuart; + +Uart suart[Nuart] = { + { + .name = "SaturnUart1", + .baud = 19200, + .bits = 8, + .stop = 1, + .parity = 'n', + .phys = &saturnphysuart, + .special = 0, + }, + { + .name = "SaturnUart2", + .baud = 115200, + .bits = 8, + .stop = 1, + .parity = 'n', + .phys = &saturnphysuart, + .special = 0, + }, +}; + +static void suinterrupt(Ureg*, void*); + +static Uart* +supnp(void) +{ + int i; + + for (i = 0; i < nelem(suart)-1; i++) + suart[i].next = &suart[i + 1]; + suart[nelem(suart)-1].next=nil; + return suart; +} + +static void +suinit(Uart*uart) +{ + UartData *ud; + Saturnuart *su; + + ud = uart->regs; + su = ud->su; + su->fcr=Fcr_txreset|Fcr_rxreset; + ud->initialized=1; +} + +static void +suenable(Uart*uart, int ie) +{ + Saturnuart *su; + UartData *ud; + int nr; + + nr = uart - suart; + if (nr < 0 || nr > Nuart) + panic("No uart %d", nr); + ud = uartdata + nr; + ud->suno = nr; + su=ud->su = (Saturnuart*)((nr == 0)? UartAoffs: UartBoffs); + uart->regs = ud; + + if(ud->initialized==0) + suinit(uart); + + if(!ud->enabled && ie){ + intrenable(Vecuart0+nr , suinterrupt, uart, uart->name); + su->ier=Ier_txempty|Ier_rxfull; + ud->enabled=1; + } +} + + +static long +sustatus(Uart* uart, void* buf, long n, long offset) +{ + Saturnuart *su; + char p[128]; + + su = ((UartData*)uart->regs)->su; + snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n" + "dev(%d) type(%d) framing(%d) overruns(%d)\n", + + uart->baud, + uart->hup_dcd, + uart->hup_dsr, + Txsize, + (su->lcr & Lcr_pen)? ((su->lcr & Lcr_peven) ? 'e': 'o'): 'n', + (su->lcr & Lcr_stop)? 2: 1, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +sufifo(Uart*, int) +{} + +static void +sudtr(Uart*, int) +{} + +static void +surts(Uart*, int) +{} + +static void +sumodemctl(Uart*, int) +{} + +static int +suparity(Uart*uart, int parity) +{ + int lcr; + Saturnuart *su; + + su = ((UartData*)uart->regs)->su; + + lcr = su->lcr & ~(Lcr_pen|Lcr_peven); + + switch(parity){ + case 'e': + lcr |= (Lcr_pen|Lcr_peven); + break; + case 'o': + lcr |= Lcr_pen; + break; + case 'n': + default: + break; + } + + su->lcr = lcr; + uart->parity = parity; + + return 0; +} + +static int +sustop(Uart* uart, int stop) +{ + int lcr; + Saturnuart *su; + + su = ((UartData*)uart->regs)->su; + lcr = su->lcr & ~Lcr_stop; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Lcr_stop; + break; + default: + return -1; + } + + /* Set new value and reenable if device was previously enabled */ + su->lcr = lcr; + uart->stop = stop; + + return 0; +} + +static int +subits(Uart*uart, int n) +{ + Saturnuart *su; + uchar lcr; + + su = ((UartData*)uart->regs)->su; + if(n<5||n>8) + return -1; + + lcr = su->lcr & ~Lcr_wrdlenmask; + lcr |= (n-5) << Lcr_wrdlenshift; + su->lcr = lcr; + return 0; +} + +static int +subaud(Uart* uart, int baud) +{ + ushort v; + Saturnuart *su; + + if (uart->enabled){ + su = ((UartData*)uart->regs)->su; + + if(baud <= 0) + return -1; + + v = Baudfreq / baud; + su->lcr |= Lcr_div; + su->dll = v; + su->dlm = v >> 8; + su->lcr &= ~Lcr_div; + } + uart->baud = baud; + + return 0; +} + +static void +subreak(Uart*, int) +{} + +static void +sukick(Uart *uart) +{ + Saturnuart *su; + int i; + + if(uart->blocked) + return; + + su = ((UartData*)uart->regs)->su; + if((su->iir & Iir_txempty) == 0) + return; + + for(i = 0; i < Txsize; i++){ + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + su->txb = *(uart->op++); + su->ier |= Ier_txempty; + break; + } +} + +static void +suputc(Uart *uart, int c) +{ + Saturnuart *su; + + su = ((UartData*)uart->regs)->su; + while((su->lsr&Lsr_tbre) == 0) + ; + + su->txb=c; + while((su->lsr&Lsr_tbre) == 0) + ; +} + +static int +getchars(Uart *uart, uchar *cbuf) +{ + int nc; + UartData *ud; + Saturnuart *su; + + ud = uart->regs; + su = ud->su; + + while((su->lsr&Lsr_rxavail) == 0) + ; + + *cbuf++ = su->rxb; + nc = 1; + while(su->lsr&Lsr_rxavail){ + *cbuf++ = su->rxb; + nc++; + } + return nc; +} + +static int +sugetc(Uart *uart) +{ + static uchar buf[128], *p; + static int cnt; + char c; + + if (cnt <= 0) { + cnt = getchars(uart, buf); + p = buf; + } + c = *p++; + cnt--; + return c; +} + +static void +suinterrupt(Ureg*, void*u) +{ + Saturnuart *su; + Uart *uart; + uchar iir; + + uart = u; + if (uart == nil) + panic("uart is nil"); + su = ((UartData*)uart->regs)->su; + iir = su->iir; + if(iir&Iir_rxfull) + while(su->lsr&Lsr_rxavail) + uartrecv(uart, su->rxb); + if(iir & Iir_txempty){ + su->ier&=~Ier_txempty; + uartkick(uart); + } + if (iir & Iir_rxerr) + uart->oerr++; + intack(); +} + +static void +sudisable(Uart* uart) +{ + Saturnuart *su; + + su = ((UartData*)uart->regs)->su; + su->ier&=~(Ier_txempty|Ier_rxfull); +} + +PhysUart saturnphysuart = { + .name = "su", + .pnp = supnp, + .enable = suenable, + .disable = sudisable, + .kick = sukick, + .dobreak = subreak, + .baud = subaud, + .bits = subits, + .stop = sustop, + .parity = suparity, + .modemctl = sumodemctl, + .rts = surts, + .dtr = sudtr, + .status = sustatus, + .fifo = sufifo, + .getc = sugetc, + .putc = suputc, +}; + +void +console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + if(n < 0 || n >= nelem(suart)) + return; + + uart = suart + n; + +/* uartctl(uart, "b115200 l8 pn s1"); */ + if(*cmd != '\0') + uartctl(uart, cmd); + (*uart->phys->enable)(uart, 0); + + consuart = uart; + uart->console = 1; +} + +Saturnuart*uart = (Saturnuart*)UartAoffs; + +void +dbgputc(int c) +{ + while((uart->lsr&Lsr_tbre) == 0) + ; + + uart->txb=c; + while((uart->lsr&Lsr_tbre) == 0) + ; +} + +void +dbgputs(char*s) +{ + while(*s) + dbgputc(*s++); +} + +void +dbgputx(ulong x) +{ + int i; + char c; + + for(i=0; i < sizeof(ulong) * 2; i++){ + c = ((x >> (28 - i * 4))) & 0xf; + if(c >= 0 && c <= 9) + c += '0'; + else + c += 'a' - 10; + + while((uart->lsr&Lsr_tbre) == 0) + ; + + uart->txb=c; + } + while((uart->lsr&Lsr_tbre) == 0) + ; + + uart->txb='\n'; + while((uart->lsr&Lsr_tbre) == 0) + ; +} diff --git a/sys/src/9/ppc/uartsmc.c b/sys/src/9/ppc/uartsmc.c new file mode 100755 index 000000000..fe48f53fe --- /dev/null +++ b/sys/src/9/ppc/uartsmc.c @@ -0,0 +1,585 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "imm.h" +#include "../port/error.h" +#include "../ppc/uartsmc.h" + +/* + * PowerPC 8260 SMC UART + */ + +enum { + /* SMC Mode Registers */ + Clen = 0x7800, /* Character length */ + Sl = 0x0400, /* Stop length, 0: one stop bit, 1: two */ + Pen = 0x0200, /* Parity enable */ + Pm = 0x0100, /* Parity mode, 0 is odd */ + Sm = 0x0030, /* SMC mode, two bits */ + SMUart = 0x0020, /* SMC mode, 0b10 is uart */ + Dm = 0x000c, /* Diagnostic mode, 00 is normal */ + Ten = 0x0002, /* Transmit enable, 1 is enabled */ + Ren = 0x0001, /* Receive enable, 1 is enabled */ + + /* SMC Event/Mask Registers */ + ce_Brke = 0x0040, /* Break end */ + ce_Br = 0x0020, /* Break character received */ + ce_Bsy = 0x0004, /* Busy condition */ + ce_Txb = 0x0002, /* Tx buffer */ + ce_Rxb = 0x0001, /* Rx buffer */ + + /* Receive/Transmit Buffer Descriptor Control bits */ + BDContin= 1<<9, + BDIdle= 1<<8, + BDPreamble= 1<<8, + BDBreak= 1<<5, + BDFrame= 1<<4, + BDParity= 1<<3, + BDOverrun= 1<<1, + + /* Tx and Rx buffer sizes (32 bytes) */ + Rxsize= CACHELINESZ, + Txsize= CACHELINESZ, +}; + +extern PhysUart smcphysuart; + +Uart smcuart[Nuart] = { + { + .name = "SMC1", + .baud = 115200, + .bits = 8, + .stop = 1, + .parity = 'n', + .phys = &smcphysuart, + .special = 0, + }, +/* Only configure SMC1 for now + { + .name = "SMC2", + .baud = 115200, + .bits = 8, + .stop = 1, + .parity = 'n', + .phys = &smcphysuart, + .special = 0, + }, +*/ +}; + +int uartinited = 0; + +static void smcinterrupt(Ureg*, void*); +static void smcputc(Uart *uart, int c); + +int +baudgen(int baud) +{ + int d; + + d = ((m->brghz+(baud>>1))/baud)>>4; + if(d >= (1<<12)) + return ((d+15)>>3)|1; + return d<<1; +} + +static Uart* +smcpnp(void) +{ + int i; + + for (i = 0; i < nelem(smcuart) - 1; i++) + smcuart[i].next = smcuart + i + 1; + return smcuart; +} + +void +smcinit(Uart *uart) +{ + Uartsmc *p; + SMC *smc; + UartData *ud; + ulong lcr; + int bits; + + ud = uart->regs; + + if (ud->initialized) + return; + + smcsetup(uart); /* Steps 1 through 4, PPC-dependent */ + p = ud->usmc; + smc = ud->smc; + + /* step 5: set up buffer descriptors */ + /* setup my uart structure */ + if (ud->rxb == nil) + ud->rxb = bdalloc(1); + if (ud->txb == nil) + ud->txb = bdalloc(1); + + p->rbase = ((ulong)ud->rxb) - (ulong)IMMR; + p->tbase = ((ulong)ud->txb) - (ulong)IMMR; + + /* step 8: receive buffer size */ + p->mrblr = Rxsize; + + /* step 9: */ + p->maxidl = 15; + + /* step 10: */ + p->brkln = 0; + p->brkec = 0; + + /* step 11: */ + p->brkcr = 0; + + /* step 12: setup receive buffer */ + ud->rxb->status = BDEmpty|BDWrap|BDInt; + ud->rxb->length = 0; + ud->rxbuf = xspanalloc(Rxsize, 0, CACHELINESZ); + ud->rxb->addr = PADDR(ud->rxbuf); + + /* step 13: step transmit buffer */ + ud->txb->status = BDWrap|BDInt; + ud->txb->length = 0; + ud->txbuf = xspanalloc(Txsize, 0, CACHELINESZ); + ud->txb->addr = PADDR(ud->txbuf); + + /* step 14: clear events */ + smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; + + /* + * step 15: enable interrupts (done later) + * smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; + */ + + /* step 17: set parity, no of bits, UART mode, ... */ + lcr = SMUart; + bits = uart->bits + 1; + + switch(uart->parity){ + case 'e': + lcr |= (Pen|Pm); + bits +=1; + break; + case 'o': + lcr |= Pen; + bits +=1; + break; + case 'n': + default: + break; + } + + if(uart->stop == 2){ + lcr |= Sl; + bits += 1; + } + + /* Set new value and reenable if device was previously enabled */ + smc->smcmr = lcr | bits <<11 | 0x3; + + ud->initialized = 1; +} + +static void +smcenable(Uart *uart, int intenb) +{ + UartData *ud; + SMC *smc; + int nr; + + nr = uart - smcuart; + if (nr < 0 || nr > Nuart) + panic("No SMC %d", nr); + ud = uartdata + nr; + ud->smcno = nr; + uart->regs = ud; + if (ud->initialized == 0) + smcinit(uart); + if (ud->enabled || intenb == 0) + return; + smc = ud->smc; + /* clear events */ + smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; + /* enable interrupts */ + smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb; + intrenable(VecSMC1 + ud->smcno, smcinterrupt, uart, uart->name); + ud->enabled = 1; +} + +static long +smcstatus(Uart* uart, void* buf, long n, long offset) +{ + SMC *sp; + char p[128]; + + sp = ((UartData*)uart->regs)->smc; + snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n" + "dev(%d) type(%d) framing(%d) overruns(%d)\n", + + uart->baud, + uart->hup_dcd, + uart->hup_dsr, + ((sp->smcmr & Clen) >>11) - ((sp->smcmr&Pen) ? 1 : 0) - ((sp->smcmr&Sl) ? 2 : 1), + (sp->smcmr & Pen) ? ((sp->smcmr & Pm) ? 'e': 'o'): 'n', + (sp->smcmr & Sl) ? 2: 1, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +smcfifo(Uart*, int) +{ + /* + * Toggle FIFOs: + * if none, do nothing; + * reset the Rx and Tx FIFOs; + * empty the Rx buffer and clear any interrupt conditions; + * if enabling, try to turn them on. + */ + return; +} + +static void +smcdtr(Uart*, int) +{ +} + +static void +smcrts(Uart*, int) +{ +} + +static void +smcmodemctl(Uart*, int) +{ +} + +static int +smcparity(Uart* uart, int parity) +{ + int lcr; + SMC *sp; + + sp = ((UartData*)uart->regs)->smc; + + lcr = sp->smcmr & ~(Pen|Pm); + + /* Disable transmitter/receiver. */ + sp->smcmr &= ~(Ren | Ten); + + switch(parity){ + case 'e': + lcr |= (Pen|Pm); + break; + case 'o': + lcr |= Pen; + break; + case 'n': + default: + break; + } + /* Set new value and reenable if device was previously enabled */ + sp->smcmr = lcr; + + uart->parity = parity; + + return 0; +} + +static int +smcstop(Uart* uart, int stop) +{ + int lcr, bits; + SMC *sp; + + sp = ((UartData*)uart->regs)->smc; + lcr = sp->smcmr & ~(Sl | Clen); + + /* Disable transmitter/receiver. */ + sp->smcmr &= ~(Ren | Ten); + + switch(stop){ + case 1: + break; + case 2: + lcr |= Sl; + break; + default: + return -1; + } + + bits = uart->bits + ((lcr & Pen) ? 1 : 0) + ((lcr & Sl) ? 2 : 1); + lcr |= bits<<11; + + /* Set new value and reenable if device was previously enabled */ + sp->smcmr = lcr; + + uart->stop = stop; + + return 0; +} + +static int +smcbits(Uart* uart, int bits) +{ + int lcr, b; + SMC *sp; + + if (bits < 5 || bits > 14) + return -1; + + sp = ((UartData*)uart->regs)->smc; + lcr = sp->smcmr & ~Clen; + + b = bits + ((sp->smcmr & Pen) ? 1 : 0) + ((sp->smcmr & Sl) ? 2 : 1); + + if (b > 15) + return -1; + + /* Disable transmitter/receiver */ + sp->smcmr &= ~(Ren | Ten); + + /* Set new value and reenable if device was previously enabled */ + sp->smcmr = lcr | b<<11; + + uart->bits = bits; + + return 0; +} + +static int +smcbaud(Uart* uart, int baud) +{ + int i; + SMC *sp; + + if (uart->enabled){ + sp = ((UartData*)uart->regs)->smc; + + if(uart->freq == 0 || baud <= 0) + return -1; + + i = sp - imm->smc; + imm->brgc[i] = (((m->brghz >> 4) / baud) << 1) | 0x00010000; + } + uart->baud = baud; + + return 0; +} + +static void +smcbreak(Uart*, int) +{ +} + +static void +smckick(Uart *uart) +{ + BD *txb; + UartData *ud; + int i; + + if(uart->blocked) + return; + + ud = uart->regs; + txb = ud->txb; + + if (txb->status & BDReady) + return; /* Still busy */ + + for(i = 0; i < Txsize; i++){ + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + ud->txbuf[i] = *(uart->op++); + } + if (i == 0) + return; + dcflush(ud->txbuf, Txsize); + txb->length = i; + sync(); + txb->status |= BDReady|BDInt; +} + +static void +smcinterrupt(Ureg*, void* u) +{ + int i, nc; + char *buf; + BD *rxb; + UartData *ud; + Uart *uart; + uchar events; + + uart = u; + if (uart == nil) + panic("uart is nil"); + ud = uart->regs; + if (ud == nil) + panic("ud is nil"); + + events = ud->smc->smce; + ud->smc->smce = events; /* Clear events */ + + if (events & 0x10) + iprint("smc%d: break\n", ud->smcno); + if (events & 0x4) + uart->oerr++; + if (events & 0x1){ + /* Receive characters + */ + rxb = ud->rxb; + buf = ud->rxbuf; + dczap(buf, Rxsize); /* invalidate data cache before copying */ + if ((rxb->status & BDEmpty) == 0){ + nc = rxb->length; + for (i=0; i<nc; i++) + uartrecv(uart, *buf++); + sync(); + rxb->status |= BDEmpty; + }else{ + iprint("uartsmc: unexpected receive event\n"); + } + } + if (events & 0x2){ + if ((ud->txb->status & BDReady) == 0) + uartkick(uart); + } +} + +static void +smcdisable(Uart* uart) +{ + SMC *sp; + + sp = ((UartData*)uart->regs)->smc; + sp->smcmr &= ~(Ren | Ten); +} + +static int +getchars(Uart *uart, uchar *cbuf) +{ + int i, nc; + char *buf; + BD *rxb; + UartData *ud; + + ud = uart->regs; + rxb = ud->rxb; + + /* Wait for character to show up. + */ + buf = ud->rxbuf; + while (rxb->status & BDEmpty) + ; + nc = rxb->length; + for (i=0; i<nc; i++) + *cbuf++ = *buf++; + sync(); + rxb->status |= BDEmpty; + + return(nc); +} + +static int +smcgetc(Uart *uart) +{ + static uchar buf[128], *p; + static int cnt; + char c; + + if (cnt <= 0) { + cnt = getchars(uart, buf); + p = buf; + } + c = *p++; + cnt--; + + return(c); +} + +static void +smcputc(Uart *uart, int c) +{ + BD *txb; + UartData *ud; + SMC *smc; + + ud = uart->regs; + txb = ud->txb; + smc = ud->smc; + smc->smcm = 0; + + /* Wait for last character to go. + */ + while (txb->status & BDReady) + ; + + ud->txbuf[0] = c; + dcflush(ud->txbuf, 1); + txb->length = 1; + sync(); + txb->status |= BDReady; + + while (txb->status & BDReady) + ; +} + +PhysUart smcphysuart = { + .name = "smc", + .pnp = smcpnp, + .enable = smcenable, + .disable = smcdisable, + .kick = smckick, + .dobreak = smcbreak, + .baud = smcbaud, + .bits = smcbits, + .stop = smcstop, + .parity = smcparity, + .modemctl = smcmodemctl, + .rts = smcrts, + .dtr = smcdtr, + .status = smcstatus, + .fifo = smcfifo, + .getc = smcgetc, + .putc = smcputc, +}; + +void +console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + if(n < 0 || n >= nelem(smcuart)) + return; + uart = smcuart + n; + +/* uartctl(uart, "b115200 l8 pn s1"); */ + if(*cmd != '\0') + uartctl(uart, cmd); + (*uart->phys->enable)(uart, 0); + + consuart = uart; + uart->console = 1; +} diff --git a/sys/src/9/ppc/ucu b/sys/src/9/ppc/ucu new file mode 100755 index 000000000..56631f851 --- /dev/null +++ b/sys/src/9/ppc/ucu @@ -0,0 +1,59 @@ +dev + root + cons + env + pipe + proc + mnt + srv + dup + ssl + cap + kprof + uart + + ether netif + ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno + ce + +link + ethersaturn + ethermedium + netdevmedium + +misc + uartsaturn + saturntimer + msaturn + +ip + tcp + udp + ipifc + icmp + icmp6 + +port + int cpuserver = 1; + +boot cpu + tcp + +bootdir + /power/bin/rc + /rc/lib/rcmain + /power/bin/bind + /power/bin/sed + /power/bin/srv + /power/bin/cat + /power/bin/cp + /power/bin/rm + /power/bin/echo + /power/bin/mount + /power/bin/sleep + /power/bin/auth/factotum + /power/bin/ip/ipconfig + /power/bin/ls + /power/bin/ps + /power/bin/auth/wrkey + /sys/lib/sysconfig/blast/boot diff --git a/sys/src/9/ppc/ucu.h b/sys/src/9/ppc/ucu.h new file mode 100755 index 000000000..91653bc42 --- /dev/null +++ b/sys/src/9/ppc/ucu.h @@ -0,0 +1,21 @@ + +#define FLASHMEM (~0) +#define MEM1BASE 0 +#define MEM1SIZE 0x02000000 +#define MEM2BASE 0 +#define MEM2SIZE 0 +#define PLAN9INI (~0) + +#define Saturn 0xf0000000 + +#define TLBENTRIES 128 + +/* + * PTE bits for fault.c. These belong to the second PTE word. Validity is + * implied for putmmu(), and we always set PTE0_V. PTEVALID is used + * here to set cache policy bits on a global basis. + */ +#define PTEVALID (PTE1_M|PTE1_W) /* write through */ +#define PTEWRITE (PTE1_RW|PTE1_C) +#define PTERONLY PTE1_RO +#define PTEUNCACHED PTE1_I |