diff -Nru /sys/src/9/vt4/NOTICE /sys/src/9/vt4/NOTICE --- /sys/src/9/vt4/NOTICE Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/NOTICE Fri Jun 7 00:00:00 2013 @@ -0,0 +1,5 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc +Plan 9® Copyright © 1991-2006 AT&T and Lucent Technologies Inc +PowerPC support Copyright © 1995-1997,2002,2003 C H Forsyth (forsyth@terzarima.net) +405EP PowerPC Inferno port Copyright © 2003 Vita Nuova Holdings Limited +405EP PowerPC Plan 9 port Copyright © 2006 The "Ron's Dream" Consortium (Bell Labs, IBM, LANL, Vita Nuova) diff -Nru /sys/src/9/vt4/aoe.h /sys/src/9/vt4/aoe.h --- /sys/src/9/vt4/aoe.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/aoe.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,78 @@ +/* + * ATA-over-Ethernet (AoE) protocol + */ +enum { + ACata, + ACconfig, +}; + +enum { + AQCread, + AQCtest, + AQCprefix, + AQCset, + AQCfset, +}; + +enum { + AEcmd = 1, + AEarg, + AEdev, + AEcfg, + AEver, +}; + +enum { + Aoetype = 0x88a2, + Aoesectsz = 512, /* standard sector size */ + Aoever = 1, + + AFerr = 1<<2, + AFrsp = 1<<3, + + AAFwrite= 1, + AAFext = 1<<6, +}; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; + uchar payload[]; +} Aoehdr; + +#define AOEHDRSZ offsetof(Aoehdr, payload[0]) + +typedef struct { + Aoehdr; + uchar aflag; + uchar errfeat; + uchar scnt; + uchar cmdstat; + uchar lba[6]; + uchar res[2]; + uchar payload[]; +} Aoeata; + +#define AOEATASZ offsetof(Aoeata, payload[0]) + +typedef struct { + Aoehdr; + uchar bufcnt[2]; + uchar fwver[2]; + uchar scnt; + uchar verccmd; + uchar cslen[2]; + uchar payload[]; +} Aoeqc; + +#define AOEQCSZ offsetof(Aoeqc, payload[0]) + +extern char Echange[]; +extern char Enotup[]; diff -Nru /sys/src/9/vt4/archvt4.c /sys/src/9/vt4/archvt4.c --- /sys/src/9/vt4/archvt4.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/archvt4.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,278 @@ +/* + * rae's virtex4 ml410 ppc405 + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "io.h" +#include "../port/netif.h" +#include "etherif.h" +#include "../ip/ip.h" + +#include "reboot.h" + +typedef struct Gpioregs Gpioregs; + +struct Gpioregs { + ulong data; /* write only? */ + ulong tri; /* tri-state */ + ulong data2; /* 2nd channel */ + ulong tri2; + + /* silly bits */ + ulong pad[67]; /* sheesh */ + ulong gie; /* global intr enable */ + ulong ier; /* intr enable */ + ulong isr; /* intr status */ +}; + +static void twinkle(void); + +void (*archclocktick)(void) = twinkle; +/* ether= for sys=torment */ +uchar mymac[Eaddrlen] = { 0x00, 0x0A, 0x35, 0x01, 0x8B, 0xB1 }; +uintptr memsz; + +void +startcpu(int) +{ + for (;;) + ; +} + +void +uncinit(void) +{ +} + +void +archreset(void) +{ + m->cpuhz = 300000000; + m->opbhz = 66600000; + m->clockgen = m->cpuhz; /* it's the internal cpu clock */ +} + +void +archvt4link(void) +{ +} + +int +cmpswap(long *addr, long old, long new) +{ + return cas32(addr, old, new); +} + +/* + * only use of GPIO now is access to LEDs + */ + +static int leds; +static uchar oldbits; +static Lock lightlck; + +static void +ledinit(void) +{ + Gpioregs *g; + + /* set access to LED */ + g = (Gpioregs*)Gpio; +/* g->gie = 0; /* wouldn't use intrs even if we had them */ + g->tri = 0; /* outputs */ + g->data = ~0; + barriers(); +} + +uchar +lightstate(int state) +{ + int r; + + if (m->machno != 0) + return oldbits; + ilock(&lightlck); + r = oldbits; + oldbits &= ~(Ledtrap|Ledkern|Leduser|Ledidle); + oldbits |= state & (Ledtrap|Ledkern|Leduser|Ledidle); + ((Gpioregs*)Gpio)->data = (uchar)~oldbits; /* 0 == lit */ + iunlock(&lightlck); + barriers(); + return r; +} + +uchar +lightbiton(int bit) +{ + int r; + + if (m->machno != 0) + return oldbits; + ilock(&lightlck); + r = oldbits; + oldbits |= bit; + ((Gpioregs*)Gpio)->data = (uchar)~oldbits; /* 0 == lit */ + iunlock(&lightlck); + barriers(); + return r; +} + +uchar +lightbitoff(int bit) +{ + int r; + + if (m->machno != 0) + return oldbits; + ilock(&lightlck); + r = oldbits; + oldbits &= ~bit; + ((Gpioregs*)Gpio)->data = (uchar)~oldbits; /* 0 == lit */ + iunlock(&lightlck); + barriers(); + return r; +} + +static void +twinkle(void) +{ + static int bit; + + if (m->machno != 0) + return; + if(m->ticks % (HZ/4) == 0) { + bit ^= 1; + if (bit) + lightbiton(Ledpulse); + else + lightbitoff(Ledpulse); + barriers(); + } + + if(securemem) + qtmclock(); + if(m->ticks % HZ == 0) { + intrs1sec = 0; + etherclock(); + } + barriers(); +} + +/* + * virtex 4 systems always have 128MB. + */ +uintptr +memsize(void) +{ + uintptr sz = 128*MB; + + return securemem? MEMTOP(sz): sz; +} + +void +meminit(void) +{ + ulong paddr; + ulong *vaddr; + static int ro[] = {0, -1}; + + /* size memory */ + securemem = gotsecuremem(); + memsz = memsize(); + conf.mem[0].npage = memsz / BY2PG; + conf.mem[0].base = PGROUND(PADDR(end)); + conf.mem[0].npage -= conf.mem[0].base/BY2PG; + + /* verify that it really is memory */ + for (paddr = ROUNDUP(PADDR(end), MB); paddr + BY2WD*(12+1) < memsz; + paddr += MB) { + vaddr = (ulong *)KADDR(paddr + BY2WD*12); /* a few bytes in */ + *vaddr = paddr; + barriers(); + dcflush(PTR2UINT(vaddr), BY2WD); + if (*vaddr != paddr) + panic("address %#lux is not memory", paddr); + } + + memset(end, 0, memsz - PADDR(end)); + sync(); +} + +void +ioinit(void) +{ + ledinit(); + addconf("console", "0"); + addconf("nobootprompt", "tcp"); +} + +int +archether(int ctlno, Ether *ether) +{ + if(ctlno > 0) + return -1; + + ether->type = "lltemac"; + ether->port = ctlno; + return 1; +} + +void +clrmchk(void) +{ + putesr(0); /* clears machine check */ +} + +/* + * the new kernel is already loaded at address `code' + * of size `size' and entry point `entry'. + */ +void +reboot(void *entry, void *code, ulong size) +{ + void (*f)(ulong, ulong, ulong); + + writeconf(); + shutdown(0); + + /* + * should be the only processor running now + */ + + print("shutting down...\n"); + delay(200); + + splhi(); + + /* turn off buffered serial console */ +// serialoq = nil; + + /* shutdown devices */ + devtabshutdown(); + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; + memmove(f, rebootcode, sizeof(rebootcode)); + sync(); + dcflush(PTR2UINT(f), sizeof rebootcode); + icflush(PTR2UINT(f), sizeof rebootcode); + + print("rebooting..."); + iprint("entry %#lux code %#lux size %ld\n", + PADDR(entry), PADDR(code), size); + delay(100); /* wait for uart to quiesce */ + + /* off we go - never to return */ + sync(); + dcflush(PTR2UINT(entry), size); + icflush(PTR2UINT(entry), size); + (*f)(PADDR(entry), PADDR(code), size); + + iprint("kernel returned!\n"); + archreboot(); +} diff -Nru /sys/src/9/vt4/clock.c /sys/src/9/vt4/clock.c --- /sys/src/9/vt4/clock.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/clock.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,132 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 1, /* system clock cycles per time base cycle */ + + Wp17= 0<<30, /* watchdog period (2^x clocks) */ + Wp21= 1<<30, + Wp25= 2<<30, + Wp29= 3<<30, + Wrnone= 0<<28, /* no watchdog reset */ + Wrcore= 1<<28, /* core reset */ + Wrchip= 2<<28, /* chip reset */ + Wrsys= 3<<28, /* system reset */ + Wie= 1<<27, /* watchdog interrupt enable */ + Pie= 1<<26, /* enable PIT interrupt */ + Fit9= 0<<24, /* fit period (2^x clocks) */ + Fit13= 1<<24, + Fit17= 2<<24, + Fit21= 3<<24, + Fie= 1<<23, /* fit interrupt enable */ + Are= 1<<22, /* auto reload enable */ + + /* dcr */ + Boot= 0x0F1, + Epctl= 0x0F3, + Pllmr0= 0x0F0, + Pllmr1= 0x0F4, + Ucr= 0x0F5, +}; + +void (*archclocktick)(void); /* set by arch*.c if used */ + +void +clockinit(void) +{ + long x; + + assert(m->cpuhz != 0); + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + assert(x != 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*(vlong)m->clockgen/1000))/(x*Timebase); + if((int)m->delayloop <= 0) + m->delayloop = 20000; + + x = (m->clockgen/Timebase)/HZ; +//iprint("initial PIT %ld\n", x); + assert(x != 0); + putpit(x); + puttsr(~0); + /* not sure if we want Wrnone or Wrcore */ +// puttcr(Pie|Are|Wie|Wrcore|Wp29); + puttcr(Pie|Are|Wrnone); +} + +void +clockintr(Ureg *ureg) +{ + /* PIT was set to reload automatically */ + puttsr(~0); + m->fastclock++; + timerintr(ureg, 0); + if(archclocktick != nil) + archclocktick(); +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->fastclock; +} + +ulong +µs(void) +{ + return fastticks2us(m->fastclock); +} + +void +timerset(uvlong) +{ +} + +ulong +perfticks(void) +{ + return (ulong)fastticks(nil); +} + +long +lcycles(void) +{ + return perfticks(); +} diff -Nru /sys/src/9/vt4/dat.h /sys/src/9/vt4/dat.h --- /sys/src/9/vt4/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/dat.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,304 @@ +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef u32int Mreg; /* Msr - bloody UART */ +typedef struct Page Page; +typedef struct FPsave FPsave; +typedef struct PMMU PMMU; +typedef struct Notsave Notsave; +typedef struct Proc Proc; +typedef struct Softtlb Softtlb; +typedef struct Sys Sys; +typedef uvlong Tval; +typedef struct Ureg Ureg; + +#pragma incomplete Ureg + +#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */ + +/* + * parameters for sysproc.c + */ +#define AOUT_MAGIC Q_MAGIC + +/* + * intc bits, as of 18 aug 2009. + * specific to rae's virtex4 design + * vanilla design defines Intuarttx. + */ +enum { + Bitllfifo, + Bittemac, + Bitdma, + Bitdma2, + Bituart, + Bitmiiphy, + Bitqtmmacfail, /* qtm only */ + Bitqtmdraminit, /* qtm only */ + + Intllfifo=1<pa|KZERO) +#define kmapinval() +#define kunmap(k) + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor [0*4] */ + ulong splpc; /* pc that called splhi() [1*4] */ + Proc *proc; /* current process on this processor [2*4] */ + uintptr pstlb; /* physical address of Mach.stlb [3*4] */ + int utlbhi; /* lowest tlb index in use by kernel [4*4] */ + int utlbnext; /* next tlb entry to use for user (round robin) [5*4] */ + int tlbfault; /* number of tlb i/d misses [6*4] */ + + /* ordering from here on irrelevant */ + + 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; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + Mach *me; /* debugging: should be my own address */ + + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + long vcohz; + long pllhz; + long plbhz; + long opbhz; + long epbhz; + long pcihz; + int cputype; + ulong delayloop; + uvlong cyclefreq; /* frequency of user readable cycle clock */ + + Mach *me2; /* debugging: should be my own address */ + + uvlong fastclock; + Perf perf; /* performance counters */ + + int tlbpurge; /* ... */ + int pfault; + int cs; + int syscall; + int load; + int intr; + int flushmmu; /* make current proc flush its mmu state */ + int ilockdepth; + + int lastpid; /* last TLB pid allocated on this machine */ + QLock stlblock; /* prevent context switch during tlb update */ + Softtlb* stlb; /* software tlb cache (see also pstlb above) */ + Proc* pidproc[NTLBPID]; /* which proc owns a given pid */ + + ulong spuriousintr; + int lastintr; + + ulong magic; /* debugging; also check for stack overflow */ + + /* MUST BE LAST */ + int stack[1]; +}; + +struct Softtlb { + u32int hi; /* tlb hi, except that low order 10 bits have (pid[8]<<2) */ + u32int lo; +}; + +struct +{ + Lock; + short machs; + short exiting; + short ispanic; + int thunderbirdsarego; /* lets the added processors continue to schedinit */ +}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]; +}; + +#define MACHP(n) ((Mach *)((int)&mach0 + (n)*BY2PG)) +extern Mach mach0; + +extern register Mach *m; +extern register Proc *up; + +/* + * Horrid. But the alternative is 'defined'. + */ +#ifdef _DBGC_ +#define DBGFLG (dbgflg[_DBGC_]) +#else +#define DBGFLG (0) +#endif /* _DBGC_ */ + +// #define DBG(...) if(DBGFLG) dbgprint(__VA_ARGS__) + +typedef struct { + ulong lasttm; /* last mutation start in seconds */ + ulong startticks; + ulong lastticks; + ulong count; + ulong totticks; + + ulong period; /* in seconds */ +} Mutstats; +extern Mutstats mutstats; + +char dbgflg[256]; +ulong intrs1sec; /* count interrupts in this second */ +uintptr memsz; +int okprint; +int securemem; +int vflag; + +#define dbgprint print /* for now */ diff -Nru /sys/src/9/vt4/dcr.c /sys/src/9/vt4/dcr.c --- /sys/src/9/vt4/dcr.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/dcr.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,41 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * we pre-compile an array of get/putdcr functions, + * later indexed by DCR number by getdcr and putdcr in l.s, + * because the DCR number is part of the instruction, + * and cannot be read from a register + */ + +#define MAXDCR 0x500 + +#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5)) +#define MTDCR(s,n) ((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1)) +#define MFDCR(n,t) ((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1)) +#define RETURN 0x4e800020 /* BR (LR) */ + +ulong _getdcr[MAXDCR][2]; +ulong _putdcr[MAXDCR][2]; + +void +dcrcompile(void) +{ + ulong *p; + int i; + + for(i=0; i= Qmax){ + unlock(&archwlock); + return nil; + } + + for(i=0; iqid.path){ + case Qdir: + return devdirread(c, a, n, archdir, narchdir, devgen); + + default: + if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + + return 0; +} + +static long +archwrite(Chan *c, void *a, long n, vlong offset) +{ + Rdwrfn *fn; + + if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + + return 0; +} + +Dev archdevtab = { + 'P', + "arch", + + devreset, + archinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +/* convert m->cputype and pvr register to a string in buf and return buf */ +char * +cputype2name(char *buf, int size) +{ + char *s, *es; + ulong cputype = getpvr(); + + s = buf; + es = buf + size; + /* ignoring 460, 8xx & 8xxx, e200*, e500* */ + switch (m->cputype) { + case 1: + seprint(s, es, "601"); + break; + case 3: case 6: case 7: + seprint(s, es, "603"); + break; + case 4: case 9: case 0xa: + seprint(s, es, "604"); + break; + case 8: case 0x7000: case 0x7002: + seprint(s, es, "G3 7xx"); + break; + case 0x000c: case 0x800c: case 0x8000: case 0x8001: + case 0x8002: case 0x8003: case 0x8004: + seprint(s, es, "G4 74xx"); + break; + case 0x39: case 0x3c: + seprint(s, es, "G5 970"); + break; + case 0x20: + seprint(s, es, "403"); + break; + case 0x1291: case 0x4011: case 0x41f1: case 0x5091: case 0x5121: + seprint(s, es, "405"); + break; + case 0x2001: /* 200 is Xilinx, 1 is ppc405 */ + cputype &= ~0xfff; + s = seprint(s, es, "Xilinx "); + switch (cputype) { + case 0x20010000: + seprint(s, es, "Virtex-II Pro 405"); + break; + case 0x20011000: + seprint(s, es, "Virtex 4 FX 405D5X2"); + break; + default: + seprint(s, es, "405"); + break; + } + break; + case 0x7ff2: + s = seprint(s, es, "Xilinx "); + if ((cputype & ~0xf) == 0x7ff21910) + seprint(s, es, "Virtex 5 FXT 440X5"); + else + seprint(s, es, "440"); + break; + default: + /* oddballs */ + if ((cputype & 0xf0000ff7) == 0x400008d4 || + (cputype & 0xf0000ffb) == 0x200008d0 || + (cputype & 0xf0000ffb) == 0x200008d8 || + (cputype & 0xfff00fff) == 0x53200891 || + (cputype & 0xfff00fff) == 0x53400890 || + (cputype & 0xfff00fff) == 0x53400891) { + seprint(s, es, "440"); + break; + } + cputype &= 0xf0000fff; + switch (cputype) { + case 0x40000440: case 0x40000481: case 0x40000850: + case 0x40000858: case 0x400008d3: case 0x400008db: + case 0x50000850: case 0x50000851: case 0x50000892: + case 0x50000894: + seprint(s, es, "440"); + break; + default: + seprint(s, es, "%#ux", m->cputype); + break; + } + break; + } + return buf; +} + +static long +cputyperead(Chan*, void *a, long n, vlong offset) +{ + char name[64], str[128]; + + cputype2name(name, sizeof name); + snprint(str, sizeof str, "PowerPC %s %lud\n", name, m->cpuhz / 1000000); + return readstr(offset, a, n, str); +} + +static long +tbread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + uvlong tb; + + cycles(&tb); + + snprint(str, sizeof(str), "%16.16llux", tb); + return readstr(offset, a, n, str); +} + +static long +nsread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + uvlong tb; + + cycles(&tb); + + snprint(str, sizeof(str), "%16.16llux", (tb/700)* 1000); + return readstr(offset, a, n, str); +} + +uvlong +fastns(void) +{ + return gettbl(); +} + +static long +mutread(Chan*, void *a, long n, vlong offset) +{ + char str[256]; + + snprint(str, sizeof str, "period (sec.s) %lud\n" + "last start sec. %lud\n" + "ticks of last mutation %lud\n" + "mutations %lud\n" + "ticks of average mutation %lud\n" + "border %#lux\n", + mutstats.period, mutstats.lasttm, mutstats.lastticks, + mutstats.count, mutstats.totticks / mutstats.count, + qtmborder()); + return readstr(offset, a, n, str); +} + +enum { + CMnow, + CMperiod, + CMstop, +}; + +Cmdtab mutmsg[] = { + CMnow, "now", 1, + CMperiod, "period", 2, + CMstop, "stop", 1, +}; + +static long +mutwrite(Chan*, void *a, long n, vlong /* offset */) +{ + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(a, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, mutmsg, nelem(mutmsg)); + switch(ct->index) { + case CMnow: + if (mutatetrigger() < 0) + error("mutate in progress"); + break; + case CMperiod: + mutstats.period = atoi(cb->f[1]); + break; + case CMstop: + mutstats.period = 0; + break; + default: + error(Ebadctl); + break; + } + poperror(); + free(cb); + return n; +} + +static long +intrsread(Chan*, void *a, long n, vlong offset) +{ + char *p; + + if(n == 0) + return 0; + p = malloc(READSTR); + intrfmtcounts(p, p + READSTR); + n = readstr(offset, a, n, p); + free(p); + return n; +} + +void +archinit(void) +{ + addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("timebase",0444, tbread, nil); + addarchfile("nsec", 0444, nsread, nil); + addarchfile("mutate", 0644, mutread, mutwrite); + addarchfile("intrs", 0444, intrsread, nil); +} diff -Nru /sys/src/9/vt4/devether.c /sys/src/9/vt4/devether.c --- /sys/src/9/vt4/devether.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/devether.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,561 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "../port/netif.h" + +#include "etherif.h" + +/* 9k compatibility */ +#define devno dev +#define iq in + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + int ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p != 0) + error(Ebadarg); + if(ctlrno < 0 || ctlrno >= MaxEther) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->devno = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->devno], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->devno], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->devno], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->devno], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->devno]; + 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->devno], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->devno], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->iq) <= 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->iq, 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)) != 0 && 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; + if(qpass(f->iq, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->iq, 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); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->devno]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + 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->devno]; + + 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 < Eaddrlen; 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[KNAMELEN], 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->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + +// if(ether->interrupt != nil) +// intrenable(ether->irq, ether->interrupt, ether, +// ether->tbdf, name); + /* done by the driver */ +// intrenable(Inttemac, ether->interrupt, "ether"); + + i = sprint(buf, "#l%d: %s: %dMbps port %#lux irq %d", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr %#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){ + /* + * our ether has no ring buffers, so make sure + * that we have adequate buffering. + */ + netifinit(ether, name, Ntypes, 1024*1024); + if(ether->oq == 0) + ether->oq = qopen(1024*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); +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown function\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +enum { + Pktwin = 60, /* must be ether activity within this interval, in sec.s */ +}; + +/* called from clock.c once per second */ +void +etherclock(void) +{ + Ether *ether = etherxx[0]; + static int cnt, oldin, oldout; + + if (++cnt < Pktwin) + return; + cnt = 0; + if ((ether->inpackets == oldin || ether->outpackets == oldout) && + ether->inpackets && ether->outpackets) { + print("no ether pkts in last %d s. ", Pktwin); + /* + * this is really unlikely, so probably the ethernet controller + * has silently wedged. whack it hard. + */ + whackether(ether); + } + oldin = ether->inpackets; + oldout = ether->outpackets; +} + +#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; +} + +void +dumpoq(Queue *oq) +{ + if (oq == nil) + print("no outq! "); + else if (qisclosed(oq)) + print("outq closed "); + else if (qfull(oq)) + print("outq full "); + else + print("outq %d ", qlen(oq)); +} + +void +dumpnetif(Netif *netif) +{ + print("netif %s ", netif->name); + print("limit %d mbps %d link %d ", + netif->limit, netif->mbps, netif->link); + print("inpkts %lld outpkts %lld errs %d\n", + netif->inpackets, netif->outpackets, + netif->crcs + netif->oerrs + netif->frames + netif->overflows + + netif->buffs + netif->soverflows); +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff -Nru /sys/src/9/vt4/devtab.c /sys/src/9/vt4/devtab.c --- /sys/src/9/vt4/devtab.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/devtab.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,46 @@ +/* + * Stub. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +extern Dev* devtab[]; + +void +devtabreset(void) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if (devtab[i]->reset == nil) + panic("corrupt memory: nil devtab[%d]->reset", i); + devtab[i]->reset(); + } +} + +void +devtabinit(void) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +devtabshutdown(void) +{ + int i; + + /* + * Shutdown in reverse order. + */ + for(i = 0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} diff -Nru /sys/src/9/vt4/dma.c /sys/src/9/vt4/dma.c --- /sys/src/9/vt4/dma.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/dma.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,476 @@ +/* + * Xilinx XPS Central DMA Controller v2.00b. + * + * addresses not need be aligned and lengths need not + * be multiples of 4, though `keyhole' operations (Sinc and Dinc + * are not both set) still operate on longs. + * + * dma transfers bypass all the usual processor (I & D) caches. + * memory-to-memory dma on the virtex 5 takes about 1µs per byte. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "io.h" + +enum { + Ndma = 2, + Rst = 0xa, /* magic rst value */ + + /* dmacr */ + Sinc = 1<<31, /* source increment */ + Dinc = 1<<30, /* dest increment */ + /* low bits are bytes per transfer; use 4 */ + + /* dmasr */ + Dmabsy = 1<<31, /* in progress */ + Dbe = 1<<30, /* dma bus error */ + + /* isr */ + De = 1<<1, /* dma error */ + Dd = 1<<0, /* dma done */ + + /* ier */ + Deie = 1<<1, /* dma error interrupt enable */ + Ddie = 1<<0, /* dma done interrupt enable */ +}; + +struct Dma { + ulong rst; /* sw reset (wo) */ + ulong dmacr; /* control */ + ulong sa; /* source addr */ + ulong da; /* dest addr */ + ulong length; /* bytes to transfer */ + ulong dmasr; /* status (ro) */ + ulong pad[5]; + ulong isr; /* interrupt status (read, toggle on write) */ + ulong ier; /* interrupt enable */ +}; + +typedef struct { + Dma *regs; + Lock; + Rendez; + int done; /* set true briefly upon dma completion */ + int busy; /* dma channel in use? */ + int chan; + + int flags; + void *dest; + uintptr len; + + char name[16]; + void (*donenotify)(int); +} Ctlr; + +int dmaready; /* flag: ready for general use? */ + +static int dmainited; +static Ctlr dmas[Ndma]; + +static void dmaprocinit(Ctlr *); +static int dmaintr(ulong bit); + +static void +dmadump(Ctlr *ctlr, Dma *dma) +{ + if (!dmaready) + return; + iprint("len\t%#lux\n", ctlr->len); + iprint("\n"); + iprint("dmacr\t%#lux\n", dma->dmacr); + iprint("sa\t%#lux\n", dma->sa); + iprint("da\t%#lux\n", dma->da); + iprint("length\t%#lux\n", dma->length); + iprint("dmasr\t%#lux\n", dma->dmasr); + iprint("isr\t%#lux\n", dma->isr); + iprint("ier\t%#lux\n", dma->ier); +} + +static void +dmaresetdump(ulong bit, Dma *dma) +{ + Ctlr *ctlr; + + if (!dmaready) + return; + ctlr = (bit == Intdma? dmas: &dmas[1]); + iprint("\n%s chan %d reset dump:\n", ctlr->name, ctlr->chan); + dmadump(ctlr, dma); +} + +static void +dmareset(ulong bit) +{ + Dma *dma; + + dma = (Dma *)(bit == Intdma? Dmactlr: Dmactlr2); +// dmaresetdump(bit, dma); + if (dmaready) /* normal case */ + dmaintr(bit); /* try this instead of a reset */ + else { /* early: just whack it */ + dma->ier = 0; + barriers(); + dma->rst = Rst; + barriers(); + while (dma->dmasr & Dmabsy) + ; + } +} + +static int +dmaisdone(void *vctlr) +{ + return ((Ctlr *)vctlr)->done; +} + +/* + * we wait for wakeups from dmaintr so that ctlr->donenotify can + * be called from a non-interrupt context. + */ +static void +dmawaitproc(void *vctlr) +{ + int n; + Ctlr *ctlr; + + ctlr = vctlr; + n = ctlr - dmas; + for (;;) { + sleep(ctlr, dmaisdone, ctlr); + ilock(ctlr); + ctlr->done = 0; /* acknowledge previous transfer done */ + barriers(); + iunlock(ctlr); + ctlr->donenotify(n); + } +} + +static int +dmaintr(ulong bit) +{ + ulong sts; + Ctlr *ctlr; + Dma *dma; + + if (bit == Intdma) + ctlr = &dmas[0]; + else if (bit == Intdma2) + ctlr = &dmas[1]; + else { + SET(ctlr); + panic("dmaintr: bit %#lux not Intdma*", bit); + } + + if (!dmainited) + panic("dmaintr: dma not yet initialised"); + dma = ctlr->regs; + sts = dma->isr; + if ((sts & (Dd | De)) == 0) + return 0; /* spurious */ + + if (dma->dmasr & Dbe) { + iprint("dmaintr: bus error, resetting\n"); + dmareset(bit); + return 0; + } else if (sts & De) { + iprint("dmaintr: error, resetting\n"); + dmareset(bit); + return 0; + } else if (!(sts & Dd)) + iprint("dmaintr: not done\n"); + else if (dma->dmasr & Dmabsy) + iprint("dmaintr: still busy\n"); + else if (ctlr->done) + iprint("dmaintr: ctlr->done already set\n"); + else { + ctlr->done = 1; /* signal dmawaitproc */ + ctlr->busy = 0; + barriers(); + wakeup(ctlr); /* awaken dmawaitproc */ + } + dma->isr = sts; /* extinguish intr source */ + barriers(); + intrack(bit); + return 1; +} + +/* have to delay starting kprocs until later */ +static void +dma1init(Ctlr *ctlr, Dma *dma) +{ + int chan; + ulong bit; + + ctlr->regs = dma; + chan = ctlr - dmas; + ctlr->chan = chan; + snprint(ctlr->name, sizeof ctlr->name, "dma%d", chan); + bit = dma == (Dma *)Dmactlr? Intdma: Intdma2; + dmareset(bit); + intrenable(bit, dmaintr, ctlr->name); +} + +void +dma0init(void) +{ + int i; + + for (i = 0; i < nelem(dmas); i++) + dma1init(&dmas[i], (Dma *)(i == 0? Dmactlr: Dmactlr2)); + dmainited = 1; + /* have to delay starting kprocs until later */ +} + +/* finally start kprocs and exercise the dma controllers */ +void +dmainit(void) +{ + int i; + + for (i = 0; i < nelem(dmas); i++) + dmaprocinit(&dmas[i]); + dmaready = 1; +} + +/* + * it's convenient if this works even with interrupts masked, + * though the time-out won't work in that situation. + */ +void +dmawait(int n) +{ + uvlong end; + Ctlr *ctlr; + Dma *dma; + static int first = 1; + + ctlr = &dmas[n]; + ilock(ctlr); + if (first) { + first = 0; + ctlr->done = 1; /* suppress "no dma interrupt" msg */ + } + if (!ctlr->busy) { + iunlock(ctlr); + return; + } + iunlock(ctlr); + + dma = ctlr->regs; + if (dma == nil) + panic("dmawait: ctlr->regs not yet set"); + end = m->fastclock + 2; /* ticks */ + while (dma->dmasr & Dmabsy && m->fastclock < end) + ; + + ilock(ctlr); + if (dma->dmasr & Dmabsy) { + iprint("dma still busy after 2 ticks\n"); + return; + } + ctlr->busy = 0; /* previous transfer done */ + barriers(); + iunlock(ctlr); +} + +void +dmatestwait(int n) +{ + uvlong end; + Ctlr *ctlr; + Dma *dma; + static int first = 1; + + ctlr = &dmas[n]; + ilock(ctlr); + if (first) { + ctlr->done = 1; /* suppress "no dma interrupt" msg */ + first = 0; + } + if (ctlr->done) { + ctlr->busy = 0; + barriers(); + } + iunlock(ctlr); + + dma = ctlr->regs; + if (dma == nil) + panic("dmatestwait: ctlr->regs not yet set"); + end = m->fastclock + 2; + while (dma && dma->dmasr & Dmabsy && m->fastclock < end) + ; + + ilock(ctlr); + if (dma && dma->dmasr & Dmabsy) + iprint("dma still busy after 2 ticks\n"); + else { + ctlr->busy = 0; + ctlr->done = 0; /* acknowledge previous transfer done */ + barriers(); + } + iunlock(ctlr); +} + +static void +dmanoop(int) +{ +} + +static void +dmacpy(int n, void *dest, void *src, ulong len, ulong flags) +{ + if (dmastart(n, dest, src, len, flags, dmanoop)) + dmatestwait(n); + else + iprint("can't start dma%d\n", n); +} + +static int pass; + +static int +trydma(Ctlr *ctlr, uintptr adest, uintptr asrc, int size) +{ + void *dest = (void *)adest, *src = (void *)asrc; + + ilock(ctlr); + assert(size > 4); + barriers(); + dcflush(PTR2UINT(src), size); + + memset(dest, 0, size); + barriers(); + dcflush(PTR2UINT(dest), size); + + ctlr->busy = 0; + iunlock(ctlr); + + dmacpy(ctlr - dmas, dest, src, size, Sinc | Dinc); + barriers(); + dcflush(PTR2UINT(dest), size); + + /* leave initialised for next use */ + ilock(ctlr); + ctlr->busy = 0; + iunlock(ctlr); + + if (memcmp(src, dest, size) != 0) { + print("\n%s: pass %d: copied dram incorrectly!\n", + ctlr->name, pass); + + print("%ux: ", (ushort)(uintptr)src); + dump(src, size / BY2WD); + + print("%ux: ", (ushort)(uintptr)dest); + dump(dest, size / BY2WD); + + panic("buggy %s", ctlr->name); + } + pass++; + return 0; +} + +/* assumes dmaprocinit has been started */ +static void +dmatest(Ctlr *ctlr, uintptr testpat, uintptr testtarg, uint size) +{ + int fail; + + assert((uintptr)testtarg % BY2WD == 0); + assert((uintptr)testpat % BY2WD == 0); + barriers(); + pass = 0; + fail = trydma(ctlr, testtarg, testpat, size); + fail |= trydma(ctlr, testtarg+BY2WD, testpat, size-BY2WD); + fail |= trydma(ctlr, testtarg, testpat+BY2WD, size-BY2WD); + fail |= trydma(ctlr, testtarg+BY2WD, testpat+BY2WD, size-BY2WD); + barriers(); + if (fail) + print("failed..."); + else + print("ok..."); +} + +enum { + Size = 32, + Pat = PHYSSRAM + 64*1024, /* step on middle of sram (9load) */ + Targ = Pat + 2*Size, +}; + +static ulong testtarg[8]; + +static void +dmaprocinit(Ctlr *ctlr) +{ + char name[32]; + static ulong testpat[8] = { + 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, + 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, + }; + + snprint(name, sizeof name, "%swait", ctlr->name); + kproc(name, dmawaitproc, ctlr); + + /* + * verify correct functioning + */ + print("%s: ", ctlr->name); + print("dram "); + dmatest(ctlr, (uintptr)testpat, (uintptr)testtarg, sizeof testpat); + print("sram "); + dmatest(ctlr, Pat, Targ, Size); + print("\n"); +} + +int +dmastart(int n, void *dest, void *src, ulong len, ulong flags, + void (*donenotify)(int)) +{ + Ctlr *ctlr; + Dma *dma; + + if (!dmainited) { + iprint("dmastart: dma not yet inited\n"); + return 0; + } + ctlr = &dmas[n]; + dma = ctlr->regs; + if (dma->dmasr & Dmabsy) { + iprint("dmastart: dma still in progress for %s\n", ctlr->name); + dmawait(n); + } + + ilock(ctlr); + if (ctlr->busy) { + iunlock(ctlr); + iprint("dmastart: ctlr busy for %s\n", ctlr->name); + dmawait(n); + ilock(ctlr); + } + assert(!(dma->dmasr & Dmabsy)); + assert(!ctlr->busy); + ctlr->donenotify = donenotify; + ctlr->flags = flags; + ctlr->dest = dest; + ctlr->len = len; + ctlr->busy = 1; + barriers(); + + if (flags & Sinc) + dcflush(PTR2UINT(src), len); + if (flags & Dinc) + dcflush(PTR2UINT(dest), len); + dma->dmacr = flags; /* granularity in v2 is 1 byte */ + dma->sa = PADDR(src); + dma->da = PADDR(dest); + dma->ier = Deie | Ddie; + barriers(); + dma->length = len; /* start dma */ + barriers(); + + iunlock(ctlr); + return 1; +} diff -Nru /sys/src/9/vt4/etherif.h /sys/src/9/vt4/etherif.h --- /sys/src/9/vt4/etherif.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/etherif.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,49 @@ +enum +{ + MaxEther = 2, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int tbusy; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + int (*interrupt)(ulong); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + /* statistics */ + ulong interrupts; + ulong dmarxintr; + ulong dmatxintr; + ulong promisc; + ulong pktsdropped; + ulong pktsmisaligned; + ulong resets; /* after initialisation */ + ulong bcasts; /* broadcast pkts rcv'd */ + ulong mcasts; /* multicast pkts rcv'd */ + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff -Nru /sys/src/9/vt4/etherlltemac.c /sys/src/9/vt4/etherlltemac.c --- /sys/src/9/vt4/etherlltemac.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/etherlltemac.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,1117 @@ +/* + * Xilinx XPS LL Temac Ethernet v1.00b + XPS LL Local Link FIFOs v1.00a driver. + * + * It uses the Central DMA controller. + * There are two interfaces per Temac controller. + * Each Temac interface is connected to a pair of FIFOs (tx and rx). + * Half-duplex is not supported by hardware. + * + * NB: The LL FIFO requires that we copy the data for a single packet + * into the FIFO, then write the frp->tlr register before writing any + * more data into the FIFO. It forbids copying the data for multiple + * packets into the FIFO and then writing frp->tlr with the length of each. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../ip/ip.h" /* to declare ethermedium */ + +#include "ethermii.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "io.h" + +enum { /* directly-addressible registers' bits */ + /* raf register */ + Htrst = 1<<0, /* hard temac reset (both ifcs) */ + Mcstrej = 1<<1, /* reject received multicast dest addr */ + Bcstrej = 1<<2, /* reject received broadcast dest addr */ + + /* is, ip, ie register */ + Hardacscmplt = 1<<0, /* hard register access complete */ + Autoneg = 1<<1, /* auto-negotiation complete */ + Rxcmplt = 1<<2, /* receive complete */ + Rxrject = 1<<3, /* receive frame rejected (unknown m'cast?) */ + Rxfifoovr = 1<<4, /* receive fifo overrun */ + Txcmplt = 1<<5, /* transmit complete */ + Rxdcmlock = 1<<6, /* receive DCM lock (ready for use) */ + + /* ctl register */ + Wen = 1<<15, /* write instead of read */ + + /* ctl register address codes; select other registers */ + Rcw0 = 0x200, /* receive configuration: pause addr low */ + Rcw1 = 0x240, /* ": misc bits + pause addr high */ + Tc = 0x280, /* tx config */ + Fcc = 0x2c0, /* flow control */ + Emmc = 0x300, /* ethernet mac mode config */ + Phyc = 0x320, /* rgmii/sgmii config */ + Mc = 0x340, /* mgmt config */ + Uaw0 = 0x380, /* unicast addr word 0 (low-order) */ + Uaw1 = 0x384, /* unicast addr word 1 (high-order) */ + Maw0 = 0x388, /* multicast addr word 0 (low) */ + Maw1 = 0x38c, /* multicast addr word 1 (high + more) */ + Afm = 0x390, /* addr filter mode */ + Tis = 0x3a0, /* intr status */ + Tie = 0x3a4, /* intr enable */ + Miimwd = 0x3b0, /* mii mgmt write data */ + Miimai = 0x3b4, /* mii mgmt access initiate */ + + /* rdy register */ + Fabrrr = 1<<0, /* fabric read ready */ + Miimrr = 1<<1, /* mii mgmt read ready */ + Miimwr = 1<<2, /* mii mgmt write ready */ + Afrr = 1<<3, /* addr filter read ready */ + Afwr = 1<<4, /* addr filter write ready */ + Cfgrr = 1<<5, /* config reg read ready */ + Cfgwr = 1<<6, /* config reg write ready */ + Hardacsrdy = 1<<16, /* hard reg access ready */ +}; +enum { /* indirectly-addressible registers' bits */ + /* Rcw1 register */ + Rst = 1<<31, /* reset */ + Jum = 1<<30, /* jumbo frame enable */ + Fcs = 1<<29, /* in-band fcs enable */ + Rx = 1<<28, /* rx enable */ + Vlan = 1<<27, /* vlan frame enable */ + Hd = 1<<26, /* half-duplex mode (must be 0) */ + Ltdis = 1<<25, /* length/type field valid check disable */ + + /* Tc register. same as Rcw1 but Rx->Tx, Ltdis->Ifg */ + Tx = Rx, /* tx enable */ + Ifg = Ltdis, /* inter-frame gap adjustment enable */ + + /* Fcc register */ + Fctx = 1<<30, /* tx flow control enable */ + Fcrx = 1<<29, /* rx flow control enable */ + + /* Emmc register */ + Linkspeed = 3<<30, /* field */ + Ls1000 = 2<<30, /* Gb */ + Ls100 = 1<<30, /* 100Mb */ + Ls10 = 0<<30, /* 10Mb */ + Rgmii = 1<<29, /* rgmii mode enable */ + Sgmii = 1<<28, /* sgmii mode enable */ + Gpcs = 1<<27, /* 1000base-x mode enable */ + Hostifen= 1<<26, /* host interface enable */ + Tx16 = 1<<25, /* tx 16-bit (vs 8-bit) data ifc enable (0) */ + Rx16 = 1<<24, /* rx 16-bit (vs 8-bit) data ifc enable (0) */ + + /* Phyc register. sgmii link speed is Emmc's Linkspeed. */ + Rgmiills = 3<<2, /* field */ + Rls1000 = 2<<2, /* Gb */ + Rls100 = 1<<2, /* 100Mb */ + Rls10 = 0<<2, /* 10Mb */ + Rgmiihd = 1<<1, /* half-duplex */ + Rgmiilink = 1<<0, /* rgmii link (is up) */ + + /* Mc register */ + Mdioen = 1<<6, /* mdio (mii mgmt) enable */ + + /* Maw1 register */ + Rnw = 1<<23, /* multicast addr table reg read (vs write) */ + Addr = 3<<16, /* field */ + + /* Afm register */ + Pm = 1<<31, /* promiscuous mode */ + + /* Tis, Tie register (*rst->*en) */ + Fabrrst = 1<<0, /* fabric read intr sts (read done) */ + Miimrst = 1<<1, /* mii mgmt read intr sts (read done) */ + Miimwst = 1<<2, /* mii mgmt write intr sts (write done) */ + Afrst = 1<<3, /* addr filter read intr sts (read done) */ + Afwst = 1<<4, /* addr filter write intr sts (write done) */ + Cfgrst = 1<<5, /* config read intr sts (read done) */ + Cfgwst = 1<<6, /* config write intr sts (write done) */ +}; + +enum { + /* tunable parameters */ + Defmbps = 1000, /* default Mb/s */ + Defls = Ls1000, /* must match Defmbps */ +}; + +enum { /* fifo's registers' bits */ + /* isr, ier registers (*?e -> *ee) */ + Rpure = 1<<31, /* rx packet underrun read error */ + Rpore = 1<<30, /* rx packet overrun read error */ + Rpue = 1<<29, /* rx packet underrun error */ + Tpoe = 1<<28, /* tx packet overrun error */ + FifoTc = 1<<27, /* tx complete */ + Rc = 1<<26, /* rx complete */ + Tse = 1<<25, /* tx size error */ + Trc = 1<<24, /* tx reset complete */ + Rrc = 1<<23, /* rx reset complete */ + + Errors = Rpure | Rpore | Rpue | Tpoe | Tse, +}; +enum { /* fifo constants */ + Chan0, /* dedicated to input */ + Chan1, /* dedicated to output */ + + Reset = 0xa5, /* magic [tr]dfr & llr value */ + + /* dmacr; copied from dma.c */ + Sinc = 1<<31, /* source increment */ + Dinc = 1<<30, /* dest increment */ + + /* field masks */ + Bytecnt = (1<<11) - 1, + Wordcnt = (1<<9) - 1, +}; + +typedef struct Temacctlr Temacctlr; +typedef struct Temacregs Temacregs; +typedef struct Llfiforegs Llfiforegs; + +struct Temacregs { + ulong raf; /* reset & addr filter */ + ulong tpf; /* tx pause frame */ + ulong ifgp; /* tx inter-frame gap adjustment */ + ulong is; /* intr status */ + ulong ip; /* intr pending */ + ulong ie; /* intr enable */ + ulong pad[2]; + + ulong msw; /* msw data; shared by ifcs */ + ulong lsw; /* lsw data; shared */ + ulong ctl; /* control; shared */ + ulong rdy; /* ready status */ + ulong pad2[4]; +}; +struct Temacctlr { /* software state */ + Temacregs *regs; + Lock; + ushort active; + + /* tx state, for Block being sent */ + int txdma; + Block *tbp; /* non-nil if dma to fifo in progress */ + + /* rx state, for packet being read */ + int rxdma; + long rlf; /* read from frp->rlf iff non-negative */ + ulong fifoier; + Block *rbp; /* non-nil if dma from fifo in progress */ +}; + +struct Llfiforegs { + ulong isr; /* intr status */ + ulong ier; /* intr enable */ + + ulong tdfr; /* tx data fifo reset */ + ulong tdfv; /* tx data fifo vacancy (words free) */ + ulong tdfd; /* tx data fifo write port */ + ulong tlf; /* tx length fifo */ + + ulong rdfr; /* rx data fifo reset */ + ulong rdfo; /* rx data fifo occupancy */ + ulong rdfd; /* rx data fifo read port */ + ulong rlf; /* rx length fifo */ + + ulong llr; /* locallink reset */ +}; + +typedef struct { + ulong bit; + char *msg; +} Error; + +extern int dmaready; /* flag: ready for general use? */ +extern uchar mymac[Eaddrlen]; + +static Error errs[] = { + Rpure, "rx pkt underrun read", + Rpore, "rx pkt overrun read", + Rpue, "rx pkt underrun", + Tpoe, "tx pkt overrun", + Tse, "tx size", +}; + +static Ether *ethers[1]; /* only first ether is connected to a fifo */ +static Llfiforegs *frp = (Llfiforegs *)Llfifo; + +int fifointr(ulong bit); + +static void fiforeset(Ether *ether); +static int dmatxstart(Ether *); + +static void +getready(Temacregs *trp) +{ + while ((trp->rdy & Hardacsrdy) == 0) + ; +} + +static ulong +rdindir(Temacregs *trp, unsigned code) +{ + ulong val; + + getready(trp); + trp->ctl = code; + barriers(); + + getready(trp); + val = trp->lsw; + return val; +} + +static int +wrindir(Temacregs *trp, unsigned code, ulong val) +{ + getready(trp); + trp->lsw = val; + barriers(); + trp->ctl = Wen | code; + barriers(); + + getready(trp); + return 0; +} + +/* + * we're only interested in temac errors; completion interrupts come + * from the fifo. + */ +static int +temacintr(ulong bit) +{ + int e, forme, sts; + Ether *ether; + Temacctlr *ctlr; + Temacregs *trp; + + forme = 0; + for (e = 0; e < nelem(ethers); e++) { + ether = ethers[e]; + if (ether == nil) + continue; + ether->interrupts++; + ctlr = ether->ctlr; + trp = ctlr->regs; + while ((sts = trp->is) & (Autoneg | Rxfifoovr)) { + forme = 1; + if (sts & Rxfifoovr) { + iprint("temac: receive fifo overrun. "); + whackether(ether); + } + if (sts & Autoneg) { + iprint("temac: autoneg done\n"); + trp->is = Autoneg; /* extinguish intr source */ + barriers(); + } + } + } + if (forme) + intrack(bit); + return forme; +} + +static void +resetsw(Ether *ether) /* reset software state */ +{ + Temacctlr *ctlr; + + ctlr = ether->ctlr; + + delay(20); /* let any dma in progress complete */ + ctlr->active = ctlr->txdma = ctlr->rxdma = 0; + ctlr->rlf = -1; + ctlr->rbp = ctlr->tbp = nil; + barriers(); +} + +static void +reset(Ether *ether) +{ + Temacctlr *ctlr; + Temacregs *trp; + + ctlr = ether->ctlr; + trp = ctlr->regs; + assert(trp); + + trp->raf = Htrst; /* resets both ether interfaces */ + barriers(); + while (trp->raf & Htrst) + ; + + fiforeset(ether); + barriers(); + + resetsw(ether); +} + +static void +attach(Ether *) +{ +} + +static void +closed(Ether *ether) +{ + Temacctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + reset(ether); + ctlr->active = 0; + } +} + +static void +temacshutdown(Ether* ether) +{ + reset(ether); /* stop dma at the very least */ +} + +static int promon, multion; + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Temacctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + ilock(ctlr); + promon = on; + if(on) { + ether->promisc = 1; + wrindir(ctlr->regs, Afm, Pm); + } else if (!multion) { + ether->promisc = 0; + wrindir(ctlr->regs, Afm, 0); + } + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Temacctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + multion = on; + ilock(ctlr); + /* + * could do better: set Maw[01] & Rnw since ipv6 needs multicast; + * see netmulti() in netif.c. + */ + USED(addr); + if(on) { + ether->promisc = 1; + wrindir(ctlr->regs, Afm, Pm); /* overkill */ + } else if (!promon) { + ether->promisc = 0; + wrindir(ctlr->regs, Afm, 0); + } + iunlock(ctlr); +} + +/* + * start next dma into tx fifo, if there's a pkt in the output queue, + * room in the tx fifo, and the dma channel is idle. + * + * called for each new packet, but okay to send + * any and all packets in output queue. + */ +void +temactransmit(Ether *ether) +{ + Temacctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + dmatxstart(ether); + iunlock(ctlr); +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +Block* +clallocb(void) +{ + Block *bp; + + /* round up for sake of word-at-a-time dma */ + bp = iallocb(ROUNDUP(ETHERMAXTU, BY2WD) + DCACHELINESZ-1); + if(bp == nil) + return bp; + dcflush(PTR2UINT(bp->base), BALLOC(bp)); + bp->wp = bp->rp = (uchar*) + ((PTR2UINT(bp->base) + DCACHELINESZ - 1) & ~(DCACHELINESZ-1)); + return bp; +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + + if(n == 0) + return 0; + + p = malloc(READSTR); + + len = snprint(p, READSTR, "interrupts: %lud\n", ether->interrupts); + len += snprint(p+len, READSTR-len, "dma rx intrs: %lud\n", + ether->dmarxintr); + len += snprint(p+len, READSTR-len, "dma tx intrs: %lud\n", + ether->dmatxintr); + len += snprint(p+len, READSTR-len, "broadcasts rcvd: %lud\n", + ether->bcasts); + len += snprint(p+len, READSTR-len, "multicasts rcvd: %lud\n", + ether->mcasts); + len += snprint(p+len, READSTR-len, "promiscuous: %lud\n", + ether->promisc); + len += snprint(p+len, READSTR-len, "dropped pkts: %lud\n", + ether->pktsdropped); + len += snprint(p+len, READSTR-len, "resets: %lud\n", ether->resets); + snprint(p+len, READSTR-len, "misaligned buffers: %lud\n", + ether->pktsmisaligned); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +init(Ether *ether) +{ + int i; + ulong ealo, eahi; + uvlong ea; + Temacctlr *ctlr; + Temacregs *trp; + static uchar pausemac[Eaddrlen] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x01 }; + + trp = (Temacregs *)Temac + ether->ctlrno; + ctlr = ether->ctlr; + + wrindir(trp, Mc, Mdioen | 29); /* 29 is divisor; see p.47 of ds537 */ + delay(100); /* guess */ + + /* + * mac addr is stored little-endian in longs in Uaw[01]. + * default address is rubbish. + */ + memmove(ether->ea, mymac, Eaddrlen); + ea = 0; + for (i = 0; i < Eaddrlen; i++) + ea |= (uvlong)mymac[i] << (i * 8); + wrindir(trp, Uaw0, (ulong)ea); + wrindir(trp, Uaw1, (ulong)(ea >> 32)); + ealo = rdindir(trp, Uaw0); + eahi = rdindir(trp, Uaw1) & 0xffff; + if (ealo != (ulong)ea || eahi != (ulong)(ea >> 32)) + panic("temac mac address wouldn't set, got %lux %lux", + eahi, ealo); + + /* + * admit broadcast packets too + */ + wrindir(trp, Maw0, ~0ul); + wrindir(trp, Maw1, 0xffff); /* write to mat reg 0 */ + + wrindir(trp, Afm, 0); /* not promiscuous */ + wrindir(trp, Tc, Tx); + ether->promisc = 0; + +if (0) { + ea = 0; + for (i = 0; i < Eaddrlen; i++) + ea |= (uvlong)pausemac[i] << (i * 8); + wrindir(trp, Rcw0, (ulong)ea); + wrindir(trp, Rcw1, (ulong)(ea >> 32) | Rx); + wrindir(trp, Fcc, Fcrx); /* honour pause frames sent to us */ +} else + wrindir(trp, Fcc, 0); /* no flow control */ + + wrindir(trp, Emmc, Defls); + + ctlr->active = 1; + barriers(); + + intrenable(Inttemac, temacintr, "lltemac"); + trp->ie = Autoneg | Rxfifoovr; + barriers(); + + fifoinit(ether); +} + +int +temacreset(Ether* ether) +{ + Temacctlr *ctlr; + Temacregs *trp; + + if ((unsigned)ether->ctlrno >= nelem(ethers) || ethers[ether->ctlrno]) + return -1; /* already probed & found */ + trp = (Temacregs *)Temac + ether->ctlrno; + + /* too early to probe on virtex 4 at least; up must be set first */ +// if (probeaddr((uintptr)trp) < 0) +// return -1; + + ethers[ether->ctlrno] = ether; + ether->ctlr = ctlr = malloc(sizeof *ctlr); + ctlr->regs = trp; + ether->port = (uintptr)trp; + trp->ie = 0; + barriers(); + ether->interrupts = 0; /* should be redundant */ + + /* we can't tell, so assert what we hope for */ + ether->mbps = Defmbps; + ether->fullduplex = ether->link = 1; + + /* + * whatever loaded this kernel (9load or another kernel) + * must have configured the ethernet in order to use it to load + * this kernel. so try not to reset the hardware, which could + * force a seconds-long link negotiation. + */ + reset(ether); +// resetsw(ether); + init(ether); + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->closed = closed; + ether->shutdown = temacshutdown; + ether->transmit = temactransmit; + ether->interrupt = temacintr; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + return 0; +} + +void +etherlltemaclink(void) +{ + addethercard("lltemac", temacreset); +} + +/* + * Xilinx Local Link FIFOs for Temac, in pairs (rx and tx). + */ + +/* + * as of dma controller v2, keyhole operations are on ulongs, + * but otherwise it's as if memmove were used. + * addresses need not be word-aligned, though registers are. + */ +static void +fifocpy(void *vdest, void *vsrc, uint bytes, ulong flags) +{ + int words; + ulong *dest, *src; + uchar buf[ETHERMAXTU + 2*BY2WD]; + + dest = vdest; + src = vsrc; + words = bytes / BY2WD; + if (bytes % BY2WD != 0) + words++; + + switch (flags & (Sinc | Dinc)) { + case Sinc | Dinc: + memmove(vdest, vsrc, bytes); + break; + case Sinc: /* mem to register */ + memmove(buf, vsrc, bytes); /* ensure src alignment */ + src = (ulong *)buf; + while (words-- > 0) + *dest = *src++; + break; + case Dinc: /* register to mem */ + dest = (ulong *)buf; + while (words-- > 0) + *dest++ = *src; + memmove(vdest, buf, bytes); /* ensure dest alignment */ + break; + case 0: /* register-to-null or vice versa */ + while (words-- > 0) + *dest = *src; + break; + } +} + +/* returns true iff we whacked the ether */ +static int +whackiferr(Ether *ether, int whack, int sts) +{ + Error *ep; + + /* + * these require a reset of the receive logic: Rpure, Rpore, Rpue. + * same for transmit logic: Tpoe, Tse. + */ + if (whack || sts & Errors) { + iprint("fifo: errors:"); + for (ep = errs; ep < errs + nelem(errs); ep++) + if (sts & ep->bit) + iprint(" %s;", ep->msg); + iprint("\n"); + whackether(ether); + return 1; + } + return 0; +} + +static int dmarecv(Ether *); + +static void +fifointrset(Temacctlr *ctlr) +{ + frp->ier = ctlr->fifoier; + barriers(); +} + +static void +enafifointr(Temacctlr *ctlr, int bits) +{ + ctlr->fifoier |= bits; + fifointrset(ctlr); +} + +static void +disfifointr(Temacctlr *ctlr, int bits) +{ + ctlr->fifoier &= ~bits; + fifointrset(ctlr); +} + +static long +getrdfo(void) +{ + ulong rdfo; + + rdfo = frp->rdfo; + if (rdfo & ~Wordcnt) { + iprint("fifo: impossible rdfo value\n"); + return -1; + } + return rdfo; +} + +static void +dmarxdone(int) +{ + int whack, multi; + long rdfo; + Block *bp; + Ether *ether; + Etherpkt *pkt; + Temacctlr *ctlr; + + ether = ethers[0]; + ctlr = ether->ctlr; + + ilock(ctlr); + ether->dmarxintr++; + bp = ctlr->rbp; + ctlr->rbp = nil; /* prevent accidents */ + if (ctlr->rxdma == 0 || bp == nil) { + if (bp != nil) + freeb(bp); + if (ctlr->rxdma == 0) + iprint("dmarxdone: no rx dma in progress\n"); + else + iprint("dmarxdone: no block for rx dma just finished!\n"); + lightbitoff(Ledethinwait); + enafifointr(ctlr, Rc); + iunlock(ctlr); + return; + } + ctlr->rxdma = 0; + barriers(); + + /* + * rx Step 2: packet is in Block, pass it upstream. + */ + /* could check for dma errors */ + pkt = (Etherpkt*)bp->rp; + assert(pkt != nil); + multi = pkt->d[0] & 1; + if (multi) + if(memcmp(pkt->d, ether->bcast, sizeof pkt->d) == 0) + ether->bcasts++; + else + ether->mcasts++; + + etheriq(ether, bp, 1); + lightbitoff(Ledethinwait); + + /* + * rx Step 3/0: if there's another packet in the rx fifo, + * start dma into a new Block, else reenable recv intrs. + */ + whack = 0; + rdfo = getrdfo(); + if (rdfo < 0) + whack = 1; /* ether is buggered */ + else if (rdfo > 0) /* more packets in rx fifo? */ + whack = dmarecv(ether); /* if dma starts, disables Rc */ + else { + if (frp->isr & Rc) + wave('|'); /* isr Rc was set when fifo was empty */ + enafifointr(ctlr, Rc); + } + /* if this whacks the ctlr, all the intr enable bits will be set */ + whackiferr(ether, whack, frp->isr); + iunlock(ctlr); +} + +static void +discard(Ether *ether, unsigned ruplen) +{ + ulong null; + + /* discard the rx fifo's packet */ + fifocpy(&null, &frp->rdfd, ruplen, 0); + ether->pktsdropped++; +} + +/* + * called when interrupt cause Rc is set (the rx fifo has at least one packet) + * to begin dma to memory of the next packet in the input fifo. + * + * returns true iff the ether ctlr needs to be whacked. + * may be called from interrupt routine; must be called with + * interrupts masked. + */ +static int +dmarecv(Ether *ether) +{ + long rdfo; + ulong len, ruplen; + Block *bp; + Temacctlr *ctlr; + + ctlr = ether->ctlr; + if (ctlr->rxdma) + return 0; /* next rx dma interrupt should process this packet*/ + + /* ignore frp->isr & Rc; just look at rx fifo occupancy */ + rdfo = getrdfo(); + if (rdfo < 0) { + iprint("dmarecv: negative rdfo\n"); + return 1; /* ether is buggered */ + } + if (rdfo == 0) + return 0; /* no packets in the rx fifo */ + + if (!(frp->isr & Rc)) + wave('@'); /* isr Rc wasn't set when fifo had stuff in it */ + + /* + * We have at least one packet in the rx fifo. Read the length + * of the first one, if not already known. + */ + if (ctlr->rlf >= 0) + len = ctlr->rlf; /* from a previous call */ + else { + assert(frp != nil); + /* read length word from rx fifo */ + len = frp->rlf; + if (len & ~Bytecnt) { + iprint("fifo: impossible rlf value\n"); + return 1; + } + if (len == 0) { + iprint("fifo: rdfo %lud > 0 but rlf == 0\n", rdfo); + return 1; + } + ctlr->rlf = len; /* save in case dma is busy below */ + } + + ruplen = ROUNDUP(len, BY2WD); + if (len > ETHERMAXTU) { + iprint("fifo: jumbo pkt tossed\n"); + discard(ether, ruplen); + return 0; + } + + if (!dmaready) /* too early, dma not really set up */ + return 0; + bp = clallocb(); + if(bp == nil){ + iprint("fifo: no buffer for input pkt\n"); + discard(ether, ruplen); + ether->soverflows++; + return 0; + } + + /* + * rx Step 1: dma from rx fifo into Block, turn off recv interrupts. + * wait for dmarxdone (interrupt) to pass the Block upstream. + */ + if (!dmastart(Chan0, bp->rp, &frp->rdfd, ruplen, Dinc, dmarxdone)) { + /* should never get here */ + iprint("dmarecv: dmastart failed for Chan0\n"); + freeb(bp); + enafifointr(ctlr, Rc); + /* we'll try again next time we're called */ + return 0; + } + ctlr->rlf = -1; /* we're committed now */ + lightbiton(Ledethinwait); + bp->wp = bp->rp + len; + assert(ctlr->rbp == nil); + ctlr->rbp = bp; + ctlr->rxdma = 1; + barriers(); + /* + * we're waiting for dma and can't start dma of another + * incoming packet until the current dma is finished. + */ + disfifointr(ctlr, Rc); + return 0; +} + +void +whackether(Ether *ether) +{ + int s = splhi(); + + iprint("resetting fifo+temac..."); + reset(ether); + init(ether); + iprint("\n"); + ether->resets++; + splx(s); +} + +/* + * we've had trouble transmitting 60-byte packets and + * 77-byte packets, so round up the length to make it even + * and enforce a minimum of 64 bytes (ETHERMINTU+4). + * rounding to a multiple of 4 (rather than 2) will make 1514 + * into 1516, which is a jumbo packet, so take care. + * it's a bit sleazy, but this will just pick up a few junk + * bytes beyond the packet for padding. + */ +static uint +padpktlen(uint len) +{ + len = ROUNDUP(len, BY2WD); + if (len > ethermedium.maxtu) + len = ethermedium.maxtu; + return len; +} + +int +fifointr(ulong bit) +{ + int r, whack, ic; + ulong sts; + Ether *ether; + + ether = ethers[0]; + if (ether == nil) + return 0; /* not for me */ + ether->interrupts++; + r = 0; + while ((sts = frp->isr) != 0) { + ic = sts & (Rc | FifoTc); /* interrupt causes */ + r |= ic; + whack = 0; + if (sts & Rc) + whack = dmarecv(ether); + else + if (getrdfo() != 0) + wave('~'); /* isr Rc off, yet fifo !empty */ + if (sts & FifoTc) { + /* + * not much to do, really. turn out the light and + * attempt another dma transfer anyway. + */ + lightbitoff(Ledethoutwait); + temactransmit(ether); + } + frp->isr = ic; /* extinguish intr sources */ + barriers(); + sts &= ~ic; + if (sts) + iprint("fifointr: sts %#lux\n", sts); + r |= whackiferr(ether, whack, sts); + } + if (r) { + intrack(bit); + return 1; + } + return 0; +} + +static void +fiforeset(Ether *ether) +{ + Temacctlr *ctlr; + + barriers(); + dma0init(); /* could be dma in progress, so shut that down */ + + ctlr = ether->ctlr; + ctlr->fifoier = 0; + fifointrset(ctlr); + + /* should put a timeout on this and do a hard reset if it fails */ + frp->tdfr = Reset; /* try it the graceful way first */ + frp->rdfr = Reset; + barriers(); + while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc)) + ; + frp->isr = Trc | Rrc; /* reset the `reset done' bits */ + barriers(); + +if (0) { + frp->llr = Reset; + barriers(); + while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc)) + ; +} +} + +void +fifoinit(Ether *ether) +{ + if (ethers[0] != nil) + assert(ethers[0] == ether); +// fiforeset(ether); + frp->isr = frp->isr; /* extinguish intr source */ + barriers(); + + intrenable(Intllfifo, fifointr, "fifo"); + barriers(); + enafifointr(ether->ctlr, Rc | FifoTc | Rpure | Rpore | Rpue | Tpoe | Tse); +} + +static void +tmoutreset(ulong bit) +{ + USED(bit); + whackether(ethers[0]); +} + +/* + * tx Step 2: write frp->tlr, thus initiating ethernet transmission of + * the Block just copied into the tx fifo, and free that Block. + */ +static void +dmatxdone(int) +{ + Block *bp; + Ether *ether; + Temacctlr *ctlr; + + ether = ethers[0]; + ctlr = ether->ctlr; + + ilock(ctlr); + ether->dmatxintr++; + ctlr->txdma = 0; + + /* + * start transmitting this packet from the output fifo. + * contrary to DS568 Table 5's description of TSE, it seems + * to be legal to write an odd value into tlf (unless the word + * `match' implies ±1), but it may be necessary to provide a + * padding byte in the fifo if you do. + */ + bp = ctlr->tbp; + if (bp != nil) { + ctlr->tbp = nil; + frp->tlf = padpktlen(BLEN(bp)); + barriers(); + freeb(bp); + ether->tbusy = 0; + } + + dmatxstart(ether); /* attempt another dma to tx fifo */ + iunlock(ctlr); +} + +/* + * if possible, start dma of the first packet of the output queue into + * the tx fifo. + * + * must be called with ether->ctlr ilocked, + * thus we cannot sleep nor qlock. + * + * output buffers are always misaligned, but that doesn't matter + * as of v2 of the dma controller. + */ +static int +dmatxstart(Ether *ether) +{ + unsigned len, ruplen; + Block *bp; + Temacctlr *ctlr; + + if (ether == nil || ether->oq == nil || ether->tbusy) + return 0; + ctlr = ether->ctlr; + if (ctlr->txdma) + return 0; + SET(len); + while ((bp = qget(ether->oq)) != nil) { + len = padpktlen(BLEN(bp)); + if (len > ETHERMAXTU) { + iprint("fifo: dropping outgoing jumbo (%ud) pkt\n", + len); + freeb(bp); + } else + break; + } + if (bp == nil) + return 0; + if (!dmaready) { /* too early? */ + iprint("dmatxstart: dma not ready\n"); + freeb(bp); + disfifointr(ctlr, FifoTc); + return 0; + } + + if (len == 0) + print("idiot sending zero-byte packet\n"); + ruplen = ROUNDUP(len, BY2WD); /* must be multiple of 4 for dma */ + + /* + * if there isn't enough room in the fifo for this + * packet, return and assume that the next transmit + * interrupt will resume transmission of it. + */ + if ((frp->tdfv & Wordcnt) < ruplen / BY2WD) { + iprint("dmatxstart: no room in tx fifo\n"); + return 0; + } + + /* tx Step 1: dma to tx fifo, wait for dma tx interrupt */ + lightbiton(Ledethoutwait); + if (dmastart(Chan1, &frp->tdfd, bp->rp, ruplen, Sinc, dmatxdone)) { + ether->tbusy = 1; + ctlr->txdma = 1; + barriers(); + /* Rc may be off if we got Rc intrs too early */ +// enafifointr(ctlr, FifoTc | Rc); + ctlr->tbp = bp; /* remember this block */ + } else + iprint("dmatxstart: dmastart failed for Chan1\n"); + return 1; +} diff -Nru /sys/src/9/vt4/ethermii.h /sys/src/9/vt4/ethermii.h --- /sys/src/9/vt4/ethermii.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/ethermii.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,143 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisement */ + Anlpar = 5, /* AN Link Partner Ability */ + Aner = 6, /* AN Expansion */ + Annptr = 7, /* AN Next Page TX */ + Annprr = 8, /* AN Next Page RX */ + Mscr = 9, /* Gb Control */ + Mssr = 10, /* Gb Status */ + Esr = 15, /* Extended Status */ + + /* 88e1116-specific paged registers */ + Scr = 16, /* special control register */ + Ssr = 17, /* special status register */ + Ier = 18, /* interrupt enable reg */ + Isr = 19, /* interrupt status reg */ + Escr = 20, /* extended special control reg */ + Recr = 21, /* RX error counter reg */ + Eadr = 22, /* extended address reg (page select) */ + Globsts = 23, /* global status */ + Impover = 24, /* RGMII output impedance override (page 2) */ + Imptarg = 25, /* RGMII output impedance target (page 2) */ + Scr2 = 26, /* special control register 2 */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 10BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +enum { /* Scr page 0 */ + Pwrdown = 0x0004, /* power down */ + Mdix = 0x0060, /* MDI crossover mode */ + Endetect = 0x0300, /* energy detect */ +}; +enum { /* Scr page 2 */ + Rgmiipwrup = 0x0008, /* RGMII power up: must sw reset after */ +}; + +enum { /* Recr page 2 */ + Txtiming = 1<<4, + Rxtiming = 1<<5, +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); diff -Nru /sys/src/9/vt4/fakeqtm.c /sys/src/9/vt4/fakeqtm.c --- /sys/src/9/vt4/fakeqtm.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fakeqtm.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,56 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "io.h" +#include "../port/netif.h" +#include "etherif.h" +#include "../ip/ip.h" + +Mutstats mutstats; +Ureg *gureg; + +int +gotsecuremem(void) +{ + return 0; +} + +ulong +qtmborder(void) +{ + return 0; +} + +void +qtmerrs(char *) +{ +} + +void +qtmerr(void) +{ +} + +void +qtminit(void) +{ +} + +int +mutatetrigger(void) +{ + return 0; +} + +void +mutateproc(void *) +{ +} + +void +qtmclock(void) +{ +} diff -Nru /sys/src/9/vt4/fns.h /sys/src/9/vt4/fns.h --- /sys/src/9/vt4/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fns.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,221 @@ +#define checkmmu(a, b) +#define countpagerefs(a, b) + +#include "../port/portfns.h" + +typedef struct Dma Dma; +typedef struct Ether Ether; +#pragma incomplete Dma + +void addconf(char*, char*); +int adec(long*); +int ainc(long*); +void archetherspeed(int, int); +void archinit(void); +uint archmiiport(int); +void archreboot(void); +void archreset(void); +ulong archuartclock(int, int); +void barriers(void); +uintptr cankaddr(uintptr pa); +Block* clallocb(void); +void clockinit(void); +void clockintr(Ureg*); +void clrmchk(void); +int cmpswap(long*, long, long); +// #define coherence() eieio() +#define coherence() /* uniprocessor */ +char *cputype2name(char *, int); +void cpuidprint(void); +#define cycles(ip) *(ip) = lcycles() +void dcflush(uintptr, usize); +void dcrcompile(void); +long decref(Ref*); +void delay(int); +void dtlbmiss(void); +void dumpmal(void); +void dumpregs(Ureg*); +void delayloopinit(void); +Dev* devtabget(int, int); +void devtabinit(void); +void devtabreset(void); +long devtabread(Chan*, void*, long, vlong); +void devtabshutdown(void); +void dump(void *, int); +void eieio(void); +void etherclock(void); +void evenaddr(uintptr); +void fifoinit(Ether *); +void firmware(int); +int fpipower(Ureg*); +int fpuavail(Ureg*); +int fpudevprocio(Proc*, void*, long, uintptr, int); +int fpuemu(Ureg*); +void fpuinit(void); +void fpunoted(void); +void fpunotify(Ureg*); +void fpuprocresetore(Proc*); +#define fpuprocresetore(p) USED(p) +void fpuprocsave(Proc*); +#define fpuprocsave(p) USED(p) +void fpusysprocsetup(Proc*); +void fpusysrfork(Ureg*); +void fpusysrforkchild(Proc*, Ureg*, Proc*); +void fputrap(Ureg*, int); +char* getconf(char*); +u32int getccr0(void); +u32int getdar(void); +u32int getdcr(int); +u32int getdear(void); +u32int getdec(void); +u32int getesr(void); +u32int getmsr(void); +u32int getpid(void); +u32int getpir(void); +u32int getpit(void); +u32int getpvr(void); +u32int gettbl(void); +u32int gettsr(void); +void gotopc(uintptr); +int gotsecuremem(void); +int havetimer(void); +void icflush(uintptr, usize); +void idlehands(void); +int inb(int); +long incref(Ref*); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void intr(Ureg*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrfmtcounts(char *s, char *se); +void intrinit(void); +void intrshutdown(void); +int ioalloc(int, int, int, char*); +void iofree(int); +void ioinit(void); +int iprint(char*, ...); +void isync(void); +void itlbmiss(void); +void kexit(Ureg*); +void* kmapphys(uintptr, uintptr, ulong, ulong, ulong); +uchar lightbitoff(int); +uchar lightbiton(int); +uchar lightstate(int); +void links(void); +void malinit(void); +void meminit(void); +void mmuinit(void); +void* mmucacheinhib(void*, ulong); +ulong mmumapsize(ulong); +void mutateproc(void *); +int mutatetrigger(void); +int newmmupid(void); +int notify(Ureg*); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +#define pokeaddr(a, v, w) probeaddr(a) +void ppc405console(void); +vlong probeaddr(uintptr addr); +#define procrestore(p) +void procsave(Proc*); +void procsetup(Proc*); +void putdcr(int, u32int); +void putdec(ulong); +void putesr(ulong); +void putevpr(ulong); +void putmsr(u32int); +void putpid(u32int); +void putpit(u32int); +void putsdr1(u32int); +void puttcr(u32int); +void puttsr(u32int); +ulong qtmborder(void); +void qtmclock(void); +void qtmerr(void); +void qtmerrs(char *); +void qtminit(void); +void shutdown(int ispanic); +void spldone(void); +int splhi(void); +int spllo(void); +void splx(int); +void splxpc(int); +void startcpu(int); +void sync(void); +void syscall(Ureg*); +uintptr sysexecstack(uintptr, int); +void sysprocsetup(Proc*); +#define tas tas32 +void temactransmit(Ether *); +void touser(uintptr); +void trapinit(void); +void trapcritvec(void); +void trapvec(void); +void tlbdump(char *s); +u32int tlbrehi(int); +u32int tlbrelo(int); +int tlbsxcc(uintptr); +void tlbwrx(int, u32int, u32int); +void uartliteconsole(void); +void uartlputc(int); +void uartlputs(char *s); +void uncinit(void); +void uncinitwait(void); +#define userureg(ur) (((ur)->status & MSR_PR) != 0) +void verifyproc(Proc *); +void verifymach(Mach *); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +void watchreset(void); +void whackether(Ether *); +void writeconf(void); + +/* + * dma.c + */ +void dma0init(void); +void dmainit(void); +int dmastart(int, void *dest, void *src, ulong len, ulong flags, void (*f)(int)); +void dmawait(int); + +/* + * intr.c + */ +void intrack(ulong); +void intrenable(ulong bit, int (*func)(ulong), char *); +void intrinit(void); + +int cas32(void*, u32int, u32int); +int tas32(void*); + +#define CASU(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) +#define CASV(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) +#define CASW(addr, exp, new) cas32((addr), (exp), (new)) +#define TAS(addr) tas32(addr) + +void forkret(void); + +#define PTR2UINT(p) ((uintptr)(p)) +#define UINT2PTR(i) ((void*)(i)) + +#define isphys(a) (((ulong)(a)&KSEGM)!=KSEG0 && ((ulong)(a)&KSEGM)!=KSEG1) +#define KADDR(a) ((void*)((ulong)(a) | KZERO)) +#define PADDR(a) (isphys(a)? (ulong)(a): ((ulong)(a) & ~KSEGM)) + +/* + * this low-level printing stuff is ugly, + * but there appears to be no other way to + * print until after #t is populated. + */ + +#define wave(c) { \ + barriers(); \ + *(ulong *)Uartlite = (c); \ + barriers(); \ +} diff -Nru /sys/src/9/vt4/fpi.c /sys/src/9/vt4/fpi.c --- /sys/src/9/vt4/fpi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fpi.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,308 @@ +/* + * Floating Point Interpreter. + * shamelessly stolen from an original by ark. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fpi.h" + +void +fpiround(Internal *i) +{ + unsigned long guard; + + guard = i->l & GuardMask; + i->l &= ~GuardMask; + if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){ + i->l += LsBit; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h++; + if(i->h & CarryBit){ + if (i->h & 0x01) + i->l |= CarryBit; + i->l >>= 1; + i->h >>= 1; + i->e++; + } + } + } +} + +static void +matchexponents(Internal *x, Internal *y) +{ + int count; + + count = y->e - x->e; + x->e = y->e; + if(count >= 2*FractBits){ + x->l = x->l || x->h; + x->h = 0; + return; + } + if(count >= FractBits){ + count -= FractBits; + x->l = x->h|(x->l != 0); + x->h = 0; + } + while(count > 0){ + count--; + if(x->h & 0x01) + x->l |= CarryBit; + if(x->l & 0x01) + x->l |= 2; + x->l >>= 1; + x->h >>= 1; + } +} + +static void +shift(Internal *i) +{ + i->e--; + i->h <<= 1; + i->l <<= 1; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h |= 0x01; + } +} + +static void +normalise(Internal *i) +{ + while((i->h & HiddenBit) == 0) + shift(i); +} + +static void +renormalise(Internal *i) +{ + if(i->e < -2 * FractBits) + i->e = -2 * FractBits; + while(i->e < 1){ + i->e++; + if(i->h & 0x01) + i->l |= CarryBit; + i->h >>= 1; + i->l = (i->l>>1)|(i->l & 0x01); + } + if(i->e >= ExpInfinity) + SetInfinity(i); +} + +void +fpinormalise(Internal *x) +{ + if(!IsWeird(x) && !IsZero(x)) + normalise(x); +} + +void +fpiadd(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + i->s = x->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + if(x->e > y->e){ + t = x; + x = y; + y = t; + } + matchexponents(x, y); + i->e = x->e; + i->h = x->h + y->h; + i->l = x->l + y->l; + if(i->l & CarryBit){ + i->h++; + i->l &= ~CarryBit; + } + if(i->h & (HiddenBit<<1)){ + if(i->h & 0x01) + i->l |= CarryBit; + i->l = (i->l>>1)|(i->l & 0x01); + i->h >>= 1; + i->e++; + } + if(IsWeird(i)) + SetInfinity(i); +} + +void +fpisub(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + if(y->e < x->e + || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){ + t = x; + x = y; + y = t; + } + i->s = y->s; + if(IsNaN(y)){ + SetQNaN(i); + return; + } + if(IsInfinity(y)){ + if(IsInfinity(x)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + matchexponents(x, y); + i->e = y->e; + i->h = y->h - x->h; + i->l = y->l - x->l; + if(i->l < 0){ + i->l += CarryBit; + i->h--; + } + if(i->h == 0 && i->l == 0) + SetZero(i); + else while(i->e > 1 && (i->h & HiddenBit) == 0) + shift(i); +} + +#define CHUNK (FractBits/2) +#define CMASK ((1<>CHUNK) & CMASK) +#define LO(x) ((short)(x) & CMASK) +#define SPILL(x) ((x)>>CHUNK) +#define M(x, y) ((long)a[x]*(long)b[y]) +#define C(h, l) (((long)((h) & CMASK)<s = x->s^y->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + else if(IsZero(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->e = x->e + y->e - (ExpBias - 1); + + a[0] = HI(x->h); b[0] = HI(y->h); + a[1] = LO(x->h); b[1] = LO(y->h); + a[2] = HI(x->l); b[2] = HI(y->l); + a[3] = LO(x->l); b[3] = LO(y->l); + + c[6] = M(3, 3); + c[5] = M(2, 3) + M(3, 2) + SPILL(c[6]); + c[4] = M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]); + c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]); + c[2] = M(0, 2) + M(1, 1) + M(2, 0) + SPILL(c[3]); + c[1] = M(0, 1) + M(1, 0) + SPILL(c[2]); + c[0] = M(0, 0) + SPILL(c[1]); + + f[0] = c[0]; + f[1] = C(c[1], c[2]); + f[2] = C(c[3], c[4]); + f[3] = C(c[5], c[6]); + + if((f[0] & HiddenBit) == 0){ + f[0] <<= 1; + f[1] <<= 1; + f[2] <<= 1; + f[3] <<= 1; + if(f[1] & CarryBit){ + f[0] |= 1; + f[1] &= ~CarryBit; + } + if(f[2] & CarryBit){ + f[1] |= 1; + f[2] &= ~CarryBit; + } + if(f[3] & CarryBit){ + f[2] |= 1; + f[3] &= ~CarryBit; + } + i->e--; + } + i->h = f[0]; + i->l = f[1]; + if(f[2] || f[3]) + i->l |= 1; + renormalise(i); +} + +void +fpidiv(Internal *x, Internal *y, Internal *i) +{ + i->s = x->s^y->s; + if(IsNaN(x) || IsNaN(y) + || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){ + SetQNaN(i); + return; + } + else if(IsZero(x) || IsInfinity(y)){ + SetInfinity(i); + return; + } + else if(IsInfinity(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->h = 0; + i->l = 0; + i->e = y->e - x->e + (ExpBias + 2*FractBits - 1); + do{ + if(y->h > x->h || (y->h == x->h && y->l >= x->l)){ + i->l |= 0x01; + y->h -= x->h; + y->l -= x->l; + if(y->l < 0){ + y->l += CarryBit; + y->h--; + } + } + shift(y); + shift(i); + }while ((i->h & HiddenBit) == 0); +/* + if(y->h > x->h || (y->h == x->h && y->l >= x->l)) + i->l |= 0x01; +*/ + if(y->h || y->l) + i->l |= 0x01; + renormalise(i); +} + +int +fpicmp(Internal *x, Internal *y) +{ + if(IsNaN(x) && IsNaN(y)) + return 0; + if(IsInfinity(x) && IsInfinity(y)) + return y->s - x->s; + if(x->e == y->e && x->h == y->h && x->l == y->l) + return y->s - x->s; + if(x->e < y->e + || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l)))) + return y->s ? 1: -1; + return x->s ? -1: 1; +} diff -Nru /sys/src/9/vt4/fpi.h /sys/src/9/vt4/fpi.h --- /sys/src/9/vt4/fpi.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fpi.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff -Nru /sys/src/9/vt4/fpimem.c /sys/src/9/vt4/fpimem.c --- /sys/src/9/vt4/fpimem.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fpimem.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,140 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fpi.h" + +/* + * the following routines depend on memory format, not the machine + */ + +void +fpis2i(Internal *i, void *v) +{ + Single *s = v; + + i->s = (*s & 0x80000000) ? 1: 0; + if((*s & ~0x80000000) == 0){ + SetZero(i); + return; + } + i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias; + i->h = (*s & 0x007FFFFF)<<(1+NGuardBits); + i->l = 0; + if(i->e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpid2i(Internal *i, void *v) +{ + Double *d = v; + + i->s = (d->h & 0x80000000) ? 1: 0; + i->e = (d->h>>20) & 0x07FF; + i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F); + i->l = (d->l & 0x01FFFFFF)<e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpiw2i(Internal *i, void *v) +{ + Word w, word = *(Word*)v; + int e; + + if(word < 0){ + i->s = 1; + word = -word; + } + else + i->s = 0; + if(word == 0){ + SetZero(i); + return; + } + if(word > 0){ + for (e = 0, w = word; w; w >>= 1, e++) + ; + } else + e = 32; + if(e > FractBits){ + i->h = word>>(e - FractBits); + i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e); + } + else { + i->h = word<<(FractBits - e); + i->l = 0; + } + i->e = (e - 1) + ExpBias; +} + +void +fpii2s(void *v, Internal *i) +{ + int e; + Single *s = (Single*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + *s = i->s ? 0x80000000: 0; + e = i->e; + if(e < ExpBias){ + if(e <= (ExpBias - SingleExpBias)) + return; + e = SingleExpBias - (ExpBias - e); + } + else if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){ + *s |= SingleExpMax<<23; + return; + } + else + e = SingleExpBias + (e - ExpBias); + *s |= (e<<23)|(i->h>>(1+NGuardBits)); +} + +void +fpii2d(void *v, Internal *i) +{ + Double *d = (Double*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits); + i->h >>= NGuardBits; + d->h = i->s ? 0x80000000: 0; + d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4); + d->l = (i->h<<28)|i->l; +} + +void +fpii2w(Word *word, Internal *i) +{ + Word w; + int e; + + fpiround(i); + e = (i->e - ExpBias) + 1; + if(e <= 0) + w = 0; + else if(e > 31) + w = 0x7FFFFFFF; + else if(e > FractBits) + w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e)); + else + w = i->h>>(FractBits-e); + if(i->s) + w = -w; + *word = w; +} diff -Nru /sys/src/9/vt4/fpipower.c /sys/src/9/vt4/fpipower.c --- /sys/src/9/vt4/fpipower.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/fpipower.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,973 @@ +/* + * this doesn't attempt to implement Power architecture floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "ureg.h" + +#include "fpi.h" + +#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) +#define FR(x) (*(Internal*)em->fr[(x)&0x1F]) +#define REGSP 0 /* in Plan 9, the user and kernel stacks are different */ + +#define getulong(a) (*(ulong*)(a)) + +enum { + CRLT = 1<<31, + CRGT = 1<<30, + CREQ = 1<<29, + CRSO = 1<<28, + CRFU = CRSO, + + CRFX = 1<<27, + CRFEX = 1<<26, + CRVX = 1<<25, + CROX = 1<<24, +}; + +#define getCR(x,w) (((w)>>(28-(x*4)))&0xF) +#define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) + +#define simm(xx, ii) xx = (short)(ii&0xFFFF); +#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) +#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) + +#define FPS_FX (1<<31) /* exception summary (sticky) */ +#define FPS_EX (1<<30) /* enabled exception summary */ +#define FPS_VX (1<<29) /* invalid operation exception summary */ +#define FPS_OX (1<<28) /* overflow exception OX (sticky) */ +#define FPS_UX (1<<27) /* underflow exception UX (sticky) */ +#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPS_XX (1<<25) /* inexact exception XX (sticky) */ +#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPS_FR (1<<18) /* fraction rounded */ +#define FPS_FI (1<<17) /* fraction inexact */ +#define FPS_FPRF (1<<16) /* floating point result class */ +#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ +#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPS_VE (1<<7) /* invalid operation exception enable */ +#define FPS_OE (1<<6) /* enable overflow exceptions */ +#define FPS_UE (1<<5) /* enable underflow */ +#define FPS_ZE (1<<4) /* enable zero divide */ +#define FPS_XE (1<<3) /* enable inexact exceptions */ +#define FPS_RN (3<<0) /* rounding mode */ + +typedef struct Emreg Emreg; + +struct Emreg { + Ureg* ur; + ulong (*fr)[3]; + FPsave* ufp; + ulong ir; + char* name; +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(r29), OFR(r30), OFR(r31), +}; + +/* + * initial FP register values assumed by qc's code + */ +static Internal fpreginit[] = { + /* s, e, l, h */ + {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ + {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ + {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ +}; + +static void +fadd(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + (a.s == b.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fsub(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + b.s ^= 1; + (b.s == a.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fmul(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpimul(&b, &a, d); +} + +static void +fdiv(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpidiv(&b, &a, d); +} + +static void +fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + b.s ^= 1; + (b.s == t.s? fpiadd: fpisub)(&b, &t, d); +} + +static void +fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + (t.s == b.s? fpiadd: fpisub)(&b, &t, d); +} + +static ulong setfpscr(Emreg*); +static void setfpcc(Emreg*, int); + +static void +unimp(Emreg *em, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +/* + * floating load/store + */ + +static void +fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) +{ + ulong ea; + long imm; + int ra, rd, upd; + + getairr(ir); + ea = imm; + upd = (ir&(1L<<26))!=0; + if(ra) { + ea += REG(ra); + if(upd){ + if(REGSP && ra == REGSP) + panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ + REG(ra) = ea; + } + } else { + if(upd) + unimp(em, ir); + } + *rdp = rd; + *eap = (void*)ea; + if(fpemudebug) + print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); +} + +static void +fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) +{ + ulong ea; + int ra, rb, rd; + + getarrr(ir); + ea = REG(rb); + if(ra){ + ea += REG(ra); + if(upd){ + if(REGSP && ra == REGSP) + panic("fpemu: r1 update"); + REG(ra) = ea; + } + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); + } else { + if(upd) + unimp(em, ir); + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); + } + *eap = (void*)ea; + *rdp = rd; +} + +static void +lfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfs"; + fpeairr(em, ir, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfsx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfd"; + fpeairr(em, ir, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +lfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +stfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfs"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfsx"; + fpearrr(em, ir, getxo(ir)==695, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfd"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +stfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +mcrfs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + static ulong fpscr0[] ={ + FPS_FX|FPS_OX, + FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, + FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, + FPS_VXVC, + 0, + FPS_VXCVI, + }; + + getarrr(ir); + if(rb || ra&3 || rd&3) + unimp(em, ir); + ra >>= 2; + rd >>= 2; + em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); + em->ufp->fpscr &= ~fpscr0[ra]; + if(fpemudebug) + print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); +} + +static void +mffs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + Double dw; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + dw.h = 0; + dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; + fpid2i(&FR(rd), &dw); + /* it's anyone's guess how CR1 should be set when ir&1 */ + em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ + if(fpemudebug) + print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb1(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr |= (1L << (31-rd)); + /* BUG: should set summary bits */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb0(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr &= ~(1L << (31-rd)); + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsf(Emreg *em, ulong ir) +{ + int fm, rb, i; + ulong v; + Internal b; + Double db; + + if(ir & ((1L << 25)|(1L << 16))) + unimp(em, ir); + rb = (ir >> 11) & 0x1F; + fm = (ir >> 17) & 0xFF; + b = FR(rb); + fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ + v = db.l; + for(i=0; i<8; i++) + if(fm & (1 << (7-i))) + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); +} + +static void +mtfsfi(Emreg *em, ulong ir) +{ + int imm, rd; + + if(ir & ((0x7F << 16)|(1L << 11))) + unimp(em, ir); + rd = (ir >> 23) & 0xF; + imm = (ir >> 12) & 0xF; + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); +} + +static void +fcmp(Emreg *em, ulong ir) +{ + int fc, rd, ra, rb, sig, i; + Internal a, b; + + getarrr(ir); + if(rd & 3) + unimp(em, ir); + rd >>= 2; + sig = 0; + switch(getxo(ir)) { + default: + unimp(em, ir); + case 32: + if(fpemudebug) + print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + sig = 1; + break; + case 0: + if(fpemudebug) + print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + break; + } + if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { + if(sig){ + ; /* BUG: should trap if not masked ... */ + } + fc = CRFU; + } else { + a = FR(ra); + b = FR(rb); + fpiround(&a); + fpiround(&b); + i = fpicmp(&a, &b); + if(i > 0) + fc = CRGT; + else if(i == 0) + fc = CREQ; + else + fc = CRLT; + } + fc >>= 28; + em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); + /* BUG: update FX, VXSNAN, VXVC */ +} + +static void +fariths(Emreg *em, ulong ir) +{ + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + Internal *d; + + fmt = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* fsqrts */ + case 24: /* fres */ + default: + unimp(em, ir); + return; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdivs"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsubs"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadds"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmuls"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsubs"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadds"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsubs"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadds"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith(Emreg *em, ulong ir) +{ + Word w; + Double dv; + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + int nocc; + Internal *d; + + fmt = 0; + nocc = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* frsqrt */ + case 23: /* fsel */ + case 26: /* fsqrte */ + default: + unimp(em, ir); + return; + case 12: /* frsp */ + *d = FR(rb); /* BUG: doesn't round to single precision */ + fmt = 1; + n = "frsp"; + break; + case 14: /* fctiw */ /* BUG: ignores rounding mode */ + case 15: /* fctiwz */ + fpii2w(&w, &FR(rb)); + dv.h = 0; + dv.l = w; + fpid2i(d, &dv); + fmt = 1; + nocc = 1; + n = "fctiw"; + break; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdiv"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsub"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadd"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmul"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsub"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadd"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsub"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadd"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + if(nocc == 0) + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith2(Emreg *em, ulong ir) +{ + int rd, ra, rb; + char *cc, *n; + ulong fpscr; + Internal *d, *b; + + getarrr(ir); + if(ra) + unimp(em, ir); + d = &FR(rd); + b = &FR(rb); + switch(getxo(ir)) { /* full XO decode */ + default: + unimp(em, ir); + case 40: + *d = *b; + d->s ^= 1; + n = "fneg"; + break; + case 72: + *d = *b; + n = "fmr"; + break; + case 136: + *d = *b; + d->s = 1; + n = "fnabs"; + break; + case 264: + *d = *b; + d->s = 0; + n = "fabs"; + break; + } + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); +} + +static ulong +setfpscr(Emreg *em) +{ + ulong fps, fpscr; + + fps = 0; /* BUG: getfsr() */ + fpscr = em->ufp->fpscr; + if(fps & FPAOVFL) + fpscr |= FPS_OX; + if(fps & FPAINEX) + fpscr |= FPS_XX; + if(fps & FPAUNFL) + fpscr |= FPS_UX; + if(fps & FPAZDIV) + fpscr |= FPS_ZX; + if(fpscr != em->ufp->fpscr) { + fpscr |= FPS_FX; + em->ufp->fpscr = fpscr; + } + return fpscr; +} + +static void +setfpcc(Emreg *em, int r) +{ + int c; + Internal *d; + + d = &FR(r); + c = 0; + if(IsZero(d)) + c |= 2; + else if(d->s == 1) + c |= 4; + else + c |= 8; + if(IsNaN(d)) + c |= 1; + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ +} + +static uchar op63flag[32] = { +[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, +[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpipower(Ureg *ur) +{ + ulong op; + int xo; + Emreg emreg, *em; + FPsave *ufp; + int n; + + ufp = &up->fpsave; /* because all the state is in FPsave, it need not be saved/restored */ + em = &emreg; + em->ur = ur; + em->fr = ufp->emreg; + em->ufp = ufp; + em->name = nil; + if(em->ufp->fpistate != FPactive) { + em->ufp->fpistate = FPactive; + em->ufp->fpscr = 0; /* TO DO */ + for(n = 0; n < nelem(fpreginit); n++) + FR(31-n) = fpreginit[n]; + } + for(n=0;;n++){ + validaddr(ur->pc, 4, 0); + op = getulong(ur->pc); + em->ir = op; + if(fpemudebug > 1) + print("%8.8lux %8.8lux: ", ur->pc, op); + switch(op>>26){ + default: + return n; + case 48: /* lfs */ + case 49: /* lfsu */ + lfs(em, op); + break; + case 50: /* lfd */ + case 51: /* lfdu */ + lfd(em, op); + break; + case 52: /* stfs */ + case 53: /* stfsu */ + stfs(em, op); + break; + case 54: /* stfd */ + case 55: /* stfdu */ + stfd(em, op); + break; + case 31: /* indexed load/store */ + xo = getxo(op); + if((xo & 0x300) != 0x200) + return n; + switch(xo){ + default: + return n; + case 535: /* lfsx */ + case 567: /* lfsux */ + lfsx(em, op); + break; + case 599: /* lfdx */ + case 631: /* lfdux */ + lfdx(em, op); + break; + case 663: /* stfsx */ + case 695: /* stfsux */ + stfsx(em, op); + break; + case 727: /* stfdx */ + case 759: /* stfdux */ + stfdx(em, op); + break; + } + break; + case 63: /* double precision */ + xo = getxo(op); + if(op63flag[xo & 0x1F]){ + farith(em, op); + break; + } + switch(xo){ + default: + return n; + case 0: /* fcmpu */ + case 32: /* fcmpo */ + fcmp(em, op); + break; + case 40: /* fneg */ + case 72: /* fmr */ + case 136: /* fnabs */ + case 264: /* fabs */ + farith2(em, op); + break; + case 38: + mtfsb1(em, op); + break; + case 64: + mcrfs(em, op); + break; + case 70: + mtfsb0(em, op); + break; + case 134: + mtfsfi(em, op); + break; + case 583: + mffs(em, op); + break; + case 711: + mtfsf(em, op); + break; + } + break; + case 59: /* single precision */ + fariths(em, op); + break; + } + ur->pc += 4; + if(anyhigher()) + sched(); + } +} + +/* +50: lfd frD,d(rA) +51: lfdu frD,d(rA) +31,631: lfdux frD,rA,rB +31,599: lfdx frD,rA,rB +48: lfs frD,d(rA) +49: lfsu frD,d(rA) +31,567: lfsux frD,rA,rB +31,535: lfsx frD,rA,rB + +54: stfd frS,d(rA) +55: stfdu frS,d(rA) +31,759: stfdux frS,rA,rB +31,727: stfdx frS,rA,rB +52: stfs frS,d(rA) +53: stfsu frS,d(rA) +31,695: stfsux frS,rA,rB +31,663: stfsx frS,rA,rB + +63,64: mcrfs crfD,crfS +63,583: mffs[.] frD +63,70: mtfsb0[.] crbD +63,38: mtfsb1[.] crbD +63,711: mtfsf[.] FM,frB +63,134: mtfsfi[.] crfD,IMM +*/ + +/* +float to int: + FMOVD g+0(SB),F1 + FCTIWZ F1,F4 + FMOVD F4,.rathole+0(SB) + MOVW .rathole+4(SB),R7 + MOVW R7,l+0(SB) +*/ + +/* +int to float: + MOVW $1127219200,R9 + MOVW l+0(SB),R7 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R7,R6 + MOVW R6,.rathole+4(SB) + FMOVD .rathole+0(SB),F0 + FSUB F27,F0 + +unsigned to float: + MOVW ul+0(SB),R5 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R5,R4 + MOVW R4,.rathole+4(SB) + FMOVD .rathole+0(SB),F3 + FSUB F27,F3 + FCMPU F3,F28 + BGE ,3(PC) + FMOVD $4.29496729600000000e+09,F2 + FADD F2,F3 + FMOVD F3,g+0(SB) +*/ diff -Nru /sys/src/9/vt4/init9.s /sys/src/9/vt4/init9.s --- /sys/src/9/vt4/init9.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/init9.s Fri Jun 7 00:00:00 2013 @@ -0,0 +1,30 @@ +/* + * This is the same as the C programme: + * + * void + * main(char* argv0) + * { + * startboot(argv0, &argv0); + * } + * + * It is in assembler because SB needs to be + * set and doing this in C drags in too many + * other routines. + */ +TEXT main(SB), 1, $8 + MOVW $setSB(SB), R2 + MOVW $0, R0 + SUB $16,R1 /* make a frame */ + + /* + * Argv0 is already passed to 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) + +loop: + BR loop /* should never get here */ diff -Nru /sys/src/9/vt4/intr.c /sys/src/9/vt4/intr.c --- /sys/src/9/vt4/intr.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/intr.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,207 @@ +/* Xilink XPS interrupt controller */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "ethermii.h" +#include "../port/netif.h" + +#include "io.h" + +enum { + /* mer bits */ + Merme = 1<<0, /* master enable */ + Merhie = 1<<1, /* hw intr enable */ + + Maxintrs = 32, + Ntimevec = 20, /* # of time buckets for each intr */ +}; + +typedef struct { + ulong isr; /* status */ + ulong ipr; /* pending (ro) */ + ulong ier; /* enable */ + ulong iar; /* acknowledge (wo) */ + ulong sieb; /* set ie bits; avoid */ + ulong cieb; /* clear ie bits; avoid */ + ulong ivr; /* vector; silly */ + ulong mer; /* master enable */ +} Intregs; + +typedef struct { + ulong bit; + int (*svc)(ulong); + char *name; + uvlong count; +} Intr; + +static Intregs *irp = (Intregs *)Intctlr; +static Intr intrs[Maxintrs]; +static Intr *nextintr; +static int tmoutok; + +ulong intrs1sec; /* count interrupts in this second */ +ulong intrtimes[256][Ntimevec]; + +static Intr * +bit2intr(ulong bit) +{ + Intr *ip; + + for (ip = intrs; ip->bit != 0 && ip->bit != bit; ip++) + ; + if (ip->bit == 0) + return nil; + return ip; +} + +/* + * keep histogram of interrupt service times + */ +void +intrtime(Mach*, int vno) +{ + ulong diff, x; + + x = perfticks(); + diff = x - m->perf.intrts; + m->perf.intrts = x; + + m->perf.inintr += diff; + if(up == nil && m->perf.inidle > diff) + m->perf.inidle -= diff; + + diff /= (m->cpuhz/1000000)*100; /* quantum = 100µsec */ + if(diff >= Ntimevec) + diff = Ntimevec-1; + intrtimes[vno][diff]++; +} + +void +intrfmtcounts(char *s, char *se) +{ + Intr *ip; + + for (ip = intrs; ip->bit != 0; ip++) + s = seprint(s, se, "bit %#lux %s\t%llud intrs\n", + ip->bit, ip->name, ip->count); +} + +static void +dumpcounts(void) +{ + Intr *ip; + + for (ip = intrs; ip->bit != 0; ip++) + iprint("bit %#lux %s\t%llud intrs\n", + ip->bit, ip->name, ip->count); + delay(100); +} + +extern Ureg *gureg; + +/* + * called from trap on external (non-clock) interrupts + * to poll interrupt handlers. + */ +void +intr(Ureg *ureg) +{ + int handled; + ulong pend; + Intr *ip; + + if (m->machno != 0) /* only 1st cpu handles interrupts */ + return; + gureg = ureg; + while ((pend = irp->ipr) != 0) { + for (ip = intrs; pend != 0 && ip->bit != 0; ip++) + if (pend & ip->bit) { + handled = ip->svc(ip->bit); + splhi(); /* in case handler went spllo() */ + if (handled) { + intrtime(m, ip - intrs); + ip->count++; + pend &= ~ip->bit; + } + } + if (pend != 0) { + dumpcounts(); + iprint("interrupt with no handler; ipr %#lux\n", pend); + } + if (++intrs1sec > 960*2 + 3*30000 + 1000) { + intrs1sec = 0; + dumpcounts(); + panic("too many intrs in one second"); + } + } +} + +void +intrinit(void) +{ + /* disable and neuter the intr controller */ + intrack(~0); + clrmchk(); + barriers(); + irp->ier = 0; + barriers(); + intrack(~0); + barriers(); + + nextintr = intrs; + nextintr->bit = 0; + barriers(); + + /* turn on the intr controller, initially with no intrs enabled */ + irp->mer = Merme | Merhie; + barriers(); + +// intrack(~0); +// clrmchk(); + tmoutok = 1; + barriers(); +} + +/* register func as the interrupt-service routine for bit */ +void +intrenable(ulong bit, int (*func)(ulong), char *name) +{ + Intr *ip; + + for (ip = intrs; ip->bit != 0; ip++) + if (bit == ip->bit) { + assert(func == ip->svc); + return; /* already registered */ + } + + assert(nextintr < intrs + nelem(intrs)); + assert(bit != 0); + assert(func != nil); + ip = nextintr++; + ip->bit = bit; + ip->svc = func; + ip->name = name; + sync(); + irp->ier |= bit; + barriers(); +} + +void +intrack(ulong bit) +{ + irp->iar = bit; + barriers(); +} + +void +intrshutdown(void) +{ + irp->ier = 0; + irp->mer = 0; + barriers(); + intrack(~0); +} diff -Nru /sys/src/9/vt4/io.h /sys/src/9/vt4/io.h --- /sys/src/9/vt4/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/io.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,15 @@ +enum +{ + MaxVector= 8, + + /* some flags to change polarity and sensitivity */ + IRQmask= 0xFF, /* actual vector address */ + IRQactivelow= 1<<8, + IRQedge= 1<<9, + IRQcritical= 1<<10, +}; + +#define BUSUNKNOWN (-1) + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) diff -Nru /sys/src/9/vt4/l.s /sys/src/9/vt4/l.s --- /sys/src/9/vt4/l.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/l.s Fri Jun 7 00:00:00 2013 @@ -0,0 +1,846 @@ +/* virtex4 ppc405 machine assist */ +#include "mem.h" + +/* + * 405 Special Purpose Registers of interest here + */ +#define SPR_CCR0 947 /* Core Configuration Register 0 */ +#define SPR_DAC1 1014 /* Data Address Compare 1 */ +#define SPR_DAC2 1015 /* Data Address Compare 2 */ +#define SPR_DBCR0 1010 /* Debug Control Register 0 */ +#define SPR_DBCR1 957 /* Debug Control Register 1 */ +#define SPR_DBSR 1008 /* Debug Status Register */ +#define SPR_DCCR 1018 /* Data Cache Cachability Register */ +#define SPR_DCWR 954 /* Data Cache Write-through Register */ +#define SPR_DVC1 950 /* Data Value Compare 1 */ +#define SPR_DVC2 951 /* Data Value Compare 2 */ +#define SPR_DEAR 981 /* Data Error Address Register */ +#define SPR_ESR 980 /* Exception Syndrome Register */ +#define SPR_EVPR 982 /* Exception Vector Prefix Register */ +#define SPR_IAC1 1012 /* Instruction Address Compare 1 */ +#define SPR_IAC2 1013 /* Instruction Address Compare 2 */ +#define SPR_IAC3 948 /* Instruction Address Compare 3 */ +#define SPR_IAC4 949 /* Instruction Address Compare 4 */ +#define SPR_ICCR 1019 /* Instruction Cache Cachability Register */ +#define SPR_ICDBDR 979 /* Instruction Cache Debug Data Register */ +#define SPR_PID 945 /* Process ID */ +#define SPR_PIT 987 /* Programmable Interval Timer */ +#define SPR_PVR 287 /* Processor Version Register */ +#define SPR_SGR 953 /* Store Guarded Register */ +#define SPR_SLER 955 /* Storage Little Endian Register */ +#define SPR_SPRG0 272 /* SPR General 0 */ +#define SPR_SPRG1 273 /* SPR General 1 */ +#define SPR_SPRG2 274 /* SPR General 2 */ +#define SPR_SPRG3 275 /* SPR General 3 */ + +#define SPR_USPRG0 256 /* user SPR G0 */ + +/* beware that these registers differ in R/W ability on 440 compared to 405 */ +#define SPR_SPRG4R SPR_SPRG4W /* SPR general 4 supervisor R*/ +#define SPR_SPRG5R SPR_SPRG5W /* SPR general 5; supervisor R */ +#define SPR_SPRG6R SPR_SPRG6W /* SPR general 6; supervisor R */ +#define SPR_SPRG7R SPR_SPRG7W /* SPR general 7; supervisor R */ +#define SPR_SPRG4W 0x114 /* SPR General 4; supervisor R/W */ +#define SPR_SPRG5W 0x115 /* SPR General 5; supervisor R/W */ +#define SPR_SPRG6W 0x116 /* SPR General 6; supervisor R/W */ +#define SPR_SPRG7W 0x117 /* SPR General 7; supervisor R/W */ + +#define SPR_SRR0 26 /* Save/Restore Register 0 */ +#define SPR_SRR1 27 /* Save/Restore Register 1 */ +#define SPR_SRR2 990 /* Save/Restore Register 2 */ +#define SPR_SRR3 991 /* Save/Restore Register 3 */ +#define SPR_SU0R 956 /* Storage User-defined 0 Register */ +#define SPR_TBL 284 /* Time Base Lower */ +#define SPR_TBU 85 /* Time Base Upper */ +#define SPR_TCR 986 /* Time Control Register */ +#define SPR_TSR 984 /* Time Status Register */ +#define SPR_ZPR 944 /* Zone Protection Register */ + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPR_SPRG0 +#define SAVER1 SPR_SPRG1 +#define SAVELR SPR_SPRG2 +#define SAVEXX SPR_SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +#define TBRL 268 /* read time base lower in MFTB */ +#define TBRU 269 /* read time base upper in MFTB */ +#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1)) + +#define TLBIA WORD $((31<<26)|(370<<1)) +#define TLBSYNC WORD $((31<<26)|(566<<1)) + +/* 400 models; perhaps others */ +#define ICCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(966<<1)) +#define DCCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(454<<1)) +/* these follow the source -> dest ordering */ +#define DCREAD(s,t) WORD $((31<<26)|((t)<<21)|((s)<<11)|(486<<1)) +/* #define MSYNC WORD $((31<<26)|(598<<1)) /* in the 405? */ +#define TLBRELO(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1)) +#define TLBREHI(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1)) +#define TLBWELO(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1)) +#define TLBWEHI(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1)) +#define TLBSXF(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)) +#define TLBSXCC(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1) +#define WRTMSR_EE(s) WORD $((31<<26)|((s)<<21)|(131<<1)); MSRSYNC +#define WRTMSR_EEI(e) WORD $((31<<26)|((e)<<15)|(163<<1)); MSRSYNC + +/* + * there are three flavours of barrier: MBAR, MSYNC and ISYNC. + * ISYNC is a context sync, a strong instruction barrier. + * MSYNC is an execution sync (weak instruction barrier) + data storage barrier. + * MBAR is a memory (data storage) barrier. + */ +#define MBAR EIEIO +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +/* + * on the 400 series, the prefetcher madly fetches across RFI, sys call, + * and others; use BR 0(PC) to stop it. + */ +#define RFI WORD $((19<<26)|(50<<1)); BR 0(PC) +#define RFCI WORD $((19<<26)|(51<<1)); BR 0(PC) + +#define ORI(imm, reg) WORD $((24<<26) | (reg)<<21 | (reg)<<16 | (imm)) +#define ORIS(imm, reg) WORD $((25<<26) | (reg)<<21 | (reg)<<16 | (imm)) + +/* print progress character. steps on R7 and R8, needs SB set. */ +#define PROG(c) MOVW $(Uartlite+4), R7; MOVW $(c), R8; MOVW R8, 0(R7) + +#define UREGSPACE (UREGSIZE+8) + + NOSCHED + + TEXT start(SB), 1, $-4 + + /* + * our bootstrap may have already turned on the MMU. + * setup MSR + * turn off interrupts, FPU & MMU + * use 0x000 as exception prefix + * don't enable machine check until the vector is set up + */ + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R3 + RLWNM $0, R3, $~MSR_FP, R3 + RLWNM $0, R3, $~(MSR_IR|MSR_DR), R3 + RLWNM $0, R3, $~MSR_ME, R3 + ISYNC + MOVW R3, MSR + MSRSYNC + + /* except during trap handling, R0 is zero from now on */ + MOVW $0, R0 + MOVW R0, CR + + /* setup SB for pre mmu */ + MOVW $setSB-KZERO(SB), R2 /* SB until mmu on */ + +PROG('\r') +PROG('\n') +PROG('P') + + MOVW $18, R18 + MOVW $19, R19 + MOVW $20, R20 + MOVW $21, R21 + MOVW $22, R22 + MOVW $23, R23 + MOVW $24, R24 + + /* + * reset the caches and disable them until mmu on + */ + MOVW R0, SPR(SPR_ICCR) + ICCCI(0, 2) /* errata cpu_121 reveals that EA is used; we'll use SB */ + ISYNC + DCCCI(0, 0) + MSRSYNC + + MOVW $((DCACHEWAYSIZE/DCACHELINESZ)-1), R3 + MOVW R3, CTR + MOVW R0, R3 +dcinv: + DCCCI(0,3) + ADD $32, R3 + BDNZ dcinv + + /* + * cache is write-back; no user-defined 0; big endian throughout. + * write-through cache would require putting ~0 into R3. + */ + MOVW R0, SPR(SPR_DCWR) /* write-through nowhere: write-back */ + + /* starting from the high bit, each bit represents 128MB */ + MOVW R0, R3 /* region bits */ + MOVW R3, SPR(SPR_DCCR) /* caches off briefly */ + MOVW R3, SPR(SPR_ICCR) + ISYNC + MOVW R0, SPR(SPR_SU0R) + MOVW R0, SPR(SPR_SLER) + ISYNC + + /* + * CCR0: + * 1<<25 LWL load word as line + * 1<<11 PFC prefetching for cacheable regions + */ + MOVW SPR(SPR_CCR0), R4 + OR $((1<<25)|(1<<11)), R4 + MOVW R4, SPR(SPR_CCR0) + + /* R3 still has region bits */ + NOR R3, R3 /* no speculative access in uncached mem */ + MOVW R3, SPR(SPR_SGR) + ISYNC + + /* + * set other system configuration values + */ + MOVW R0, SPR(SPR_PIT) + MOVW $~0, R3 + MOVW R3, SPR(SPR_TSR) + +PROG('l') + BL kernelmmu(SB) + /* now running with MMU on */ + + /* set R2 to correct value */ + MOVW $setSB(SB), R2 + + /* + * now running with MMU in kernel address space + * invalidate the caches again to flush any addresses + * below KZERO + */ + ICCCI(0, 2) /* errata cpu_121 reveals that EA is used; we'll use SB */ + ISYNC + +PROG('a') + + /* + * config caches for kernel in real mode; data is write-through. + * cache bottom 128MB (dram) & top 128MB (sram), but not I/O reg.s. + */ + MOVW $((1<<31) | (1<<0)), R3 + MOVW R3, SPR(SPR_DCCR) + MOVW R3, SPR(SPR_ICCR) + ISYNC + /* R3 still has region bits */ + NOR R3, R3 /* no speculative access in uncached mem */ + MOVW R3, SPR(SPR_SGR) + ISYNC + + /* no kfpinit on 4xx */ + + MOVW dverify(SB), R3 + MOVW $0x01020304, R4 + CMP R3, R4 + BEQ dataok + +PROG('?') + /* seriously bad news, punt to vector 0x1500 (unused) */ + MOVW $(PHYSSRAM + 0x1500), R3 + BL 0(R3) + +dataok: + /* set up Mach */ + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 /* set stack */ +/* + * mach0 is in bss, so this loop is redundant + SUB $4, R(MACH), R3 + ADD $4, R1, R4 +clrmach: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrmach + */ + +PROG('n') + MOVW $edata-4(SB), R3 + MOVW $end(SB), R4 +clrbss: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrbss + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + +PROG(' ') +PROG('9') +PROG('\r') +PROG('\n') + BL main(SB) + BR 0(PC) /* paranoia -- not reached */ + +GLOBL mach0(SB), $(MAXMACH*BY2PG) + +TEXT kernelmmu(SB), 1, $-4 + TLBIA + ISYNC + SYNC + + /* make following TLB entries shared, TID=PID=0 */ + MOVW R0, SPR(SPR_PID) + ISYNC + + /* zone 0 is superviser-only; zone 1 is user and supervisor; all access controlled by TLB */ + MOVW $((0<<30)|(1<<28)), R5 + MOVW R5, SPR(SPR_ZPR) + + /* map various things 1:1 */ + MOVW $tlbtab-KZERO(SB), R4 + MOVW $tlbtabe-KZERO(SB), R5 + SUB R4, R5 + /* size in bytes is now in R5 */ + MOVW $(2*4), R6 + DIVW R6, R5 + /* Number of TLBs is now in R5 */ + SUB $4, R4 + MOVW R5, CTR + /* at start of this loop, # TLBs in CTR, R3 is tlb index 0, R4 is pointing at + * tlbstart[-1] (for pre-increment below) + */ + /* last thing to do: use 63 as index as we put kernel TLBs at top */ + MOVW $63, R3 +ltlb: + MOVWU 4(R4), R5 /* TLBHI */ + TLBWEHI(5,3) + MOVWU 4(R4), R5 /* TLBLO */ + TLBWELO(5,3) + SUB $1, R3 + BDNZ ltlb + + /* enable MMU */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SPR_SRR0) + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR), R4 + MOVW R4, SPR(SPR_SRR1) +/* ISYNC /* no ISYNC here as we have no TLB entry for the PC (without KZERO) */ + SYNC /* fix 405 errata cpu_210 */ + RFI /* resume in kernel mode in caller */ + +TEXT splhi(SB), 1, $-4 + MOVW MSR, R3 + WRTMSR_EEI(0) + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + RETURN + +/* + * everything from here up to, but excluding, spldone + * will be billed by devkprof to the pc saved when we went splhi. + */ +TEXT spllo(SB), 1, $-4 + MOVW MSR, R3 + WRTMSR_EEI(1) + RETURN + +TEXT splx(SB), 1, $-4 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + /* fall though */ + +TEXT splxpc(SB), 1, $-4 + WRTMSR_EE(3) + RETURN + +/***/ +TEXT spldone(SB), 1, $-4 + RETURN + +TEXT islo(SB), 1, $-4 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), 1, $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), 1, $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +TEXT touser(SB), 1, $-4 + WRTMSR_EEI(0) + MOVW R(USER), R4 /* up */ + MOVW 8(R4), R4 /* up->kstack */ + RLWNM $0, R4, $~KZERO, R4 /* PADDR(up->kstack) */ + ADD $(KSTACK-UREGSPACE), R4 + MOVW R4, SPR(SPR_SPRG7W) /* save for use in traps/interrupts */ + MOVW $(UTZERO+32), R5 /* header appears in text */ + MOVW $UMSR, R4 + MOVW R4, SPR(SPR_SRR1) + MOVW R3, R1 + MOVW R5, SPR(SPR_SRR0) + ISYNC + SYNC /* fix 405 errata cpu_210 */ + RFI + +TEXT icflush(SB), 1, $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(ICACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(ICACHELINESZ-1), R4 + SRAW $ICACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $ICACHELINESZ, R5 + BDNZ icf0 + ISYNC + RETURN + +TEXT dcflush(SB), 1, $-4 /* dcflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(DCACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(DCACHELINESZ-1), R4 + SRAW $DCACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $DCACHELINESZ, R5 + BDNZ dcf0 +dcf1: + SYNC + RETURN + +TEXT tas32(SB), 1, $0 + SYNC + MOVW R3, R4 + MOVW $0xdead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + DCBT (R4) /* fix 405 errata cpu_210 */ + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT _xinc(SB), 1, $-4 /* int _xinc(int*); */ +TEXT ainc(SB), 1, $-4 /* int ainc(int*); */ + MOVW R3, R4 +_ainc: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $1, R3 + DCBT (R4) /* fix 405 errata cpu_210 */ + STWCCC R3, (R4) + BNE _ainc + + CMP R3, $0 /* overflow if -ve or 0 */ + BGT _return +_trap: + MOVW $0, R0 + MOVW (R0), R0 /* over under sideways down */ +_return: + RETURN + +TEXT _xdec(SB), 1, $-4 /* int _xdec(int*); */ +TEXT adec(SB), 1, $-4 /* int adec(int*); */ + MOVW R3, R4 +_adec: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $-1, R3 + DCBT (R4) /* fix 405 errata cpu_210 */ + STWCCC R3, (R4) + BNE _adec + + CMP R3, $0 /* underflow if -ve */ + BLT _trap + + RETURN + +TEXT cas32(SB), 1, $0 /* int cas32(void*, u32int, u32int) */ + MOVW R3, R4 /* addr */ + MOVW old+4(FP), R5 + MOVW new+8(FP), R6 + DCBF (R4) /* fix for 603x bug? */ + LWAR (R4), R3 + CMP R3, R5 + BNE fail + DCBT (R4) /* fix 405 errata cpu_210 */ + STWCCC R6, (R4) + BNE fail + MOVW $1, R3 + RETURN +fail: + MOVW $0, R3 + RETURN + +TEXT getpit(SB), 1, $0 + MOVW SPR(SPR_PIT), R3 + RETURN + +TEXT putpit(SB), 1, $0 + MOVW R3, SPR(SPR_PIT) + RETURN + +TEXT putpid(SB), 1, $0 + ISYNC + MOVW R3, SPR(SPR_PID) + ISYNC + RETURN + +TEXT getpid(SB), 1, $0 + MOVW SPR(SPR_PID), R3 + RETURN + +/* 405s have no PIR, so use low bits of PVR, which rae can set. */ +TEXT getpir(SB), 1, $-4 + MOVW SPR(SPR_PVR), R3 + ANDCC $017, R3 + RETURN + +TEXT gettbl(SB), 1, $0 + MFTB(TBRL, 3) + RETURN + +TEXT gettbu(SB), 1, $0 + MFTB(TBRU, 3) + RETURN + +TEXT gettsr(SB), 1, $0 + MOVW SPR(SPR_TSR), R3 + RETURN + +TEXT puttsr(SB), 1, $0 + MOVW R3, SPR(SPR_TSR) + RETURN + +TEXT gettcr(SB), 1, $0 + MOVW SPR(SPR_TCR), R3 + RETURN + +TEXT puttcr(SB), 1, $0 + MOVW R3, SPR(SPR_TCR) + RETURN + +TEXT getpvr(SB), 1, $0 + MOVW SPR(SPR_PVR), R3 + RETURN + +TEXT getmsr(SB), 1, $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), 1, $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT putsr(SB), 1, $0 + MOVW 4(FP), R4 + MOVW R4, SEG(R3) + RETURN + +TEXT getesr(SB), 1, $0 + MOVW SPR(SPR_ESR), R3 + RETURN + +TEXT putesr(SB), 1, $0 + MOVW R3, SPR(SPR_ESR) + ISYNC + RETURN + +TEXT putevpr(SB), 1, $0 + MOVW R3, SPR(SPR_EVPR) + ISYNC + RETURN + +TEXT getccr0(SB), 1, $-4 + MOVW SPR(SPR_CCR0), R3 + RETURN + +TEXT getdear(SB), 1, $0 + MOVW SPR(SPR_DEAR), R3 + RETURN + +TEXT mbar(SB), 1, $-4 +TEXT eieio(SB), 1, $-4 + MBAR + RETURN + +TEXT barriers(SB), 1, $-4 +TEXT sync(SB), 1, $-4 + SYNC + RETURN + +TEXT isync(SB), 1, $-4 + ISYNC + RETURN + +TEXT tlbwrx(SB), 1, $-4 + MOVW hi+4(FP), R5 + MOVW lo+8(FP), R6 + SYNC + TLBWEHI(5, 3) + TLBWELO(6, 3) + ISYNC + SYNC /* paranoia; inferno cerf405 port does this */ + RETURN + +TEXT tlbrehi(SB), 1, $-4 + TLBREHI(3, 3) + RETURN + +TEXT tlbrelo(SB), 1, $-4 + TLBRELO(3, 3) + RETURN + +TEXT tlbsxcc(SB), 1, $-4 + TLBSXCC(0, 3, 3) + BEQ tlbsxcc0 + MOVW $-1, R3 /* not found */ +tlbsxcc0: + RETURN + +TEXT gotopc(SB), 1, $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +TEXT dtlbmiss(SB), 1, $-4 + MOVW R3, SPR(SPR_SPRG1) + MOVW $INT_DMISS, R3 + MOVW R3, SPR(SAVEXX) /* value for cause if entry not in soft tlb */ + MOVW SPR(SPR_DEAR), R3 + BR tlbmiss + +TEXT itlbmiss(SB), 1, $-4 + MOVW R3, SPR(SPR_SPRG1) + MOVW $INT_IMISS, R3 + MOVW R3, SPR(SAVEXX) + MOVW SPR(SPR_SRR0), R3 + +tlbmiss: + /* R3 contains missed address */ + RLWNM $0, R3, $~(BY2PG-1), R3 /* just the page */ + MOVW R2, SPR(SPR_SPRG0) + MOVW R4, SPR(SPR_SPRG2) + MOVW R5, SPR(SPR_SPRG6W) + MOVW R6, SPR(SPR_SPRG4W) + MOVW CR, R6 + MOVW R6, SPR(SPR_SPRG5W) + MOVW $setSB-KZERO(SB), R2 + MOVW $mach0-KZERO(SB), R2 + MOVW (6*4)(R2), R4 /* m->tlbfault++ */ + ADD $1, R4 + MOVW R4, (6*4)(R2) + MOVW SPR(SPR_PID), R4 + SRW $12, R3, R6 + RLWMI $2, R4, $(0xFF<<2), R3 /* shift and insert PID for match */ + XOR R3, R6 /* hash=(va>>12)^(pid<<2); (assumes STLBSIZE is 10 to 12 bits) */ + MOVW (3*4)(R2), R5 /* m->pstlb == PADDR(m->stlb) */ + RLWNM $3, R6, $((STLBSIZE-1)<<3), R6 /* shift to index 8-byte entries, and mask */ + MOVWU (R6+R5), R4 /* fetch Softtlb.hi for comparison; updated address goes to R5 */ + CMP R4, R3 + BNE tlbtrap + MFTB(TBRL, 6) + MOVW (4*4)(R2), R4 /* m->utlbhi */ + RLWNM $0, R6, $(NTLB-1), R6 /* pseudo-random tlb index */ + CMP R6, R4 + BLE tlbm1 + SUB R4, R6 +tlbm1: + RLWNM $0, R3, $~(BY2PG-1), R3 + OR $(TLB4K | TLBVALID), R3 /* make valid tlb hi */ + TLBWEHI(3, 6) + MOVW 4(R5), R4 /* tlb lo; R3 is high */ + TLBWELO(4, 6) + ISYNC + MOVW SPR(SPR_SPRG5R), R6 + MOVW R6, CR + MOVW SPR(SPR_SPRG4R), R6 + MOVW SPR(SPR_SPRG6R), R5 + MOVW SPR(SPR_SPRG2), R4 + MOVW SPR(SPR_SPRG1), R3 + MOVW SPR(SPR_SPRG0), R2 + + ISYNC + SYNC /* fixes 405 errata cpu_210 */ + RFI + +tlbtrap: + MOVW SPR(SPR_SPRG5R), R6 + MOVW R6, CR + MOVW SPR(SPR_SPRG4R), R6 + MOVW SPR(SPR_SPRG6R), R5 + MOVW SPR(SPR_SPRG2), R4 + MOVW SPR(SPR_SPRG1), R3 + MOVW SPR(SPR_SPRG0), R2 + MOVW R0, SPR(SAVER0) + MOVW LR, R0 + MOVW R0, SPR(SAVELR) + BR trapcommon + +/* + * traps force memory mapping off. + * the following code has been executed at the exception + * vector location already: + * MOVW R0, SPR(SAVER0) + * MOVW LR, R0 + * MOVW R0, SPR(SAVELR) + * bl trapvec(SB) + */ +TEXT trapvec(SB), 1, $-4 + MOVW LR, R0 + RLWNM $0, R0, $~0x1F, R0 /* mask LR increment to get ivoff */ + MOVW R0, SPR(SAVEXX) /* save ivoff--interrupt vector offset */ +trapcommon: /* entry point for critical interrupts */ + + MOVW R1, SPR(SAVER1) /* save stack pointer */ + /* did we come from user space */ + MOVW SPR(SPR_SRR1), R0 + MOVW CR, R1 + MOVW R0, CR + BC 4,17,ktrap /* if MSR[PR]=0, we are in kernel space */ + + /* switch to kernel stack */ + MOVW R1, CR + MOVW SPR(SPR_SPRG7R), R1 /* PADDR(up->kstack+KSTACK-UREGSPACE), set in touser and sysrforkret */ + BL saveureg(SB) + MOVW $mach0(SB), R(MACH) + MOVW 8(R(MACH)), R(USER) + BL trap(SB) + BR restoreureg + +ktrap: + MOVW R1, CR + MOVW SPR(SAVER1), R1 + RLWNM $0, R1, $~KZERO, R1 /* PADDR(R1) */ + SUB $UREGSPACE, R1 /* push onto current kernel stack */ + BL saveureg(SB) + BL trap(SB) + +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, LR + MOVW 20(R1), R0 + MOVW R0, SPR(SPR_SPRG7W) /* PADDR(up->kstack etc.) for traps from user space */ + MOVW 16(R1), R0 + MOVW R0, SPR(SPR_SRR0) /* old PC */ + MOVW 12(R1), R0 + RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */ + MOVW R0, SPR(SPR_SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVER0), R0 + ISYNC + SYNC /* fixes 405 errata cpu_210 */ + RFI + +/* + * critical trap/interrupt. + * the only one we can take is machine check, synchronously, and + * outside any other trap handler. + * [MSR_ME not cleared => handler may be interrupted by machine check] + */ +TEXT trapcritvec(SB), 1, $-4 + MOVW LR, R0 + MOVW R0, SPR(SAVEXX) + MOVW SPR(SPR_SRR2), R0 /* PC or excepting insn */ + MOVW R0, SPR(SPR_SRR0) + MOVW SPR(SPR_SRR3), R0 /* old MSR */ + MOVW R0, SPR(SPR_SRR1) + BR trapcommon + +/* + * enter with stack set and mapped. + * on return, R0 is zero, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * Stack (R1), R(MACH) and R(USER) are set by caller, if required. + */ +TEXT saveureg(SB), 1, $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* save gprs r2 to r31 */ + MOVW $setSB(SB), R2 + 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) + MOVW SPR(SPR_SPRG7R), R6 /* PADDR(up->kstack+KSTACK-UREGSPACE) */ + MOVW R6, 20(R1) + MOVW SPR(SPR_SRR0), R0 + MOVW R0, 16(R1) /* PC of excepting insn (or next insn) */ + MOVW SPR(SPR_SRR1), R0 + MOVW R0, 12(R1) /* old MSR */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + OR $KZERO, R3 /* fix ureg */ + DCBT (R1) /* fix 405 errata cpu_210 */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(MSR_IR|MSR_DR), R5 /* enable MMU */ + MOVW R5, SPR(SPR_SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SPR_SRR0) + OR $KZERO, R1 /* fix stack pointer */ +/* ISYNC /* no ISYNC here either */ + SYNC /* fix 405 errata cpu_210 */ + RFI /* returns to trap handler */ + +/* + * restore state from Ureg and return from trap/interrupt + */ +TEXT forkret(SB), 1, $-4 + RLWNM $0, R1, $~KZERO, R2 /* PADDR(up->kstack+KSTACK-UREGSPACE) */ + MOVW R2, 20(R1) /* set in ureg */ + BR restoreureg + +/* + * 4xx specific + */ +TEXT firmware(SB), 1, $0 + MOVW $(3<<28), R3 + MOVW R3, SPR(SPR_DBCR0) /* system reset */ + BR 0(PC) diff -Nru /sys/src/9/vt4/main.c /sys/src/9/vt4/main.c --- /sys/src/9/vt4/main.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/main.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,606 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../ip/ip.h" + +#include +#include + +#include "qtm.h" + +#include "init.h" + +#define MAXCONF 32 + +ulong dverify = 0x01020304; +ulong bverify; +int securemem; +Conf conf; + +/* + * Option arguments from the command line. + * oargv[0] is the boot file. + * Optionsinit() is called from multiboot() + * or some other machine-dependent place + * to set it all up. + */ +static int oargc; +static char* oargv[20]; +static char oargb[128]; +static int oargblen; +static char oenv[4096]; + +static uintptr sp; /* XXX - must go - user stack of init proc */ + +static char confname[MAXCONF][KNAMELEN]; +static char *confval[MAXCONF]; +static int nconf; + +static void +mypanic(Pool *p, char *fmt, ...) +{ + USED(p, fmt); + print("malloc panic\n"); + delay(1000); + splhi(); + for (;;) + ; +} + +void +machninit(int machno) +{ + if (machno >= MAXMACH) + panic("machno %d >= MAXMACH %d", machno, MAXMACH); + conf.nmach++; + m = MACHP(machno); + memset(m, 0, sizeof(Mach)); + m->machno = machno; + m->cputype = getpvr()>>16; /* OWN[0:11] and PCF[12:15] fields */ + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->perf.period = 1; + /* other values set by archreset */ +} + +void +cpuidprint(void) +{ + char name[64]; + + cputype2name(name, sizeof name); + print("cpu%d: %ldMHz PowerPC %s\n", m->machno, m->cpuhz/1000000, name); +} + +void +mach0init(void) +{ + conf.nmach = 0; + + machninit(0); + + active.machs = 1; + active.exiting = 0; +} + +static void +setconf(char *name, char *val) +{ + strncpy(confname[nconf], name, KNAMELEN); + kstrdup(&confval[nconf], val); + nconf++; +} + +static void +plan9iniinit(void) +{ + /* virtex configuration */ + setconf("nvram", "/boot/nvram"); + setconf("nvroff", "0"); + setconf("nvrlen", "512"); + + setconf("aoeif", "ether0"); + setconf("aoedev", "e!#æ/aoe/1.0"); +} + +char* +getenv(char* name, char* buf, int n) +{ + char *e, *p, *q; + + p = oenv; + while(*p != 0){ + if((e = strchr(p, '=')) == nil) + break; + for(q = name; p < e; p++){ + if(*p != *q) + break; + q++; + } + if(p == e && *q == 0){ + strecpy(buf, buf+n, e+1); + return buf; + } + p += strlen(p)+1; + } + + return nil; +} + +static void +optionsinit(char* s) +{ + char *o; + + o = strecpy(oargb, oargb+sizeof(oargb), s)+1; + if(getenv("bootargs", o, o - oargb) != nil) + *(o-1) = ' '; + + oargblen = strlen(oargb); + oargc = tokenize(oargb, oargv, nelem(oargv)-1); + oargv[oargc] = nil; +} + +void +main(void) +{ + int machno; + + /* entry to main pushed stuff onto the stack. */ + +// memset(edata, 0, (ulong)end-(ulong)edata); + + machno = getpir(); + if (machno > 0) + startcpu(machno); + +// dcrcompile(); + if (dverify != 0x01020304) { + uartlputs("data segment not initialised\n"); + panic("data segment not initialised"); + } + if (bverify != 0) { + uartlputs("bss segment not zeroed\n"); + panic("bss segment not zeroed"); + } + mach0init(); + archreset(); + quotefmtinstall(); + optionsinit("/boot/boot boot"); +// archconsole(); + + meminit(); + confinit(); + mmuinit(); + xinit(); /* xinit would print if it could */ + trapinit(); + qtminit(); + ioinit(); + uncinit(); + printinit(); + uartliteconsole(); + + mainmem->flags |= POOL_ANTAGONISM; + mainmem->panic = mypanic; + ethermedium.maxtu = 1512; /* must be multiple of 4 for temac's dma */ + +// print("\n\nPlan 9k H\n"); /* already printed by l.s */ + plan9iniinit(); + timersinit(); + clockinit(); + + dma0init(); /* does not start kprocs; see init0 */ + fpuinit(); + procinit0(); + initseg(); + links(); + + chandevreset(); + okprint = 1; /* only now can we print */ + barriers(); + dcflush((uintptr)&okprint, sizeof okprint); + + cpuidprint(); + + print("%d Hz clock", HZ); + print("; memory size %,ud (%#ux)\n", (uint)memsz, (uint)memsz); + + pageinit(); + swapinit(); + userinit(); + active.thunderbirdsarego = 1; + dcflush((uintptr)&active.thunderbirdsarego, + sizeof active.thunderbirdsarego); + schedinit(); + /* no return */ + panic("schedinit returned"); +} + +void +confinit(void) +{ + char *p; + int i, userpcnt; + ulong kpages; + + if(p = getconf("*kernelpercent")) + userpcnt = 100 - strtol(p, 0, 0); + else + userpcnt = 85; + + conf.npage = 0; + for(i=0; i 2000) + conf.nproc = 2000; + conf.nimage = 200; + conf.nswap = conf.nproc*80; + conf.nswppo = 4096; + conf.copymode = 0; /* copy on write */ + + if(userpcnt < 10) + userpcnt = 60; + kpages = conf.npage - (conf.npage*userpcnt)/100; + + conf.upages = conf.npage - kpages; + conf.ialloc = (kpages/2)*BY2PG; + + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page); + mainmem->maxsize = kpages; +} + +void +init0(void) +{ + int i; + char buf[2*KNAMELEN]; + + assert(up != nil); + up->nerrlab = 0; + barriers(); + intrack(~0); + clrmchk(); + barriers(); + 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); + + dmainit(); /* starts dma kprocs */ + devtabinit(); + + if(!waserror()){ + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + ksetenv("cputype", "power", 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + + /* virtex configuration */ + ksetenv("nvram", "/boot/nvram", 0); + ksetenv("nvroff", "0", 0); + ksetenv("nvrlen", "512", 0); + + ksetenv("nobootprompt", "tcp", 0); + + poperror(); + } + for(i = 0; i < nconf; i++){ + if(confval[i] == nil) + continue; + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + if(!waserror()){ + ksetenv(confname[i], confval[i], 1); + poperror(); + } + } + + kproc("alarm", alarmkproc, 0); + if (securemem) + kproc("mutate", mutateproc, 0); + else + print("no secure memory found\n"); + + /* + * The initial value of the user stack must be such + * that the total used is larger than the maximum size + * of the argument list checked in syscall. + */ + sync(); + isync(); + touser(sp); +} + +static void +bootargs(uintptr base) +{ + int i; + ulong ssize; + char **av, *p; + + /* + * Push the boot args onto the stack. + * The initial value of the user stack must be such + * that the total used is larger than the maximum size + * of the argument list checked in syscall. + */ + i = oargblen+1; + p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(up->s.args) - i)); + memmove(p, oargb, i); + + /* + * Now push argc and the argv pointers. + * This isn't strictly correct as the code jumped to by + * touser in init9.s calls startboot (port/initcode.c) which + * expects arguments + * startboot(char *argv0, char **argv) + * not the usual (int argc, char* argv[]), but argv0 is + * unused so it doesn't matter (at the moment...). + */ + av = (char**)(p - (oargc+2)*sizeof(char*)); + ssize = base + BY2PG - PTR2UINT(av); + *av++ = (char*)oargc; + for(i = 0; i < oargc; i++) + *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG); + *av = nil; + + /* + * Leave space for the return PC of the + * caller of initcode. + */ + sp = USTKTOP - ssize - sizeof(void*); +} + +void +userinit(void) +{ + Proc *p; + Segment *s; + KMap *k; + Page *pg; + + /* no processes yet */ + up = nil; + + 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); + + /* + * Kernel Stack + */ + p->sched.pc = PTR2UINT(init0); + p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr)); + p->sched.sp = STACKALIGN(p->sched.sp); + + /* + * User Stack + * + * Technically, newpage can't be called here because it + * should only be called when in a user context as it may + * try to sleep if there are no pages available, but that + * shouldn't be the case here. + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + segpage(s, pg); + k = kmap(pg); + bootargs(VA(k)); + kunmap(k); + + /* + * 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(UINT2PTR(VA(k)), initcode, sizeof initcode); + sync(); + kunmap(k); + + ready(p); +} + +/* + * power-saving wait for interrupt when nothing ready. + * ../port/proc.c should really call this splhi itself + * to avoid the race avoided here by the call to anyready + */ +void +idlehands(void) +{ + int s, oldbits; + + /* we only use one processor, no matter what */ +// if (conf.nmach > 1) +// return; + s = splhi(); + if(!anyready()) { + oldbits = lightstate(Ledidle); + putmsr(getmsr() | MSR_WE | MSR_EE | MSR_CE); /* MSR_DE too? */ + lightstate(oldbits); + } + splx(s); +} + +/* + * set mach dependent process state for a new process + */ +void +procsetup(Proc* p) +{ + fpusysprocsetup(p); +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + fpuprocsave(p); +} + +void +shutdown(int ispanic) +{ + int ms, once; + + lock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<machno); + active.machs &= ~(1<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; + } + +#ifdef notdef + if(active.ispanic && m->machno == 0){ + if(cpuserver) + delay(30000); + else + for(;;) + halt(); + } + else +#endif /* notdef */ + delay(1000); +} + +void +writeconf(void) +{ + char *p, *q; + int n; + + p = getconfenv(); + + if(waserror()) { + free(p); + nexterror(); + } + + /* convert to name=value\n format */ + for(q=p; *q; q++) { + q += strlen(q); + *q = '='; + q += strlen(q); + *q = '\n'; + } + n = q - p + 1; + if(n >= BOOTARGSLEN) + error("kernel configuration too large"); + memset(BOOTLINE, 0, BOOTLINELEN); /* zero 9load boot line */ + memmove(BOOTARGS, p, n); + poperror(); + free(p); +} + +void +archreboot(void) +{ + splhi(); + iprint("reboot requested; going into wait state\n"); + for(;;) + putmsr(getmsr() | MSR_WE); +} + +void +exit(int ispanic) +{ + shutdown(ispanic); + archreboot(); +} + +static int +findconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return i; + return -1; +} + +void +addconf(char *name, char *val) +{ + int i; + + i = findconf(name); + if(i < 0){ + if(val == nil || nconf >= MAXCONF) + return; + i = nconf++; + strecpy(confname[i], confname[i]+sizeof(confname[i]), name); + } + confval[i] = val; +} + +char* +getconf(char *name) +{ + int i; + + i = findconf(name); + if(i >= 0) + return confval[i]; + return nil; +} + +void +dump(void *vaddr, int words) +{ + ulong *addr; + + addr = vaddr; + while (words-- > 0) + print("%.8lux%c", *addr++, words % 8 == 0? '\n': ' '); +} diff -Nru /sys/src/9/vt4/mem.h /sys/src/9/vt4/mem.h --- /sys/src/9/vt4/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/mem.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,211 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ +#define KiB 1024u /* Kibi 0x0000000000000400 */ +#define MiB 1048576u /* Mebi 0x0000000000100000 */ +#define GiB 1073741824u /* Gibi 000000000040000000 */ +#define TiB 1099511627776ull /* Tebi 0x0000010000000000 */ +#define PiB 1125899906842624ull /* Pebi 0x0004000000000000 */ +#define EiB 1152921504606846976ull /* Exbi 0x1000000000000000 */ + +/* + * 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 BY2SE 4 /* bytes per stack element */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */ + +#define BY2PTE 8 /* bytes per pte entry */ +#define BY2PTEG 64 /* bytes per pte group */ + +#define ICACHESIZE 16384 /* 0, 4, 8, 16, or 32 KB */ +#define ICACHEWAYSIZE (ICACHESIZE/2) /* 2-way set associative */ +#define ICACHELINELOG 5 /* 8 words (4 bytes) per line */ +#define ICACHELINESZ (1< */ +#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)) + +#define PTEVALID (1<<0) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEUNCACHED (1<<2) + +/* + * Physical MMU + */ +#define NTLB 64 /* number of entries */ +#define NTLBPID 256 /* number of hardware pids (0 = global) */ + +/* TLBHI */ +#define TLBEPN(x) ((x) & ~0x3FF) +#define TLB1K (0<<7) +#define TLB4K (1<<7) +#define TLB16K (2<<7) +#define TLB64K (3<<7) +#define TLB256K (4<<7) +#define TLB1MB (5<<7) +#define TLB4MB (6<<7) +#define TLB16MB (7<<7) +#define TLBVALID (1<<6) +#define TLBLE (1<<5) /* little-endian */ +#define TLBU0 (1<<4) /* user-defined attribute */ + +/* TLBLO */ +#define TLBRPN(x) ((x) & ~0x3FF) +#define TLBEX (1<<9) /* execute enable */ +#define TLBWR (1<<8) /* write enable */ +#define TLBZONE(x) ((x)<<4) +#define TLBW (1<<3) /* write-through */ +#define TLBI (1<<2) /* cache inhibit */ +#define TLBM (1<<1) /* memory coherent */ +/* + * WARNING: applying TLBG to instruction pages will cause ISI traps + * on Xilinx 405s, despite the words of the Xilinx manual (p. 155). + */ +#define TLBG (1<<0) /* guarded */ + +/* + * software TLB (for quick reload by [id]tlbmiss) + */ +#define STLBLOG 10 +#define STLBSIZE (1<$target.list + size $target + +$p$CONF.elf:DQ: $CONF.$O $OBJ $LIB + echo '# linking elf kernel' + $LD -H5 -R$pagesize -T$loadaddr -P$physaddr -o $target -l $OBJ $CONF.$O $LIB + +$p$CONF.gz:D: $p$CONF + gzip -9 <$p$CONF >$target + +$OBJ: $HFILES + +install:V: /$objtype/$p$CONF + +/$objtype/$p$CONF:D: $p$CONF $p$CONF.elf + cp -x $p$CONF $p$CONF.elf /$objtype + { 9fs lookout && cp -x $p$CONF $p$CONF.elf /n/lookout/$objtype } & + { 9fs piestand && cp -x $p$CONF $p$CONF.elf /n/piestand/$objtype } & + { 9fs slocum && cp -x $p$CONF.list /n/slocum/home/rae/hbsr/ml410_ddr2 } & + wait + touch $target + +<../boot/bootmkfile +<../port/portmkfile +<|../port/mkbootrules $CONF + +archvt4.$O: io.h +devboot.$O: ../port/error.h +devether.$O: ../port/error.h ../port/netif.h etherif.h +ethertemac.$O: ../port/netif.h etherif.h io.h +etherplbtemac.$O: ../port/netif.h etherif.h io.h +fpi.$O: fpi.h +fpimem.$O: fpi.h +fpipower.$O: /$objtype/include/ureg.h fpi.h +fpu.$O: /$objtype/include/ureg.h +main.$O: /sys/include/pool.h /sys/include/tos.h init.h reboot.h +random.$O: ../port/error.h +syscall.$O: ../port/error.h +syscall.$O: /sys/include/tos.h /$objtype/include/ureg.h +trap.$O: /sys/include/tos.h /$objtype/include/ureg.h io.h +uartlite.$O: ../port/error.h io.h + +init.h:D: ../port/initcode.c init9.s + $CC ../port/initcode.c + $AS init9.s + $LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a + {echo 'uchar initcode[]={' + xd -1x init.h + +init.out:D: init9.$O initcode.$O /$objtype/lib/libc.a + $LD -l -R4 -o init.out init9.$O initcode.$O -lc + +reboot.h:D: rebootcode.s + $AS rebootcode.s + # -lc is only for memmove. -T arg is PADDR(REBOOTADDR) + $LD -l -a -s -T0x2640 -R4 -o reboot.out rebootcode.$O -lc >reboot.list + {echo 'uchar rebootcode[]={' + xd -1x reboot.out | + sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' + echo '};'} > reboot.h +errstr.h:D: ../port/mkerrstr ../port/error.h + rc ../port/mkerrstr > errstr.h + +$CONF.clean: + rm -rf $p$CONF $p$CONF.elf $CONF.c boot$CONF.c ../boot/libboot.aq diff -Nru /sys/src/9/vt4/mmu.c /sys/src/9/vt4/mmu.c --- /sys/src/9/vt4/mmu.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/mmu.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,381 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * l.s has set some global TLB entries for the kernel at + * the top of the tlb (NTLB-1 down), partly to account for + * the way the firmware sets things up on some platforms (eg, 440). + * The first entry not used by the kernel (eg, by kmapphys) is m->utlbhi. + * User tlbs are assigned indices from 0 to m->utlbhi, round-robin. + * m->utlbnext is the next to use. Kernel tlbs are added at utlbhi, which then moves. + * + * In this version, the kernel TLB entries are in zone 0, + * and user pages are in zone 1. The kernel entries are also PID 0 (global) + * so they are usable as the PID changes (0=no user; non-zero=user process mapped). + */ + +enum { + DEBUG = 0, + Minutlb= NTLB/2, /* at least this many for user process */ +}; + +Softtlb softtlb[MAXMACH][STLBSIZE]; + +static int newtlbpid(Proc*); +static void purgetlb(int); +static void putstlb(int, u32int, u32int); + +/* clobbers SPR_PID */ +static int +probehi(uintptr va) +{ + int i; + + for (i = 0; i < NTLBPID; i++) { + putpid(i); + if (tlbsxcc(va) >= 0) + return i; + } + return -1; +} + +void +tlbdump(char *s) +{ + int i, p, ppid; + u32int hi, lo; + + p = getpid(); + iprint("tlb[%s] pid %d lastpid %d\n", s, p, m->lastpid); + for(i=0; iutlbhi) + iprint("-----\n"); + } + putpid(p); /* tlbrehi changes PID */ +} + +/* + * l.s provides a set of tlb entries for the kernel, allocating + * them starting at the highest index down. user-level entries + * will be allocated from index 0 up. + */ +void +mmuinit(void) +{ + int i; + + if(DEBUG) + tlbdump("init0"); + m->lastpid = 0; + m->utlbhi = 0; + for(i = 0; i < NTLB; i++){ + if(tlbrehi(i) & TLBVALID) + break; + m->utlbhi = i; + tlbwrx(i, 0, 0); + } + if(DEBUG) + tlbdump("init1"); + putpid(0); + + m->stlb = softtlb[m->machno]; + m->pstlb = PADDR(m->stlb); + + /* + * set OCM mapping, assuming: + * caches were invalidated earlier; + * and we aren't currently using it + * must also set a tlb entry that validates the virtual address but + * the translation is not used (see p. 5-2) + */ +/* + putdcr(Isarc, OCMZERO); + putdcr(Dsarc, OCMZERO); + putdcr(Iscntl, Isen); + putdcr(Iscntl, Dsen|Dof); + tlbwrx(tlbx, OCMZERO|TLB4K|TLBVALID, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI); + tlbx--; +*/ +} + +void +flushmmu(void) +{ + int x; + + x = splhi(); + up->newtlb = 1; + mmuswitch(up); + splx(x); +} + +/* + * called with splhi + */ +void +mmuswitch(Proc *p) +{ + int tp; + + if(p->newtlb){ + p->mmupid = 0; + p->newtlb = 0; + } + tp = p->mmupid; + if(tp == 0 && !p->kp) + tp = newtlbpid(p); + putpid(tp); +} + +void +mmurelease(Proc* p) +{ + p->mmupid = 0; + putpid(0); +} + +void +putmmu(uintptr va, uintptr pa, Page* page) +{ + int x, s, tp; + char *ctl; + u32int tlbhi, tlblo; + + if(va >= KZERO) + panic("mmuput"); + + tlbhi = TLBEPN(va) | TLB4K | TLBVALID; + tlblo = TLBRPN(pa) | TLBEX | TLBZONE(1); /* user page */ + if(pa & PTEWRITE) + tlblo |= TLBWR; + /* must not set TLBG on instruction pages */ + if(pa & PTEUNCACHED) + tlblo |= TLBI; + /* else use write-back cache; write-through would need TLBW set */ + + s = splhi(); + tp = up->mmupid; + if(tp == 0){ + if(up->kp) + panic("mmuput kp"); + tp = newtlbpid(up); + putpid(tp); + }else if(getpid() != tp) + panic("mmuput pid %#ux %#ux", tp, getpid()); + + /* see if it's already there: note that tlbsx[cc] uses current PID */ + x = tlbsxcc(va); + if(x < 0){ + if(m->utlbnext > m->utlbhi) + m->utlbnext = 0; + x = m->utlbnext++; + }else if(x > m->utlbhi) /* shouldn't touch kernel entries */ + panic("mmuput index va=%#p x=%d klo=%d", va, x, m->utlbhi); + if(DEBUG) + iprint("put %#p %#p-> %d: %#ux %8.8ux\n", va, pa, x, tlbhi, tlblo); + barriers(); sync(); isync(); + tlbwrx(x, tlbhi, tlblo); + putstlb(tp, TLBEPN(va), tlblo); + barriers(); sync(); isync(); + /* verify that tlb entry was written into the tlb okay */ + if (tlbsxcc(va) < 0) + panic("tlb entry for va %#lux written into but not received by tlb", + va); + splx(s); + + ctl = &page->cachectl[m->machno]; + switch(*ctl){ + case PG_TXTFLUSH: + dcflush(page->va, BY2PG); + icflush(page->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_DATFLUSH: + dcflush(page->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_NEWCOL: +// cleancache(); /* expensive, but fortunately not needed here */ + *ctl = PG_NOFLUSH; + break; + } +} + +/* + * Process must be splhi + */ +static int +newtlbpid(Proc *p) +{ + int i, s; + Proc **h; + + i = m->lastpid; + h = m->pidproc; + for(s = 0; s < NTLBPID; s++) { + i++; + if(i >= NTLBPID) + i = 1; + if(h[i] == nil) + break; + } + + if(h[i] != nil){ + purgetlb(i); + if(h[i] != nil) + panic("newtlb"); + } + + m->pidproc[i] = p; + p->mmupid = i; + m->lastpid = i; + + return i; +} + +static void +purgetlb(int pid) +{ + int i, p; + Proc *sp, **pidproc; + Softtlb *entry, *etab; + u32int hi; + + m->tlbpurge++; + + /* + * find all pid entries that are no longer used by processes + */ + pidproc = m->pidproc; + for(i=1; immupid != i) + pidproc[i] = nil; + } + + /* + * shoot down the one we want + */ + sp = pidproc[pid]; + if(sp != nil) + sp->mmupid = 0; + pidproc[pid] = nil; + + /* + * clean out all dead pids from the stlb; + */ + entry = m->stlb; + for(etab = &entry[STLBSIZE]; entry < etab; entry++) + if(pidproc[(entry->hi>>2)&0xFF] == nil){ + entry->hi = 0; + entry->lo = 0; + } + + /* + * clean up the hardware + */ + p = getpid(); + for(i = 0; i <= m->utlbhi; i++){ + hi = tlbrehi(i); + if(hi & TLBVALID && pidproc[getpid()] == nil) + tlbwrx(i, 0, 0); + } + putpid(p); +} + +/* + * return required size and alignment to map n bytes in a tlb entry + */ +ulong +mmumapsize(ulong n) +{ + ulong size; + int i; + + size = 1024; + for(i = 0; i < 8 && size < n; i++) + size <<= 2; + return size; +} + +/* + * map a physical addresses at pa to va, with the given attributes. + * the virtual address must not be mapped already. + * if va is nil, map it at pa in virtual space. + */ +void* +kmapphys(uintptr va, uintptr pa, ulong nb, ulong attr, ulong le) +{ + int s, i, p; + ulong size; + + if(va == 0) + va = pa; /* simplest is to use a 1-1 map */ + size = 1024; + for(i = 0; i < 8 && size < nb; i++) + size <<= 2; + if(i >= 8) + return 0; + if(m->utlbhi <= Minutlb) + panic("kmapphys"); + s = splhi(); + p = getpid(); + putpid(0); + tlbwrx(m->utlbhi, va | (i<<7) | TLBVALID | le, pa | TLBZONE(0) | attr); + m->utlbhi--; + putpid(p); + splx(s); + if(DEBUG) + tlbdump("kmapphys"); + + return UINT2PTR(va); +} + +/* + * return an uncached alias for the memory at a + * (unused) +void* +mmucacheinhib(void* a, ulong nb) +{ + uintptr pa; + + if(a == nil) + return nil; + dcflush(PTR2UINT(a), nb); + pa = PADDR(a); + return kmapphys(KSEG1|pa, pa, nb, TLBWR | TLBI | TLBG, 0); +} + */ + +static void +putstlb(int pid, u32int va, u32int tlblo) +{ + Softtlb *entry; + + pid <<= 2; + entry = &m->stlb[((va>>12)^pid)&(STLBSIZE-1)]; + entry->hi = va | pid; + entry->lo = tlblo; +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +uintptr +cankaddr(uintptr pa) +{ + if( /* pa >= PHYSDRAM && */ pa < PHYSDRAM + 512*MiB) + return PHYSDRAM + 512*MiB - pa; + return 0; +} Binary files /sys/src/9/vt4/nvram and /sys/src/9/vt4/nvram differ diff -Nru /sys/src/9/vt4/physmem.h /sys/src/9/vt4/physmem.h --- /sys/src/9/vt4/physmem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/physmem.h Fri Jun 7 00:00:00 2013 @@ -0,0 +1,32 @@ +/* + * Memory-mapped IO + */ + +/* + * virtex4 system loses top 1/9th of 128MB to ECC in the secure memory system. + */ +#define MEMTOP(phys) ((((((phys)/32)*8)/9) * 8*BY2WD) & -128) +#define MAXMEM (128*MB) + +/* memory map for rae's virtex4 design */ +#define PHYSDRAM 0 +#define PHYSSRAM 0xfffe0000 /* 128K long, in top 128M */ + +#define PHYSMMIO Io + +#define Io 0xf0000000 /* ~512K of IO registers */ +#define Uartlite 0xf0000000 +#define Gpio 0xf0010000 +#define Intctlr 0xf0020000 +#define Temac 0xf0030000 +#define Llfifo 0xf0040000 +#define Dmactlr 0xf0050000 +#define Dmactlr2 0xf0060000 +/* + * if these devices exist in a given hardware configuration, + * they will be at these addresses. + */ +#define Qtm 0xf0070000 /* encrypted memory control */ +#define Mpmc 0xf0080000 /* multi-port memory controller */ +/* setting low bit interrupts cpu0; don't set Hie */ +#define Intctlr2 0xf0090000 /* sw interrupt controller */ diff -Nru /sys/src/9/vt4/ppcmul64fract.s /sys/src/9/vt4/ppcmul64fract.s --- /sys/src/9/vt4/ppcmul64fract.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/ppcmul64fract.s Fri Jun 7 00:00:00 2013 @@ -0,0 +1,25 @@ + +TEXT mul64fract(SB), 1, $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 -Nru /sys/src/9/vt4/random.c /sys/src/9/vt4/random.c --- /sys/src/9/vt4/random.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/random.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,139 @@ +#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[128]; + 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) +{ + /* Frequency close but not equal to HZ */ + addclock0link(randomclock, 13); + 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 predictable 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 -Nru /sys/src/9/vt4/rebootcmd.c /sys/src/9/vt4/rebootcmd.c --- /sys/src/9/vt4/rebootcmd.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/rebootcmd.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,103 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include + +static ulong +l2be(long l) +{ + uchar *cp; + + cp = (uchar*)&l; + return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3]; +} + + +static void +readn(Chan *c, void *vp, long n) +{ + char *p; + long nn; + + p = vp; + while(n > 0) { + nn = devtab[c->type]->read(c, p, n, c->offset); + if(nn == 0) + error(Eshort); + c->offset += nn; + p += nn; + n -= nn; + } +} + +static void +setbootcmd(int argc, char *argv[]) +{ + char *buf, *p, *ep; + int i; + + buf = malloc(1024); + if(buf == nil) + error(Enomem); + p = buf; + ep = buf + 1024; + for(i=0; ipgsize from libmach */ + rtext = ROUNDUP(entry + text, 1024*1024) - entry; + size = rtext + data; + p = malloc(size); + if(p == nil) + error(Enomem); + + if(waserror()){ + free(p); + nexterror(); + } + + memset(p, 0, size); + readn(c, p, text); + readn(c, p + rtext, data); + + ksetenv("bootfile", argv[0], 1); + setbootcmd(argc-1, argv+1); + + reboot((void*)entry, p, size); + + panic("return from reboot!"); +} diff -Nru /sys/src/9/vt4/rebootcode.s /sys/src/9/vt4/rebootcode.s --- /sys/src/9/vt4/rebootcode.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/rebootcode.s Fri Jun 7 00:00:00 2013 @@ -0,0 +1,141 @@ +/* virtex4 ppc405 reboot code */ +#include "mem.h" + +#define SPR_SRR0 26 /* Save/Restore Register 0 */ +#define SPR_SRR1 27 /* Save/Restore Register 1 */ +#define SPR_DCWR 954 /* Data Cache Write-through Register */ +#define SPR_DCCR 1018 /* Data Cache Cachability Register */ +#define SPR_ICCR 1019 /* Instruction Cache Cachability Register */ + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +#define ICCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(966<<1)) +#define DCCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(454<<1)) + +/* on the 400 series, the prefetcher madly fetches across RFI, sys call, and others; use BR 0(PC) to stop */ +#define RFI WORD $((19<<26)|(50<<1)); BR 0(PC) +#define RFCI WORD $((19<<26)|(51<<1)); BR 0(PC) + +/* print progress character. steps on R7 and R8, needs SB set. */ +#define PROG(c) MOVW $(Uartlite+4), R7; MOVW $(c), R8; MOVW R8, 0(R7); SYNC + +/* + * Turn off MMU, then copy the new kernel to its correct location + * in physical memory. Then jump to the start of the kernel. + */ + + NOSCHED + +TEXT main(SB),1,$-4 + MOVW $setSB+KZERO(SB), R2 + +PROG('*') + MOVW R3, p1+0(FP) /* destination, passed in R3 */ + MOVW R3, R6 /* entry point */ + MOVW p2+4(FP), R4 /* source */ + MOVW n+8(FP), R5 /* byte count */ + + BL mmuoff(SB) + MOVW $setSB(SB), R2 + +// BL resetcaches(SB) + +PROG('*') + MOVW R3, R1 /* tiny trampoline stack */ + SUB $0x20, R1 /* bypass a.out header */ + + MOVWU R0, -48(R1) /* allocate stack frame */ + MOVW R3, 44(R1) /* save dest */ + MOVW R5, 40(R1) /* save count */ + MOVW R3, 0(R1) + MOVW R3, 4(R1) /* push dest */ + MOVW R4, 8(R1) /* push src */ + MOVW R5, 12(R1) /* push size */ + BL memmove(SB) + SYNC + MOVW 44(R1), R6 /* restore R6 (dest) */ + MOVW 40(R1), R5 /* restore R5 (count) */ +PROG('-') + /* + * flush data cache + */ + MOVW R6, R3 /* virtaddr */ + MOVW R5, R4 /* count */ + RLWNM $0, R3, $~(DCACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(DCACHELINESZ-1), R4 + SRAW $DCACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $DCACHELINESZ, R5 + BDNZ dcf0 +dcf1: + SYNC + + /* be sure icache is cleared */ + BL resetcaches(SB) + +/* + * jump to kernel entry point. Note the true kernel entry point is + * the virtual address KZERO|R6, but this must wait until + * the MMU is enabled by the kernel in l.s + */ + OR R6, R6 /* NOP: avoid link bug */ + MOVW R6, LR +PROG('>') +PROG('\r'); +PROG('\n'); + BL (LR) + BR 0(PC) + +TEXT mmuoff(SB), 1, $-4 + MOVW LR, R7 + RLWNM $0, R7, $~KZERO, R7 /* new PC */ + MOVW R7, SPR(SPR_SRR0) + + RLWNM $0, R1, $~KZERO, R1 /* make stack pointer physical */ + + /* + * turn off interrupts & MMU + * disable machine check + */ + MOVW MSR, R7 + RLWNM $0, R7, $~(MSR_EE), R7 + RLWNM $0, R7, $~(MSR_ME), R7 + RLWNM $0, R7, $~(MSR_IR|MSR_DR), R7 + MOVW R7, SPR(SPR_SRR1) +/* ISYNC /* no ISYNC here as we have no TLB entry for the PC */ + SYNC /* fix 405 errata cpu_210 */ + RFI /* resume in kernel mode in caller */ + +/* clobbers R7 */ +TEXT resetcaches(SB),1,$-4 + /* + * reset the caches and disable them + */ + MOVW R0, SPR(SPR_ICCR) + ICCCI(0, 2) /* errata cpu_121 reveals that EA is used; we'll use SB */ + ISYNC + DCCCI(0, 0) + SYNC; ISYNC + + MOVW $((DCACHEWAYSIZE/DCACHELINESZ)-1), R7 + MOVW R7, CTR + MOVW R0, R7 +dcinv: + DCCCI(0,7) + ADD $32, R7 + BDNZ dcinv + + /* cache is copy-back, disabled */ + MOVW R0, SPR(SPR_DCWR) + MOVW R0, SPR(SPR_DCCR) + MOVW R0, SPR(SPR_ICCR) + ISYNC + SYNC + BR (LR) diff -Nru /sys/src/9/vt4/softfpu.c /sys/src/9/vt4/softfpu.c --- /sys/src/9/vt4/softfpu.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/softfpu.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,74 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * Find a way to finesse this away if not needed. + */ +int +fpudevprocio(Proc* proc, void* a, long n, uintptr offset, int write) +{ + USED(proc, a, n, offset, write); + return 0; +} + +void +fpusysrfork(Ureg*) +{ +} + +void +fpusysrforkchild(Proc*, Ureg*, Proc*) +{ +} + +void +fputrap(Ureg*, int) +{ +} + +int +fpuemu(Ureg *ur) +{ + int nfp; + + if(waserror()){ + postnote(up, 1, up->errstr, NDebug); + return 1; + } + spllo(); + nfp = fpipower(ur); + splhi(); + poperror(); + return nfp; +} + +void +fpuinit(void) +{ +} + +int +fpuavail(Ureg*) +{ + return 0; +} + +void +fpunotify(Ureg*) +{ +} + +void +fpunoted(void) +{ +} + +void +fpusysprocsetup(Proc *p) +{ + p->fpstate = FPinit; +// fpoff(); /* add to l.s */ +} diff -Nru /sys/src/9/vt4/syscall.c /sys/src/9/vt4/syscall.c --- /sys/src/9/vt4/syscall.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/syscall.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,364 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/systab.h" + +#include +#include "ureg.h" + +typedef struct { + uintptr pc; + Ureg* arg0; + char* arg1; + char msg[ERRMAX]; + Ureg* old; + Ureg ureg; +} NFrame; + +/* + * Return user to state before notify() + */ +static void +noted(Ureg* cur, uintptr arg0) +{ + Ureg *nur; + NFrame *nf; + + 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; + fpunoted(); + + nf = up->ureg; + + /* sanity clause */ + if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){ + qunlock(&up->debug); + pprint("bad ureg in noted %#p\n", nf); + pexit("Suicide", 0); + } + + /* don't let user change system status */ + nur = &nf->ureg; + nur->status = cur->status; + + memmove(cur, nur, sizeof(Ureg)); + + switch((int)arg0){ + case NCONT: + case NRSTR: + if(!okaddr(nur->pc, BY2SE, 0) || !okaddr(nur->usp, BY2SE, 0)){ + qunlock(&up->debug); + pprint("suicide: trap in noted\n"); + pexit("Suicide", 0); + } + up->ureg = nf->old; + qunlock(&up->debug); + break; + + case NSAVE: + if(!okaddr(nur->pc, BY2SE, 0) || !okaddr(nur->usp, BY2SE, 0)){ + qunlock(&up->debug); + pprint("suicide: trap in noted\n"); + pexit("Suicide", 0); + } + qunlock(&up->debug); + + splhi(); + nf->arg1 = nf->msg; + nf->arg0 = &nf->ureg; + cur->r3 = PTR2UINT(nf->arg0); + nf->pc = 0; + cur->sp = PTR2UINT(nf); + break; + + default: + pprint("unknown noted arg %#p\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + case NDFLT: + if(up->lastnote.flag == NDebug){ + qunlock(&up->debug); + pprint("suicide: %s\n", up->lastnote.msg); + } + else + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag != NDebug); + } +} + +/* + * Call user, if necessary, with note. + * Pass user the Ureg struct and the note on his stack. + */ +int +notify(Ureg* ureg) +{ + int l; + Note *n; + ulong s; + uintptr sp; + NFrame *nf; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + fpunotify(ureg); + + 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=%#.8lux", ureg->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 == nil){ + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + if(!okaddr(PTR2UINT(up->notify), BY2WD, 0)){ + qunlock(&up->debug); + pprint("suicide: notify function address %#p\n", up->notify); + pexit("Suicide", 0); + } + + sp = ureg->usp & ~(BY2V-1); + sp -= (sizeof(NFrame)+BY2V-1) & ~(BY2V-1); + + if(!okaddr(sp, sizeof(NFrame), 1)){ + qunlock(&up->debug); + pprint("suicide: notify stack address %#p\n", sp); + pexit("Suicide", 0); + } + + nf = UINT2PTR(sp); + memmove(&nf->ureg, ureg, sizeof(Ureg)); + nf->old = up->ureg; + up->ureg = nf; + memmove(nf->msg, up->note[0].msg, ERRMAX); + nf->arg1 = nf->msg; + nf->arg0 = up->ureg; + ureg->r3 = PTR2UINT(up->ureg); + nf->pc = 0; + + ureg->usp = sp; + ureg->pc = PTR2UINT(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; +} + +/* + * system calls and 'orrible notes + */ + +/* TO DO: make this trap a separate asm entry point, as on some other RISC architectures */ +void +syscall(Ureg* ureg) +{ + char *e; + u32int s; + uintptr sp; + long ret; + int i, scallnr; + + cycles(&up->kentry); + + m->syscall++; + up->insyscall = 1; + up->pc = ureg->pc; + up->dbgreg = ureg; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + procctl(up); + } + + scallnr = ureg->r3; + up->scallnr = scallnr; + if(scallnr == RFORK) + fpusysrfork(ureg); + spllo(); + + sp = ureg->sp; + up->nerrlab = 0; + ret = -1; + if(!waserror()){ + if(scallnr >= nsyscall){ + 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 [%d]: %d extra\n", scallnr, up->nerrlab); + for(i = 0; i < NERR; i++) + print("sp=%#p pc=%#p\n", + up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + + /* + * Put return value in frame. + */ + ureg->r3 = ret; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + s = splhi(); + procctl(up); + splx(s); + } + + up->insyscall = 0; + up->psstate = 0; + + if(scallnr == NOTED) + noted(ureg, *(uintptr*)(sp+BY2SE)); + + splhi(); + if(scallnr != RFORK && (up->procctl || up->nnote)) + notify(ureg); + + /* if we delayed sched because we held a lock, sched now */ + if(up->delaysched){ + sched(); + splhi(); + } + kexit(ureg); +} + +/* + * called in sysfile.c + */ +void +evenaddr(uintptr addr) +{ + if(addr & (sizeof(uintptr)-1)){ + postnote(up, 1, "sys: misaligned address", NDebug); + error(Ebadarg); + } +} + +uintptr +sysexecstack(uintptr stack, int argc) +{ + /* + * Given a current bottom-of-stack and a count + * of pointer arguments to be pushed onto it followed + * by an integer argument count, return a suitably + * aligned new bottom-of-stack which will satisfy any + * hardware stack-alignment contraints. + * Rounding the stack down to be aligned with the + * natural size of a pointer variable usually suffices, + * but some architectures impose further restrictions, + * e.g. 32-bit SPARC, where the stack must be 8-byte + * aligned although pointers and integers are 32-bits. + */ + USED(argc); + + return STACKALIGN(stack); +} + +long +execregs(ulong entry, ulong ssize, ulong nargs) +{ + ulong *sp; + Ureg *ureg; + + fpusysprocsetup(up); /* up is a guess */ + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ureg = up->dbgreg; +// memset(ureg, 0, 31*sizeof(ulong)); + ureg->usp = (ulong)sp; + ureg->pc = entry; + ureg->srr1 &= ~MSR_FP; /* disable floating point */ + + /* + * return the address of kernel/user shared data + * (e.g. clock stuff) + */ + return USTKTOP-sizeof(Tos); +} + +void +sysprocsetup(Proc* p) +{ + fpusysprocsetup(p); +} + +/* + * Craft a return frame which will cause the child to pop out of + * the scheduler in user mode with the return register zero. Set + * pc to point to a l.s return function. + */ +void +forkchild(Proc *p, Ureg *ureg) +{ + Ureg *cureg; + +//print("%lud setting up for forking child %lud\n", up->pid, p->pid); + p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2SE); + p->sched.pc = (ulong)forkret; + + cureg = (Ureg*)(p->sched.sp+2*BY2SE); + memmove(cureg, ureg, sizeof(Ureg)); + + /* syscall returns 0 for child */ + cureg->r3 = 0; + + /* Things from bottom of syscall which were never executed */ + p->psstate = 0; + p->insyscall = 0; + + fpusysrforkchild(p, cureg, up); +} diff -Nru /sys/src/9/vt4/tlb.s /sys/src/9/vt4/tlb.s --- /sys/src/9/vt4/tlb.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/tlb.s Fri Jun 7 00:00:00 2013 @@ -0,0 +1,60 @@ +/* virtex4 ppc405 initial tlbs */ +#include "mem.h" +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table size to ensure it fits in small TLBs. + */ +#define TLBE(hi, lo) WORD $(hi); WORD $(lo) + +/* + * WARNING: applying TLBG to instruction pages will cause ISI traps + * on Xilinx 405s, despite the words of most of the Xilinx manual + * (e.g., UG011, p. 155). p. 153 has the (insufficiently bold-faced) + * warning: ``In virtual mode, attempts to fetch instructions either + * from guarded storage or from no-execute memory locations normally + * cause an instruction-storage interrupt [i.e., trap or exception] + * to occur.'' In other words, WARNING: LARKS' VOMIT! + * + * Using write-back caches requires more care with cache flushing + * and use of memory barriers, but cuts run times to about 40% of + * what we see with write-through caches (using the TLBW bit). + */ +#define RAMATTRS 0 /* write-back cache */ + +/* largest page size is 16MB */ +#define TLBED16MB(i) \ + TLBE(KZERO | (PHYSDRAM + ((i)*16*MB)) | TLB16MB | TLBVALID, \ + PHYSDRAM + ((i)*16*MB) | TLBZONE(0) | TLBWR | TLBEX | RAMATTRS) + +TEXT tlbtab(SB), 1, $-4 + /* tlbhi tlblo */ + + /* DRAM, at most 128MB */ + TLBED16MB(0) + TLBED16MB(1) + TLBED16MB(2) + TLBED16MB(3) + + TLBED16MB(4) + TLBED16MB(5) + TLBED16MB(6) + /* MACs are stored in 113.78—128 MB in the secure-memory design */ + TLBED16MB(7) + + /* memory-mapped IO, 512KB */ + TLBE(PHYSMMIO | TLB256K | TLBVALID, + PHYSMMIO | TLBZONE(0) | TLBWR | TLBI | TLBG) + TLBE(PHYSMMIO+256*1024 | TLB256K | TLBVALID, + PHYSMMIO+256*1024 | TLBZONE(0) | TLBWR | TLBI | TLBG) + + /* SRAM, 128K. put vectors here. */ + TLBE(PHYSSRAM | TLB64K | TLBVALID, + PHYSSRAM | TLBZONE(0) | TLBWR | TLBEX | RAMATTRS) + TLBE(PHYSSRAM+64*1024 | TLB64K | TLBVALID, + PHYSSRAM+64*1024 | TLBZONE(0) | TLBWR | TLBEX | RAMATTRS) + +TEXT tlbtabe(SB), 1, $-4 + RETURN diff -Nru /sys/src/9/vt4/trap.c /sys/src/9/vt4/trap.c --- /sys/src/9/vt4/trap.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/trap.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,525 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include +#include "ureg.h" + +#include "io.h" + +enum { + VECSIZE = 0x100, + VECBASE = PHYSSRAM, +}; + +static struct { + ulong off; + char *name; +} intcause[] = { + { INT_RESET, "system reset" }, + { INT_MCHECK, "machine check" }, + { INT_DSI, "data access" }, + { INT_ISI, "instruction access" }, + { INT_EI, "external interrupt" }, + { INT_ALIGN, "alignment" }, + { INT_PROG, "program exception" }, + { INT_FPU, "floating-point unavailable" }, + { INT_DEC, "decrementer" }, + { INT_SYSCALL, "system call" }, + { INT_TRACE, "trace trap" }, + { INT_FPA, "floating point unavailable" }, + { INT_APU, "auxiliary processor unavailable" }, + { INT_PIT, "programmable interval timer interrrupt" }, + { INT_FIT, "fixed interval timer interrupt" }, + { INT_WDT, "watch dog timer interrupt" }, + { INT_DMISS, "data TLB miss" }, + { INT_IMISS, "instruction TLB miss" }, + { INT_DEBUG, "debug interrupt" }, + { INT_DEBUG+VECSIZE, "system reset" }, + { 0, "unknown interrupt" } +}; + +static char *excname(ulong, u32int); + +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", +}; + +static int probing, trapped; + +/* populate a normal vector */ +static void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = (ulong*)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: running from ROM */ + 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) */ + if (v == VECBASE + INT_PIT || v == VECBASE + INT_FIT) { + uartlputc('?'); + uartlputc('v'); + print("branch too far for vector %#x!\n", v); + } + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(PTR2UINT(vp), 8*BY2WD); +} + +/* populate a tlb-miss vector */ +static void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + char msg[128]; + + if (((ulong)r & ~KSEGM) >= (1<<26)) { + uartlputc('?'); + uartlputc('t'); + snprint(msg, sizeof msg, + "tlb miss handler address %#p too high\n", r); + uartlputs(msg); + } + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */ + dcflush(PTR2UINT(vp), sizeof *vp); +} + +static void +faultpower(Ureg *ureg, ulong addr, int read) +{ + int user, insyscall, n; + char buf[ERRMAX]; + + user = (ureg->srr1 & MSR_PR) != 0; + if(!user){ +// if(vmapsync(addr)) +// return; +// if(addr >= USTKTOP) +// panic("kernel fault: bad address pc=%.8#lux addr=%.8#lux", +// ureg->pc, addr); + if(up == nil) + panic("kernel fault: no user process pc=%.8#lux addr=%.8#lux", + ureg->pc, addr); + } + if(up == nil) + panic("user fault: up=0 pc=%.8#lux addr=%.8#lux", ureg->pc, addr); + insyscall = 0; + if (up) { + insyscall = up->insyscall; + up->insyscall = 1; + } + n = fault(addr, read); /* repair user-mode fault */ + if(n < 0){ + if(!user){ + dumpregs(ureg); + panic("fault: addr %#lux", addr); + }else if(0) + dumpregs(ureg); + seprint(buf, buf + sizeof buf, "sys: trap: fault %s addr=%#lux", + read? "read" : "write", addr); + postnote(up, 1, buf, NDebug); + } + if (up) + up->insyscall = insyscall; +} + +static void +setlights(int user) +{ + if (up == nil) + lightstate(Ledidle); + else + lightstate(user == 0? Ledkern: Leduser); +} + +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; +// surely only need to set tos->pid on rfork and exec? +} + +void +trap(Ureg *ur) +{ + int ecode, user, v; + u32int esr; + char buf[ERRMAX]; + + if (ur == nil) + panic("trap: nil ur"); + v = ur->cause; + lightstate(Ledtrap); + ur->cause &= 0xFFE0; + ecode = ur->cause; + esr = getesr(); + clrmchk(); + + user = (ur->srr1 & MSR_PR) != 0; + if(user){ + cycles(&up->kentry); + up->dbgreg = ur; + } + switch(ecode){ + case INT_SYSCALL: + if(!user) + panic("syscall in kernel: srr1 %#4.4luX pc %#p", + ur->srr1, ur->pc); + syscall(ur); + setlights(user); + return; /* syscall() calls notify itself */ + + case INT_PIT: + m->intr++; + clockintr(ur); + break; + + case INT_WDT: + puttsr(~0); + panic("watchdog timer went off at pc %#lux", ur->pc); + break; + + case INT_MCHECK: + if (probing && !user) { + if (trapped++ > 0) + panic("trap: recursive probe"); + break; /* continue at next instruction */ + } + if(esr & ESR_MCI){ + iprint("mcheck-mci %lux\n", ur->pc); + faultpower(ur, ur->pc, 1); + break; + } + iprint("mcheck %#lux esr=%#ux dear=%#ux\n", ur->pc, esr, getdear()); + ur->pc -= 4; /* back up to faulting instruction */ + /* fall through */ + case INT_DSI: + case INT_DMISS: + faultpower(ur, getdear(), !(esr&ESR_DST)); + break; + + case INT_ISI: + case INT_IMISS: + faultpower(ur, ur->pc, 1); + break; + + case INT_EI: + m->intr++; + intr(ur); + break; + + case 0: + puttsr(~0); + if (v == 0) + panic("watchdog reset? probable jump via " + "zeroed pointer; pc %#lux lr %#lux", + ur->pc, ur->lr); + else + panic("watchdog reset? interrupt at vector zero; " + "pc %#lux lr %#lux", ur->pc, ur->lr); + break; + + case INT_DEBUG + VECSIZE: + panic("reset"); + break; + + case INT_PROG: + if(esr & ESR_PIL && user){ + if(fpuemu(ur)) + break; + /* otherwise it's an illegal instruction */ + } + /* fall through */ + default: + if(user){ + spllo(); + sprint(buf, "sys: trap: %s", excname(ecode, esr)); + postnote(up, 1, buf, NDebug); + break; + } + splhi(); + print("kernel %s; vector=%#ux pc=%#lux\n", + excname(ecode, esr), ecode, ur->pc); + if (ecode == 0) + print("probable jump via zeroed pointer; pc %#lux lr %#lux\n", + ur->pc, ur->lr); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + splhi(); + setlights(user); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && ecode == INT_PIT){ + sched(); + splhi(); + setlights(user); + } + + if(user){ + if(up->procctl || up->nnote) + notify(ur); + kexit(ur); + } +} + +void +trapinit(void) +{ + int i; + + clrmchk(); + intrinit(); + + /* + * set all exceptions to trap by default + */ + for(i = 0; i < INT_DEBUG + VECSIZE; i += VECSIZE) + sethvec(VECBASE + i, trapvec); + + /* + * set exception handlers + */ + sethvec(VECBASE + INT_RESET, trapcritvec); + sethvec(VECBASE + INT_MCHECK, trapcritvec); + sethvec(VECBASE + INT_DSI, trapvec); + sethvec(VECBASE + INT_ISI, trapvec); + sethvec(VECBASE + INT_EI, trapvec); + sethvec(VECBASE + INT_ALIGN, trapvec); + sethvec(VECBASE + INT_PROG, trapvec); + sethvec(VECBASE + INT_FPU, trapvec); + sethvec(VECBASE + INT_SYSCALL, trapvec); + sethvec(VECBASE + INT_APU, trapvec); + sethvec(VECBASE + INT_PIT, trapvec); + sethvec(VECBASE + INT_FIT, trapvec); + sethvec(VECBASE + INT_WDT, trapcritvec); + sethvec2(VECBASE + INT_DMISS, dtlbmiss); + sethvec2(VECBASE + INT_IMISS, itlbmiss); + sethvec(VECBASE + INT_DEBUG, trapcritvec); + /* + * the last word of sram (0xfffffffc) contains a branch + * to the start of sram beyond the vectors (0xfffe2100), + * which initially will be the start of our bootstrap loader. + * overwrite it so that we can get control if the machine should + * reset. + */ + sethvec(VECBASE + INT_DEBUG + VECSIZE, trapcritvec); + sethvec(0, trapcritvec); + sync(); + + putevpr(VECBASE); + sync(); + + putmsr(getmsr() | MSR_ME); + sync(); +} + +static char* +excname(ulong ivoff, u32int esr) +{ + int i; + + if(ivoff == INT_PROG){ + if(esr & ESR_PIL) + return "illegal instruction"; + if(esr & ESR_PPR) + return "privileged"; + if(esr & ESR_PTR) + return "trap with successful compare"; + if(esr & ESR_PEU) + return "unimplemented APU/FPU"; + if(esr & ESR_PAP) + return "APU exception"; + if(esr & ESR_U0F) + return "data storage: u0 fault"; + } + for(i=0; intcause[i].off != 0; i++) + if(intcause[i].off == ivoff) + break; + return intcause[i].name; +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + ureg.pc = getcallerpc(&fn); + ureg.sp = PTR2UINT(&fn); + fn(&ureg); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + iprint("%lux=%lux, ", l, v); + if(i++ == 4){ + iprint("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ureg) +{ + int i; + uintptr *l; + + splhi(); /* prevent recursive dumps */ + if(up != nil) + iprint("cpu%d: registers for %s %ld\n", + m->machno, up->text, up->pid); + else + iprint("cpu%d: registers for kernel\n", m->machno); + + if (ureg == nil) { + iprint("nil ureg, no dump\n"); + return; + } + l = &ureg->cause; + for(i = 0; i < nelem(regname); i += 2, l += 2) + iprint("%s\t%.8p\t%s\t%.8p\n", regname[i], l[0], regname[i+1], l[1]); +} + +static void +linkproc(void) +{ + spllo(); + up->kpfun(up->kparg); + pexit("", 0); +} + +void +kprocchild(Proc* p, void (*func)(void*), void* arg) +{ + p->sched.pc = PTR2UINT(linkproc); + p->sched.sp = PTR2UINT(p->kstack+KSTACK); + p->sched.sp = STACKALIGN(p->sched.sp); + + p->kpfun = func; + p->kparg = arg; +} + +uintptr +userpc(void) +{ + 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 *ureg, char *pureg, char *uva, int n) +{ + u32int status; + + status = ureg->status; + memmove(pureg, uva, n); + ureg->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+BY2SE; +} + +uintptr +dbgpc(Proc* p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + return ureg->pc; +} + +vlong +probeaddr(uintptr addr) +{ + vlong v; + static Lock fltlck; + + ilock(&fltlck); + trapped = 0; + probing = 1; + barriers(); + + v = *(ulong *)addr; /* this may cause a fault */ + USED(probing); + barriers(); + + probing = 0; + barriers(); + if (trapped) + v = -1; + iunlock(&fltlck); + return v; +} diff -Nru /sys/src/9/vt4/uartlite.c /sys/src/9/vt4/uartlite.c --- /sys/src/9/vt4/uartlite.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/uartlite.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,347 @@ +/* + * Xilinx uartlite driver. + * uartlite has 16-byte fifos. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +enum { + /* control register bit positions */ + Ctlintrena = 0x10, /* enable interrupt */ + Rxfiforst = 0x02, /* reset receive FIFO */ + Txfiforst = 0x01, /* reset transmit FIFO */ + + /* status register bit positions */ + Parerr = 0x80, + Framerr = 0x40, + Overrunerr = 0x20, + Stsintrena = 0x10, /* interrupt enabled */ + Txfifofull = 0x08, /* transmit FIFO full */ + Txfifoempty = 0x04, /* transmit FIFO empty */ + Rxfifofull = 0x02, /* receive FIFO full */ + Rxfifohasdata = 0x01, /* data in receive FIFO */ +}; + +typedef struct Uartregs Uartregs; +struct Uartregs { + ulong rxfifo; + ulong txfifo; + ulong status; /* read-only; read clears error bits */ + ulong ctl; /* write-only */ +}; + +typedef struct Ctlr { + int port; + Uartregs *regs; + int irq; + int tbdf; + int iena; + int poll; + + int fena; +// Rendez xmitrend; +// Rendez rcvrend; +} Ctlr; + +extern PhysUart litephysuart; + +static Ctlr litectlr[1] = { +{ + .port = 0, + .regs = (Uartregs *)Uartlite, + .poll = 0, }, /* was 1 */ +}; + +static Uart liteuart[1] = { +{ .regs = &litectlr[0], + .name = "eia0", + .phys = &litephysuart, + .special= 0, + .next = nil, }, +}; + +static int +canxmit(void *stsp) +{ + return !(*(ulong *)stsp & Txfifofull); +} + +static int +canread(void *stsp) +{ + return (*(ulong *)stsp & Rxfifohasdata) != 0; +} + +static long +litestatus(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + + ctlr = uart->regs; + p = malloc(READSTR); + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + 0, + uart->hup_dsr, + 8, + 0, + 'n', + 0, + 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + "", + "", + "", + "" + ); + n = readstr(offset, buf, n, p); + free(p); + return n; +} + +#define litedtr litefifo +#define literts litefifo +#define litemodemctl litefifo +#define litebreak litefifo + +static void +litefifo(Uart*, int) +{ +} + +#define litestop liteparity +#define litebits liteparity +#define litebaud liteparity + +static int +liteparity(Uart*, int) +{ + return 0; +} + +static void +litekick(Uart* uart) +{ + Ctlr *ctlr; + Uartregs *urp; + + if(uart == nil || uart->blocked) + return; + ctlr = uart->regs; + urp = ctlr->regs; + while (!(urp->status & Txfifofull)) { + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + uartputc(*uart->op++); + } +} + +static int +liteinterrupt(ulong bit) +{ + int sts; + Ctlr *ctlr; + Uart *uart; + Uartregs *urp; + + uart = liteuart; + if (uart == nil) + return 0; + ctlr = uart->regs; + urp = ctlr->regs; + sts = urp->status; /* clears error bits */ + + while (urp->status & Rxfifohasdata) { + uartrecv(uart, urp->rxfifo); +// wakeup(&ctlr->rcvrend); + } + if (!(urp->status & Txfifofull)) { + uartkick(uart); +// wakeup(&ctlr->xmitrend); + } + if (sts & (Parerr|Framerr|Overrunerr|Txfifoempty|Rxfifofull| + Txfifofull|Rxfifohasdata)) { + intrack(bit); + return 1; + } + return 0; +} + +static void +litedisable(Uart* uart) +{ + Ctlr *ctlr; + Uartregs *urp; + + ctlr = uart->regs; + if(ctlr->iena != 0){ + urp = ctlr->regs; + /* wait for output to drain */ + while(!(urp->status & Txfifoempty)) + delay(1); +// intrdisable(Intuart, liteinterrupt); + urp->ctl = Txfiforst | Rxfiforst; + barriers(); + } +} + +static void +liteenable(Uart* uart, int ie) +{ + int ier; + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + ier = 0; + if(ie){ + if(ctlr->iena == 0 && !ctlr->poll){ + intrenable(Intuart, liteinterrupt, "uartlite"); + barriers(); + ier = Ctlintrena; + ctlr->iena++; + } + } + ctlr->regs->ctl = ier; + barriers(); +} + +static Uart* +litepnp(void) +{ + return liteuart; +} + +static int +litegetc(Uart *uart) +{ + Ctlr *ctlr; + Uartregs *urp; + + ctlr = uart->regs; + urp = ctlr->regs; + while(!(urp->status & Rxfifohasdata)) + delay(1); + return (uchar)urp->rxfifo; +} + +static void +liteputc(Uart *uart, int c) +{ + Ctlr *ctlr; + Uartregs *urp; + + ctlr = uart->regs; + urp = ctlr->regs; + while(urp->status & Txfifofull) + delay(1); +// sleep(&ctlr->xmitrend, canxmit, &urp->status); + urp->txfifo = (uchar)c; + barriers(); + while(urp->status & Txfifofull) + delay(1); +} + +/* + * for debugging. no function calls are possible as this is called before + * we have a valid stack pointer. + */ +void +uartlputc(int c) +{ + ulong cnt; + Uartregs *urp; + + urp = (Uartregs *)Uartlite; + for (cnt = m->cpuhz; urp->status & Txfifofull && cnt-- > 0; ) + ; + urp->txfifo = (uchar)c; + barriers(); + for (cnt = m->cpuhz; urp->status & Txfifofull && cnt-- > 0; ) + ; +} + +void +uartlputs(char *s) +{ + while (*s != '\0') + uartlputc(*s++); +} + +static void +litepoll(Uart* uart) +{ + Ctlr *ctlr; + + /* + * If PhysUart has a non-nil .poll member, this + * routine will be called from the uartclock timer. + * If the Ctlr .poll member is non-zero, when the + * Uart is enabled interrupts will not be enabled + * and the result is polled input and output. + * Not very useful here, but ports to new hardware + * or simulators can use this to get serial I/O + * without setting up the interrupt mechanism. + */ + ctlr = uart->regs; + if(ctlr && !ctlr->iena && ctlr->poll) + liteinterrupt(Intuart); +} + +PhysUart litephysuart = { + .name = "lite", + .pnp = litepnp, + .enable = liteenable, + .disable = litedisable, + .kick = litekick, + .dobreak = litebreak, + .baud = litebaud, + .bits = litebits, + .stop = litestop, + .parity = liteparity, + .modemctl = litemodemctl, + .rts = literts, + .dtr = litedtr, + .status = litestatus, + .fifo = litefifo, + .getc = litegetc, + .putc = liteputc, +// .poll = litepoll, +}; + +void +uartliteconsole(void) +{ + Uart *uart; + + uart = &liteuart[0]; + (*uart->phys->enable)(uart, 0); + uartctl(uart, "b115200 l8 pn s1 i1 x0"); + uart->console = 1; + consuart = uart; +} diff -Nru /sys/src/9/vt4/vt4cpu /sys/src/9/vt4/vt4cpu --- /sys/src/9/vt4/vt4cpu Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/vt4cpu Fri Jun 7 00:00:00 2013 @@ -0,0 +1,60 @@ +# rae's virtex4 ppc405 fpga cpu server +dev + root + cons + env + pipe + proc + mnt + srv + dup +# rtc + arch + ssl + tls + cap + kprof +# aoe +# sd + + ether netif + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno + + uart + +link + archvt4 + ethermedium + etherlltemac + loopbackmedium + netdevmedium + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + dma + rdb +# sdaoe sdscsi + softfpu + syscall + uartlite +# ucalloc + +port + int cpuserver = 1; + int i8250freq = 3686000; + +boot cpu + tcp + +bootdir + boot$CONF.out boot + /power/bin/ip/ipconfig ipconfig + /power/bin/auth/factotum factotum + nvram diff -Nru /sys/src/9/vt4/words/9k-9.diffs /sys/src/9/vt4/words/9k-9.diffs --- /sys/src/9/vt4/words/9k-9.diffs Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/words/9k-9.diffs Fri Jun 7 00:00:00 2013 @@ -0,0 +1,726 @@ +Only in .: 9k-9.diffs +diff /sys/src/9k/vt4/archvt4.c ./archvt4.c +40,68d39 +< #ifndef incref +< int +< incref(Ref *r) +< { +< int x; +< +< lock(r); +< x = ++r->ref; +< unlock(r); +< return x; +< } +< #endif /* incref */ +< +< #ifndef decref +< int +< decref(Ref *r) +< { +< int x; +< +< lock(r); +< x = --r->ref; +< unlock(r); +< if(x < 0) +< panic("decref pc=%#p", getcallerpc(&r)); +< +< return x; +< } +< #endif /* decref */ +< +88a60,70 +> void +> archvt4link(void) +> { +> } +> +> int +> cmpswap(long *addr, long old, long new) +> { +> return cas32(addr, old, new); +> } +> +254c236 +< reboot(void *entry, void *code, long size) +--- +> reboot(void *entry, void *code, ulong size) +diff /sys/src/9k/vt4/clock.c ./clock.c +126a127,132 +> +> long +> lcycles(void) +> { +> return perfticks(); +> } +diff /sys/src/9k/vt4/dat.h ./dat.h +9c9 +< typedef struct PFPU PFPU; +--- +> typedef struct FPsave FPsave; +11c11 +< typedef struct PNOTIFY PNOTIFY; +--- +> typedef struct Notsave Notsave; +14a15 +> typedef uvlong Tval; +87c88 +< struct PFPU +--- +> struct FPsave +101c102 +< * PFPU.status +--- +> * FPsave.status +146c147 +< struct PNOTIFY +--- +> struct Notsave +148c149 +< void emptiness; +--- +> int emptiness; +216c217 +< int mmuflush; /* make current proc flush its mmu state */ +--- +> int flushmmu; /* make current proc flush its mmu state */ +280c281 +< #define DBG(...) if(DBGFLG) dbgprint(__VA_ARGS__) +--- +> // #define DBG(...) if(DBGFLG) dbgprint(__VA_ARGS__) +diff /sys/src/9k/vt4/devarch.c ./devarch.c +80,81c80,81 +< static long +< archstat(Chan* c, uchar* dp, long n) +--- +> static int +> archstat(Chan* c, uchar* dp, int n) +diff /sys/src/9k/vt4/devether.c ./devether.c +11a12,15 +> /* 9k compatibility */ +> #define devno dev +> #define iq in +> +50,51c54,55 +< static long +< etherstat(Chan* chan, uchar* dp, long n) +--- +> static int +> etherstat(Chan* chan, uchar* dp, int n) +63c67 +< ethercreate(Chan*, char*, int, int) +--- +> ethercreate(Chan*, char*, int, ulong) +95c99 +< etherbread(Chan* chan, long n, vlong offset) +--- +> etherbread(Chan* chan, long n, ulong offset) +100,101c104,105 +< static long +< etherwstat(Chan* chan, uchar* dp, long n) +--- +> static int +> etherwstat(Chan* chan, uchar* dp, int n) +294c298 +< etherbwrite(Chan* chan, Block* bp, vlong) +--- +> etherbwrite(Chan* chan, Block* bp, ulong) +532c536 +< print("inpkts %d outpkts %d errs %d\n", +--- +> print("inpkts %lld outpkts %lld errs %d\n", +Only in .: devtab.c +diff /sys/src/9k/vt4/etherif.h ./etherif.h +3,8c3 +< Eaddrlen = 6, +< ETHERMINTU = 60, /* minimum transmit size */ +< ETHERMAXTU = 1514, /* maximum transmit size */ +< ETHERHDRSIZE = 14, /* size of an ethernet header */ +< +< MaxEther = 4, +--- +> MaxEther = 2, +17d11 +< // int tbdf; /* type+busno+devno+funcno */ +51,59d44 +< }; +< +< typedef struct Etherpkt Etherpkt; +< struct Etherpkt +< { +< uchar d[Eaddrlen]; +< uchar s[Eaddrlen]; +< uchar type[2]; +< uchar data[1500]; /* ETHERMAXTU - ETHERHDRSIZE */ +diff /sys/src/9k/vt4/etherlltemac.c ./etherlltemac.c +19c19 +< #include "ip.h" /* to declare ethermedium */ +--- +> #include "../ip/ip.h" /* to declare ethermedium */ +21c21 +< #include "../port/ethermii.h" +--- +> #include "ethermii.h" +Only in .: ethermii.h +diff /sys/src/9k/vt4/fns.h ./fns.h +0a1,3 +> #define checkmmu(a, b) +> #define countpagerefs(a, b) +> +7a11,12 +> int adec(long*); +> int ainc(long*); +14a20 +> uintptr cankaddr(uintptr pa); +18a25 +> int cmpswap(long*, long, long); +22a30 +> #define cycles(ip) *(ip) = lcycles() +25c33 +< #define decref(r) adec(&(r)->ref) +--- +> long decref(Ref*); +30a39,43 +> Dev* devtabget(int, int); +> void devtabinit(void); +> void devtabreset(void); +> long devtabread(Chan*, void*, long, vlong); +> void devtabshutdown(void); +72c85 +< #define incref(r) ainc(&(r)->ref) +--- +> long incref(Ref*); +116a130 +> void procsetup(Proc*); +141d154 +< void* sysexecregs(uintptr, ulong, ulong); +143a157 +> #define tas tas32 +190c204 +< void sysrforkret(void); +--- +> void forkret(void); +diff /sys/src/9k/vt4/fpipower.c ./fpipower.c +75c75 +< PFPU* ufp; +--- +> FPsave* ufp; +801c801 +< PFPU *ufp; +--- +> FPsave *ufp; +804c804 +< ufp = &up->PFPU; /* because all the state is in PFPU, it need not be saved/restored */ +--- +> ufp = &up->fpsave; /* because all the state is in FPsave, it need not be saved/restored */ +817c817 +< validaddr((void *)ur->pc, 4, 0); +--- +> validaddr(ur->pc, 4, 0); +diff /sys/src/9k/vt4/intr.c ./intr.c +9c9 +< #include "../port/ethermii.h" +--- +> #include "ethermii.h" +diff /sys/src/9k/vt4/l.s ./l.s +440a441 +> TEXT _xinc(SB), 1, $-4 /* int _xinc(int*); */ +458a460 +> TEXT _xdec(SB), 1, $-4 /* int _xdec(int*); */ +831c833 +< TEXT sysrforkret(SB), 1, $-4 +--- +> TEXT forkret(SB), 1, $-4 +diff /sys/src/9k/vt4/main.c ./main.c +123c123 +< mallocinit(); +--- +> xinit(); /* xinit would print if it could */ +144,145c144,145 +< psinit(conf.nproc); +< initimage(); +--- +> procinit0(); +> initseg(); +148c148 +< devtabreset(); +--- +> chandevreset(); +165a166 +> panic("schedinit returned"); +311c312 +< p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr)); +--- +> p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr)); +357a359,367 +> } +> +> /* +> * set mach dependent process state for a new process +> */ +> void +> procsetup(Proc* p) +> { +> fpusysprocsetup(p); +diff /sys/src/9k/vt4/mem.h ./mem.h +27a28 +> #define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +diff /sys/src/9k/vt4/mkfile ./mkfile +11c11 +< default:V: 9k +--- +> DEVS=`{rc ../port/mkdevlist $CONF} +13c13,40 +< 9k:V: $p$CONF $p$CONF.elf +--- +> PORT=\ +> alarm.$O\ +> alloc.$O\ +> allocb.$O\ +> auth.$O\ +> cache.$O\ +> chan.$O\ +> dev.$O\ +> devtab.$O\ +> edf.$O\ +> fault.$O\ +> latin1.$O\ +> rebootcmd.$O\ +> page.$O\ +> parse.$O\ +> pgrp.$O\ +> portclock.$O\ +> print.$O\ +> proc.$O\ +> qio.$O\ +> qlock.$O\ +> segment.$O\ +> swap.$O\ +> sysfile.$O\ +> sysproc.$O\ +> taslock.$O\ +> tod.$O\ +> xalloc.$O\ +15,17c42,61 +< <| awk -f ../mk/parse $CONF +< <../mk/bootmkfile +< <../mk/portmkfile +--- +> OBJ=\ +> l.$O\ +> tlb.$O\ +> clock.$O\ +> dcr.$O\ +> fpi.$O\ +> fpimem.$O\ +> fpipower.$O\ +> intr.$O\ +> main.$O\ +> mmu.$O\ +> ppcmul64fract.$O\ +> # fakeqtm.$O\ +> qtm.$O\ +> random.$O\ +> trap.$O\ +> $CONF.root.$O\ +> $CONF.rootc.$O\ +> $DEVS\ +> $PORT\ +19,21c63,69 +< OBJ=$MACH $CONF.root.$O $DEVS $PORT +< CFLAGS=-. -I../port -I../ip -I../boot $CFLAGS +< HFILES=`{ls -d *.h ../port/*.h ../ip/*.h | grep -v '^(errstr|init)\.h$'} +--- +> LIB=\ +> /$objtype/lib/libmemlayer.a\ +> /$objtype/lib/libmemdraw.a\ +> /$objtype/lib/libdraw.a\ +> /$objtype/lib/libip.a\ +> /$objtype/lib/libsec.a\ +> /$objtype/lib/libc.a\ +22a71,72 +> 9:V: $p$CONF $p$CONF.elf +> +48,49c98,100 +< init.out:D: init9.$O initcode.$O /$objtype/lib/libc.a +< $LD -l -R4 -o init.out init9.$O initcode.$O -lc +--- +> <../boot/bootmkfile +> <../port/portmkfile +> <|../port/mkbootrules $CONF +51,59d101 +< reboot.h:D: rebootcode.s +< $AS rebootcode.s +< # -lc is only for memmove. -T arg is PADDR(REBOOTADDR) +< $LD -l -a -s -T0x2640 -R4 -o reboot.out rebootcode.$O -lc >reboot.list +< {echo 'uchar rebootcode[]={' +< xd -1x reboot.out | +< sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' +< echo '};'} > reboot.h +< +63,64c105,106 +< ethertemac.$O: ../port/ethermii.h ../port/netif.h etherif.h io.h +< etherplbtemac.$O: ../port/ethermii.h ../port/netif.h etherif.h io.h +--- +> ethertemac.$O: ../port/netif.h etherif.h io.h +> etherplbtemac.$O: ../port/netif.h etherif.h io.h +74a117,142 +> +> init.h:D: ../port/initcode.c init9.s +> $CC ../port/initcode.c +> $AS init9.s +> $LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a +> {echo 'uchar initcode[]={' +> xd -1x sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' +> echo '};'} > init.h +> +> init.out:D: init9.$O initcode.$O /$objtype/lib/libc.a +> $LD -l -R4 -o init.out init9.$O initcode.$O -lc +> +> reboot.h:D: rebootcode.s +> $AS rebootcode.s +> # -lc is only for memmove. -T arg is PADDR(REBOOTADDR) +> $LD -l -a -s -T0x2640 -R4 -o reboot.out rebootcode.$O -lc >reboot.list +> {echo 'uchar rebootcode[]={' +> xd -1x reboot.out | +> sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' +> echo '};'} > reboot.h +> errstr.h:D: ../port/mkerrstr ../port/error.h +> rc ../port/mkerrstr > errstr.h +> +> $CONF.clean: +> rm -rf $p$CONF $p$CONF.elf $CONF.c boot$CONF.c ../boot/libboot.aq +Only in .: mkfile.9k +diff /sys/src/9k/vt4/mmu.c ./mmu.c +112c112 +< mmuflush(void) +--- +> flushmmu(void) +148c148 +< mmuput(uintptr va, uintptr pa, Page* page) +--- +> putmmu(uintptr va, uintptr pa, Page* page) +368a369,380 +> } +> +> /* +> * Return the number of bytes that can be accessed via KADDR(pa). +> * If pa is not a valid argument to KADDR, return 0. +> */ +> uintptr +> cankaddr(uintptr pa) +> { +> if( /* pa >= PHYSDRAM && */ pa < PHYSDRAM + 512*MiB) +> return PHYSDRAM + 512*MiB - pa; +> return 0; +diff /sys/src/9k/vt4/rebootcmd.c ./rebootcmd.c +28c28 +< nn = c->dev->read(c, p, n, c->offset); +--- +> nn = devtab[c->type]->read(c, p, n, c->offset); +diff /sys/src/9k/vt4/syscall.c ./syscall.c +6d5 +< +8c7 +< #include "/sys/src/libc/9syscall/sys.h" +--- +> #include "../port/systab.h" +192c191 +< Ar0 ar0; +--- +> u32int s; +194,195c193,194 +< int i, s, scallnr; +< static Ar0 zar0; +--- +> long ret; +> int i, scallnr; +196a196,197 +> cycles(&up->kentry); +> +215c216 +< ar0 = zar0; +--- +> ret = -1; +217c218 +< if(scallnr >= nsyscall || systab[scallnr].f == nil){ +--- +> if(scallnr >= nsyscall){ +224,225c225,226 +< if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(up->arg)-BY2SE)) +< validaddr((void *)sp, sizeof(up->arg)+BY2SE, 0); +--- +> if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) +> validaddr(sp, sizeof(Sargs)+BY2WD, 0); +227,228c228,229 +< memmove(up->arg, UINT2PTR(sp+BY2SE), sizeof(up->arg)); +< up->psstate = systab[scallnr].n; +--- +> up->s = *((Sargs*)(sp+BY2WD)); +> up->psstate = sysctab[scallnr]; +230c231 +< systab[scallnr].f(&ar0, (va_list)up->arg); +--- +> ret = systab[scallnr](up->s.args); +237,240d237 +< if(0 && vflag && up->pid == 1) +< iprint("%s: syscall %s error %s\n", +< up->text, systab[scallnr].n, up->syserrstr); +< ar0 = systab[scallnr].r; +253c250 +< ureg->r3 = ar0.p; +--- +> ureg->r3 = ret; +312,313c309,310 +< void* +< sysexecregs(uintptr entry, ulong ssize, ulong nargs) +--- +> long +> execregs(ulong entry, ulong ssize, ulong nargs) +318c315 +< sp = (uintptr*)(USTKTOP - ssize); +--- +> sp = (ulong*)(USTKTOP - ssize); +322c319 +< ureg->usp = PTR2UINT(sp); +--- +> ureg->usp = (ulong)sp; +324c321 +< ureg->srr1 &= ~MSR_FP; /* disable floating point */ +--- +> // ureg->srr1 &= ~MSR_FP; /* TODO disable floating point */ +330c327 +< return UINT2PTR(USTKTOP-sizeof(Tos)); +--- +> return USTKTOP-sizeof(Tos); +338a336,340 +> /* +> * Craft a return frame which will cause the child to pop out of +> * the scheduler in user mode with the return register zero. Set +> * pc to point to a l.s return function. +> */ +340c342 +< sysrforkchild(Proc* child, Proc* parent) +--- +> forkchild(Proc *p, Ureg *ureg) +344,347c346,348 +< /* +< */ +< child->sched.sp = PTR2UINT(child->kstack+KSTACK-(UREGSIZE+2*BY2SE)); +< child->sched.pc = PTR2UINT(sysrforkret); +--- +> //print("%lud setting up for forking child %lud\n", up->pid, p->pid); +> p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg); +> p->sched.pc = (ulong)forkret; +349,350c350,351 +< cureg = (Ureg*)(child->sched.sp+2*BY2SE); +< memmove(cureg, parent->dbgreg, sizeof(Ureg)); +--- +> cureg = (Ureg*)(p->sched.sp); +> memmove(cureg, ureg, sizeof(Ureg)); +352,355c353 +< /* Things from bottom of syscall which were never executed */ +< child->psstate = 0; +< child->insyscall = 0; +< +--- +> /* syscall returns 0 for child */ +358c356,358 +< fpusysrforkchild(child, cureg, parent); +--- +> /* Things from bottom of syscall which were never executed */ +> p->psstate = 0; +> p->insyscall = 0; +diff /sys/src/9k/vt4/trap.c ./trap.c +383a384,387 +> /* +> * Fill in enough of Ureg to get a stack trace, and call a function. +> * Used by debugging interface rdb. +> */ +384a389,398 +> callwithureg(void (*fn)(Ureg*)) +> { +> Ureg ureg; +> +> ureg.pc = getcallerpc(&fn); +> ureg.sp = PTR2UINT(&fn); +> fn(&ureg); +> } +> +> void +413c427 +< iprint("cpu%d: registers for %s %d\n", +--- +> iprint("cpu%d: registers for %s %ld\n", +447c461 +< userpc(Ureg* ureg) +--- +> userpc(void) +449,450c463 +< if(ureg == nil) +< ureg = up->dbgreg; +--- +> Ureg *ureg = up->dbgreg; +diff /sys/src/9k/vt4/uartlite.c ./uartlite.c +246d245 +< int s; +252,254c251 +< while(!(urp->status & Rxfifohasdata)) { +< // idlehands(); +< // s = spllo(); +--- +> while(!(urp->status & Rxfifohasdata)) +256,258d252 +< // splx(s); +< // sleep(&ctlr->rcvrend, canread, &urp->status); +< } +344c338 +< .poll = litepoll, +--- +> // .poll = litepoll, +diff /sys/src/9k/vt4/vt4cpu ./vt4cpu +2,33c2 +< power +dev +< l +< tlb +< clock +< fpi +< fpimem +< fpipower +< main +< mmu +< ppcmul64fract +< random +< softfpu +< syscall +< trap +< intr +< dcr +< # fakeqtm +< qtm +< +< boot cpu +< tcp +< +< rootdir +< bootvt4cpu.out boot +< /power/bin/ip/ipconfig ipconfig +< /power/bin/auth/factotum factotum +< nvram +< +< conf +< int cpuserver = 1; +< +< dev +dev +--- +> dev +35c4 +< cons archvt4 +--- +> cons +41a11 +> # rtc +47,49c17,18 +< uart +< sd +< aoe +--- +> # aoe +> # sd +52c21 +< ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno +--- +> ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno +54,55c23 +< uart +dev +< uartlite +--- +> uart +57c25,32 +< ip +dev +--- +> link +> archvt4 +> ethermedium +> etherlltemac +> loopbackmedium +> netdevmedium +> +> ip +62a38 +> ipmux +64,68c40,47 +< link +dev +< etherlltemac ethermii dma +< ethermedium +< netdevmedium +< loopbackmedium +--- +> misc +> dma +> rdb +> # sdaoe sdscsi +> softfpu +> syscall +> uartlite +> # ucalloc +70,77d48 +< misc +dev +< cache +< +< sd +dev +< sdaoe sdscsi +< +< dir +< ip +78a50,51 +> int cpuserver = 1; +> int i8250freq = 3686000; +80,83c53,54 +< lib +< libc +< libip +< libsec +--- +> boot cpu +> tcp +85,115c56,60 +< port +< alarm +< alloc +< allocb +< chan +< dev +< devtab +< edf +< fault +< image +< latin1 +< page +< parse +< pgrp +< portclock +< print +< proc +< ps +< qio +< qlock +< rebootcmd +< segment +< swap +< sysauth +< sysfile +< sysproc +< sysseg +< systab +< taslock +< tod +< xalloc +--- +> bootdir +> boot$CONF.out boot +> /power/bin/ip/ipconfig ipconfig +> /power/bin/auth/factotum factotum +> nvram diff -Nru /sys/src/9/vt4/words/_mmu /sys/src/9/vt4/words/_mmu --- /sys/src/9/vt4/words/_mmu Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/words/_mmu Fri Jun 7 00:00:00 2013 @@ -0,0 +1,42 @@ +void mmuinit(void) + setup + +void mmuflush(void) + flush all mmu info for current process (up). nearly always + s = splhi(); + up->newtlb = 1; + mmuswitch(up); + splx(s); + +void mmuswitch(Proc *p) + mmu is now owned by p + check p->newtlb and flush p's mappings if needed + +void mmurelease(Proc *p) + discard mmu mapping for p, which is now Moribund + +void mmuput(ulong va, ulong pa, Page *pg) + make mmu mapping for up make va refer to pa, which has (low-order) PTEVALID and one or more of + PTEWRITE or PTERONLY (mutually exclusive, and one or the other is set) + PTEUNCACHED + pg is the relevant Page value + check pg->cachectl[m->machno] + PG_NOFLUSH + PG_TXTFLUSH + PG_NEWCOL + -- reset to PG_NOFLUSH after action (flush data to memory, flush icache, for PG_TXTFLUSH) + +KMap* kmap(Page *p) + map p into the kernel address space (if necessary), and return the map's description + +void kunmap(KMap *k) + remove the mapping k + +KMap is platform-dependent + +if all physical memory can be mapped by the kernel, one can use + typedef void KMap; + #define kmap(pg) ((KMap*)KADDR((pg)->pa)) + #define kunmap(k) + +also relevant: PADDR and KADDR diff -Nru /sys/src/9/vt4/words/etherlltemac.kprocs.c /sys/src/9/vt4/words/etherlltemac.kprocs.c --- /sys/src/9/vt4/words/etherlltemac.kprocs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/words/etherlltemac.kprocs.c Fri Jun 7 00:00:00 2013 @@ -0,0 +1,102 @@ +/* + * alternate implementation with kprocs (TODO) + */ + +Rendez oqgotpkt; +Rendez txdmadone; +Rendez gotfifotxq; +Rendez txdone; + +int +isdmaready(Ether *ether) +{ + Block *bp; + Queue *q; + + q = ether->oq; + if (!qcanread(q)) + return 0; + bp = qget(q); + qputback(q, bp); + return (frp->tdfv & Wordcnt) >= + ROUNDUP(padpktlen(BLEN(bp)), BY2WD) / BY2WD; +} + +void +txdmaproc(Ether *ether) /* owns dma chan 1 */ +{ + unsigned ruplen; + Block *bp; + + for(;;) { + /* sleep until oq has pkt & tx fifo has room for it */ + sleep(&oqgotpkt, isdmaready, ether); + + bp = qget(ether->oq); + ruplen = ROUNDUP(padpktlen(BLEN(bp)), BY2WD); + + dmastart(Chan1, &frp->tdfd, bp->rp, ruplen, Sinc, dmatxdone); + sleep(&txdmadone); + + addfifotxq(ether, bp); + wakeup(&gotfifotxq); /* have work for temac now */ + } +} + +int +istemacready(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + return ether->fifotxq != nil && !ctlr->xmitting; +} + +void +txtemacproc(Ether *ether) /* owns temac transmitter */ +{ + Block *bp; + + for(;;) { + /* sleep until txq has pkt & tx is idle */ + sleep(&gotfifotxq, istemacready, ether); + + bp = ether->fifotxq; + ether->fifotxq = bp->next; + + kicktemac(ether); + sleep(&txdone); + + free(bp); + wakeup(&oqgotpkt); /* might be work for dma1 now */ + } +} + +dmatxdone() +{ + // ... + wakeup(&txdmadone); + // ... +} + +temactransmit(Ether *ether) +{ + // ... + wakeup(&oqgotpkt); + // ... +} + +dmaintr() +{ + // ... + wakeup(&oqgotpkt); /* if txfifo now has room */ + wakeup(&txdmadone); + // ... +} + +fifointr() +{ + // ... + wakeup(&txdone); + // ... +} diff -Nru /sys/src/9/vt4/words/mkfile.9k /sys/src/9/vt4/words/mkfile.9k --- /sys/src/9/vt4/words/mkfile.9k Thu Jan 1 00:00:00 1970 +++ /sys/src/9/vt4/words/mkfile.9k Fri Jun 7 00:00:00 2013 @@ -0,0 +1,74 @@ +CONF=vt4cpu +CONFLIST=vt4cpu + +loadaddr=0x80100020 +pagesize=0x100000 + +objtype=power +$target.list + size $target + +$p$CONF.elf:DQ: $CONF.$O $OBJ $LIB + echo '# linking elf kernel' + $LD -H5 -s -R$pagesize -T$loadaddr -o $target -l $OBJ $CONF.$O $LIB + +$p$CONF.gz:D: $p$CONF + gzip -9 <$p$CONF >$target + +$OBJ: $HFILES + +install:V: /$objtype/$p$CONF + +/$objtype/$p$CONF:D: $p$CONF $p$CONF.elf + cp -x $p$CONF $p$CONF.elf /$objtype + { 9fs lookout && cp -x $p$CONF $p$CONF.elf /n/lookout/$objtype } & + { 9fs piestand && cp -x $p$CONF $p$CONF.elf /n/piestand/$objtype } & + { 9fs slocum && cp -x $p$CONF.list /n/slocum/home/rae/hbsr/ml410_ddr2 } & + wait + touch $target + +init.out:D: init9.$O initcode.$O /$objtype/lib/libc.a + $LD -l -R4 -o init.out init9.$O initcode.$O -lc + +reboot.h:D: rebootcode.s + $AS rebootcode.s + # -lc is only for memmove. -T arg is PADDR(REBOOTADDR) + $LD -l -a -s -T0x2640 -R4 -o reboot.out rebootcode.$O -lc >reboot.list + {echo 'uchar rebootcode[]={' + xd -1x reboot.out | + sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' + echo '};'} > reboot.h + +archvt4.$O: io.h +devboot.$O: ../port/error.h +devether.$O: ../port/error.h ../port/netif.h etherif.h +ethertemac.$O: ../port/ethermii.h ../port/netif.h etherif.h io.h +etherplbtemac.$O: ../port/ethermii.h ../port/netif.h etherif.h io.h +fpi.$O: fpi.h +fpimem.$O: fpi.h +fpipower.$O: /$objtype/include/ureg.h fpi.h +fpu.$O: /$objtype/include/ureg.h +main.$O: /sys/include/pool.h /sys/include/tos.h init.h reboot.h +random.$O: ../port/error.h +syscall.$O: ../port/error.h +syscall.$O: /sys/include/tos.h /$objtype/include/ureg.h +trap.$O: /sys/include/tos.h /$objtype/include/ureg.h io.h +uartlite.$O: ../port/error.h io.h