--- /sys/lib/lp/bin/lpsend.rc Sun Mar 26 04:24:18 2000 +++ /sys/lib/lp/bin/lpsend.rc Tue Jul 23 21:40:10 2013 @@ -1,18 +1,13 @@ #!/bin/rc -if (! ~ $DEBUG '') { flag x + } -if (test -e /net/tcp/clone) { - dialstring=`{ndb/query sys $1 dom} - network=tcp - if (~ $#dialstring 0 || ! ~ $dialstring '') { - dialstring=$1 - } - if(lpsend $network^!^$dialstring^!printer) exit '' - rv='tcp failed' -} -if not rv='no tcp' +# lpsend.rc dialstring - run lpsend network!dialstring!printer +if (! ~ $DEBUG '') + flag x + +if (! test -e /net/tcp/clone) + exit 'no tcp' +dialstring=`{ndb/query sys $1 dom} +if (~ $#dialstring 0 || ~ $dialstring '') # no dom for sys in ndb? + dialstring=$1 # use arg unchanged -if (! ~ $dialstring '') - exit 'lpsend: no dialstring' -if not - exit 'lpsend: '^$rv +lpsend tcp!^$dialstring^!printer +exit $status --- /sys/src/9/mkfile Wed Jan 30 22:31:09 2013 +++ /sys/src/9/mkfile Wed Jul 24 00:28:06 2013 @@ -5,6 +5,7 @@ omap\ pc\ ppc\ + rb\ teg2\ all:V: --- /sys/src/9/rb/c_fcr0.s Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/c_fcr0.s Tue Jul 23 02:21:30 2013 @@ -0,0 +1,3 @@ +TEXT C_fcr0(SB), $-4 + MOVW $0x500, R1 /* claim to be an r4k, thus have ll/sc */ + RET --- /sys/src/9/rb/clock.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/clock.c Tue Jul 16 00:01:47 2013 @@ -0,0 +1,299 @@ +/* + * ar7161 clocks and timers + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +enum { + Cyccntres = 2, /* counter advances at ½ clock rate (mips 24k) */ + Basetickfreq = 680*Mhz / Cyccntres, /* rb450g */ +}; + +void (*kproftimer)(ulong); + +void +silencewdog(void) +{ + *Rstwdogtimer = Basetickfreq * 2 * 5; /* pet the dog */ +} + +void +sicwdog(void) +{ + *Rstwdogtimer = Basetickfreq * 2; + *Rstwdogctl = Wdogreset; /* wake the dog */ +} + +void +wdogreset(void) +{ + *Rstwdogtimer = Basetickfreq / 100; + *Rstwdogctl = Wdogreset; /* wake the dog */ + coherence(); + *Rstwdogtimer = Basetickfreq / 10000; + coherence(); +} + +void +stopwdog(void) +{ + *Rstwdogtimer = ~0; + *Rstwdogctl = Wdognoaction; /* put the dog to sleep */ +} + +void +clockshutdown(void) +{ + stopwdog(); +} + +/* + * delay for l milliseconds more or less. + */ +void +delay(int l) +{ + while(l-- > 0) + microdelay(1000); +} + +/* + * microseconds delay + */ +void +microdelay(int l) +{ + int s; + ulong x, cyc, cnt, speed; + + speed = m->speed; + if (speed == 0) + speed = Basetickfreq / Mhz * Cyccntres; + cyc = (ulong)l * (speed / Cyccntres); + s = splhi(); + cnt = rdcount(); + x = cnt + cyc; + if (x < cnt || x >= ~0ul - Basetickfreq) { + /* counter will wrap between now and x, or x is too near ~0 */ + wrcount(0); /* somewhat drastic */ + wrcompare(rdcompare() - cnt); /* match new count */ + x = cyc; + } + while(rdcount() < x) + ; + splx(s); + silencewdog(); +} + +void +clock(Ureg *ureg) +{ + wrcompare(rdcount()+m->maxperiod); /* side-effect: dismiss intr */ + silencewdog(); + timerintr(ureg, 0); +} + +enum { + Instrs = 10*Mhz, +}; + +static long +issue1loop(void) +{ + register int i; + long st; + + i = Instrs; + st = perfticks(); + do { + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; + /* omit 3 (--i) to account for conditional branch, nop & jump */ + i -= 1+3; /* --i plus 3 omitted (--i) instructions */ + } while(--i >= 0); + return perfticks() - st; +} + +/* estimate instructions/s. */ +static int +guessmips(long (*loop)(void), char *) +{ + int s; + long cyc; + + do { + s = splhi(); + cyc = loop(); + splx(s); + if (cyc < 0) + iprint("again..."); + } while (cyc < 0); + /* + * Instrs instructions took cyc cycles @ Basetickfreq Hz. + * round the result. + */ + return (((vlong)Basetickfreq * Instrs) / cyc + Mhz/2) / Mhz; +} + +void +clockinit(void) +{ + int mips; + + silencewdog(); + + /* + * calibrate fastclock + */ + mips = guessmips(issue1loop, "single"); + + /* + * m->delayloop should be the number of delay loop iterations + * needed to consume 1 ms, assuming 2 instr'ns in the delay loop. + */ + m->delayloop = mips*Mhz / (1000 * 2); + if(m->delayloop == 0) + m->delayloop = 1; + + m->speed = mips; + m->hz = m->speed*Mhz; + + m->maxperiod = Basetickfreq / HZ; + m->minperiod = Basetickfreq / (100*HZ); + wrcompare(rdcount()+m->maxperiod); + + /* + * desynchronize the processor clocks so that they all don't + * try to resched at the same time. + */ + delay(m->machno*2); + + syncclock(); + intron(INTR7); +} + +/* + * Tval is supposed to be in fastticks units. + * One fasttick unit is 1/Basetickfreq seconds. + */ +void +timerset(Tval next) +{ + int x; + long period; + + if(next == 0) + return; + x = splhi(); /* don't let us get scheduled */ + period = next - fastticks(nil); + if(period > m->maxperiod - m->minperiod) + period = m->maxperiod; + else if(period < m->minperiod) + period = m->minperiod; + wrcompare(rdcount()+period); + silencewdog(); + splx(x); +} + +/* + * The rewriting of compare in this routine shouldn't be necessary. + * However, we lose clock interrupts if I don't, either a chip bug + * or one of ours -- presotto + */ +uvlong +fastticks(uvlong *hz) +{ + int x; + ulong delta, count; + + if(hz) + *hz = Basetickfreq; + + /* avoid reentry on interrupt or trap, to prevent recursion */ + x = splhi(); + count = rdcount(); + if(rdcompare() - count > m->maxperiod) + wrcompare(count+m->maxperiod); + silencewdog(); + + if (count < m->lastcount) /* wrapped around? */ + delta = count + ((1ull<<32) - m->lastcount); + else + delta = count - m->lastcount; + m->lastcount = count; + m->fastticks += delta; + splx(x); + return m->fastticks; +} + +ulong +µs(void) +{ + return fastticks2us(fastticks(nil)); +} + +/* + * performance measurement ticks. must be low overhead. + * doesn't have to count over a second. + */ +ulong +perfticks(void) +{ + return rdcount(); +} + +long +lcycles(void) +{ + return perfticks(); +} + +/* should use vlong hw counters ideally; lcycles is inadequate */ +void +cycles(uvlong *cycp) +{ + *cycp = fastticks(nil); +} + +Lock mpsynclock; + +/* + * synchronize all clocks with processor 0 + */ +void +syncclock(void) +{ + uvlong x; + + if(m->machno == 0){ + m->lastcount = rdcount(); + m->fastticks = 0; + m->ticks = 0; + wrcompare(rdcount()+m->maxperiod); + } else { + /* wait for processor 0's soft clock to change and then sync ours */ + lock(&mpsynclock); + x = MACHP(0)->fastticks; + while(MACHP(0)->fastticks == x) + ; + m->lastcount = rdcount(); + m->fastticks = MACHP(0)->fastticks; + m->ticks = MACHP(0)->ticks; + wrcompare(rdcount()+m->maxperiod); + unlock(&mpsynclock); + } +} --- /sys/src/9/rb/dat.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/dat.h Sun Jul 21 08:22:29 2013 @@ -0,0 +1,225 @@ +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct KMap KMap; +typedef struct Lance Lance; +typedef struct Lancemem Lancemem; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct MMU MMU; +typedef struct Notsave Notsave; +typedef struct Pcidev Pcidev; +typedef struct PMMU PMMU; +typedef struct Softtlb Softtlb; +typedef struct Ureg Ureg; +typedef struct Proc Proc; +typedef uvlong Tval; + +#pragma incomplete Pcidev + +#define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */ + +/* + * parameters for sysproc.c and rebootcmd.c + */ +#define AOUT_MAGIC V_MAGIC || magic==M_MAGIC +/* r3k or r4k boot images */ +#define BOOT_MAGIC (0x160<<16) || magic == ((0x160<<16)|3) + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Lock +{ + ulong key; /* semaphore (non-zero = locked) */ + ulong sr; + ulong pc; + Proc *p; + Mach *m; + ushort isilock; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Confmem +{ + ulong base; + ulong npage; + ulong kbase; + ulong klimit; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + Confmem mem[1]; + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* bytes available for interrupt-time allocation */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +/* + * floating point registers + */ +enum +{ + /* floating point state */ + FPinit, + FPactive, + FPinactive, + FPemu, + + /* bit meaning floating point illegal */ + FPillegal= 0x100, +}; + +enum { + Nfpregs = 32, /* floats; half as many doubles */ +}; + +/* + * emulated floating point (mips32r2 with ieee fp regs) + * fpstate is separate, kept in Proc + */ +struct FPsave +{ + /* /dev/proc expects the registers to be first in FPsave */ + ulong reg[Nfpregs]; /* the canonical bits */ + union { + ulong fpstatus; /* both are fcr31 */ + ulong fpcontrol; + }; + + int fpdelayexec; /* executing delay slot of branch */ + uintptr fpdelaypc; /* pc to resume at after */ + ulong fpdelaysts; /* save across user-mode delay-slot execution */ + + /* stuck-fault detection */ + uintptr fppc; /* addr of last fault */ + int fpcnt; /* how many consecutive at that addr */ +}; + +int fpemudebug; + +/* + * mmu goo in the Proc structure + */ +struct PMMU +{ + int pidonmach[MAXMACH]; +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong nonempty; +}; + +#include "../port/portdat.h" + +/* First FIVE members' offsets known by l.s */ +struct Mach +{ + /* the following are all known by l.s and cannot be moved */ + int machno; /* physical id of processor FIRST */ + Softtlb*stb; /* Software tlb simulation SECOND */ + Proc* proc; /* process on this processor THIRD */ + ulong splpc; /* pc that called splhi() FOURTH */ + ulong tlbfault; /* # of tlb faults FIFTH */ + ulong ktlbfault; + ulong utlbfault; + + /* the following is safe to move */ + ulong tlbpurge; + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + void* alarm; /* alarms bound to this clock */ + int lastpid; /* last pid allocated on this machine */ + Proc* pidproc[NTLBPID]; /* proc that owns tlbpid on this mach */ + KMap* kactive; /* active on this machine */ + int knext; + uchar ktlbx[NTLB]; /* tlb index used for kmap */ + uchar ktlbnext; + int speed; /* cpu speed */ + ulong delayloop; /* for the delay() routine */ + ulong fairness; /* for runproc */ + int flushmmu; + int inclockintr; + int ilockdepth; + Perf perf; /* performance counters */ + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + + /* for per-processor timers */ + ulong lastcount; + uvlong fastticks; + ulong hz; + ulong maxperiod; + ulong minperiod; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + int pfault; + int cs; + int syscall; + int load; + int intr; + int hashcoll; /* soft-tlb hash collisions */ + int paststartup; /* for putktlb */ + + int stack[1]; +}; + +struct KMap +{ + Ref; + ulong virt; + ulong phys0; + ulong phys1; + KMap* next; + KMap* konmach[MAXMACH]; + Page* pg; + ulong pc; /* of caller to kmap() */ +}; + +#define VA(k) ((k)->virt) +#define PPN(x) ((ulong)(x)>>6) + +/* offsets known by l.s */ +struct Softtlb +{ + ulong virt; + ulong phys0; + ulong phys1; +}; + +struct +{ + Lock; + long machs; /* bitmap of processors */ + short exiting; + int ispanic; +}active; + +extern KMap kpte[]; +extern register Mach *m; +extern register Proc *up; + +extern FPsave initfp; + +extern int normalprint; --- /sys/src/9/rb/devarch.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/devarch.c Sun Jul 14 22:37:45 2013 @@ -0,0 +1,258 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include "../ip/ip.h" + +enum { + Qdir = 0, + Qbase, + + Qmax = 16, +}; + +typedef long Rdwrfn(Chan*, void*, long, vlong); + +static Rdwrfn *readfn[Qmax]; +static Rdwrfn *writefn[Qmax]; + +static Dirtab archdir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0555, +}; + +Lock archwlock; /* the lock is only for changing archdir */ +int narchdir = Qbase; + +/* + * Add a file to the #P listing. Once added, you can't delete it. + * You can't add a file with the same name as one already there, + * and you get a pointer to the Dirtab entry so you can do things + * like change the Qid version. Changing the Qid path is disallowed. + */ +Dirtab* +addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) +{ + int i; + Dirtab d; + Dirtab *dp; + + memset(&d, 0, sizeof d); + strcpy(d.name, name); + d.perm = perm; + + lock(&archwlock); + if(narchdir >= 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; +} + +void archinit(void); + +Dev archdevtab = { + 'P', + "arch", + + devreset, + archinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +static long +cputyperead(Chan*, void *a, long n, vlong offset) +{ + char str[128]; + + snprint(str, sizeof str, "MIPS 24k %lud\n", m->hz / Mhz); + 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); +} + +char *cputype = "mips"; + +char *faultsprint(char *, char *); +char *fpemuprint(char *, char *); + +static long +archctlread(Chan*, void *a, long nn, vlong offset) +{ + int n; + char *buf, *p, *ep; + + p = buf = malloc(READSTR); + if(p == nil) + error(Enomem); + ep = p + READSTR; + p = seprint(p, ep, "cpu %s %lud\n", cputype, + (ulong)(m->hz+999999)/1000000); + p = seprint(p, ep, "stlb hash collisions"); + for (n = 0; n < conf.nmach; n++) + p = seprint(p, ep, " %d", MACHP(n)->hashcoll); + p = seprint(p, ep, "\n"); + p = seprint(p, ep, "NKTLB %d ktlb misses %ld utlb misses %ld\n", + NKTLB, m->ktlbfault, m->utlbfault); + p = fpemuprint(p, ep); + faultsprint(p, ep); + n = readstr(offset, a, nn, buf); + free(buf); + return n; +} + +enum +{ + CMfpemudebug, +}; + +static Cmdtab archctlmsg[] = +{ +#ifdef FPEMUDEBUG + CMfpemudebug, "fpemudebug", 2, +#else + CMfpemudebug, "dummy", 1, +#endif +}; + +static long +archctlwrite(Chan*, void *a, long n, vlong) +{ + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); + switch(ct->index){ + case CMfpemudebug: + fpemudebug = atoi(cb->f[1]); + break; + } + free(cb); + poperror(); + return n; +} + +void +archinit(void) +{ + addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("timebase",0444, tbread, nil); + addarchfile("archctl", 0664, archctlread, archctlwrite); +// addarchfile("nsec", 0444, nsread, nil); +} --- /sys/src/9/rb/devether.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/devether.c Tue Jul 23 03:37:08 2013 @@ -0,0 +1,1475 @@ +/* + * Atheros 71xx ethernets for rb450g. + * + * all 5 PHYs are accessible only through first ether's register space. + * + * TODO: + * promiscuous mode. + * make ether1 work: probably needs mii/phy initialisation, + * maybe needs 8316 switch code too (which requires mdio, phy, etc. glop). + * to maybe do some day: + * dig mac addresses out & config phy/mii via spi or other grot and swill + * (instead of editing rb config file). + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" +#include "ethermii.h" +#include + +enum { + Ntd = 64, + Nrd = 256, + Nrb = 1024, + + Bufalign= 4, + Rbsz = ETHERMAXTU + 4, /* 4 for CRC */ +}; + +extern uchar arge0mac[Eaddrlen]; /* see rb config file */ +extern uchar arge1mac[Eaddrlen]; + +typedef struct Arge Arge; +typedef struct Ctlr Ctlr; +typedef struct Desc Desc; +typedef struct Etherif Etherif; + +/* + * device registers + */ +struct Arge { + ulong cfg1; + ulong cfg2; + ulong ifg; + ulong hduplex; + ulong maxframelen; + uchar _pad0[0x20 - 0x14]; + + ulong miicfg; + ulong miicmd; + ulong miiaddr; + ulong miictl; + ulong miists; + ulong miiindic; + + ulong ifctl; + ulong _pad1; + ulong staaddr1; + ulong staaddr2; + + ulong fifocfg[3]; + ulong fifotxthresh; + ulong fiforxfiltmatch; + ulong fiforxfiltmask; + ulong fiforam[7]; + uchar _pad2[0x180 - 0x7c]; + + /* dma */ + ulong txctl; + ulong txdesc; + ulong txsts; + ulong rxctl; + ulong rxdesc; + ulong rxsts; + ulong dmaintr; + ulong dmaintrsts; +}; + +enum { + Cfg1softrst = 1 << 31, + Cfg1simulrst = 1 << 30, + Cfg1macrxblkrst = 1 << 19, + Cfg1mactxblkrst = 1 << 18, + Cfg1rxfuncrst = 1 << 17, + Cfg1txfuncrst = 1 << 16, + Cfg1loopback = 1 << 8, + Cfg1rxflowctl = 1 << 5, + Cfg1txflowctl = 1 << 4, + Cfg1syncrx = 1 << 3, + Cfg1rxen = 1 << 2, + Cfg1synctx = 1 << 1, + Cfg1txen = 1 << 0, + + Cfg2preamblelenmask = 0xf, + Cfg2preamblelenshift = 12, + Cfg2ifmode1000 = 2 << 8, + Cfg2ifmode10_100 = 1 << 8, + Cfg2ifmodeshift = 8, + Cfg2ifmodemask = 3, + Cfg2hugeframe = 1 << 5, + Cfg2lenfield = 1 << 4, + Cfg2enpadcrc = 1 << 2, + Cfg2encrc = 1 << 1, + Cfg2fdx = 1 << 0, + + Miicfgrst = 1 << 31, + Miicfgscanautoinc = 1 << 5, + Miicfgpreamblesup = 1 << 4, + Miicfgclkselmask = 0x7, + Miicfgclkdiv4 = 0, + Miicfgclkdiv6 = 2, + Miicfgclkdiv8 = 3, + Miicfgclkdiv10 = 4, + Miicfgclkdiv14 = 5, + Miicfgclkdiv20 = 6, + Miicfgclkdiv28 = 7, + + Miicmdscancycle = 1 << 1, + Miicmdread = 1, + Miicmdwrite = 0, + + Miiphyaddrshift = 8, + Miiphyaddrmask = 0xff, + Miiregmask = 0x1f, + + Miictlmask = 0xffff, + + Miistsmask = 0xffff, + + Miiindicinvalid = 1 << 2, + Miiindicscanning = 1 << 1, + Miiindicbusy = 1 << 0, + + Ifctlspeed = 1 << 16, + + Fifocfg0txfabric = 1 << 4, + Fifocfg0txsys = 1 << 3, + Fifocfg0rxfabric = 1 << 2, + Fifocfg0rxsys = 1 << 1, + Fifocfg0watermark = 1 << 0, + Fifocfg0all = MASK(5), + Fifocfg0enshift = 8, + + /* + * these flags applicable both to filter mask and to filter match. + * `Ff' is for `fifo filter'. + */ + Ffunicast = 1 << 17, + Fftruncframe = 1 << 16, + Ffvlantag = 1 << 15, + Ffunsupopcode = 1 << 14, + Ffpauseframe = 1 << 13, + Ffctlframe = 1 << 12, + Fflongevent = 1 << 11, + Ffdribblenibble = 1 << 10, + Ffbcast = 1 << 9, + Ffmcast = 1 << 8, + Ffok = 1 << 7, + Ffoorange = 1 << 6, + Fflenmsmtch = 1 << 5, + Ffcrcerr = 1 << 4, + Ffcodeerr = 1 << 3, + Fffalsecarrier = 1 << 2, + Ffrxdvevent = 1 << 1, + Ffdropevent = 1 << 0, + /* + * exclude unicast and truncated frames from matching. + */ + Ffmatchdflt = Ffvlantag | Ffunsupopcode | Ffpauseframe | Ffctlframe | + Fflongevent | Ffdribblenibble | Ffbcast | Ffmcast | Ffok | + Ffoorange | Fflenmsmtch | Ffcrcerr | Ffcodeerr | + Fffalsecarrier | Ffrxdvevent | Ffdropevent, + + /* `Frm' is for `fifo receive mask'. */ + Frmbytemode = 1 << 19, + Frmnoshortframe = 1 << 18, + Frmbit17 = 1 << 17, + Frmbit16 = 1 << 16, + Frmtruncframe = 1 << 15, + Frmlongevent = 1 << 14, + Frmvlantag = 1 << 13, + Frmunsupopcode = 1 << 12, + Frmpauseframe = 1 << 11, + Frmctlframe = 1 << 10, + Frmdribblenibble = 1 << 9, + Frmbcast = 1 << 8, + Frmmcast = 1 << 7, + Frmok = 1 << 6, + Frmoorange = 1 << 5, + Frmlenmsmtch = 1 << 4, + Frmcodeerr = 1 << 3, + Frmfalsecarrier = 1 << 2, + Frmrxdvevent = 1 << 1, + Frmdropevent = 1 << 0, + /* + * len. mismatch, unsupp. opcode and short frame bits excluded + */ + Ffmaskdflt = Frmnoshortframe | Frmbit17 | Frmbit16 | Frmtruncframe | + Frmlongevent | Frmvlantag | Frmpauseframe | Frmctlframe | + Frmdribblenibble | Frmbcast | Frmmcast | Frmok | Frmoorange | + Frmcodeerr | Frmfalsecarrier | Frmrxdvevent | Frmdropevent, + + Dmatxctlen = 1 << 0, + + /* dma tx status */ + Txpcountmask = 0xff, + Txpcountshift = 16, + Txbuserr = 1 << 3, + Txunderrun = 1 << 1, + Txpktsent = 1 << 0, + + Dmarxctlen = 1 << 0, + + /* dma rx status */ + Rxpcountmask = 0xff, + Rxpcountshift = 16, + Rxbuserr = 1 << 3, + Rxovflo = 1 << 2, + Rxpktrcvd = 1 << 0, + + /* dmaintr & dmaintrsts bits */ + Dmarxbuserr = 1 << 7, + Dmarxovflo = 1 << 6, + Dmarxpktrcvd = 1 << 4, + Dmatxbuserr = 1 << 3, + Dmatxunderrun = 1 << 1, + Dmatxpktsent = 1 << 0, + /* we don't really need most tx interrupts */ + Dmaall = Dmarxbuserr | Dmarxovflo | Dmarxpktrcvd | Dmatxbuserr, + + Spictlremapdisable = 1 << 6, + Spictlclkdividermask = MASK(6), + + Spiioctlcs2 = 1 << 18, + Spiioctlcs1 = 1 << 17, + Spiioctlcs0 = 1 << 16, + Spiioctlcsmask = 7 << 16, + Spiioctlclk = 1 << 8, + Spiioctldo = 1, +}; + +struct Spi { /* at 0x1f000000 */ + ulong fs; + ulong ctl; + ulong ioctl; + ulong rds; +}; + +/* hw descriptors of buffer rings (rx and tx), need to be uncached */ +struct Desc { + ulong addr; /* of packet buffer */ + ulong ctl; + Desc *next; + ulong _pad; +}; + +enum { + Descempty = 1 << 31, + Descmore = 1 << 24, + Descszmask = MASK(12), +}; +#define DMASIZE(len) ((len) & Descszmask) + +struct Ctlr { + Arge *regs; + Ether* edev; /* backward pointer */ + + Lock; /* attach */ + int init; + int attached; + + Mii* mii; + Rendez lrendez; + int lim; + int link; + int phymask; + + /* receiver */ + Rendez rrendez; + uint rintr; /* count */ + int pktstoread; /* flag */ + int discard; + /* rx descriptors */ + Desc* rdba; /* base address */ + Block** rd; + uint rdh; /* head */ + uint rdt; /* tail */ + uint nrdfree; /* rd's awaiting pkts (sort of) */ + + /* transmitter */ + Rendez trendez; + uint tintr; /* count */ + int pktstosend; /* flag */ + int ntq; + /* tx descriptors */ + Desc* tdba; /* base address */ + Block** td; + uint tdh; /* head */ + uint tdt; /* tail */ +}; + +struct Etherif { + uintptr regs; + int irq; + uchar *mac; + int phymask; +}; + +static Etherif etherifs[] = { + { 0x1a000000, ILenet0, arge0mac, 1<<4 }, + { 0x19000000, ILenet1, arge1mac, MASK(4) }, +}; + +static Ether *etherxx[MaxEther]; +static Lock athrblock; /* free receive Blocks */ +static Block* athrbpool; /* receive Blocks for all ath controllers */ + +static void athrbfree(Block* bp); + +/* + * ar8316 ether switch + */ + +enum { + Swrgmii = 0, + Swgmii = 1, + Swphy4cpu = 0, /* flag: port 4 connected to CPU (not internal switch) */ +}; + +typedef struct Switch Switch; +struct Switch { + int page; + int scdev; +}; + +enum { + /* atheros-specific mii registers */ + Miiathdbgaddr = 0x1d, + Miiathdbgdata = 0x1e, + + Swregmask = 0, + Swmaskrevmask = 0x00ff, + Swmaskvermask = 0xff00, + Swmaskvershift = 8, + Swmasksoftreset = 1 << 31, + + Swregmode = 8, + Swdir615uboot = 0x8d1003e0, + /* from ubiquiti rspro */ + Swrgmiiport4iso = 0x81461bea, + Swrgmiiport4sw = 0x01261be2, + /* avm fritz!box 7390 */ + Swgmiiavm = 0x010e5b71, + + Swmac0gmiien = 1 << 0, + Swmac0rgmiien = 1 << 1, + Swphy4gmiien = 1 << 2, + Swphy4rgmiien = 1 << 3, + Swmac0macmode = 1 << 4, + Swrgmiirxclkdelayen= 1 << 6, + Swrgmiitxclkdelayen= 1 << 7, + Swmac5macmode = 1 << 14, + Swmac5phymode = 1 << 15, + Swtxdelays0 = 1 << 21, + Swtxdelays1 = 1 << 22, + Swrxdelays0 = 1 << 23, + Swledopenen = 1 << 24, + Swspien = 1 << 25, + Swrxdelays1 = 1 << 26, + Swpoweronsel = 1 << 31, + + Swregfloodmask = 0x2c, + Swfloodmaskbcast2cpu= 1 << 26, + + Swregglobal = 0x30, + Swglobalmtumask = 0x7fff, +}; + +#ifdef NOTYET +void * +devicegetparent(int) +{ + static int glop; + + return &glop; +} + +static void +arswsplitsetpage(int dev, ulong addr, ushort *phy, ushort *reg) +{ + static Switch ar8316; + Switch *sc = &ar8316; + ushort page; + + page = ((addr) >> 9) & 0xffff; + *phy = (((addr) >> 6) & 0x7) | 0x10; + *reg = ((addr) >> 1) & 0x1f; + MDIOWRREG(devicegetparent(dev), 0x18, 0, page); + sc->page = page; +} + +/* + * Read half a register. Some of the registers define control bits, and + * the sequence of half-word accesses matters. The register addresses + * are word-even (mod 4). + */ +static int +arswrdreg16(int dev, int addr) +{ + ushort phy, reg; + + arswsplitsetpage(dev, addr, &phy, ®); + return MDIORDREG(devicegetparent(dev), phy, reg); +} + +void +arswwritedbg(int dev, int phy, ushort dbgaddr, ushort dbgdata) +{ + MDIOWRREG(devicegetparent(dev), phy, Miiathdbgaddr, dbgaddr); + MDIOWRREG(devicegetparent(dev), phy, Miiathdbgdata, dbgdata); +} + +/* + * Write half a register + */ +static inline int +arswwrreg16(int dev, int addr, int data) +{ + ushort phy, reg; + + arswsplitsetpage(dev, addr, &phy, ®); + return MDIOWRREG(devicegetparent(dev), phy, reg, data); +} + +/* arsw??reglsb routines operate on lower 16 bits; *msb on upper ones */ + +int +arswrdreg(int dev, int addr) +{ + return arswrdreglsb(dev, addr) | arswrdregmsb(dev, addr); +} + +int +arswwrreg(int dev, int addr, int value) +{ + arswwrreglsb(dev, addr, value); /* XXX check this write too? */ + return arswwrregmsb(dev, addr, value); +} + +int +arswmodifyreg(int dev, int addr, int mask, int set) +{ + return arswwrreg(dev, addr, (arswrdreg(dev, addr) & ~mask) | set); +} + +/* + * initialise the switch + */ +static int +ar8316init(Switch *sc) +{ + if (Swrgmii && Swphy4cpu) { + arswwrreg(sc->scdev, Swregmode, Swrgmiiport4iso); + iprint("ar8316: MAC port == RGMII, port 4 = dedicated PHY\n"); + } else if (Swrgmii) { + arswwrreg(sc->scdev, Swregmode, Swrgmiiport4sw); + iprint("ar8316: MAC port == RGMII, port 4 = switch port\n"); + } else if (Swgmii) { + arswwrreg(sc->scdev, Swregmode, Swgmiiavm); + iprint("ar8316: MAC port == GMII\n"); + } else { + iprint("ar8316: unknown switch PHY config\n"); + return -1; + } + + delay(1); /* wait for things to settle */ + + if (Swrgmii && Swphy4cpu) { + iprint("ar8316: port 4 RGMII hack\n"); + + /* work around for phy4 rgmii mode */ + arswwritedbg(sc->scdev, 4, 0x12, 0x480c); + arswwritedbg(sc->scdev, 4, 0x0, 0x824e); /* rx delay */ + arswwritedbg(sc->scdev, 4, 0x5, 0x3d47); /* tx delay */ + delay(1); /* again to let things settle */ + } + arswwrreg(sc->scdev, 0x38, 0xc000050e); /* mystery */ + + /* + * Flood address table misses to all ports, and enable forwarding of + * broadcasts to the cpu port. + */ + arswwrreg(sc->scdev, Swregfloodmask, Swfloodmaskbcast2cpu | 0x003f003f); + arswmodifyreg(sc->scdev, Swregglobal, Swglobalmtumask, ETHERMAXTU+8+2); + return 0; +} +#endif /* NOTYET */ + +static long +ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l, i, r; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + if(p == nil) + error(Enomem); + l = 0; + l += snprint(p+l, READSTR-l, "tintr: %ud\n", ctlr->tintr); + l += snprint(p+l, READSTR-l, "rintr: %ud\n", ctlr->rintr); + l += snprint(p+l, READSTR-l, "discarded: %ud\n", ctlr->discard); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, READSTR-l, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, READSTR-l, " %4.4uX", r); + } + snprint(p+l, READSTR-l, "\n"); + } + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + Ctlr *ctlr; + + ether->inpackets++; + ctlr = ether->ctlr; + + 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){ + ctlr->discard++; + 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) != nil && (f->type == type || f->type < 0)) + if(tome || multi || f->prom) + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + else if(f->headersonly) + etherrtrace(f, pkt, len); + else if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0){ + iprint("soverflow for f->in\n"); + ether->soverflows++; + } + }else{ + iprint("soverflow iallocb\n"); + ether->soverflows++; + } + if(fx){ + if(qpass(fx->in, bp) < 0){ + iprint("soverflow for fx->in\n"); + ether->soverflows++; + } + return 0; + } + if(fromwire){ + ctlr->discard++; + freeb(bp); + return 0; + } + return bp; +} + +static void +athhwreset(Ether *ether) +{ + Ctlr *ctlr; + Arge *arge; + + ctlr = ether->ctlr; + if (ctlr == nil) + return; + arge = ctlr->regs; + if (arge == nil) + return; + + arge->dmaintr = 0; + + arge->rxctl = 0; + arge->txctl = 0; + coherence(); + + /* + * give tx & rx time to stop, otherwise clearing desc registers + * too early will cause random memory corruption. + */ + delay(1); + + arge->rxdesc = 0; + arge->txdesc = 0; + coherence(); + + /* clear all interrupts */ + while (arge->rxsts & Rxpktrcvd) + arge->rxsts = Rxpktrcvd; + while (arge->txsts & Txpktsent) + arge->txsts = Txpktsent; + + /* and errors */ + arge->rxsts = Rxbuserr | Rxovflo; + arge->txsts = Txbuserr | Txunderrun; +} + +static void +txreclaim(Ctlr *ctlr) +{ + uint tdh; + Arge *arge; + Block *bp; + + arge = ctlr->regs; + tdh = ctlr->tdh; + while (tdh != ctlr->tdt && ctlr->tdba[tdh].ctl & Descempty){ + arge->txsts = Txpktsent; + + bp = ctlr->td[tdh]; + ctlr->td[tdh] = nil; + if (bp) + freeb(bp); + + ctlr->tdba[tdh].addr = 0; + ctlr->ntq--; + tdh = NEXT(tdh, Ntd); + } + ctlr->tdh = tdh; +} + +static Block* +athrballoc(void) +{ + Block *bp; + + ilock(&athrblock); + if((bp = athrbpool) != nil){ + athrbpool = bp->next; + bp->next = nil; + _xinc(&bp->ref); /* prevent bp from being freed */ + } + iunlock(&athrblock); + return bp; +} + +static void +athrbfree(Block* bp) +{ + bp->wp = bp->rp = bp->lim - ROUND(Rbsz, BLOCKALIGN); + bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck); + + ilock(&athrblock); + bp->next = athrbpool; + athrbpool = bp; + iunlock(&athrblock); +} + +static void +rxnewbuf(Ctlr *ctlr, int i) +{ + Block *bp; + Desc *rd; + + if (ctlr->rd[i] != nil) + return; + ctlr->rd[i] = bp = athrballoc(); + if(bp == nil) + panic("#l%d: can't allocate receive buffer", + ctlr->edev->ctlrno); + dcflush(bp->rp, Rbsz); /* writeback & invalidate */ + + rd = &ctlr->rdba[i]; + rd->addr = PADDR(bp->rp); + rd->ctl = Descempty | DMASIZE(Rbsz); + ctlr->nrdfree++; +} + +static void +rxreclaim(Ctlr *ctlr) +{ + uint rdt; + + rdt = ctlr->rdt; + while (rdt != ctlr->rdh && !(ctlr->rdba[rdt].ctl & Descempty)){ + rxnewbuf(ctlr, rdt); + rdt = NEXT(rdt, Nrd); + } + ctlr->rdt = rdt; +} + +static void +etherintr(void *arg) +{ + int sts; + Arge *arge; + Ctlr *ctlr; + Ether *ether; + + ether = arg; + ctlr = ether->ctlr; + arge = ctlr->regs; + ilock(ctlr); + sts = arge->dmaintrsts; + if (sts & Dmarxpktrcvd) { + arge->dmaintr &= ~Dmarxpktrcvd; + ctlr->pktstoread = 1; + wakeup(&ctlr->rrendez); + ctlr->rintr++; + sts &= ~Dmarxpktrcvd; + } + if (sts & (Dmatxpktsent | Dmatxunderrun)) { + arge->dmaintr &= ~(Dmatxpktsent | Dmatxunderrun); + ctlr->pktstosend = 1; + wakeup(&ctlr->trendez); + ctlr->tintr++; + sts &= ~(Dmatxpktsent | Dmatxunderrun); + } + iunlock(ctlr); + if (sts) + iprint("#l%d: sts %#ux\n", ether->ctlrno, sts); +} + +static int +pktstoread(void* v) +{ + Ctlr *ctlr = v; + + return ctlr->pktstoread || !(ctlr->rdba[ctlr->rdh].ctl & Descempty); +} + +static void +rproc(void* arg) +{ + uint rdh, sz; + Arge *arge; + Block *bp; + Ctlr *ctlr; + Desc *rd; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + arge = ctlr->regs; + for(;;){ + /* wait for next interrupt */ + ilock(ctlr); + arge->dmaintr |= Dmarxpktrcvd; + iunlock(ctlr); + + sleep(&ctlr->rrendez, pktstoread, ctlr); + ctlr->pktstoread = 0; + + rxreclaim(ctlr); + rdh = ctlr->rdh; + for (rd = &ctlr->rdba[rdh]; !(rd->ctl & Descempty); + rd = &ctlr->rdba[rdh]){ + bp = ctlr->rd[rdh]; + assert(bp != nil); + ctlr->rd[rdh] = nil; + + /* omit final 4 bytes (crc), pass pkt upstream */ + sz = DMASIZE(rd->ctl) - 4; + assert(sz > 0 && sz <= Rbsz); + bp->wp = bp->rp + sz; + bp = etheriq(edev, bp, 1); + assert(bp == nil); /* Block was consumed */ + + arge->rxsts = Rxpktrcvd; + + ctlr->nrdfree--; + rdh = NEXT(rdh, Nrd); + if(ctlr->nrdfree < Nrd/2) { + /* rxreclaim reads ctlr->rdh */ + ctlr->rdh = rdh; + rxreclaim(edev->ctlr); + } + } + ctlr->rdh = rdh; + } +} + +static int +pktstosend(void* v) +{ + Ether *edev = v; + Ctlr *ctlr = edev->ctlr; + + return ctlr->pktstosend || ctlr->ntq > 0 || qlen(edev->oq) > 0; +} + +static void +tproc(void* arg) +{ + uint tdt, added; + Arge *arge; + Block *bp; + Ctlr *ctlr; + Desc *td; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + arge = ctlr->regs; + for(;;){ + /* wait for next free buffer and output queue block */ + sleep(&ctlr->trendez, pktstosend, edev); + ctlr->pktstosend = 0; + + txreclaim(ctlr); + + /* copy as much of my output q as possible into output ring */ + added = 0; + tdt = ctlr->tdt; + while(ctlr->ntq < Ntd - 1){ + td = &ctlr->tdba[tdt]; + if (!(td->ctl & Descempty)) + break; + bp = qget(edev->oq); + if(bp == nil) + break; + + /* make sure the whole packet is in ram */ + dcflush(bp->rp, BLEN(bp)); + + /* + * Give ownership of the descriptor to the chip, + * increment the software ring descriptor pointer. + */ + ctlr->td[tdt] = bp; + td->addr = PADDR(bp->rp); + td->ctl = DMASIZE(BLEN(bp)); + coherence(); + + added++; + ctlr->ntq++; + tdt = NEXT(tdt, Ntd); + } + ctlr->tdt = tdt; + /* + * Underrun turns off TX. Clear underrun indication. + * If there's anything left in the ring, reactivate the tx. + */ + if (arge->dmaintrsts & Dmatxunderrun) + arge->txsts = Txunderrun; + if(1 || added) + arge->txctl = Dmatxctlen; /* kick xmiter */ + ilock(ctlr); + if(ctlr->ntq >= Ntd/2) /* tx ring half-full? */ + arge->dmaintr |= Dmatxpktsent; + else if (ctlr->ntq > 0) + arge->dmaintr |= Dmatxunderrun; + iunlock(ctlr); + txreclaim(ctlr); + } +} + +/* + * turn promiscuous mode on/off + */ +static void +promiscuous(void *ve, int on) +{ + USED(ve, on); +} + +static void +multicast(void *ve, uchar*, int on) +{ + USED(ve, on); +} + +static void +linkdescs(Desc *base, int ndesc) +{ + int i; + + for(i = 0; i < ndesc - 1; i++) + base[i].next = (Desc *)PADDR(&base[i+1]); + base[ndesc - 1].next = (Desc *)PADDR(&base[0]); +} + +/* + * Initialise the receive and transmit buffer rings. + * + * This routine is protected by ctlr->init. + */ +static void +ringinit(Ctlr* ctlr) +{ + int i; + void *v; + + if(ctlr->rdba == 0){ + v = xspanalloc(Nrd * sizeof(Desc), CACHELINESZ, 0); + assert(v); + ctlr->rdba = (Desc *)KSEG1ADDR(v); + ctlr->rd = xspanalloc(Nrd * sizeof(Block *), 0, 0); + assert(ctlr->rd != nil); + linkdescs(ctlr->rdba, Nrd); + for(i = 0; i < Nrd; i++) + rxnewbuf(ctlr, i); + } + ctlr->rdt = ctlr->rdh = 0; + + if(ctlr->tdba == 0) { + v = xspanalloc(Ntd * sizeof(Desc), CACHELINESZ, 0); + assert(v); + ctlr->tdba = (Desc *)KSEG1ADDR(v); + ctlr->td = xspanalloc(Ntd * sizeof(Block *), 0, 0); + assert(ctlr->td != nil); + } + memset(ctlr->td, 0, Ntd * sizeof(Block *)); + + linkdescs(ctlr->tdba, Ntd); + for(i = 0; i < Ntd; i++) + ctlr->tdba[i].ctl = Descempty; + + ctlr->tdh = ctlr->tdt = 0; +} + +static void +cfgmediaduplex(Ether *ether) +{ + Arge *arge, *arge0; + Ctlr *ctlr; + + ctlr = ether->ctlr; + arge = ctlr->regs; + arge->cfg2 = (arge->cfg2 & ~Cfg2ifmode10_100) | Cfg2ifmode1000 | Cfg2fdx; + arge->ifctl &= ~Ifctlspeed; + arge->fiforxfiltmask |= Frmbytemode; + arge->fifotxthresh = 0x008001ff; /* undocumented magic */ + + if (ether->ctlrno > 0) { + /* set PLL registers: copy from arge0 */ + arge0 = (Arge *)(KSEG1 | etherifs[0].regs); + USED(arge0); + } +} + +static void +athmii(Ether *ether, int phymask) +{ + USED(ether, phymask); +} + +static void +athcfg(Ether *ether, int phymask) +{ + uchar *eaddr; + Arge *arge; + Ctlr *ctlr; + + ctlr = ether->ctlr; + arge = ctlr->regs; + if(ether->ctlrno > 0){ + if(0){ + /* doing this seems to disable both ethers */ + arge->cfg1 |= Cfg1softrst; /* stop */ + delay(20); + *Reset |= Rstge1mac; + delay(100); + } + *Reset &= ~Rstge1mac; + delay(200); + } + + /* configure */ + arge->cfg1 = Cfg1syncrx | Cfg1rxen | Cfg1synctx | Cfg1txen; + arge->cfg2 |= Cfg2enpadcrc | Cfg2lenfield | Cfg2encrc; + arge->maxframelen = Rbsz; + + if(ether->ctlrno > 0){ + arge->miicfg = Miicfgrst; + delay(100); + arge->miicfg = Miicfgclkdiv28; + delay(100); + } + + /* + * Set all Ethernet address registers to the same initial values + * set all four addresses to 66-88-aa-cc-dd-ee + */ + eaddr = ether->ea; + arge->staaddr1 = eaddr[2]<<24 | eaddr[3]<<16 | eaddr[4]<<8 | eaddr[5]; + arge->staaddr2 = eaddr[0]<< 8 | eaddr[1]; + + arge->fifocfg[0] = Fifocfg0all << Fifocfg0enshift; /* undocumented magic */ + arge->fifocfg[1] = 0x0fff0000; /* undocumented magic */ + arge->fifocfg[2] = 0x00001fff; /* undocumented magic */ + + arge->fiforxfiltmatch = Ffmatchdflt; + arge->fiforxfiltmask = Ffmaskdflt; + + /* phy goo */ + athmii(ether, phymask); + if (ether->ctlrno > 0) + cfgmediaduplex(ether); +} + +static int +athattach(Ether *ether) +{ + int i; + char name[32]; + Arge *arge; + Block *bp; + Ctlr *ctlr; + + ctlr = ether->ctlr; + if (ctlr->attached) + return -1; + ilock(ctlr); + ctlr->init = 1; + for(i = 0; i < Nrb; i++){ + if((bp = allocb(Rbsz + Bufalign)) == nil) + error(Enomem); + bp->free = athrbfree; + freeb(bp); + } + ringinit(ctlr); + ctlr->init = 0; + iunlock(ctlr); + + athcfg(ether, ctlr->phymask); + + /* start */ + arge = ctlr->regs; + arge->txdesc = PADDR(ctlr->tdba); + arge->rxdesc = PADDR(ctlr->rdba); + coherence(); + arge->rxctl = Dmarxctlen; + + snprint(name, KNAMELEN, "#l%drproc", ether->ctlrno); + kproc(name, rproc, ether); + + snprint(name, KNAMELEN, "#l%dtproc", ether->ctlrno); + kproc(name, tproc, ether); + + ilock(ctlr); + arge->dmaintr |= Dmaall; + iunlock(ctlr); + + ctlr->attached = 1; + return 0; +} + +/* + * strategy: RouterBOOT has initialised arge0, try to leave it alone. + * copy arge0 registers to arge1, with a few exceptions. + */ +static int +athreset(Ether *ether) +{ + Arge *arge; + Ctlr *ctlr; + Etherif *ep; + + if (ether->ctlrno < 0 || ether->ctlrno >= MaxEther) + return -1; + if (ether->ctlr == nil) { + /* + * Allocate a controller structure and start to initialise it. + */ + ether->ctlr = ctlr = malloc(sizeof(Ctlr)); + if (ctlr == nil) + return -1; + ctlr->edev = ether; + ep = etherifs + ether->ctlrno; + ctlr->regs = arge = (Arge *)(KSEG1 | ep->regs); + ctlr->phymask = ep->phymask; + + ether->port = (uint)arge; + ether->irq = ep->irq; + memmove(ether->ea, ep->mac, Eaddrlen); + ether->ifstat = ifstat; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + } + athhwreset(ether); + return 0; +} + +static Ether* +etherprobe(int ctlrno) +{ + int i, lg; + ulong mb, bsz; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + if(ether == nil) + error(Enomem); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->mbps = 1000; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->mtu = ETHERMAXTU; + + if(ctlrno >= MaxEther || athreset(ether) < 0){ + free(ether); + return nil; + } + + snprint(name, sizeof(name), "ether%d", ctlrno); + + /* + * If ether->irq is <0, it is a hack to indicate no interrupt + * used by ethersink. + * apparently has to be done here and cannot be deferred until attach. + */ + if(ether->irq >= 0) + intrenable(ether->irq, etherintr, ether); + + i = sprint(buf, "#l%d: atheros71xx: ", ctlrno); + if(ether->mbps >= 1000) + i += sprint(buf+i, "%dGbps", ether->mbps/1000); + else + i += sprint(buf+i, "%dMbps", ether->mbps); + i += sprint(buf+i, " port %#luX irq %d", PADDR(ether->port), ether->irq); + 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); + + /* + * input queues are allocated by ../port/netif.c:/^openfile. + * the size will be the last argument to netifinit() below. + * + * output queues should be small, to minimise `bufferbloat', + * which confuses tcp's feedback loop. at 1Gb/s, it only takes + * ~15µs to transmit a full-sized non-jumbo packet. + */ + + /* compute log10(ether->mbps) into lg */ + for(lg = 0, mb = ether->mbps; mb >= 10; lg++) + mb /= 10; + if (lg > 13) /* sanity cap; 2**(13+16) = 2²⁹ */ + lg = 13; + + /* allocate larger input queues for higher-speed interfaces */ + bsz = 1UL << (lg + 16); /* 2ⁱ⁶ = 64K, bsz = 2ⁿ × 64K */ + while (bsz > mainmem->maxsize / 8 && bsz > 128*1024) /* sanity */ + bsz /= 2; + netifinit(ether, name, Ntypes, bsz); + + if(ether->oq == nil) + ether->oq = qopen(1 << (lg + 13), Qmsg, 0, 0); + if(ether->oq == nil) + panic("etherreset %s: can't allocate output queue", name); + + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + return ether; +} + +static void +etherreset(void) +{ + int ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++) + etherxx[ctlrno] = etherprobe(ctlrno); +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether) + athhwreset(ether); + } +} + +static Chan * +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->dev = ctlrno; + athattach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return netifwalk(etherxx[c->dev], c, nc, name, nname); +} + +static Chan* +etheropen(Chan *c, int omode) +{ + return netifopen(etherxx[c->dev], c, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan *c) +{ + netifclose(etherxx[c->dev], c); +} + +static long +etherread(Chan *chan, void *buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan *c, long n, ulong offset) +{ + return netifbread(etherxx[c->dev], c, n, offset); +} + +/* kick the transmitter to drain the output ring */ +static void +athtransmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + ctlr->pktstosend = 1; + wakeup(&ctlr->trendez); + iunlock(ctlr); +} + +static long (*athctl)(Ether *, char *, int) = nil; + +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){ + if(qfull(ether->oq)) + print("etheroq: WARNING: ether->oq full!\n"); + qbwrite(ether->oq, bp); + athtransmit(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->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(cb->f[0] && 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(athctl != nil) + return athctl(ether, buf, n); + error(Ebadctl); + } + + assert(ether->ctlr != nil); + if(n > ether->mtu) + 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 *c, Block *bp, ulong offset) +{ + return devbwrite(c, bp, offset); +} + +static int +etherstat(Chan *c, uchar *dp, int n) +{ + return netifstat(etherxx[c->dev], c, dp, n); +} + +static int +etherwstat(Chan *c, uchar *dp, int n) +{ + return netifwstat(etherxx[c->dev], c, dp, n); +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + devpower, + devconfig, +}; --- /sys/src/9/rb/etherif.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/etherif.h Tue Jul 16 22:19:09 2013 @@ -0,0 +1,36 @@ +enum { + MaxEther = 2, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + int ctlrno; + int port; + int irq; + int tbdf; /* type+busno+devno+funcno */ + + void *ctlr; + Queue* oq; + uchar ea[Eaddrlen]; + + long (*ifstat)(Ether*, void*, long, ulong); +#ifdef MULTIETHERTYPES + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ +#endif + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) --- /sys/src/9/rb/ethermii.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/ethermii.c Wed Jun 6 22:11:14 2007 @@ -0,0 +1,235 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, oui, phyno, r, rmask; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + r = mii->mir(mii, phyno, Phyidr1); + oui = (r & 0x3FFF)<<6; + r = mii->mir(mii, phyno, Phyidr2); + oui |= r>>10; + if(oui == 0xFFFFF || oui == 0) + continue; + + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + miiphy->oui = oui; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); + microdelay(1); + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) { + // print("miistatus: auto-neg incomplete\n"); + return -1; + } + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ + // print("miistatus: link down\n"); + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) { + // print("miistatus: phy speed 0\n"); + return -1; + } + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} --- /sys/src/9/rb/ethermii.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/ethermii.h Fri Feb 18 08:13:38 2005 @@ -0,0 +1,116 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Esr = 0x0F, /* Extended Status */ + + 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 */ +}; + +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*); --- /sys/src/9/rb/faultmips.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/faultmips.c Wed Jul 17 00:35:46 2013 @@ -0,0 +1,238 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "io.h" + +enum { + Debug = 0, +}; + +typedef struct Fault Fault; +struct Fault { + uintptr va; + ulong pid; + uintptr pc; + int cnt; + char *prog; + int code; +}; + +extern char *excname[]; + +static Fault lflt, maxflt; + +/* + * Ask if the instruction at EPC could have cause this badvaddr + */ +int +tstbadvaddr(Ureg *ur) +{ + int rn; + ulong iw, off, ea; + + iw = ur->pc; + if(ur->cause & BD) + iw += 4; + + if(seg(up, iw, 0) == 0) + return 0; + + iw = *(ulong*)iw; + +/* print("iw: %#lux\n", iw); /**/ + + switch((iw>>26) & 0x3f) { + default: + return 1; + case 0x20: /* LB */ + case 0x24: /* LBU */ + /* LD */ + case 0x35: + case 0x36: + case 0x37: /* LDCz */ + case 0x1A: /* LDL */ + case 0x1B: /* LDR */ + case 0x21: /* LH */ + case 0x25: /* LHU */ + case 0x30: /* LL */ + case 0x34: /* LLD */ + case 0x23: /* LW */ + case 0x31: + case 0x32: /* LWCz possible 0x33 */ + case 0x27: /* LWU */ + case 0x22: /* LWL */ + case 0x26: /* LWR */ + break; + + case 0x28: /* SB */ + case 0x38: /* SC */ + case 0x3C: /* SCD */ + case 0x3D: + case 0x3E: + case 0x3F: /* SDCz */ + case 0x2C: /* SDL */ + case 0x2D: /* SDR */ + case 0x29: /* SH */ + case 0x2B: /* SW */ + case 0x39: + case 0x3A: /* SWCz */ + case 0x2A: /* SWL */ + case 0x2E: /* SWR */ + break; + } + + off = iw & 0xffff; + if(off & 0x8000) + off |= ~0xffff; + + rn = (iw>>21) & 0x1f; + ea = *reg(ur, rn); + if(rn == 0) + ea = 0; + ea += off; + + /* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/ + + if(ur->badvaddr == ea) + return 0; + + return 1; +} + +/* + * we think we get consecutive page faults from unlucky combinations of + * scheduling and stlb hashes, and they only happen with 16K pages. + * however, we also get page faults while servicing the exact same fault. + * more than 5 consecutive faults is unusual, now that we have a better + * hash function. + * + * this can be helpful during mmu and cache debugging. + */ +static int +ckfaultstuck(Ureg *ur, int read, int code) +{ + uintptr pc, va; + + va = ur->badvaddr; + pc = ur->pc; + if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc || + code != lflt.code) { + /* at least one address or cause is different from last time */ + lflt.cnt = 1; + lflt.va = va; + lflt.pid = up->pid; + lflt.pc = pc; + lflt.code = code; + return 0; + } + ++lflt.cnt; + if (lflt.cnt >= 1000) /* fixfault() isn't fixing underlying cause? */ + panic("fault: %d consecutive faults for va %#p", lflt.cnt, va); + if (lflt.cnt > maxflt.cnt) { + maxflt.cnt = lflt.cnt; + maxflt.va = va; + maxflt.pid = up->pid; + maxflt.pc = pc; + kstrdup(&maxflt.prog, up->text); + } + + /* we're servicing that fault now! */ + /* adjust the threshold and program name to suit */ + if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0) + return 0; + iprint("%d consecutive faults for va %#p at pc %#p in %s " + "pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid); + iprint("\t%s: %s%s r31 %#lux tlbvirt %#lux\n", + excname[code], va == pc? "[instruction] ": "", + (read? "read": "write"), ur->r31, tlbvirt()); + return 0; +} + +char * +faultsprint(char *p, char *ep) +{ + if (Debug) + p = seprint(p, ep, + "max consecutive faults %d for va %#p in %s\n", + maxflt.cnt, maxflt.va, maxflt.prog); + return p; +} + +/* + * find out fault address and type of access. + * Call common fault handler. + */ +void +faultmips(Ureg *ur, int user, int code) +{ + int read; + ulong addr; + char *p, buf[ERRMAX]; + static int infault, printed; + + if (0 && infault && !printed) { + printed = 1; + print("fault: recursive fault (%d deep) pc %#p va %#p\n", + infault+1, ur->pc, ur->badvaddr); + } + infault++; + if(waserror()){ + infault--; + nexterror(); + } + + addr = ur->badvaddr; + addr &= ~(BY2PG-1); + + read = !(code==CTLBM || code==CTLBS); + +/* print("fault: %s code %d va %#p pc %#p r31 %#lux tlbvirt %#lux\n", + up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/ + + if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0){ + infault--; + poperror(); + return; + } + + infault--; + poperror(); + + if(tstbadvaddr(ur)) { + print("fault: spurious badvaddr %#lux in %s at pc %#lux\n", + ur->badvaddr, up->text, ur->pc);/**/ + return; + } + + if(user) { + p = "store"; + if(read) + p = "load"; + snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#lux r31=%#lux", + p, ur->badvaddr, ur->r31); + postnote(up, 1, buf, NDebug); + return; + } + + print("kernel %s vaddr=%#lux\n", excname[code], ur->badvaddr); + print("st=%#lux pc=%#lux r31=%#lux sp=%#lux\n", + ur->status, ur->pc, ur->r31, ur->sp); + dumpregs(ur); + panic("fault"); +} + +/* + * called in sysfile.c + */ +void +evenaddr(ulong addr) +{ + if(addr & 3){ + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} --- /sys/src/9/rb/fns.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/fns.h Tue Jul 16 22:23:09 2013 @@ -0,0 +1,146 @@ +#include "../port/portfns.h" + +void arginit(void); +int busprobe(ulong); +ulong cankaddr(ulong); +void cleancache(void); +void clearmmucache(void); +void clock(Ureg*); +void clockinit(void); +void clockshutdown(void); +int cmpswap(long*, long, long); +void coherence(void); +void cycles(uvlong *); +void dcflush(void*, ulong); +void evenaddr(ulong); +void faultmips(Ureg*, int, int); +ulong fcr31(void); +void firmware(int); +void fpclear(void); +void fpsave(FPsave *); +void fptrap(Ureg*); +int fpuemu(Ureg *); +void fpwatch(Ureg *); +ulong getcause(void); +char* getconf(char*); +ulong getconfig(void); +ulong getconfig1(void); +ulong getconfig2(void); +ulong getconfig3(void); +ulong getconfig7(void); +ulong gethwreg3(void); +ulong getpagemask(void); +ulong getstatus(void); +int gettlbp(ulong, ulong*); +ulong gettlbvirt(int); +void gotopc(ulong); +void hinv(void); +int i8250console(void); +void icflush(void *, ulong); +void idle(void); +void idlehands(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void ioinit(void); +void introff(int); +void intron(int); +void intrshutdown(void); +void kfault(Ureg*); +KMap* kmap(Page*); +void kmapinit(void); +void kmapinval(void); +void kunmap(KMap*); +void launchinit(void); +void launch(int); +void links(void); +ulong machstatus(void); +void newstart(void); +int newtlbpid(Proc*); +void online(void); +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); +ulong pcibarsize(Pcidev*, int); +void pcibussize(Pcidev*, ulong*, ulong*); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pciclrioe(Pcidev*); +void pciclrmwi(Pcidev*); +int pcigetpms(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev*, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +int pciscan(int, Pcidev**); +void pcisetbme(Pcidev*); +void pcisetioe(Pcidev*); +void pcisetmwi(Pcidev*); +int pcisetpms(Pcidev*, int); +ulong prid(void); +void procrestore(Proc *); +void procsave(Proc *); +#define procsetup(p) ((p)->fpstate = FPinit) +void purgetlb(int); +Softtlb* putstlb(ulong, ulong); +int puttlb(ulong, ulong, ulong); +void puttlbx(int, ulong, ulong, ulong, int); +ulong rdcompare(void); +ulong rdcount(void); +ulong* reg(Ureg*, int); +void restfpregs(FPsave*, ulong); +void intrenable(int, void(*)(void *), void *); +void setleveldest(int, int, uvlong*); +void setpagemask(ulong); +void setsp(ulong); +void setstatus(ulong); +void setwatchhi0(ulong); +void setwatchlo0(ulong); +void setwired(ulong); +void sicwdog(void); +void silencewdog(void); +ulong stlbhash(ulong); +void stopwdog(void); +void syncclock(void); +long syscall(Ureg*); +void syscallfmt(int syscallno, ulong pc, va_list list); +void sysretfmt(int syscallno, va_list list, long ret, uvlong start, uvlong stop); +int tas(ulong*); +void tlbinit(void); +ulong tlbvirt(void); +void touser(uintptr); +void unleash(void); +#define userureg(ur) ((ur)->status & KUSER) +void vecinit(void); +void vector0(void); +void vector100(void); +void vector180(void); +void wdogreset(void); +void wrcompare(ulong); +void wrcount(ulong); +ulong wiredpte(vlong); +void machwire(void); +void _uartputs(char*, int); +int _uartprint(char*, ...); + +#define PTR2UINT(p) ((uintptr)(p)) +#define UINT2PTR(i) ((void*)(i)) + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) + +#define KADDR(a) ((void*)((ulong)(a)|KSEG0)) +#define PADDR(a) ((ulong)(a)&~KSEGM) + +#define KSEG1ADDR(a) ((void*)((ulong)(a)|KSEG1)) --- /sys/src/9/rb/fpimips.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/fpimips.c Tue Jul 23 02:38:26 2013 @@ -0,0 +1,1485 @@ +/* + * this doesn't attempt to implement MIPS 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. + * + * we emulate the original MIPS FP register model: 32-bits each, + * F(2n) and F(2n+1) are a double, with lower-order word first; + * note that this is little-endian order, unlike the rest of the + * machine, so double-word operations will need to swap the words + * when transferring between FP registers and memory. + * + * on some machines, we can convert to an FP internal representation when + * moving to FPU registers and back (to integer, for example) when moving + * from them. the MIPS is different: its conversion instructions operate + * on FP registers only, and there's no way to tell if data being moved + * into an FP register is integer or FP, so it must be possible to store + * integers in FP registers without conversion. Furthermore, pairs of FP + * registers can be combined into a double. So we keep the raw bits + * around as the canonical representation and convert only to and from + * Internal FP format when we must (i.e., before calling the common fpi + * code). + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/fpi.h" +#include + +#ifdef FPEMUDEBUG +#define DBG(bits) (fpemudebug & (bits)) +#define intpr _intpr +#define internsane _internsane +#define dbgstuck _dbgstuck +#else +#define DBG(bits) (0) +#define internsane(i, ur) do { USED(ur); } while(0) +#define intpr(i, reg, fmt, ufp) do {} while(0) +#define dbgstuck(pc, ur, ufp) do {} while(0) +#endif + +#define OFR(memb) (uintptr)&((Ureg*)0)->memb /* offset into Ureg of memb */ +#define REG(ur, r) *acpureg(ur, r) /* cpu reg in Ureg */ +#define FREG(ufp, fr) (ufp)->reg[(fr) & REGMASK] /* fp reg raw bits */ + +/* + * instruction decoding for COP1 instructions; integer instructions + * are laid out differently. + */ +#define OP(ul) ((ul) >> 26) +#define REGMASK MASK(5) /* mask for a register number */ +#define FMT(ul) (((ul) >> 21) & REGMASK) /* data type */ +#define REGT(ul) (((ul) >> 16) & REGMASK) /* source2 register */ +#define REGS(ul) (((ul) >> 11) & REGMASK) /* source1 register */ +#define REGD(ul) (((ul) >> 6) & REGMASK) /* destination register */ +#define FUNC(ul) ((ul) & MASK(6)) + +enum { + Dbgbasic = 1<<0, /* base debugging: ops, except'ns */ + Dbgmoves = 1<<1, /* not very exciting usually */ + Dbgregs = 1<<2, /* print register contents around ops */ + Dbgdelay = 1<<3, /* branch-delay-slot-related machinery */ + + /* fpimips status codes */ + Failed = -1, + Advpc, /* advance pc normally */ + Leavepc, /* don't change the pc */ + Leavepcret, /* ... and return to user mode now */ + Nomatch, + + /* no-ops */ + NOP = 0x27, /* NOR R0, R0, R0 */ + MIPSNOP = 0, /* SLL R0, R0, R0 */ + + /* fp op-codes */ + COP1 = 0x11, /* fpu op */ + LWC1 = 0x31, /* load float/long */ + LDC1 = 0x35, /* load double/vlong */ + SWC1 = 0x39, /* store float/long */ + SDC1 = 0x3d, /* store double/vlong */ + + N = 1<<31, /* condition codes */ + Z = 1<<30, + C = 1<<29, + V = 1<<28, + + /* data types (format field values) */ + MFC1 = 0, /* and func == 0 ... */ + DMFC1, /* vlong move */ + CFC1, /* ctl word move */ + MTC1 = 4, + DMTC1, + CTC1, /* ... end `and func == 0' */ + BRANCH = 8, + Ffloat = 16, + Fdouble, + Flong = 20, + Fvlong, + + /* fp control registers */ + Fpimp = 0, + Fpcsr = 31, +}; + +typedef struct FP1 FP1; +typedef struct FP2 FP2; +typedef struct FPcvt FPcvt; +typedef struct Instr Instr; + +struct Instr { /* a COP1 instruction, broken out and registers converted */ + int iw; /* whole word */ + uintptr pc; + int o; /* opcode or cop1 func code */ + int fmt; /* operand format */ + int rm; /* first operand register */ + int rn; /* second operand register */ + int rd; /* destination register */ + + Internal *fm; /* converted from FREG(ufp, rm) */ + Internal *fn; + char *dfmt; + FPsave *ufp; /* fp state, including fp registers */ + Ureg *ur; /* user registers */ +}; + +struct FP2 { + char* name; + void (*f)(Internal*, Internal*, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +struct FPcvt { + char* name; + void (*f)(int, int, int, Ureg *, FPsave *); +}; + +static int roff[32] = { + 0, 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(sp), OFR(r30), OFR(r31), +}; + +/* + * plan 9 assumes F24 initialized to 0.0, F26 to 0.5, F28 to 1.0, F30 to 2.0. + */ +enum { + FZERO = 24, + FHALF = 26, +}; +static Internal fpconst[Nfpregs] = { /* indexed by register no. */ + /* s, e, l, h */ +[FZERO] {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ +[FHALF] {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ +[28] {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ +[30] {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ +}; + +static char *fmtnames[] = { +[MFC1] "MF", +[DMFC1] "DMF", +[CFC1] "CF", +[MTC1] "MT", +[DMTC1] "DMT", +[CTC1] "CT", +[BRANCH]"BR", + +[Ffloat]"F", +[Fdouble]"D", +[Flong] "W", +[Fvlong]"L", +}; + +static char *prednames[] = { +[0] "F", +[1] "UN", +[2] "EQ", +[3] "UEQ", +[4] "OLT", +[5] "ULT", +[6] "OLE", +[7] "ULE", +[8] "SF", +[9] "NGLE", +[10] "SEQ", +[11] "NGL", +[12] "LT", +[13] "NGE", +[14] "LE", +[15] "NGT", +}; + +int fpemudebug = 0; /* settable via /dev/archctl */ + +static ulong dummyr0; +static QLock watchlock; /* lock for watch-points */ + +ulong branch(Ureg*, ulong); +int isbranch(ulong *); + +static int fpimips(ulong, ulong, Ureg *, FPsave *); + +char * +fpemuprint(char *p, char *ep) +{ +#ifdef FPEMUDEBUG + return seprint(p, ep, "fpemudebug %d\n", fpemudebug); +#else + USED(ep); + return p; +#endif +} + +static ulong * +acpureg(Ureg *ur, int r) +{ + r &= REGMASK; + if (r == 0 || roff[r] == 0) { + dummyr0 = 0; + return &dummyr0; + } + return (ulong *)((char*)ur + roff[r]); +} + +ulong * +reg(Ureg *ur, int r) /* for faultmips */ +{ + return ®(ur, r); +} + +static void +_internsane(Internal *i, Ureg *ur) +{ + static char buf[ERRMAX]; + + USED(i); + if (!(DBG(Dbgbasic))) + return; + if ((unsigned)i->s > 1) { + snprint(buf, sizeof buf, + "fpuemu: bogus Internal sign at pc=%#p", ur->pc); + error(buf); + } + if ((unsigned)i->e > DoubleExpMax) { + snprint(buf, sizeof buf, + "fpuemu: bogus Internal exponent at pc=%#p", ur->pc); + error(buf); + } +} + +/* + * mips binary operations (d = n operator m) + */ + +static void +fadd(Internal *m, Internal *n, Internal *d) +{ + (m->s == n->s? fpiadd: fpisub)(m, n, d); +} + +static void +fsub(Internal *m, Internal *n, Internal *d) +{ + m->s ^= 1; + (m->s == n->s? fpiadd: fpisub)(m, n, d); +} + +/* + * mips unary operations + */ + +static void +frnd(Internal *m, Internal *d) +{ + short e; + Internal tmp; + + tmp = fpconst[FHALF]; + (m->s? fsub: fadd)(&tmp, m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +/* debugging: print internal representation of an fp reg */ +static void +_intpr(Internal *i, int reg, int fmt, FPsave *ufp) +{ + USED(i); + if (!(DBG(Dbgregs))) + return; + if (fmt == Fdouble && reg < 31) + iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n", + reg, FREG(ufp, reg), FREG(ufp, reg+1), + i->s, i->e, i->h, i->l); + else + iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n", + reg, FREG(ufp, reg), + i->s, i->e, i->h, i->l); + delay(75); +} + +static void +dreg2dbl(Double *dp, int reg, FPsave *ufp) +{ + reg &= ~1; + dp->l = FREG(ufp, reg); + dp->h = FREG(ufp, reg+1); +} + +static void +dbl2dreg(int reg, Double *dp, FPsave *ufp) +{ + reg &= ~1; + FREG(ufp, reg) = dp->l; + FREG(ufp, reg+1) = dp->h; +} + +static void +vreg2dbl(Double *dp, int reg, FPsave *ufp) +{ + reg &= ~1; + dp->l = FREG(ufp, reg+1); + dp->h = FREG(ufp, reg); +} + +static void +dbl2vreg(int reg, Double *dp, FPsave *ufp) +{ + reg &= ~1; + FREG(ufp, reg+1) = dp->l; + FREG(ufp, reg) = dp->h; +} + +/* convert fmt (rm) to double (rd) */ +static void +fcvtd(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) +{ + Double d; + Internal intrn; + + switch (fmt) { + case Ffloat: + fpis2i(&intrn, &FREG(ufp, rm)); + internsane(&intrn, ur); + fpii2d(&d, &intrn); + break; + case Fdouble: + dreg2dbl(&d, rm, ufp); + break; + case Flong: + fpiw2i(&intrn, &FREG(ufp, rm)); + internsane(&intrn, ur); + fpii2d(&d, &intrn); + break; + case Fvlong: + vreg2dbl(&d, rm, ufp); + fpiv2i(&intrn, &d); + internsane(&intrn, ur); + fpii2d(&d, &intrn); + break; + } + dbl2dreg(rd, &d, ufp); + if (fmt != Fdouble && DBG(Dbgregs)) + intpr(&intrn, rm, Fdouble, ufp); +} + +/* convert fmt (rm) to single (rd) */ +static void +fcvts(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) +{ + Double d; + Internal intrn; + + switch (fmt) { + case Ffloat: + FREG(ufp, rd) = FREG(ufp, rm); + break; + case Fdouble: + dreg2dbl(&d, rm, ufp); + fpid2i(&intrn, &d); + break; + case Flong: + fpiw2i(&intrn, &FREG(ufp, rm)); + break; + case Fvlong: + vreg2dbl(&d, rm, ufp); + fpiv2i(&intrn, &d); + break; + } + if (fmt != Ffloat) { + if(DBG(Dbgregs)) + intpr(&intrn, rm, Ffloat, ufp); + internsane(&intrn, ur); + fpii2s(&FREG(ufp, rd), &intrn); + } +} + +/* convert fmt (rm) to long (rd) */ +static void +fcvtw(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) +{ + Double d; + Internal intrn; + + switch (fmt) { + case Ffloat: + fpis2i(&intrn, &FREG(ufp, rm)); + break; + case Fdouble: + dreg2dbl(&d, rm, ufp); + fpid2i(&intrn, &d); + break; + case Flong: + FREG(ufp, rd) = FREG(ufp, rm); + break; + case Fvlong: + vreg2dbl(&d, rm, ufp); + fpiv2i(&intrn, &d); + break; + } + if (fmt != Flong) { + if(DBG(Dbgregs)) + intpr(&intrn, rm, Flong, ufp); + internsane(&intrn, ur); + fpii2w((long *)&FREG(ufp, rd), &intrn); + } +} + +/* convert fmt (rm) to vlong (rd) */ +static void +fcvtv(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) +{ + Double d; + Internal intrn; + + switch (fmt) { + case Ffloat: + fpis2i(&intrn, &FREG(ufp, rm)); + break; + case Fdouble: + dreg2dbl(&d, rm, ufp); + fpid2i(&intrn, &d); + break; + case Flong: + fpiw2i(&intrn, &FREG(ufp, rm)); + break; + case Fvlong: + vreg2dbl(&d, rm, ufp); + dbl2vreg(rd, &d, ufp); + break; + } + if (fmt != Fvlong) { + if(DBG(Dbgregs)) + intpr(&intrn, rm, Fvlong, ufp); + internsane(&intrn, ur); + fpii2v((vlong *)&FREG(ufp, rd), &intrn); + } +} + +/* + * MIPS function codes + */ + +static FP2 optab2[] = { /* Fd := Fn OP Fm (binary) */ +[0] {"ADDF", fadd}, /* can ignore fmt, just use doubles */ +[1] {"SUBF", fsub}, +[2] {"MULF", fpimul}, +[3] {"DIVF", fpidiv}, +}; + +static FP1 optab1[32] = { /* Fd := OP Fm (unary) */ +[4] {"SQTF", /*fsqt*/0}, +[5] {"ABSF", /*fabsf*/0}, /* inline in unaryemu... */ +[6] {"MOVF", /*fmov*/0}, +[7] {"NEGF", /*fmovn*/0}, +[8] {"ROUND.L", /*froundl*/0}, /* 64-bit integer results ... */ +[9] {"TRUNC.L", /*ftruncl*/0}, +[10] {"CEIL.L", /*fceill*/0}, +[11] {"FLOOR.L", /*ffloorl*/0}, +[12] {"ROUND.W", frnd}, /* 32-bit integer results ... */ +[13] {"TRUNC.W", /*ftrunc*/0}, +[14] {"CEIL.W", /*fceil*/0}, +[15] {"FLOOR.W", /*ffloor*/0}, +/* 17—19 are newish MIPS32/64 conditional moves */ +/* 21, 22, 28—31 are newish reciprocal or sqrt */ +}; + +static FPcvt optabcvt[] = { /* Fd := OP(fmt, Fm) (unary) */ +[32] {"CVT.S", fcvts}, /* must honour fmt as src format */ +[33] {"CVT.D", fcvtd}, +[36] {"CVT.W", fcvtw}, +[37] {"CVT.L", fcvtv}, +}; + +/* + * No type conversion is implied and the type of the cpu register is + * unknown, so copy the bits into reg. + * Later instructions will have to know the correct type and use the + * right format specifier to convert to or from Internal FP. + */ +static void +fld(int d, ulong ea, int n, FPsave *ufp) +{ + if(DBG(Dbgmoves)) + iprint("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); + if (n == 4) + memmove(&FREG(ufp, d), (void *)ea, 4); + else if (n == 8){ + d &= ~1; + /* NB: we swap order of the words */ + memmove(&FREG(ufp, d), (void *)(ea+4), 4); + memmove(&FREG(ufp, d+1), (void *)ea, 4); + } else + panic("fld: n (%d) not 4 nor 8", n); +} + +static void +fst(ulong ea, int s, int n, FPsave *ufp) +{ + if(DBG(Dbgmoves)) + iprint("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + if (n == 4) + memmove((void *)ea, &FREG(ufp, s), 4); + else if (n == 8){ + s &= ~1; + /* NB: we swap order of the words */ + memmove((void *)(ea+4), &FREG(ufp, s), 4); + memmove((void *)ea, &FREG(ufp, s+1), 4); + } else + panic("fst: n (%d) not 4 nor 8", n); +} + +void +unimp(ulong pc, ulong op, char *msg) +{ + char buf[120]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%#lux unimp fp %#.8lux: %s", + pc, op, msg); + if(DBG(Dbgbasic)) + iprint("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static int +isfpop(ulong iw) +{ + switch (OP(iw)) { + case COP1: + case LWC1: + case LDC1: + case SWC1: + case SDC1: + return 1; + default: + return 0; + } +} + +static int +ldst(ulong op, Ureg *ur, FPsave *ufp) +{ + int rn, rd, o, size, wr; + short off; + ulong ea; + + /* we're using the COP1 macros, but the fields have diff'nt meanings */ + o = OP(op); + rn = FMT(op); + off = op; + ea = REG(ur, rn) + off; + rd = REGT(op); +//iprint("fpemu: ld/st (F%d)=%#lux + %d => ea %#lux\n", rn, REG(ur, rn), off, ea); + + size = 4; + if (o == LDC1 || o == SDC1) + size = 8; + wr = (o == SWC1 || o == SDC1); + validaddr(ea, size, wr); + + switch (o) { + case LWC1: /* load an fp register, rd, from memory */ + case LDC1: /* load an fp register pair, (rd, rd+1), from memory */ + fld(rd, ea, size, ufp); + break; + case SWC1: /* store an fp register, rd, into memory */ + case SDC1: /* store an fp register pair, (rd, rd+1), into memory */ + fst(ea, rd, size, ufp); + break; + default: + unimp(ur->pc, op, "unknown non-COP1 load or store"); + return Failed; + } + return Advpc; +} + +static int +cop1mov(Instr *ip) +{ + int fs, rt; + uvlong vl; + FPsave *ufp; + Ureg *ur; + + fs = ip->rm; /* F(s) aka rm */ + rt = ip->rn; /* R(t) aka rn */ + ur = ip->ur; + ufp = ip->ufp; +//iprint("fpemu: cop1 prob ld/st (R%d)=%#lux FREG%d\n", rn, REG(ip->ur, rn), rm); + + /* MIPS fp register pairs are in little-endian order: low word first */ + switch (ip->fmt) { + case MTC1: + /* load an fp register, F(s), from cpu register R(t) */ + fld(fs, (uintptr)®(ur, rt), 4, ufp); + return Advpc; + case DMTC1: + /* + * load an fp register pair, (F(s), F(s+1)), + * from cpu registers (rt, rt+1) + */ + iprint("fpemu: 64-bit DMTC1 may have words backward\n"); + rt &= ~1; + vl = (uvlong)REG(ur, rt+1) << 32 | REG(ur, rt); + fld(fs & ~1, (uintptr)&vl, 8, ufp); + return Advpc; + case MFC1: + /* store an fp register, fs, into a cpu register rt */ + fst((uintptr)®(ur, rt), fs, 4, ufp); + return Advpc; + case DMFC1: + /* + * store an fp register pair, (F(s), F(s+1)), + * into cpu registers (rt, rt+1) + */ + iprint("fpemu: 64-bit DMFC1 may have words backward\n"); + fst((uintptr)&vl, fs & ~1, 8, ufp); + rt &= ~1; + REG(ur, rt) = (ulong)vl; + REG(ur, rt+1) = vl>>32; + return Advpc; + case CFC1: + switch (fs) { + case Fpimp: /* MOVW FCR0,Rn */ + REG(ur, rt) = 0x500; /* claim to be r4k */ + break; + case Fpcsr: + REG(ur, rt) = ufp->fpcontrol; + break; + } + if(DBG(Dbgbasic)) + iprint("MOVW FCR%d, R%d\n", fs, rt); + return Advpc; + case CTC1: + switch (fs) { + case Fpcsr: + ufp->fpcontrol = REG(ur, rt); + break; + } + if(DBG(Dbgbasic)) + iprint("MOVW R%d, FCR%d\n", rt, fs); + return Advpc; + } + return Nomatch; /* not a load or store; keep looking */ +} + +static char * +decodefmt(int fmt) +{ + if (fmtnames[fmt]) + return fmtnames[fmt]; + else + return "GOK"; +} + +static char * +predname(int pred) /* predicate name */ +{ + if (prednames[pred]) + return prednames[pred]; + else + return "GOK"; +} + +static int +fcmpf(Internal m, Internal n, int, int cond) +{ + int i; + + if(IsWeird(&m) || IsWeird(&n)){ + /* BUG: should trap if not masked */ + return 0; + } + fpiround(&n); + fpiround(&m); + i = fpicmp(&m, &n); /* returns -1, 0, or 1 */ + switch (cond) { + case 0: /* F - false */ + case 1: /* UN - unordered */ + return 0; + case 2: /* EQ */ + case 3: /* UEQ */ + return i == 0; + case 4: /* OLT */ + case 5: /* ULT */ + return i < 0; + case 6: /* OLE */ + case 7: /* ULE */ + return i <= 0; + case 8: /* SF */ + case 9: /* NGLE - not >, < or = */ + return 0; + case 10: /* SEQ */ + return i == 0; + case 11: /* NGL */ + return i != 0; + case 12: /* LT */ + case 13: /* NGE */ + return i < 0; + case 14: /* LE */ + case 15: /* NGT */ + return i <= 0; + } + return 0; +} + +/* + * assuming that ur->pc points to a branch instruction, + * change it to point to the branch's target and return it. + */ +static uintptr +followbr(Ureg *ur) +{ + uintptr npc; + + npc = branch(ur, up->fpsave.fpstatus); + if(npc == 0) + panic("fpemu: branch expected but not seen at %#p", ur->pc); + ur->pc = npc; + return npc; +} + +/* emulate COP1 instruction in branch delay slot */ +static void +dsemu(Instr *ip, ulong dsinsn, Ureg *ur, FPsave *ufp) +{ + uintptr npc; + + npc = ur->pc; /* save ur->pc since fpemu will change it */ + if(DBG(Dbgdelay)) + iprint(">>> emulating br delay slot\n"); + + fpimips(ip->pc + 4, dsinsn, ur, ufp); + + if(DBG(Dbgdelay)) + iprint("<<< done emulating br delay slot\n"); + ur->pc = npc; +} + +/* + * execute non-COP1 instruction in branch delay slot, in user mode with + * user registers, then trap so we can finish up and take the branch. + */ +static void +dsexec(Instr *ip, Ureg *ur, FPsave *ufp) +{ + ulong dsaddr, wpaddr; + Tos *tos; + + /* + * copy delay slot, EHB, EHB, EHB to tos->kscr, flush caches, + * point pc there, set watch point on tos->kscr[2], return. + * this is safe since we've already checked for branches (and FP + * instructions) in the delay slot, so the instruction can be + * executed at any address. + */ + dsaddr = ip->pc + 4; + tos = (Tos*)(USTKTOP-sizeof(Tos)); + tos->kscr[0] = *(ulong *)dsaddr; + tos->kscr[1] = 0xc0; /* EHB; we could use some trap instead */ + tos->kscr[2] = 0xc0; /* EHB */ + tos->kscr[3] = 0xc0; /* EHB */ + dcflush(tos->kscr, sizeof tos->kscr); + icflush(tos->kscr, sizeof tos->kscr); + + wpaddr = (ulong)&tos->kscr[2] & ~7; /* clear I/R/W bits */ + ufp->fpdelayexec = 1; + ufp->fpdelaypc = ip->pc; /* remember branch ip->pc */ + ufp->fpdelaysts = ufp->fpstatus; /* remember state of FPCOND */ + ur->pc = (ulong)tos->kscr; /* restart in tos */ + qlock(&watchlock); /* wait for first watchpoint */ + setwatchlo0(wpaddr | 1<<2); /* doubleword addr(!); i-fetches only */ + setwatchhi0(TLBPID(tlbvirt())<<16); /* asid; see mmu.c */ + if (DBG(Dbgdelay)) + iprint("fpemu: set %s watch point at %#lux, after br ds %#lux...", + up->text, wpaddr, *(ulong *)dsaddr); + /* return to user mode, await fpwatch() trap */ +} + +void +fpwatch(Ureg *ur) /* called on watch-point trap */ +{ + FPsave *ufp; + + ufp = &up->fpsave; + if(ufp->fpdelayexec == 0) + panic("fpwatch: unexpected watch trap"); + + /* assume we got here after branch-delay-slot execution */ + ufp->fpdelayexec = 0; + setwatchlo0(0); + setwatchhi0(0); + qunlock(&watchlock); + + ur->pc = ufp->fpdelaypc; /* pc of fp branch */ + ur->cause &= BD; /* take no chances */ + ufp->fpstatus = ufp->fpdelaysts; + followbr(ur); /* sets ur->pc to fp branch target */ + if (DBG(Dbgdelay)) + iprint("delay slot executed; resuming at %#lux\n", ur->pc); +} + +static ulong +validiw(uintptr pc) +{ + validaddr(pc, 4, 0); + return *(ulong*)pc; +} + +/* + * COP1 (6) | BRANCH (5) | cc (3) | likely | true | offset(16) + * cc = ip->rn >> 2; // assume cc == 0 + */ +static int +bremu(Instr *ip) +{ + int off, taken; + ulong dsinsn; + FPsave *ufp; + Ureg *ur; + + if (ip->iw & (1<<17)) + error("fpuemu: `likely' fp branch (obs)"); + ufp = ip->ufp; + if (ufp->fpstatus & FPCOND) + taken = ip->iw & (1<<16); /* taken iff BCT */ + else + taken = !(ip->iw & (1<<16)); /* taken iff BCF */ + dsinsn = validiw(ip->pc + 4); /* delay slot addressible? */ + if(DBG(Dbgdelay)){ + off = (short)(ip->iw & MASK(16)); + iprint("BFP%c\t%d(PC): %staken\n", (ip->iw & (1<<16)? 'T': 'F'), + off, taken? "": "not "); + iprint("\tdelay slot: %08lux\n", dsinsn); + delay(75); + } + ur = ip->ur; + assert(ur->pc == ip->pc); + if(!taken) + return Advpc; /* didn't branch, so return to delay slot */ + + /* + * fp branch taken; emulate or execute the delay slot, then jump. + */ + if(dsinsn == NOP || dsinsn == MIPSNOP){ + ; /* delay slot does nothing */ + }else if(isbranch((ulong *)(ip->pc + 4))) + error("fpuemu: branch in fp branch delay slot"); + else if (isfpop(dsinsn)) + dsemu(ip, dsinsn, ur, ufp); /* emulate delay slot */ + else{ + /* + * The hard case: we need to execute the delay slot + * in user mode with user registers. Set a watch point, + * return to user mode, await fpwatch() trap. + */ + dsexec(ip, ur, ufp); + return Leavepcret; + } + followbr(ur); + return Leavepc; +} + +/* interpret fp reg as fmt (float or double) and convert to Internal */ +static void +reg2intern(Internal *i, int reg, int fmt, Ureg *ur) +{ + Double d; + FPsave *ufp; + + /* we may see other fmt types on conversion or unary ops; ignore */ + ufp = &up->fpsave; + switch (fmt) { + case Ffloat: + fpis2i(i, &FREG(ufp, reg)); + internsane(i, ur); + break; + case Fdouble: + dreg2dbl(&d, reg, ufp); + fpid2i(i, &d); + internsane(i, ur); + break; + default: + SetQNaN(i); /* cause trouble if we try to use i */ + break; + } +} + +/* convert Internal to fp reg as fmt (float or double) */ +static void +intern2reg(int reg, Internal *i, int fmt, Ureg *ur) +{ + Double d; + FPsave *ufp; + Internal tmp; + + ufp = &up->fpsave; + tmp = *i; /* make a disposable copy */ + internsane(&tmp, ur); + switch (fmt) { + case Ffloat: + fpii2s(&FREG(ufp, reg), &tmp); + break; + case Fdouble: + fpii2d(&d, &tmp); + dbl2dreg(reg, &d, ufp); + break; + default: + panic("intern2reg: bad fmt %d", fmt); + } +} + +/* + * comparisons - encoded slightly differently than arithmetic: + * COP1 (6) | fmt(5) | ft (5) | fs (5) | # same + * cc (3) | 0 | A=0 | # diff, was REGD + * FC=11 | cond (4) # FUNC + */ +static int +cmpemu(Instr *ip) +{ + int cc, cond; + + cc = ip->rd >> 2; + cond = ip->o & MASK(4); + reg2intern(ip->fn, ip->rn, ip->fmt, ip->ur); + /* fpicmp args are swapped, so this is `n compare m' */ + if (fcmpf(*ip->fm, *ip->fn, cc, cond)) + ip->ufp->fpstatus |= FPCOND; + else + ip->ufp->fpstatus &= ~FPCOND; + if(DBG(Dbgbasic)) + iprint("CMP%s.%s F%d,F%d =%d\n", predname(cond), ip->dfmt, + ip->rm, ip->rn, (ip->ufp->fpstatus & FPCOND? 1: 0)); + if(DBG(Dbgregs)) { + intpr(ip->fm, ip->rm, ip->fmt, ip->ufp); + intpr(ip->fn, ip->rn, ip->fmt, ip->ufp); + delay(75); + } + return Advpc; +} + +static int +binemu(Instr *ip) +{ + FP2 *fp; + Internal fd, prfd; + Internal *fn; + + fp = &optab2[ip->o]; + if(fp->f == nil) + unimp(ip->pc, ip->iw, "missing binary op"); + + /* convert the second operand */ + fn = ip->fn; + reg2intern(fn, ip->rn, ip->fmt, ip->ur); + if(DBG(Dbgregs)) + intpr(fn, ip->rn, ip->fmt, ip->ufp); + + if(DBG(Dbgbasic)){ + iprint("%s.%s\tF%d,F%d,F%d\n", fp->name, ip->dfmt, + ip->rm, ip->rn, ip->rd); + delay(75); + } + /* + * fn and fm are scratch Internals just for this instruction, + * so it's okay to let the fpi routines trash them in the course + * of operation. + */ + /* NB: fpi routines take m and n (s and t) in reverse order */ + (*fp->f)(fn, ip->fm, &fd); + + /* convert the result */ + if(DBG(Dbgregs)) + prfd = fd; /* intern2reg modifies fd */ + intern2reg(ip->rd, &fd, ip->fmt, ip->ur); + if(DBG(Dbgregs)) + intpr(&prfd, ip->rd, ip->fmt, ip->ufp); + return Advpc; +} + +static int +unaryemu(Instr *ip) +{ + int o; + FP1 *fp; + FPsave *ufp; + + o = ip->o; + fp = &optab1[o]; + if(DBG(Dbgbasic)){ + iprint("%s.%s\tF%d,F%d\n", fp->name, ip->dfmt, ip->rm, ip->rd); + delay(75); + } + if(o == 6){ /* MOV */ + int rm, rd; + + ufp = ip->ufp; + rd = ip->rd; + rm = ip->rm; + if(ip->fmt == Fdouble){ + rd &= ~1; + rm &= ~1; + FREG(ufp, rd+1) = FREG(ufp, rm+1); + } + FREG(ufp, rd) = FREG(ufp, rm); + }else{ + Internal fdint, prfd; + Internal *fd; + + switch(o){ + case 5: /* ABS */ + fd = ip->fm; /* use src Internal as dest */ + fd->s = 0; + break; + case 7: /* NEG */ + fd = ip->fm; /* use src Internal as dest */ + fd->s ^= 1; + break; + default: + if(fp->f == nil) + unimp(ip->pc, ip->iw, "missing unary op"); + fd = &fdint; + (*fp->f)(ip->fm, fd); + break; + } + if(DBG(Dbgregs)) + prfd = *fd; /* intern2reg modifies fd */ + intern2reg(ip->rd, fd, ip->fmt, ip->ur); + if(DBG(Dbgregs)) + intpr(&prfd, ip->rd, ip->fmt, ip->ufp); + } + return Advpc; +} + +static int +cvtemu(Instr *ip) +{ + FPcvt *fp; + + fp = &optabcvt[ip->o]; + if(fp->f == nil) + unimp(ip->pc, ip->iw, "missing conversion op"); + if(DBG(Dbgbasic)){ + iprint("%s.%s\tF%d,F%d\n", fp->name, ip->dfmt, ip->rm, ip->rd); + delay(75); + } + (*fp->f)(ip->fmt, ip->rm, ip->rd, ip->ur, ip->ufp); + return Advpc; +} + +static void +cop1decode(Instr *ip, ulong iw, ulong pc, Ureg *ur, FPsave *ufp, + Internal *imp, Internal *inp) +{ + ip->iw = iw; + ip->pc = pc; + ip->ur = ur; + ip->ufp = ufp; + ip->fmt = FMT(iw); + ip->rm = REGS(iw); /* 1st operand */ + ip->rn = REGT(iw); /* 2nd operand (ignored by unary ops) */ + ip->rd = REGD(iw); /* destination */ + ip->o = FUNC(iw); + ip->fm = imp; + ip->fn = inp; + if (DBG(Dbgbasic)) + ip->dfmt = decodefmt(ip->fmt); +} + +void +fpstuck(uintptr pc, FPsave *fp) +{ + USED(pc); + if(!(DBG(Dbgbasic))) + return; + if (fp->fppc == pc) { + fp->fpcnt++; + if (fp->fpcnt > 4) + panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p " + "instr %#8.8lux", m->machno, up->pid, up->text, + pc, *(ulong *)pc); + } else { + fp->fppc = pc; + fp->fpcnt = 0; + } +} + +static void +_dbgstuck(ulong pc, Ureg *ur, FPsave *ufp) +{ + fpstuck(pc, ufp); + if (DBG(Dbgdelay) && ur->cause & BD) + iprint("fpuemu: FP in a branch delay slot\n"); +} + +/* decode the opcode and call common emulation code */ +static int +fpimips(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int r, o; + Instr insn; + Instr *ip; + Internal im, in; + + /* note: would update fault status here if we noted numeric exceptions */ + dummyr0 = 0; + switch (OP(op)) { + case LWC1: + case LDC1: + case SWC1: + case SDC1: + dbgstuck(pc, ur, ufp); + return ldst(op, ur, ufp); + default: + unimp(pc, op, "non-FP instruction"); + return Failed; + case COP1: + dbgstuck(pc, ur, ufp); + break; + } + + ip = &insn; + cop1decode(ip, op, pc, ur, ufp, &im, &in); + if (ip->fmt == BRANCH) { /* FP conditional branch? */ + r = bremu(ip); + if(DBG(Dbgdelay)){ + iprint("resuming after br, at %#lux", ur->pc); + if (r == Leavepcret) + iprint("..."); /* we'll be right back */ + else + iprint("\n"); + } + return r; + } + o = ip->o; + if (o == 0 && ip->rd == 0) { /* *[TF]C1 load or store? */ + r = cop1mov(ip); + if (r != Nomatch) + return r; + /* else wasn't a [tf]c1 move */ + } + /* don't decode & print rm yet; it might be an integer */ + if(o >= 32 && o < 40) /* conversion? */ + return cvtemu(ip); + + /* decode the mandatory operand, rm */ + reg2intern(ip->fm, ip->rm, ip->fmt, ip->ur); + if(DBG(Dbgregs)) + intpr(&im, ip->rm, ip->fmt, ip->ufp); + + /* + * arithmetic + * all operands must be of the same format + */ + if(o >= 4 && o < 32) /* monadic */ + return unaryemu(ip); + if(o < 4) /* the few binary ops */ + return binemu(ip); + + if(o >= 48 && (ip->rd & MASK(2)) == 0) /* comparison? */ + return cmpemu(ip); + + /* don't recognise the opcode */ + if(DBG(Dbgbasic)) + iprint("fp at %#lux: %#8.8lux BOGON\n", pc, op); + unimp(pc, op, "unknown opcode"); + return Failed; +} + +static FPsave * +fpinit(Ureg *ur) +{ + int i, n; + Double d; + FPsave *ufp; + Internal tmp; + + /* + * because all the emulated fp state is in the proc structure, + * it need not be saved/restored + */ + ufp = &up->fpsave; + switch(up->fpstate){ + case FPactive: + case FPinactive: + error("fpu (in)active but fp is emulated"); + case FPinit: + up->fpstate = FPemu; + ufp->fpcontrol = 0; + ufp->fpstatus = 0; + ufp->fpcnt = 0; + ufp->fppc = 0; + for(n = 0; n < Nfpregs-1; n += 2) { + if (fpconst[n].h == 0) /* uninitialised consts */ + i = FZERO; /* treated as 0.0 */ + else + i = n; + tmp = fpconst[i]; + internsane(&tmp, ur); + fpii2d(&d, &tmp); + dbl2dreg(n, &d, ufp); + } + break; + } + return ufp; +} + +/* + * called from trap.c's CCPU case, only to deal with user-mode + * instruction faults. + * + * libc/mips/lock.c reads FCR0 to determine what kind of system + * this is (and thus if it can use LL/SC or must use some + * system-dependent method). So we simulate the move from FCR0. + * All modern mips have LL/SC, so just claim to be an r4k. + */ +int +fpuemu(Ureg *ureg) +{ + int s; + uintptr pc; + ulong iw, r; + + if(waserror()){ + postnote(up, 1, up->errstr, NDebug); + return -1; + } + + if(up->fpstate & FPillegal) + error("floating point in note handler"); + if(up->fpsave.fpdelayexec) + panic("fpuemu: entered with outstanding watch trap"); + + pc = ureg->pc; + validaddr(pc, 4, 0); + /* only the first instruction can be in a branch delay slot */ + if(ureg->cause & BD) { + pc += 4; + validaddr(pc, 4, 0); /* check branch delay slot */ + } + iw = *(ulong*)pc; + do { + /* recognise & optimise a common case */ + if (iw == 0x44410000){ /* MOVW FCR0,R1 (CFC1) */ + ureg->r1 = 0x500; /* claim an r4k */ + r = Advpc; + if (DBG(Dbgbasic)) + iprint("faked MOVW FCR0,R1\n"); + }else{ + s = spllo(); + if(waserror()){ + splx(s); + nexterror(); + } + r = fpimips(pc, iw, ureg, fpinit(ureg)); + splx(s); + poperror(); + if (r == Failed || r == Leavepcret) + break; + } + if (r == Advpc) /* simulation succeeded, advance the pc? */ + if(ureg->cause & BD) + followbr(ureg); + else + ureg->pc += 4; + ureg->cause &= ~BD; + + pc = ureg->pc; + iw = validiw(pc); + while (iw == NOP || iw == MIPSNOP) { /* skip NOPs */ + pc += 4; + ureg->pc = pc; + iw = validiw(pc); + } + /* is next ins'n also FP? */ + } while (isfpop(iw)); + if (r == Failed){ + iprint("fpuemu: fp emulation failed for %#lux" + " at pc %#p in %lud %s\n", + iw, ureg->pc, up->pid, up->text); + unimp(ureg->pc, iw, "no fp instruction"); + /* no return */ + } + ureg->cause &= ~BD; + poperror(); + return 0; +} + +int +isbranch(ulong *pc) +{ + ulong iw; + + iw = *(ulong*)pc; + /* + * Integer unit jumps first + */ + switch(iw>>26){ + case 0: /* SPECIAL: JR or JALR */ + switch(iw&0x3F){ + case 0x09: /* JALR */ + case 0x08: /* JR */ + return 1; + default: + return 0; + } + case 1: /* BCOND */ + switch((iw>>16) & 0x1F){ + case 0x10: /* BLTZAL */ + case 0x00: /* BLTZ */ + case 0x11: /* BGEZAL */ + case 0x01: /* BGEZ */ + return 1; + default: + return 0; + } + case 3: /* JAL */ + case 2: /* JMP */ + case 4: /* BEQ */ + case 5: /* BNE */ + case 6: /* BLEZ */ + case 7: /* BGTZ */ + return 1; + } + /* + * Floating point unit jumps + */ + if((iw>>26) == COP1) + switch((iw>>16) & 0x3C1){ + case 0x101: /* BCT */ + case 0x181: /* BCT */ + case 0x100: /* BCF */ + case 0x180: /* BCF */ + return 1; + } + return 0; +} + +/* + * if current instruction is a (taken) branch, return new pc and, + * for jump-and-links, set r31. + */ +ulong +branch(Ureg *ur, ulong fcr31) +{ + ulong iw, npc, rs, rt, rd, offset, targ, next; + + iw = ur->pc; + iw = *(ulong*)iw; + rs = (iw>>21) & 0x1F; + if(rs) + rs = REG(ur, rs); + rt = (iw>>16) & 0x1F; + if(rt) + rt = REG(ur, rt); + offset = iw & ((1<<16)-1); + if(offset & (1<<15)) /* sign extend */ + offset |= ~((1<<16)-1); + offset <<= 2; + targ = ur->pc + 4 + offset; /* branch target */ + /* ins'n after delay slot (assumes delay slot has already been exec'd) */ + next = ur->pc + 8; + /* + * Integer unit jumps first + */ + switch(iw>>26){ + case 0: /* SPECIAL: JR or JALR */ + switch(iw&0x3F){ + case 0x09: /* JALR */ + rd = (iw>>11) & 0x1F; + if(rd) + REG(ur, rd) = next; + /* fall through */ + case 0x08: /* JR */ + return rs; + default: + return 0; + } + case 1: /* BCOND */ + switch((iw>>16) & 0x1F){ + case 0x10: /* BLTZAL */ + ur->r31 = next; + /* fall through */ + case 0x00: /* BLTZ */ + if((long)rs < 0) + return targ; + return next; + case 0x11: /* BGEZAL */ + ur->r31 = next; + /* fall through */ + case 0x01: /* BGEZ */ + if((long)rs >= 0) + return targ; + return next; + default: + return 0; + } + case 3: /* JAL */ + ur->r31 = next; + /* fall through */ + case 2: /* JMP */ + npc = iw & ((1<<26)-1); + npc <<= 2; + return npc | (ur->pc&0xF0000000); + case 4: /* BEQ */ + if(rs == rt) + return targ; + return next; + case 5: /* BNE */ + if(rs != rt) + return targ; + return next; + case 6: /* BLEZ */ + if((long)rs <= 0) + return targ; + return next; + case 7: /* BGTZ */ + if((long)rs > 0) + return targ; + return next; + } + /* + * Floating point unit jumps + */ + if((iw>>26) == COP1) + switch((iw>>16) & 0x3C1){ + case 0x101: /* BCT */ + case 0x181: /* BCT */ + if(fcr31 & FPCOND) + return targ; + return next; + case 0x100: /* BCF */ + case 0x180: /* BCF */ + if(!(fcr31 & FPCOND)) + return targ; + return next; + } + /* shouldn't get here */ + return 0; +} --- /sys/src/9/rb/init9.s Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/init9.s Fri May 17 21:32:33 2013 @@ -0,0 +1,8 @@ +TEXT _main(SB), $8 + MOVW $setR30(SB), R30 + MOVW $boot(SB), R1 + ADDU $12, R29, R2 /* get a pointer to 0(FP) */ + MOVW R1, 4(R29) + MOVW R2, 8(R29) + JAL startboot(SB) + --- /sys/src/9/rb/initcode Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/initcode Fri Dec 26 15:46:03 2003 @@ -0,0 +1,44 @@ +#include "/sys/src/libc/9syscall/sys.h" + +/* + * we pass in the argument of the exec parameters as 0(FP) + */ + +TEXT main(SB),$8 + + MOVW $setR30(SB), R30 + + MOVW $boot(SB), R1 + ADD $12, R29, R2 /* get a pointer to 0(FP) */ + MOVW R1, 4(R29) + MOVW R2, 8(R29) + JAL exec(SB) + + MOVW $(1<<4), R1 + MOVW R1, 4(R29) + MOVW $RFORK, R1 + SYSCALL + MOVW $RFORK, R1 + SYSCALL + MOVW $RFORK, R1 + SYSCALL +again: + ADDU $1, R2 + MOVW $0, R1 /* print r1 */ + SYSCALL + MOVW $5000000, R3 +foo: + SUBU $1, R3 + BNE R3, foo + JMP again + +TEXT exec(SB), $0 + MOVW $EXEC, R1 + SYSCALL + RET + +DATA boot+0(SB)/5,$"/boot" +DATA boot+5(SB)/5,$"/boot" +DATA bootv+0(SB)/4,$boot+6(SB) +GLOBL boot+0(SB),$11 +GLOBL bootv+0(SB),$8 --- /sys/src/9/rb/initreboot.s Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/initreboot.s Wed Jun 19 20:25:33 2013 @@ -0,0 +1,60 @@ +/* + * mips 24k machine assist for routerboard rb450g (minimal for reboot) + */ +#include "mem.h" +#include "mips.s" + + NOSCHED + +TEXT _main(SB), $0 + MOVW $setR30(SB), R30 + JMP main(SB) + +/* target for JALRHB in BARRIERS */ +TEXT ret(SB), $-4 + JMP (R22) + NOP + +TEXT setsp(SB), $-4 + MOVW R1, SP + RETURN + +TEXT coherence(SB), $-4 + BARRIERS(7, R7, cohhb) + SYNC + EHB + RETURN + +/* + * cache manipulation + */ + +/* the i and d caches may be different sizes, so clean them separately */ +TEXT cleancache(SB), $-4 + DI(10) /* intrs off, old status -> R10 */ + SYNC + EHB + + MOVW R0, R1 /* index, not address */ + MOVW $ICACHESIZE, R9 +iccache: + CACHE PI+IWBI, (R1) /* flush & invalidate I by index */ + SUBU $CACHELINESZ, R9 + ADDU $CACHELINESZ, R1 + BGTZ R9, iccache + NOP + + MOVW R0, R1 /* index, not address */ + MOVW $DCACHESIZE, R9 +dccache: + CACHE PD+IWBI, (R1) /* flush & invalidate D by index */ + SUBU $CACHELINESZ, R9 + ADDU $CACHELINESZ, R1 + BGTZ R9, dccache + NOP + + SYNC + MOVW R10, M(STATUS) + JRHB(31) /* return and clear all hazards */ + + SCHED --- /sys/src/9/rb/io.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/io.h Wed Jul 3 23:11:08 2013 @@ -0,0 +1,333 @@ +enum { + Mhz = 1000*1000, +}; + +/* + * duarts, frequency and registers + */ +#define DUARTFREQ 3672000 + +/* + * interrupt levels on CPU boards. + */ +enum +{ + ILmin = 2, + ILpci = 2, + ILehci = 3, + ILenet1 = 4, /* arge1 @ 0x19:: w switch */ + ILenet0 = 5, /* arge0 @ 0x1a:: */ + ILduart0 = 6, /* actually APB, uart is subintr 3 */ + ILclock = 7, + ILmax = 7, + + ILshift = 8, +}; + +#define Rstblockbase (ulong *)KSEG1ADDR(0x18060000) + +#define Rstwdogctl (ulong *)KSEG1ADDR(0x18060008) +#define Wdoglast (1 << 31) +#define Wdogmask 3 +#define Wdognoaction 0 +#define Wdoggpintr 1 +#define Wdognmi 2 +#define Wdogreset 3 +#define Rstwdogtimer (ulong *)KSEG1ADDR(0x1806000c) + +/* + * APB interrupt status and mask register and interrupt bits + */ +#define Apbintrsts (ulong *)KSEG1ADDR(0x18060010) +#define Apbintrmask (ulong *)KSEG1ADDR(0x18060014) +#define Apbintrtimer 0 +#define Apbintrerror 1 +#define Apbintrgpio 2 +#define Apbintruart 3 +#define Apbintrwatchdog 4 +#define Apbintrperf 5 +#define Apbintrohci 6 +#define Apbintrdma 7 + +#define Pciintrsts (ulong *)KSEG1ADDR(0x18060018) +#define Pciintrmask (ulong *)KSEG1ADDR(0x1806001C) +#define PCI_INTR_CORE (1 << 4) + +#define Reset (ulong *)KSEG1ADDR(0x18060024) +#define Rstfullchip (1 << 24) /* same as pulling the reset pin */ +#define Rstcpucold (1 << 20) /* cold reset */ +#define Rstge1mac (1 << 13) +#define Rstge1phy (1 << 12) +#define Rstge0mac (1 << 9) +#define Rstge0phy (1 << 8) +#define Rstusbohcidll (1 << 6) +#define Rstusbhost (1 << 5) +#define Rstusbphy (1 << 4) +#define Rstpcibus (1 << 1) +#define Rstpcicore (1 << 0) + +/* + * mostly PCI from here on + */ + +typedef struct Pcisiz Pcisiz; +typedef struct Pcidev Pcidev; +typedef struct Vctl Vctl; + +struct Vctl { + Vctl* next; /* handlers on this vector */ + + char name[KNAMELEN]; /* of driver */ + int isintr; /* interrupt or fault/trap */ + int irq; + int tbdf; + int (*isr)(int); /* get isr bit for this irq */ + int (*eoi)(int); /* eoi */ + + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +}; + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + CfgEISA = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 & type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +/* ccrb (base class code) values; controller types */ +enum { + Pcibcpci1 = 0, /* pci 1.0; no class codes defined */ + Pcibcstore = 1, /* mass storage */ + Pcibcnet = 2, /* network */ + Pcibcdisp = 3, /* display */ + Pcibcmmedia = 4, /* multimedia */ + Pcibcmem = 5, /* memory */ + Pcibcbridge = 6, /* bridge */ + Pcibccomm = 7, /* simple comms (e.g., serial) */ + Pcibcbasesys = 8, /* base system */ + Pcibcinput = 9, /* input */ + Pcibcdock = 0xa, /* docking stations */ + Pcibcproc = 0xb, /* processors */ + Pcibcserial = 0xc, /* serial bus (e.g., USB) */ + Pcibcwireless = 0xd, /* wireless */ + Pcibcintell = 0xe, /* intelligent i/o */ + Pcibcsatcom = 0xf, /* satellite comms */ + Pcibccrypto = 0x10, /* encryption/decryption */ + Pcibcdacq = 0x11, /* data acquisition & signal proc. */ +}; + +/* ccru (sub-class code) values; common cases only */ +enum { + /* mass storage */ + Pciscscsi = 0, /* SCSI */ + Pciscide = 1, /* IDE (ATA) */ + Pciscsata = 6, /* SATA */ + + /* network */ + Pciscether = 0, /* Ethernet */ + + /* display */ + Pciscvga = 0, /* VGA */ + Pciscxga = 1, /* XGA */ + Pcisc3d = 2, /* 3D */ + + /* bridges */ + Pcischostpci = 0, /* host/pci */ + Pciscpcicpci = 1, /* pci/pci */ + + /* simple comms */ + Pciscserial = 0, /* 16450, etc. */ + Pciscmultiser = 1, /* multiport serial */ + + /* serial bus */ + Pciscusb = 3, /* USB */ +}; + +enum { /* type 0 pre-defined header */ + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + ushort pcr; + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + uchar cls; + uchar ltr; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + + int pmrb; /* power management register block */ +}; + +enum { + /* vendor ids */ + Vatiamd = 0x1002, + Vintel = 0x8086, + Vjmicron= 0x197b, + Vmarvell= 0x1b4b, + Vmyricom= 0x14c1, +}; + +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +#define ISAWINDOW 0 +#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) + +/* SMBus transactions */ +enum +{ + SMBquick, /* sends address only */ + + /* write */ + SMBsend, /* sends address and cmd */ + SMBbytewrite, /* sends address and cmd and 1 byte */ + SMBwordwrite, /* sends address and cmd and 2 bytes */ + + /* read */ + SMBrecv, /* sends address, recvs 1 byte */ + SMBbyteread, /* sends address and cmd, recv's byte */ + SMBwordread, /* sends address and cmd, recv's 2 bytes */ +}; + +typedef struct SMBus SMBus; +struct SMBus { + QLock; /* mutex */ + Rendez r; /* rendezvous point for completion interrupts */ + void *arg; /* implementation dependent */ + ulong base; /* port or memory base of smbus */ + int busy; + void (*transact)(SMBus*, int, int, int, uchar*); +}; + +#pragma varargck type "T" int +#pragma varargck type "T" uint --- /sys/src/9/rb/l.s Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/l.s Tue Jul 23 03:29:03 2013 @@ -0,0 +1,1073 @@ +/* + * mips 24k machine assist for routerboard rb450g + */ +#include "mem.h" +#include "mips.s" + +#define SANITY 0x12345678 + + NOSCHED + +/* + * Boot only processor + */ +TEXT start(SB), $-4 + MOVW $setR30(SB), R30 + +PUTC('9', R1, R2) + DI(0) + + MOVW sanity(SB), R1 + CONST(SANITY, R2) + SUBU R1, R2, R2 + BNE R2, insane + NOP + + MOVW R0, M(COMPARE) + EHB + + /* don't enable any interrupts nor FP, but leave BEV on. */ + MOVW $BEV,R1 + MOVW R1, M(STATUS) + UBARRIERS(7, R7, stshb) /* returns to kseg1 space */ + MOVW R0, M(CAUSE) + EHB + + /* silence the atheros watchdog */ + MOVW $(KSEG1|0x18060008), R1 + MOVW R0, (R1) /* set no action */ + SYNC + + MOVW $PE, R1 + MOVW R1, M(CACHEECC) /* aka ErrCtl */ + EHB + JAL cleancache(SB) + NOP + + MOVW $TLBROFF, R1 + MOVW R1, M(WIRED) + + MOVW R0, M(CONTEXT) + EHB + + /* set KSEG0 cachability before trying LL/SC in lock code */ + MOVW M(CONFIG), R1 + AND $~CFG_K0, R1 + /* make kseg0 cachable, enable write-through merging */ + OR $((PTECACHABILITY>>3)|CFG_MM), R1 + MOVW R1, M(CONFIG) + BARRIERS(7, R7, cfghb) /* back to kseg0 space */ + + MOVW $setR30(SB), R30 /* again */ + + /* initialize Mach, including stack */ + MOVW $MACHADDR, R(MACH) + ADDU $(MACHSIZE-BY2V), R(MACH), SP + MOVW R(MACH), R1 +clrmach: + MOVW R0, (R1) + ADDU $BY2WD, R1 + BNE R1, SP, clrmach + NOP + MOVW R0, 0(R(MACH)) /* m->machno = 0 */ + MOVW R0, R(USER) /* up = nil */ + + /* zero bss, byte-by-byte */ + MOVW $edata(SB), R1 + MOVW $end(SB), R2 +clrbss: + MOVB R0, (R1) + ADDU $1, R1 + BNE R1, R2, clrbss + NOP + + MOVW $0x16, R16 + MOVW $0x17, R17 + MOVW $0x18, R18 + MOVW $0x19, R19 + MOVW $0x20, R20 + MOVW $0x21, R21 + MOVW $0x22, R22 + MOVW $0x23, R23 + + MOVW R0, HI + MOVW R0, LO + +PUTC('\r', R1, R2) +PUTC('\n', R1, R2) + JAL main(SB) + NOP + CONST(ROM, R1) + JMP (R1) /* back to the rom */ + +#define PUT(c) PUTC(c, R1, R2) +#define DELAY(lab) \ + CONST(34000000, R3); \ +lab: SUBU $1, R3; \ + BNE R3, lab; \ + NOP + +insane: + /* + * data segment is misaligned; kernel needs vl -R4096 or -R16384, + * as appropriate, for reboot. + */ + PUT('?'); PUT('d'); PUT('a'); PUT('t'); PUT('a'); PUT(' '); DELAY(dl1) + PUT('s'); PUT('e'); PUT('g'); PUT('m'); PUT('e'); PUT('n'); DELAY(dl2) + PUT('t'); PUT(' '); PUT('m'); PUT('i'); PUT('s'); PUT('a'); DELAY(dl3) + PUT('l'); PUT('i'); PUT('g'); PUT('n'); PUT('e'); PUT('d'); DELAY(dl4) + PUT('\r'); PUT('\n'); DELAY(dl5) + CONST(ROM, R1) + JMP (R1) /* back to the rom */ + NOP + +/* target for JALRHB in BARRIERS */ +TEXT ret(SB), $-4 + JMP (R22) + NOP + +/* print R1 in hex; clobbers R3—8 */ +TEXT printhex(SB), $-4 + MOVW $32, R5 + MOVW $9, R7 +prtop: + SUB $4, R5 + MOVW R1, R6 + SRL R5, R6 + AND $0xf, R6 + SGTU R6, R7, R8 + BEQ R8, prdec /* branch if R6 <= 9 */ + NOP + ADD $('a'-10), R6 + JMP prchar + NOP +prdec: + ADD $'0', R6 +prchar: + PUTC(R6, R3, R4) + BNE R5, prtop + NOP + RETURN + +/* + * Take first processor into user mode + * - argument is stack pointer to user + */ +TEXT touser(SB), $-4 + MOVW R1, SP + MOVW $(UTZERO+32), R2 /* header appears in text */ + MOVW R2, M(EPC) + EHB + MOVW M(STATUS), R4 + AND $(~KMODEMASK), R4 + OR $(KUSER|IE|EXL), R4 /* switch to user mode, intrs on, exc */ + MOVW R4, M(STATUS) /* " */ + ERET /* clears EXL */ + +/* + * manipulate interrupts + */ + +/* enable an interrupt; bit is in R1 */ +TEXT intron(SB), $0 + MOVW M(STATUS), R2 + OR R1, R2 + MOVW R2, M(STATUS) + EHB + RETURN + +/* disable an interrupt; bit is in R1 */ +TEXT introff(SB), $0 + MOVW M(STATUS), R2 + XOR $-1, R1 + AND R1, R2 + MOVW R2, M(STATUS) + EHB + RETURN + +/* on our 24k, wait instructions are not interruptible, alas. */ +TEXT idle(SB), $-4 + EI(1) /* old M(STATUS) into R1 */ + EHB + /* fall through */ + +TEXT wait(SB), $-4 + WAIT + NOP + + MOVW R1, M(STATUS) /* interrupts restored */ + EHB + RETURN + +TEXT splhi(SB), $0 + EHB + MOVW R31, 12(R(MACH)) /* save PC in m->splpc */ + DI(1) /* old M(STATUS) into R1 */ + EHB + RETURN + +TEXT splx(SB), $0 + EHB + MOVW R31, 12(R(MACH)) /* save PC in m->splpc */ + MOVW M(STATUS), R2 + AND $IE, R1 + AND $~IE, R2 + OR R2, R1 + MOVW R1, M(STATUS) + EHB + RETURN + +TEXT spllo(SB), $0 + EHB + EI(1) /* old M(STATUS) into R1 */ + EHB + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW M(STATUS), R1 + AND $IE, R1 + RETURN + +TEXT coherence(SB), $-4 + BARRIERS(7, R7, cohhb) + SYNC + EHB + RETURN + +/* + * process switching + */ + +TEXT setlabel(SB), $-4 + MOVW R29, 0(R1) + MOVW R31, 4(R1) + MOVW R0, R1 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 0(R1), R29 + MOVW 4(R1), R31 + MOVW $1, R1 + RETURN + +/* + * the tlb routines need to be called at splhi. + */ + +TEXT puttlb(SB), $0 /* puttlb(virt, phys0, phys1) */ + EHB + MOVW R1, M(TLBVIRT) + EHB + MOVW 4(FP), R2 /* phys0 */ + MOVW 8(FP), R3 /* phys1 */ + MOVW R2, M(TLBPHYS0) + EHB + MOVW $PGSZ, R1 + MOVW R3, M(TLBPHYS1) + EHB + MOVW R1, M(PAGEMASK) + OR R2, R3, R4 /* MTC0 delay slot */ + AND $PTEVALID, R4 /* MTC0 delay slot */ + EHB + TLBP /* tlb probe */ + EHB + MOVW M(INDEX), R1 + BGEZ R1, index /* if tlb entry found, use it */ + NOP + BEQ R4, dont /* not valid? cf. kunmap */ + NOP + MOVW M(RANDOM), R1 /* write random tlb entry */ + MOVW R1, M(INDEX) +index: + EHB + TLBWI /* write indexed tlb entry */ + JRHB(31) /* return and clear all hazards */ +dont: + RETURN + +TEXT getwired(SB),$0 + MOVW M(WIRED), R1 + RETURN + +TEXT setwired(SB),$0 + MOVW R1, M(WIRED) + EHB + RETURN + +TEXT getrandom(SB),$0 + MOVW M(RANDOM), R1 + RETURN + +TEXT getpagemask(SB),$0 + MOVW M(PAGEMASK), R1 + RETURN + +TEXT setpagemask(SB),$0 + EHB + MOVW R1, M(PAGEMASK) + EHB + MOVW R0, R1 /* prevent accidents */ + RETURN + +TEXT puttlbx(SB), $0 /* puttlbx(index, virt, phys0, phys1, pagemask) */ + MOVW 4(FP), R2 + MOVW 8(FP), R3 + MOVW 12(FP), R4 + MOVW 16(FP), R5 + EHB + MOVW R2, M(TLBVIRT) + EHB + MOVW R3, M(TLBPHYS0) + MOVW R4, M(TLBPHYS1) + MOVW R5, M(PAGEMASK) + EHB + MOVW R1, M(INDEX) + EHB + TLBWI /* write indexed tlb entry */ + JRHB(31) /* return and clear all hazards */ + +TEXT tlbvirt(SB), $0 + EHB + MOVW M(TLBVIRT), R1 + EHB + RETURN + +TEXT gettlbx(SB), $0 /* gettlbx(index, &entry) */ + MOVW 4(FP), R5 + MOVW M(TLBVIRT), R10 /* save our asid */ + EHB + MOVW R1, M(INDEX) + EHB + TLBR /* read indexed tlb entry */ + EHB + MOVW M(TLBVIRT), R2 + MOVW M(TLBPHYS0), R3 + MOVW M(TLBPHYS1), R4 + MOVW R2, 0(R5) + MOVW R3, 4(R5) + MIPS24KNOP + MOVW R4, 8(R5) + EHB + MOVW R10, M(TLBVIRT) /* restore our asid */ + EHB + RETURN + +TEXT gettlbp(SB), $0 /* gettlbp(tlbvirt, &entry) */ + MOVW 4(FP), R5 + MOVW M(TLBVIRT), R10 /* save our asid */ + EHB + MOVW R1, M(TLBVIRT) + EHB + TLBP /* probe tlb */ + EHB + MOVW M(INDEX), R1 + BLTZ R1, gettlbp1 /* if no tlb entry found, return */ + NOP + EHB + TLBR /* read indexed tlb entry */ + EHB + MOVW M(TLBVIRT), R2 + MOVW M(TLBPHYS0), R3 + MOVW M(TLBPHYS1), R4 + MOVW M(PAGEMASK), R6 + MOVW R2, 0(R5) + MOVW R3, 4(R5) + MIPS24KNOP + MOVW R4, 8(R5) + MOVW R6, 12(R5) +gettlbp1: + EHB + MOVW R10, M(TLBVIRT) /* restore our asid */ + EHB + RETURN + +TEXT gettlbvirt(SB), $0 /* gettlbvirt(index) */ + MOVW M(TLBVIRT), R10 /* save our asid */ + EHB + MOVW R1, M(INDEX) + EHB + TLBR /* read indexed tlb entry */ + EHB + MOVW M(TLBVIRT), R1 + EHB + MOVW R10, M(TLBVIRT) /* restore our asid */ + EHB + RETURN + +/* + * exceptions. + * mips promises that there will be no current hazards upon entry + * to exception handlers. + */ + +TEXT vector0(SB), $-4 + MOVW $utlbmiss(SB), R26 + JMP (R26) + NOP + +/* + * compute stlb hash index. + * must match index calculation in mmu.c/putstlb() + * + * M(TLBVIRT) [page & asid] in arg, result in arg. + * stir in swizzled asid; we get best results with asid in both high & low bits. + * + * page = tlbvirt >> (PGSHIFT+1); // ignoring even/odd bit + * R27 = ((tlbvirt<<(STLBLOG-8) ^ (uchar)tlbvirt ^ page ^ + * ((page & (MASK(HIPFNBITS) << STLBLOG)) >> HIPFNBITS)) & + * (STLBSIZE-1)) * 12; + */ +#define STLBHASH(arg, tmp, tmp2) \ + MOVW arg, tmp2; \ + SRL $(PGSHIFT+1), arg; /* move low page # bits to low bits */ \ + CONST ((MASK(HIPFNBITS) << STLBLOG), tmp); \ + AND arg, tmp; /* extract high page # bits */ \ + SRL $HIPFNBITS, tmp; /* position them */ \ + XOR tmp, arg; /* include them */ \ + MOVW tmp2, tmp; /* asid in low byte */ \ + SLL $(STLBLOG-8), tmp; /* move asid to high bits */ \ + XOR tmp, arg; /* include asid in high bits too */ \ + AND $0xff, tmp2, tmp; /* asid in low byte */ \ + XOR tmp, arg; /* include asid in low bits */ \ + CONST (STLBSIZE-1, tmp); \ + AND tmp, arg /* chop to fit */ + +TEXT utlbmiss(SB), $-4 + /* + * don't use R28 by using constants that span both word halves, + * it's unsaved so far. avoid R24 (up in kernel) and R25 (m in kernel). + */ + /* update statistics */ + CONST (MACHADDR, R26) /* R26 = m-> */ + MOVW 16(R26), R27 + ADDU $1, R27 + MOVW R27, 16(R26) /* m->tlbfault++ */ + + MOVW R23, M(DESAVE) /* save R23 */ + +#ifdef KUTLBSTATS + MOVW M(STATUS), R23 + AND $KUSER, R23 + BEQ R23, kmiss + + MOVW 24(R26), R27 + ADDU $1, R27 + MOVW R27, 24(R26) /* m->utlbfault++ */ + JMP either +kmiss: + MOVW 20(R26), R27 + ADDU $1, R27 + MOVW R27, 20(R26) /* m->ktlbfault++ */ +either: +#endif + + /* compute stlb index */ + EHB + MOVW M(TLBVIRT), R27 /* asid in low byte */ + STLBHASH(R27, R26, R23) + MOVW M(DESAVE), R23 /* restore R23 */ + + /* scale to a byte index (multiply by 12) */ + SLL $1, R27, R26 /* × 2 */ + ADDU R26, R27 /* × 3 */ + SLL $2, R27 /* × 12 */ + + CONST (MACHADDR, R26) /* R26 = m-> */ + MOVW 4(R26), R26 /* R26 = m->stb */ + ADDU R26, R27 /* R27 = &m->stb[hash] */ + + MOVW M(BADVADDR), R26 + AND $BY2PG, R26 + BNE R26, utlbodd /* odd page? */ + NOP + +utlbeven: + MOVW 4(R27), R26 /* R26 = m->stb[hash].phys0 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + NOP + MOVW R26, M(TLBPHYS0) + EHB + MOVW 8(R27), R26 /* R26 = m->stb[hash].phys1 */ + JMP utlbcom + MOVW R26, M(TLBPHYS1) /* branch delay slot */ + +utlbodd: + MOVW 8(R27), R26 /* R26 = m->stb[hash].phys1 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + NOP + MOVW R26, M(TLBPHYS1) + EHB + MOVW 4(R27), R26 /* R26 = m->stb[hash].phys0 */ + MOVW R26, M(TLBPHYS0) + +utlbcom: + EHB /* MTC0/MFC0 hazard */ + MOVW M(TLBVIRT), R26 + MOVW (R27), R27 /* R27 = m->stb[hash].virt */ + BEQ R27, stlbm /* nothing cached? do it the hard way */ + NOP + /* is the stlb entry for the right virtual address? */ + BNE R26, R27, stlbm /* M(TLBVIRT) != m->stb[hash].virt? */ + NOP + + /* if an entry exists, overwrite it, else write a random one */ + CONST (PGSZ, R27) + MOVW R27, M(PAGEMASK) /* select page size */ + EHB + TLBP /* probe tlb */ + EHB + MOVW M(INDEX), R26 + BGEZ R26, utlindex /* if tlb entry found, rewrite it */ + EHB /* delay slot */ + TLBWR /* else write random tlb entry */ + ERET +utlindex: + TLBWI /* write indexed tlb entry */ + ERET + +/* not in the stlb either; make trap.c figure it out */ +stlbm: + MOVW $exception(SB), R26 + JMP (R26) + NOP + +TEXT stlbhash(SB), $-4 + STLBHASH(R1, R2, R3) + RETURN + +TEXT vector100(SB), $-4 + MOVW $exception(SB), R26 + JMP (R26) + NOP + +TEXT vector180(SB), $-4 + MOVW $exception(SB), R26 + JMP (R26) + NOP + +TEXT exception(SB), $-4 + MOVW M(STATUS), R26 + AND $KUSER, R26, R27 + BEQ R27, waskernel + MOVW SP, R27 /* delay slot */ + +wasuser: + CONST (MACHADDR, SP) /* m-> */ + MOVW 8(SP), SP /* m->proc */ + MOVW 8(SP), SP /* m->proc->kstack */ + MOVW M(STATUS), R26 /* redundant load */ + ADDU $(KSTACK-UREGSIZE), SP + MOVW R31, Ureg_r31(SP) + + JAL savereg1(SB) + NOP + + MOVW R30, Ureg_r30(SP) + MOVW R(MACH), Ureg_r25(SP) + MIPS24KNOP + MOVW R(USER), Ureg_r24(SP) + + MOVW $setR30(SB), R30 + CONST (MACHADDR, R(MACH)) /* R(MACH) = m-> */ + MOVW 8(R(MACH)), R(USER) /* up = m->proc */ + + AND $(EXCMASK<<2), R26, R1 + SUBU $(CSYS<<2), R1 + BNE R1, notsys + NOP + + /* the carrera does this: */ +// ADDU $8, SP, R1 /* first arg for syscall */ + + MOVW SP, R1 /* first arg for syscall */ + JAL syscall(SB) + SUBU $Notuoffset, SP /* delay slot */ +sysrestore: + JAL restreg1(SB) + ADDU $Notuoffset, SP /* delay slot */ + + MOVW Ureg_r31(SP), R31 + MOVW Ureg_status(SP), R26 + MOVW Ureg_r30(SP), R30 + MOVW R26, M(STATUS) + EHB + MOVW Ureg_pc(SP), R26 /* old pc */ + MOVW Ureg_sp(SP), SP + MOVW R26, M(EPC) + ERET + +notsys: + JAL savereg2(SB) + NOP + + /* the carrera does this: */ +// ADDU $8, SP, R1 /* first arg for trap */ + + MOVW SP, R1 /* first arg for trap */ + JAL trap(SB) + SUBU $Notuoffset, SP /* delay slot */ + + ADDU $Notuoffset, SP + +restore: + JAL restreg1(SB) + NOP + JAL restreg2(SB) /* restores R28, among others */ + NOP + + MOVW Ureg_r30(SP), R30 + MOVW Ureg_r31(SP), R31 + MOVW Ureg_r25(SP), R(MACH) + MOVW Ureg_r24(SP), R(USER) + MOVW Ureg_sp(SP), SP + MOVW R26, M(EPC) + ERET + +waskernel: + SUBU $UREGSIZE, SP + OR $7, SP /* conservative rounding */ + XOR $7, SP + MOVW R31, Ureg_r31(SP) + + JAL savereg1(SB) + NOP + JAL savereg2(SB) + NOP + + /* the carrera does this: */ +// ADDU $8, SP, R1 /* first arg for trap */ + + MOVW SP, R1 /* first arg for trap */ + JAL trap(SB) + SUBU $Notuoffset, SP /* delay slot */ + + ADDU $Notuoffset, SP + + JAL restreg1(SB) + NOP + + /* + * if about to return to `wait', interrupt arrived just before + * executing wait, so move saved pc past it. + */ + MOVW Ureg_pc(SP), R26 + MOVW R26, R31 + MOVW $wait(SB), R1 + SUBU R1, R31 + BNE R31, notwait + NOP + ADD $BY2WD, R26 /* advance saved pc */ + MOVW R26, Ureg_pc(SP) +notwait: + JAL restreg2(SB) /* restores R28, among others */ + NOP + + MOVW Ureg_r31(SP), R31 + MOVW Ureg_sp(SP), SP + MOVW R26, M(EPC) + ERET + +TEXT forkret(SB), $0 + JMP sysrestore + MOVW R0, R1 /* delay slot; child returns 0 */ + +/* + * save mandatory registers. + * called with old M(STATUS) in R26. + * called with old SP in R27 + * returns with M(CAUSE) in R26 + */ +TEXT savereg1(SB), $-4 + MOVW R1, Ureg_r1(SP) + + MOVW $(~KMODEMASK),R1 /* don't use R28, it's unsaved so far */ + AND R26, R1 + MOVW R1, M(STATUS) + EHB + + MOVW R26, Ureg_status(SP) /* status */ + MOVW R27, Ureg_sp(SP) /* user SP */ + + MOVW M(EPC), R1 + MOVW M(CAUSE), R26 + + MOVW R23, Ureg_r23(SP) + MOVW R22, Ureg_r22(SP) + MIPS24KNOP + MOVW R21, Ureg_r21(SP) + MOVW R20, Ureg_r20(SP) + MIPS24KNOP + MOVW R19, Ureg_r19(SP) + MOVW R1, Ureg_pc(SP) + RETURN + +/* + * all other registers. + * called with M(CAUSE) in R26 + */ +TEXT savereg2(SB), $-4 + MOVW R2, Ureg_r2(SP) + + MOVW M(BADVADDR), R2 + MOVW R26, Ureg_cause(SP) + MOVW M(TLBVIRT), R1 + MOVW R2, Ureg_badvaddr(SP) + MOVW R1, Ureg_tlbvirt(SP) + MOVW HI, R1 + MOVW LO, R2 + MOVW R1, Ureg_hi(SP) + MOVW R2, Ureg_lo(SP) + MIPS24KNOP + /* LINK,SB,SP missing */ + MOVW R28, Ureg_r28(SP) + /* R27, R26 not saved */ + /* R25, R24 missing */ + /* R23- R19 saved in save1 */ + MOVW R18, Ureg_r18(SP) + MIPS24KNOP + MOVW R17, Ureg_r17(SP) + MOVW R16, Ureg_r16(SP) + MIPS24KNOP + MOVW R15, Ureg_r15(SP) + MOVW R14, Ureg_r14(SP) + MIPS24KNOP + MOVW R13, Ureg_r13(SP) + MOVW R12, Ureg_r12(SP) + MIPS24KNOP + MOVW R11, Ureg_r11(SP) + MOVW R10, Ureg_r10(SP) + MIPS24KNOP + MOVW R9, Ureg_r9(SP) + MOVW R8, Ureg_r8(SP) + MIPS24KNOP + MOVW R7, Ureg_r7(SP) + MOVW R6, Ureg_r6(SP) + MIPS24KNOP + MOVW R5, Ureg_r5(SP) + MOVW R4, Ureg_r4(SP) + MIPS24KNOP + MOVW R3, Ureg_r3(SP) + RETURN + +TEXT restreg1(SB), $-4 + MOVW Ureg_r23(SP), R23 + MOVW Ureg_r22(SP), R22 + MOVW Ureg_r21(SP), R21 + MOVW Ureg_r20(SP), R20 + MOVW Ureg_r19(SP), R19 + RETURN + +TEXT restreg2(SB), $-4 + /* LINK,SB,SP missing */ + MOVW Ureg_r28(SP), R28 + /* R27, R26 not saved */ + /* R25, R24 missing */ + /* R19- R23 restored in rest1 */ + MOVW Ureg_r18(SP), R18 + MOVW Ureg_r17(SP), R17 + MOVW Ureg_r16(SP), R16 + MOVW Ureg_r15(SP), R15 + MOVW Ureg_r14(SP), R14 + MOVW Ureg_r13(SP), R13 + MOVW Ureg_r12(SP), R12 + MOVW Ureg_r11(SP), R11 + MOVW Ureg_r10(SP), R10 + MOVW Ureg_r9(SP), R9 + MOVW Ureg_r8(SP), R8 + MOVW Ureg_r7(SP), R7 + MOVW Ureg_r6(SP), R6 + MOVW Ureg_r5(SP), R5 + MOVW Ureg_r4(SP), R4 + MOVW Ureg_r3(SP), R3 + MOVW Ureg_lo(SP), R2 + MOVW Ureg_hi(SP), R1 + MOVW R2, LO + MOVW R1, HI + + MOVW Ureg_status(SP), R1 + MOVW Ureg_r2(SP), R2 + MOVW R1, M(STATUS) /* could change interruptibility */ + EHB + MOVW Ureg_r1(SP), R1 /* BOTCH */ + MOVW Ureg_pc(SP), R26 + RETURN + +#ifdef OLD_MIPS_EXAMPLE +/* this appears to be a dreg from the distant past */ +TEXT rfnote(SB), $0 + MOVW R1, R26 /* 1st arg is &uregpointer */ + JMP restore + SUBU $(BY2WD), R26, SP /* delay slot: pc hole */ +#endif + +/* + * degenerate floating-point stuff + */ + +TEXT clrfpintr(SB), $0 + RETURN + +TEXT savefpregs(SB), $0 + RETURN + +TEXT restfpregs(SB), $0 + RETURN + +TEXT fcr31(SB), $0 /* fp csr */ + MOVW R0, R1 + RETURN + +/* + * Emulate 68020 test and set: load linked / store conditional + */ + +TEXT tas(SB), $0 + MOVW R1, R2 /* address of key */ +tas1: + MOVW $1, R3 + LL(2, 1) + NOP + SC(2, 3) + NOP + BEQ R3, tas1 + NOP + RETURN + +TEXT _xinc(SB), $0 + MOVW R1, R2 /* address of counter */ +loop: + MOVW $1, R3 + LL(2, 1) + NOP + ADDU R1, R3, R3 + SC(2, 3) + NOP + BEQ R3, loop + NOP + RETURN + +TEXT _xdec(SB), $0 + SYNC + MOVW R1, R2 /* address of counter */ +loop1: + MOVW $-1, R3 + LL(2, 1) + NOP + ADDU R1, R3, R3 + MOVW R3, R1 + SC(2, 3) + NOP + BEQ R3, loop1 + NOP + RETURN + +TEXT cmpswap(SB), $0 + MOVW R1, R2 /* address of key */ + MOVW old+4(FP), R3 /* old value */ + MOVW new+8(FP), R4 /* new value */ + LL(2, 1) /* R1 = (R2) */ + NOP + BNE R1, R3, fail + NOP + MOVW R4, R1 + SC(2, 1) /* (R2) = R1 if (R2) hasn't changed; R1 = success */ + NOP + RETURN +fail: + MOVW R0, R1 + RETURN + +/* + * cache manipulation + */ + +/* + * we avoided using R4, R5, R6, and R7 so gotopc can call us without saving + * them, but gotopc is now gone. + */ +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW 4(FP), R9 + DI(10) /* intrs off, old status -> R10 */ + UBARRIERS(7, R7, ichb); /* return to kseg1 (uncached) */ + ADDU R1, R9 /* R9 = last address */ + MOVW $(~(CACHELINESZ-1)), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDU $(CACHELINESZ-1), R9 + AND $(~(CACHELINESZ-1)), R9 /* round last address up */ + SUBU R8, R9 /* R9 = revised count */ +icflush1: +// CACHE PD+HWB, (R8) /* flush D to ram */ + CACHE PI+HINV, (R8) /* invalidate in I */ + SUBU $CACHELINESZ, R9 + BGTZ R9, icflush1 + ADDU $CACHELINESZ, R8 /* delay slot */ + + BARRIERS(7, R7, ic2hb); /* return to kseg0 (cached) */ + MOVW R10, M(STATUS) + JRHB(31) /* return and clear all hazards */ + +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + MOVW 4(FP), R9 + DI(10) /* intrs off, old status -> R10 */ + SYNC + EHB + ADDU R1, R9 /* R9 = last address */ + MOVW $(~(CACHELINESZ-1)), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDU $(CACHELINESZ-1), R9 + AND $(~(CACHELINESZ-1)), R9 /* round last address up */ + SUBU R8, R9 /* R9 = revised count */ +dcflush1: +// CACHE PI+HINV, (R8) /* invalidate in I */ + CACHE PD+HWBI, (R8) /* flush & invalidate in D */ + SUBU $CACHELINESZ, R9 + BGTZ R9, dcflush1 + ADDU $CACHELINESZ, R8 /* delay slot */ + SYNC + EHB + MOVW R10, M(STATUS) + JRHB(31) /* return and clear all hazards */ + +/* the i and d caches may be different sizes, so clean them separately */ +TEXT cleancache(SB), $-4 + DI(10) /* intrs off, old status -> R10 */ + + UBARRIERS(7, R7, cchb); /* return to kseg1 (uncached) */ + MOVW R0, R1 /* index, not address */ + MOVW $ICACHESIZE, R9 +iccache: + CACHE PI+IWBI, (R1) /* flush & invalidate I by index */ + SUBU $CACHELINESZ, R9 + BGTZ R9, iccache + ADDU $CACHELINESZ, R1 /* delay slot */ + + BARRIERS(7, R7, cc2hb); /* return to kseg0 (cached) */ + + MOVW R0, R1 /* index, not address */ + MOVW $DCACHESIZE, R9 +dccache: + CACHE PD+IWBI, (R1) /* flush & invalidate D by index */ + SUBU $CACHELINESZ, R9 + BGTZ R9, dccache + ADDU $CACHELINESZ, R1 /* delay slot */ + + SYNC + MOVW R10, M(STATUS) + JRHB(31) /* return and clear all hazards */ + +/* + * access to CP0 registers + */ + +TEXT prid(SB), $0 + MOVW M(PRID), R1 + RETURN + +TEXT rdcount(SB), $0 + MOVW M(COUNT), R1 + RETURN + +TEXT wrcount(SB), $0 + MOVW R1, M(COUNT) + EHB + RETURN + +TEXT wrcompare(SB), $0 + MOVW R1, M(COMPARE) + EHB + RETURN + +TEXT rdcompare(SB), $0 + MOVW M(COMPARE), R1 + RETURN + +TEXT getconfig(SB), $-4 + MOVW M(CONFIG), R1 + RETURN + +TEXT getconfig1(SB), $-4 + MFC0(CONFIG, 1, 1) + RETURN + +TEXT getconfig2(SB), $-4 + MFC0(CONFIG, 2, 1) + RETURN + +TEXT getconfig3(SB), $-4 + MFC0(CONFIG, 3, 1) + RETURN + +TEXT getconfig4(SB), $-4 + MFC0(CONFIG, 4, 1) + RETURN + +TEXT getconfig7(SB), $-4 + MFC0(CONFIG, 7, 1) + RETURN + +TEXT gethwreg3(SB), $-4 + RDHWR(3, 1) + RETURN + +TEXT getcause(SB), $-4 + MOVW M(CAUSE), R1 + RETURN + +TEXT C_fcr0(SB), $-4 /* fp implementation */ + MOVW $0x500, R1 /* claim to be an r4k, thus have ll/sc */ + RETURN + +TEXT getstatus(SB), $0 + MOVW M(STATUS), R1 + RETURN + +TEXT setstatus(SB), $0 + MOVW R1, M(STATUS) + EHB + RETURN + +TEXT setwatchhi0(SB), $0 + MOVW R1, M(WATCHHI) + EHB + RETURN + +/* + * beware that the register takes a double-word address, so it's not + * precise to the individual instruction. + */ +TEXT setwatchlo0(SB), $0 + MOVW R1, M(WATCHLO) + EHB + RETURN + +TEXT setsp(SB), $-4 + MOVW R1, SP + RETURN + +TEXT getintctl(SB), $-4 + MFC0(STATUS, 1, 1) + RETURN + +TEXT getsrsctl(SB), $-4 + MFC0(STATUS, 2, 1) + RETURN + +TEXT getsrsmap(SB), $-4 + MFC0(STATUS, 3, 1) + RETURN + +TEXT getperfctl0(SB), $-4 + MFC0(PERFCOUNT, 0, 1) + RETURN + +TEXT getperfctl1(SB), $-4 + MFC0(PERFCOUNT, 2, 1) + RETURN + + GLOBL sanity(SB), $4 + DATA sanity(SB)/4, $SANITY + + SCHED --- /sys/src/9/rb/main.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/main.c Tue Jul 23 03:48:59 2013 @@ -0,0 +1,622 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "init.h" +#include "pool.h" +#include "../ip/ip.h" +#include +#include +#include "reboot.h" + +typedef struct mipsexec Mipsexec; + +/* + * Option arguments from the command line. + * oargv[0] is the boot file. + */ +static int oargc; +static char* oargv[20]; +static char oargb[128]; +static int oargblen; + +static uintptr sp; /* XXX - must go - user stack of init proc */ + +/* + * software tlb simulation + */ +static Softtlb stlb[MAXMACH][STLBSIZE]; + +Conf conf; +FPsave initfp; + +int normalprint; + +char * +getconf(char *) +{ + return nil; /* stub */ +} + +static void +optionsinit(char* s) +{ + strecpy(oargb, oargb+sizeof(oargb), s); + + oargblen = strlen(oargb); + oargc = tokenize(oargb, oargv, nelem(oargv)-1); + oargv[oargc] = nil; +} + +static void +prcpuid(void) +{ + ulong cpuid, cfg1; + char *cpu; + + cpuid = prid(); + if (((cpuid>>16) & MASK(8)) == 0) /* vendor */ + cpu = "old mips"; + else if (((cpuid>>16) & MASK(8)) == 1) + switch ((cpuid>>8) & MASK(8)) { /* processor */ + case 0x93: + cpu = "mips 24k"; + break; + case 0x96: + cpu = "mips 24ke"; + break; + default: + cpu = "mips"; + break; + } + else + cpu = "other mips"; + delay(20); + print("cpu%d: %ldMHz %s %se v%ld.%ld rev %ld, ", + m->machno, m->hz / Mhz, cpu, getconfig() & (1<<15)? "b": "l", + (cpuid>>5) & MASK(3), (cpuid>>2) & MASK(3), cpuid & MASK(2)); + delay(200); + cfg1 = getconfig1(); + print("%s fpu\n", (cfg1 & 1? "has": "no")); + print("cpu%d: %ld tlb entries, using %dK pages\n", m->machno, + ((cfg1>>25) & MASK(6)) + 1, BY2PG/1024); + delay(50); + print("cpu%d: l1 i cache: %d sets 4 ways 32 bytes/line\n", m->machno, + 64 << ((cfg1>>22) & MASK(3))); + delay(50); + print("cpu%d: l1 d cache: %d sets 4 ways 32 bytes/line\n", m->machno, + 64 << ((cfg1>>13) & MASK(3))); + delay(500); + if (0) + print("cpu%d: cycle counter res = %ld\n", + m->machno, gethwreg3()); +} + +static void +fmtinit(void) +{ + printinit(); + quotefmtinstall(); + /* ipreset installs these when chandevreset runs */ + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +static int +ckpagemask(ulong mask, ulong size) +{ + int s; + ulong pm; + + s = splhi(); + setpagemask(mask); + pm = getpagemask(); + splx(s); + if(pm != mask){ + iprint("page size %ldK not supported on this cpu; " + "mask %#lux read back as %#lux\n", size/1024, mask, pm); + return -1; + } + return 0; +} + +/* called from rebootcmd() */ +int +parsemipsboothdr(Chan *c, ulong magic, Execvals *evp) +{ + long extra; + Mipsexec me; + + /* + * BOOT_MAGIC is sometimes defined like this: + * #define BOOT_MAGIC (0x160<<16) || magic == ((0x160<<16)|3) + * so we can only use it in a fairly stylized manner. + */ + if(magic == BOOT_MAGIC) { + c->offset = 0; /* back up */ + readn(c, &me, sizeof me); + /* if binary is -H1, read an extra long */ + if (l2be(me.amagic) == 0407 && me.nscns == 0) + readn(c, &extra, sizeof extra); + evp->entry = l2be(me.mentry); + evp->textsize = l2be(me.tsize); + evp->datasize = l2be(me.dsize); + return 0; + } else + return -1; +} + +void +main(void) +{ + stopwdog(); /* tranquilise the dog */ + optionsinit("/boot/boot boot"); + confinit(); + savefpregs(&initfp); + + machinit(); /* calls clockinit */ + active.exiting = 0; + active.machs = 1; + + kmapinit(); + xinit(); + + timersinit(); + fmtinit(); + vecinit(); + + normalprint = 1; + print("\nPlan 9\n"); + prcpuid(); + if (PTECACHABILITY == PTENONCOHERWT) + print("caches configured as write-through\n"); + if (0) + xsummary(); + + ckpagemask(PGSZ, BY2PG); + tlbinit(); + machwire(); + pageinit(); + procinit0(); + initseg(); + links(); + chandevreset(); + + swapinit(); + userinit(); + sicwdog(); + parseboothdr = parsemipsboothdr; + schedinit(); + panic("schedinit returned"); +} + +/* + * initialize a processor's mach structure. each processor does this + * for itself. + */ +void +machinit(void) +{ + /* Ensure CU1 is off */ + clrfpintr(); + + m->stb = &stlb[m->machno][0]; + + clockinit(); +} + +/* + * setup MIPS trap vectors + */ +void +vecinit(void) +{ + memmove((ulong*)UTLBMISS, (ulong*)vector0, 0x80); + memmove((ulong*)XEXCEPTION, (ulong*)vector0, 0x80); + memmove((ulong*)CACHETRAP, (ulong*)vector100, 0x80); + memmove((ulong*)EXCEPTION, (ulong*)vector180, 0x80); + memmove((ulong*)(KSEG0+0x200), (ulong*)vector180, 0x80); + icflush((ulong*)UTLBMISS, 4*1024); + + setstatus(getstatus() & ~BEV); +} + +void +init0(void) +{ + char buf[128]; + + up->nerrlab = 0; + + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "mips", 0); + snprint(buf, sizeof buf, "mips %s rb450g", conffile); + ksetenv("terminal", buf, 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + /* + * we don't have a good way to read our cfg file in + * RouterBOOT, so set the configuration here. + */ + ksetenv("nobootprompt", "tcp", 0); + ksetenv("nvram", "/boot/nvram", 0); + poperror(); + } + kproc("alarm", alarmkproc, 0); + i8250console(); + touser(sp); +} + +FPsave initfp; + +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(Tos) - i)); + memmove(p, oargb, i); + + /* + * Now push the argv pointers. + * The code jumped to by touser in lproc.s expects arguments + * main(char* argv0, ...) + * and calls + * startboot("/boot/boot", &argv0) + * not the usual (int argc, char* argv[]) + */ + av = (char**)(p - (oargc+1)*sizeof(char*)); + ssize = base + BY2PG - PTR2UINT(av); + for(i = 0; i < oargc; i++) + *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG); + *av = nil; + sp = USTKTOP - ssize; +} + +void +userinit(void) +{ + Proc *p; + KMap *k; + Page *pg; + Segment *s; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + p->fpstate = FPinit; + p->fpsave.fpstatus = initfp.fpstatus; + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); + 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]); + memset((void *)VA(k), 0, BY2PG); + memmove((ulong*)VA(k), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +void +procrestore(Proc *p) +{ + uvlong t; + + if(p->kp) + return; + cycles(&t); + p->pcycles -= t; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + uvlong t; + + cycles(&t); + p->pcycles += t; + /* no fpu, so no fp state to save */ +} + +static 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; +#ifdef BOOTARGS_EXIST + if(n >= BOOTARGSLEN) + error("kernel configuration too large"); + memmove(BOOTARGS, p, n); + memset(BOOTARGS + n, '\n', BOOTARGSLEN - n); +#endif + USED(n); + poperror(); + free(p); +} + +static void +shutdown(int ispanic) +{ + int ms, once; + + ilock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<machno); + /* + * setting exiting will make hzclock() on each processor call exit(0), + * which calls shutdown(0) and idles non-bootstrap cpus and returns + * on bootstrap processors (to permit a reboot). clearing our bit + * in machs avoids calling exit(0) from hzclock() on this processor. + */ + active.machs &= ~(1<machno); + active.exiting = 1; + iunlock(&active); + + if(once) { + delay(m->machno*1000); /* stagger them */ + iprint("cpu%d: exiting\n", m->machno); + } + spllo(); + ms = MAXMACH * 1000; + for(; ms > 0; ms -= TK2MS(2)){ + delay(TK2MS(2)); + if(active.machs == 0 && consactive() == 0) + break; + } + delay(100); +} + +/* + * 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(); + + /* + * the boot processor is cpu0. execute this function on it + * so that the new kernel has the same cpu0. + */ + if (m->machno != 0) { + procwired(up, 0); + sched(); + } + if (m->machno != 0) + print("on cpu%d (not 0)!\n", m->machno); + + shutdown(0); + + /* + * should be the only processor running now + */ +// iprint("reboot: entry %#p code %#p size %ld\n", entry, code, size); +// iprint("code[0] = %#lux\n", *(ulong *)code); + + /* turn off buffered serial console */ + serialoq = nil; + kprintoq = nil; + screenputs = nil; + + /* shutdown devices */ + chandevshutdown(); + + /* call off the dog */ + clockshutdown(); + + splhi(); + intrshutdown(); + + /* is the watchdog tied into the usb machinery? */ +// *Reset |= Rstusbohcidll | Rstusbhost | Rstusbphy; +// Rstge0mac | Rstge0phy | +// Rstge1mac | Rstge1phy; + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; + memmove(f, rebootcode, sizeof(rebootcode)); + icflush(f, sizeof(rebootcode)); + + setstatus(BEV); /* also, kernel mode, no interrupts */ + coherence(); + + /* off we go - never to return */ + (*f)((ulong)entry, (ulong)code, size); + + panic("loaded kernel returned!"); +} + +void +exit(int type) +{ + int timer; + void (*fnp)(void); + + stopwdog(); + + delay(1000); + lock(&active); + active.machs &= ~(1<machno); + active.exiting = 1; + unlock(&active); + spllo(); + + print("cpu %d exiting\n", m->machno); + timer = 0; + while(active.machs || consactive()) { + if(timer++ > 400) + break; + delay(10); + } + delay(1000); + splhi(); + USED(type); + + setstatus(BEV); + coherence(); + + iprint("exit: awaiting reset\n"); + wdogreset(); /* wake the dog with v. short timeout */ + +// *Reset |= Rstfullchip; +// *Reset |= Rstcpucold; + + delay(1000); /* await a reset */ + + iprint("exit: jumping to rom\n"); + fnp = (void (*)(void))ROM; + (*fnp)(); + + iprint("exit: looping\n"); + for (;;) + ; +} + +void +idlehands(void) +{ + stopwdog(); + idle(); + sicwdog(); /* wake the dog */ +} + +void +confinit(void) +{ + ulong kpages, ktop; + + /* + * divide memory twixt user pages and kernel. + */ + conf.mem[0].base = ktop = PADDR(PGROUND((ulong)end)); + /* fixed memory on routerboard */ + conf.mem[0].npage = MEMSIZE/BY2PG - ktop/BY2PG; + conf.npage = conf.mem[0].npage; + conf.nuart = 1; + + kpages = conf.npage - (conf.npage*80)/100; + if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ + kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; + kpages += (conf.nproc*KSTACK)/BY2PG; + } + 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; + + /* + * set up CPU's mach structure + * cpu0's was zeroed in l.s and our stack is in Mach, so don't zero it. + */ + m->machno = 0; + m->speed = 680; /* initial guess at MHz, for rb450g */ + m->hz = m->speed * Mhz; + conf.nmach = 1; + + /* set up other configuration parameters */ + conf.nproc = 2000; + conf.nswap = 262144; + conf.nswppo = 4096; + conf.nimage = 200; + + conf.copymode = 0; /* copy on write */ +} --- /sys/src/9/rb/mem.h Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/mem.h Tue Jul 23 02:28:29 2013 @@ -0,0 +1,328 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ + +#define MAXBY2PG (16*1024) /* rounding for UTZERO in executables; see mkfile */ +#define UTROUND(t) ROUNDUP((t), MAXBY2PG) + +#ifndef BIGPAGES +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log2(BY2PG) */ +#define PGSZ PGSZ4K +#define MACHSIZE (2*BY2PG) +#else +/* 16K pages work very poorly */ +#define BY2PG (16*1024) /* bytes per page */ +#define PGSHIFT 14 /* log2(BY2PG) */ +#define PGSZ PGSZ16K +#define MACHSIZE BY2PG +#endif + +#define KSTACK 8192 /* Size of kernel stack */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ + +#define MAXMACH 1 /* max # cpus system can run; see active.machs */ +#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */ +#define BLOCKALIGN 16 +#define CACHELINESZ 32 /* mips24k */ +#define ICACHESIZE (64*1024) /* rb450g */ +#define DCACHESIZE (32*1024) /* rb450g */ + +#define MASK(w) FMASK(0, w) + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * CP0 registers + */ + +#define INDEX 0 +#define RANDOM 1 +#define TLBPHYS0 2 /* aka ENTRYLO0 */ +#define TLBPHYS1 3 /* aka ENTRYLO1 */ +#define CONTEXT 4 +#define PAGEMASK 5 +#define WIRED 6 +#define BADVADDR 8 +#define COUNT 9 +#define TLBVIRT 10 /* aka ENTRYHI */ +#define COMPARE 11 +#define STATUS 12 +#define CAUSE 13 +#define EPC 14 +#define PRID 15 +#define CONFIG 16 +#define LLADDR 17 +#define WATCHLO 18 +#define WATCHHI 19 +#define DEBUG 23 +#define DEPC 24 +#define PERFCOUNT 25 +#define CACHEECC 26 +#define CACHEERR 27 +#define TAGLO 28 +#define TAGHI 29 +#define ERROREPC 30 +#define DESAVE 31 + +/* + * M(STATUS) bits + */ +#define KMODEMASK 0x0000001f +#define IE 0x00000001 /* master interrupt enable */ +#define EXL 0x00000002 /* exception level */ +#define ERL 0x00000004 /* error level */ +#define KSUPER 0x00000008 +#define KUSER 0x00000010 +#define KSU 0x00000018 +//#define UX 0x00000020 /* no [USK]X 64-bit extension bits on 24k */ +//#define SX 0x00000040 +//#define KX 0x00000080 +#define INTMASK 0x0000ff00 +#define INTR0 0x00000100 /* interrupt enable bits */ +#define INTR1 0x00000200 +#define INTR2 0x00000400 +#define INTR3 0x00000800 +#define INTR4 0x00001000 +#define INTR5 0x00002000 +#define INTR6 0x00004000 +#define INTR7 0x00008000 +//#define DE 0x00010000 /* not on 24k */ +#define TS 0x00200000 /* tlb shutdown; on 24k at least */ +#define BEV 0x00400000 /* bootstrap exception vectors */ +#define RE 0x02000000 /* reverse-endian in user mode */ +#define FR 0x04000000 /* enable 32 FP regs */ +#define CU0 0x10000000 +#define CU1 0x20000000 /* FPU enable */ + +/* + * M(CONFIG) bits + */ + +#define CFG_K0 7 /* kseg0 cachability */ +#define CFG_MM (1<<18) /* write-through merging enabled */ + +/* + * M(CAUSE) bits + */ + +#define BD (1<<31) /* last excep'n occurred in branch delay slot */ + +/* + * Exception codes + */ +#define EXCMASK 0x1f /* mask of all causes */ +#define CINT 0 /* external interrupt */ +#define CTLBM 1 /* TLB modification: store to unwritable page */ +#define CTLBL 2 /* TLB miss (load or fetch) */ +#define CTLBS 3 /* TLB miss (store) */ +#define CADREL 4 /* address error (load or fetch) */ +#define CADRES 5 /* address error (store) */ +#define CBUSI 6 /* bus error (fetch) */ +#define CBUSD 7 /* bus error (data load or store) */ +#define CSYS 8 /* system call */ +#define CBRK 9 /* breakpoint */ +#define CRES 10 /* reserved instruction */ +#define CCPU 11 /* coprocessor unusable */ +#define COVF 12 /* arithmetic overflow */ +#define CTRAP 13 /* trap */ +#define CVCEI 14 /* virtual coherence exception (instruction) */ +#define CFPE 15 /* floating point exception */ +#define CTLBRI 19 /* tlb read-inhibit */ +#define CTLBXI 20 /* tlb execute-inhibit */ +#define CWATCH 23 /* watch exception */ +#define CMCHK 24 /* machine checkcore */ +#define CCACHERR 30 /* cache error */ +#define CVCED 31 /* virtual coherence exception (data) */ + +/* + * M(CACHEECC) a.k.a. ErrCtl bits + */ +#define PE (1<<31) +#define LBE (1<<25) +#define WABE (1<<24) + +/* + * Trap vectors + */ + +#define UTLBMISS (KSEG0+0x000) +#define XEXCEPTION (KSEG0+0x080) +#define CACHETRAP (KSEG0+0x100) +#define EXCEPTION (KSEG0+0x180) + +/* + * Magic registers + */ + +#define USER 24 /* R24 is up-> */ +#define MACH 25 /* R25 is m-> */ + +/* + * offsets in ureg.h for l.s + */ +#define Ureg_status (Uoffset+0) +#define Ureg_pc (Uoffset+4) +#define Ureg_sp (Uoffset+8) +#define Ureg_cause (Uoffset+12) +#define Ureg_badvaddr (Uoffset+16) +#define Ureg_tlbvirt (Uoffset+20) + +#define Ureg_hi (Uoffset+24) +#define Ureg_lo (Uoffset+28) +#define Ureg_r31 (Uoffset+32) +#define Ureg_r30 (Uoffset+36) +#define Ureg_r28 (Uoffset+40) +#define Ureg_r27 (Uoffset+44) +#define Ureg_r26 (Uoffset+48) +#define Ureg_r25 (Uoffset+52) +#define Ureg_r24 (Uoffset+56) +#define Ureg_r23 (Uoffset+60) +#define Ureg_r22 (Uoffset+64) +#define Ureg_r21 (Uoffset+68) +#define Ureg_r20 (Uoffset+72) +#define Ureg_r19 (Uoffset+76) +#define Ureg_r18 (Uoffset+80) +#define Ureg_r17 (Uoffset+84) +#define Ureg_r16 (Uoffset+88) +#define Ureg_r15 (Uoffset+92) +#define Ureg_r14 (Uoffset+96) +#define Ureg_r13 (Uoffset+100) +#define Ureg_r12 (Uoffset+104) +#define Ureg_r11 (Uoffset+108) +#define Ureg_r10 (Uoffset+112) +#define Ureg_r9 (Uoffset+116) +#define Ureg_r8 (Uoffset+120) +#define Ureg_r7 (Uoffset+124) +#define Ureg_r6 (Uoffset+128) +#define Ureg_r5 (Uoffset+132) +#define Ureg_r4 (Uoffset+136) +#define Ureg_r3 (Uoffset+140) +#define Ureg_r2 (Uoffset+144) +#define Ureg_r1 (Uoffset+148) + +/* ch and carrera used these defs */ + /* Sizeof(Ureg) + (R5,R6) + 16 bytes slop + retpc + ur */ +// #define UREGSIZE ((Ureg_r1+4-Uoffset) + 2*BY2V + 16 + BY2WD + BY2WD) +// #define Uoffset 8 + +// #define UREGSIZE (Ureg_r1 + 4 - Uoffset) /* this ought to work */ +#define UREGSIZE ((Ureg_r1+4-Uoffset) + 2*BY2V + 16 + BY2WD + BY2WD) +#define Uoffset 0 +#define Notuoffset 8 + +/* + * MMU + */ +#define PGSZ4K (0x00<<13) +#define PGSZ16K (0x03<<13) /* on 24k */ +#define PGSZ64K (0x0F<<13) +#define PGSZ256K (0x3F<<13) +#define PGSZ1M (0xFF<<13) +#define PGSZ4M (0x3FF<<13) +// #define PGSZ8M (0x7FF<<13) /* not on 24k */ +#define PGSZ16M (0xFFF<<13) +#define PGSZ64M (0x3FFF<<13) /* on 24k */ +#define PGSZ256M (0xFFFF<<13) /* on 24k */ + +/* mips address spaces, tlb-mapped unless marked otherwise */ +#define KUSEG 0x00000000 /* user process */ +#define KSEG0 0x80000000 /* kernel (direct mapped, cached) */ +#define KSEG1 0xA0000000 /* kernel (direct mapped, uncached: i/o) */ +#define KSEG2 0xC0000000 /* kernel, used for TSTKTOP */ +#define KSEG3 0xE0000000 /* kernel, used by kmap */ +#define KSEGM 0xE0000000 /* mask to check which seg */ + +/* + * Fundamental addresses + */ + +#define REBOOTADDR KADDR(0x1000) /* just above vectors */ +#define MACHADDR 0x80005000 /* Mach structures */ +#define MACHP(n) ((Mach *)(MACHADDR+(n)*MACHSIZE)) +#define ROM 0xbfc00000 +#define KMAPADDR 0xE0000000 /* kmap'd addresses */ +#define WIREDADDR 0xE2000000 /* address wired kernel space */ + +#define PHYSCONS (KSEG1|0x18020000) /* i8250 uart */ + +#define PIDXSHFT 12 +#ifndef BIGPAGES +#define NCOLOR 8 +#define PIDX ((NCOLOR-1)<>PIDXSHFT) % NCOLOR) +#else +/* no cache aliases are possible with pages of 16K or larger */ +#define NCOLOR 1 +#define PIDX 0 +#define getpgcolor(a) 0 +#endif +#define KMAPSHIFT 15 + +#define PTEGLOBL (1<<0) +#define PTEVALID (1<<1) +#define PTEWRITE (1<<2) +#define PTERONLY 0 +#define PTEALGMASK (7<<3) +#define PTENONCOHERWT (0<<3) /* cached, write-through (slower) */ +#define PTEUNCACHED (2<<3) +#define PTENONCOHERWB (3<<3) /* cached, write-back */ +#define PTEUNCACHEDACC (7<<3) +/* rest are reserved on 24k */ +#define PTECOHERXCL (4<<3) +#define PTECOHERXCLW (5<<3) +#define PTECOHERUPDW (6<<3) + +/* how much faster is it? mflops goes from about .206 (WT) to .37 (WB) */ +#define PTECACHABILITY PTENONCOHERWT /* 24k erratum 48 disallows WB */ +// #define PTECACHABILITY PTENONCOHERWB + +#define PTEPID(n) (n) +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 512 +#define SSEGMAPSIZE 16 + +#define STLBLOG 15 +#define STLBSIZE (1< init.h + +reboot.h:D: initreboot.s rebootcode.c mem.h + $AS initreboot.s + $CC -FTVw rebootcode.c + # -lc is only for memmove. -T arg is REBOOTADDR. + $LD -l -a -s -T0x80001000 -R4 -o reboot.out initreboot.$O 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 + +l.$O: mips.s +arch.$O clock.$O fpimips.$O faultmips.$O mmu.$O syscall.$O \ + trap.$O: /$objtype/include/ureg.h +main.$O: /$objtype/include/ureg.h errstr.h init.h reboot.h +fpi.$O fpiarm.$O fpimem.$O: ../port/fpi.h +devether.$O: ../port/netif.h etherif.h + +%.clean:V: + rm -f $stem.c [9bz]$stem [9bz]$stem.gz boot$stem.* reboot.h apbootstrap.h init.h *.list + +# override ../port/portmkfile +# create /boot/boot +boot$CONF.out: $CONF print.$O $BOOTDIR/boot.c $BOOTLIB + $BOOTDIR/mkboot $CONF > boot$CONF.c + $CC $CFLAGS boot$CONF.c ../boot/printstub.c + $AS c_fcr0.s + $LD -o boot$CONF.out -T$UTZERO -R$MAXBY2PG boot$CONF.$O $BOOTLIB printstub.$O c_fcr0.$O --- /sys/src/9/rb/mmu.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/mmu.c Wed Jul 17 05:39:04 2013 @@ -0,0 +1,540 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" + +/* + * tlb entry 0 is used only by mmuswitch() to set the current tlb pid. + * + * It is apparently assumed that user tlb entries are not + * overwritten during start-up, so ... + * During system start-up (before up first becomes non-nil), + * Kmap entries start at tlb index 1 and work their way up until + * kmapinval() removes them. They then restart at 1. As long as there + * are few kmap entries they will not pass tlbroff (the WIRED tlb entry + * limit) and interfere with user tlb entries. + * Once start-up is over, we combine the kernel and user tlb pools into one, + * in the hope of making better use of the tlb on systems with small ones. + * + * All invalidations of the tlb are via indexed entries. The virtual + * address used is always 'KZERO | (x<<(PGSHIFT+1) | currentpid' where + * 'x' is the index into the tlb. This ensures that the current pid doesn't + * change and that no two invalidated entries have matching virtual + * addresses just in case SGI/MIPS ever makes a chip that cares (as + * they keep threatening). These entries should never be used in + * lookups since accesses to KZERO addresses don't go through the tlb + * (actually only true of KSEG0 and KSEG1; KSEG2 and KSEG3 do go + * through the tlb). + */ + +#define TLBINVAL(x, pid) puttlbx(x, KZERO|((x)<<(PGSHIFT+1))|(pid), 0, 0, PGSZ) + +enum { + Debugswitch = 0, + Debughash = 0, +}; + +static ulong ktime[8]; /* only for first 8 cpus */ + +void +tlbinit(void) +{ + int i; + + for(i=0; inext) + i++; + if(inext = k+1; + k->next = 0; + unlock(&kmaplock); + + m->ktlbnext = TLBOFF; +} + +void +kmapdump(void) +{ + int i; + + for(i=0; ivirt & ~BY2PG | TLBPID(tlbvirt()); + x = gettlbp(virt, tlbent); + if (!m->paststartup) + if (up) { /* startup just ended? */ + tlbroff = 1; + setwired(tlbroff); /* share all-but-one entries */ + m->paststartup = 1; + } else if (x < 0) { /* no such entry? use next */ + x = m->ktlbnext++; + if(m->ktlbnext >= tlbroff) + m->ktlbnext = TLBOFF; + } + if (x < 0) /* no entry for va? overwrite random one */ + x = puttlb(virt, k->phys0, k->phys1); + else + puttlbx(x, virt, k->phys0, k->phys1, PGSZ); + m->ktlbx[x] = 1; + return x; +} + +/* + * Arrange that the KMap'd virtual address will hit the same + * primary cache line as pg->va by making bits 14...12 of the + * tag the same as virtual address. These bits are the index + * into the primary cache and are checked whenever accessing + * the secondary cache through the primary. Violation causes + * a VCE trap. + */ +KMap * +kmap(Page *pg) +{ + int s, printed = 0; + ulong pte, virt; + KMap *k; + + s = splhi(); + lock(&kmaplock); + + if(kmapfree == 0) { +retry: + unlock(&kmaplock); + kmapinval(); /* try and free some */ + lock(&kmaplock); + if(kmapfree == 0){ + unlock(&kmaplock); + splx(s); + if(printed++ == 0){ + /* using iprint here we get mixed up with other prints */ + print("%d KMAP RETRY %#lux ktime %ld %ld %ld %ld %ld %ld %ld %ld\n", + m->machno, getcallerpc(&pg), + ktime[0], ktime[1], ktime[2], ktime[3], + ktime[4], ktime[5], ktime[6], ktime[7]); + delay(200); + } + splhi(); + lock(&kmaplock); + goto retry; + } + } + + k = kmapfree; + kmapfree = k->next; + + k->pg = pg; + /* + * One for the allocation, + * One for kactive + */ + k->pc = getcallerpc(&pg); + k->ref = 2; + k->konmach[m->machno] = m->kactive; + m->kactive = k; + + virt = pg->va; + /* bits 14..12 form the secondary-cache virtual index */ + virt &= PIDX; + virt |= KMAPADDR | ((k-kpte)<virt = virt; + pte = PPN(pg->pa)|PTECACHABILITY|PTEGLOBL|PTEWRITE|PTEVALID; + if(virt & BY2PG) { + k->phys0 = PTEGLOBL | PTECACHABILITY; + k->phys1 = pte; + } + else { + k->phys0 = pte; + k->phys1 = PTEGLOBL | PTECACHABILITY; + } + + putktlb(k); + unlock(&kmaplock); + + splx(s); + return k; +} + +void +kunmap(KMap *k) +{ + int s; + + s = splhi(); + if(decref(k) == 0) { + k->virt = 0; + k->phys0 = 0; + k->phys1 = 0; + k->pg = 0; + + lock(&kmaplock); + k->next = kmapfree; + kmapfree = k; +//nfree(); + unlock(&kmaplock); + } + splx(s); +} + +void +kfault(Ureg *ur) /* called from trap() */ +{ + ulong index, addr; + KMap *k, *f; + + addr = ur->badvaddr; + index = (addr & ~KSEGM) >> KMAPSHIFT; + if(index >= KPTESIZE) + panic("kmapfault: %#lux", addr); + + k = &kpte[index]; + if(k->virt == 0) + panic("kmapfault: unmapped %#lux", addr); + + for(f = m->kactive; f; f = f->konmach[m->machno]) + if(f == k) + break; + if(f == 0) { + incref(k); + k->konmach[m->machno] = m->kactive; + m->kactive = k; + } + putktlb(k); +} + +void +kmapinval(void) +{ + int mno, i, curpid; + KMap *k, *next; + uchar *ktlbx; + + if(m->machno < nelem(ktime)) + ktime[m->machno] = MACHP(0)->ticks; + if(m->kactive == 0) + return; + + curpid = PTEPID(TLBPID(tlbvirt())); + ktlbx = m->ktlbx; + for(i = 0; i < NTLB; i++, ktlbx++){ + if(*ktlbx == 0) + continue; + TLBINVAL(i, curpid); + *ktlbx = 0; + } + + mno = m->machno; + for(k = m->kactive; k; k = next) { + next = k->konmach[mno]; + kunmap(k); + } + + m->kactive = 0; + m->ktlbnext = TLBOFF; +} + +struct +{ + ulong va; + ulong pl; + ulong ph; +} wired[NWTLB+1]; /* +1 to avoid zero size if NWTLB==0 */ +// = { +// PCIMEM, +// (PCIMEM>>6) | PTEUNCACHED|PTEGLOBL|PTEWRITE|PTEVALID, +// ((PCIMEM>>6) | PTEUNCACHED|PTEGLOBL|PTEWRITE|PTEVALID)+(1<<(PGSHIFT-6)), +//}; + +/* + * allocate a virtual address corresponding to physical addr and map them. + * run on cpu 0. + */ +ulong +wiredpte(vlong addr) +{ + int i; + ulong va; + + for(i = 0; i < NWTLB; i++) + if(wired[i].va == 0) + break; + if(i >= NWTLB) + panic("wiredpte: not enough wired TLB entries"); + + va = WIREDADDR + i*256*MB; + wired[i].va = va; + wired[i].pl = (addr >> 6) | PTEUNCACHED|PTEGLOBL|PTEWRITE|PTEVALID; + wired[i].ph = wired[i].pl + (1<<(PGSHIFT-6)); + + puttlbx(i+WTLBOFF, va, wired[i].pl, wired[i].ph, PGSZ256M); + return va; +} + +void +machwire(void) +{ + int i; + + if(m->machno == 0) + return; + for(i = 0; i < NWTLB; i++) + if(wired[i].va) + puttlbx(i+WTLBOFF, wired[i].va, wired[i].pl, + wired[i].ph, PGSZ256M); +} + +void +mmuswitch(Proc *p) +{ + int tp; + static char lasttext[32]; + + if(Debugswitch && !p->kp){ + if(strncmp(lasttext, p->text, sizeof lasttext) != 0) + iprint("[%s]", p->text); + strncpy(lasttext, p->text, sizeof lasttext); + } + + if(p->newtlb) { + memset(p->pidonmach, 0, sizeof p->pidonmach); + p->newtlb = 0; + } + tp = p->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(p); + puttlbx(0, KZERO|PTEPID(tp), 0, 0, PGSZ); +} + +void +mmurelease(Proc *p) +{ + memset(p->pidonmach, 0, sizeof p->pidonmach); +} + +/* + * Process must be splhi + */ +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] == 0) + break; + } + + if(h[i]) + purgetlb(i); + if(h[i] != 0) + panic("newtlb"); + + m->pidproc[i] = p; + p->pidonmach[m->machno] = i; + m->lastpid = i; + + return i; +} + +void +putmmu(ulong tlbvirt, ulong tlbphys, Page *pg) +{ + short tp; + char *ctl; + Softtlb *entry; + int s; + + s = splhi(); + tp = up->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(up); + + tlbvirt |= PTEPID(tp); + if((tlbphys & PTEALGMASK) != PTEUNCACHED) { + tlbphys &= ~PTEALGMASK; + tlbphys |= PTECACHABILITY; + } + + entry = putstlb(tlbvirt, tlbphys); + puttlb(entry->virt, entry->phys0, entry->phys1); + + ctl = &pg->cachectl[m->machno]; + switch(*ctl) { + case PG_TXTFLUSH: + icflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_DATFLUSH: + dcflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_NEWCOL: + cleancache(); /* Too expensive */ + *ctl = PG_NOFLUSH; + break; + } + splx(s); +} + +void +purgetlb(int pid) +{ + int i, mno; + Proc *sp, **pidproc; + Softtlb *entry, *etab; + + m->tlbpurge++; + + /* + * find all pid entries that are no longer used by processes + */ + mno = m->machno; + pidproc = m->pidproc; + for(i=1; ipidonmach[mno] != i) + pidproc[i] = 0; + } + + /* + * shoot down the one we want + */ + sp = pidproc[pid]; + if(sp != 0) + sp->pidonmach[mno] = 0; + pidproc[pid] = 0; + + /* + * clean out all dead pids from the stlb; + */ + entry = m->stb; + for(etab = &entry[STLBSIZE]; entry < etab; entry++) + if(pidproc[TLBPID(entry->virt)] == 0) + entry->virt = 0; + + /* + * clean up the hardware + */ + for(i=tlbroff; inewtlb = 1; + mmuswitch(up); + splx(s); +} + +/* tlbvirt also has TLBPID() in its low byte as the asid */ +Softtlb* +putstlb(ulong tlbvirt, ulong tlbphys) +{ + int odd; + Softtlb *entry; + + /* identical calculation in l.s/utlbmiss */ + entry = &m->stb[stlbhash(tlbvirt)]; + odd = tlbvirt & BY2PG; /* even/odd bit */ + tlbvirt &= ~BY2PG; /* zero even/odd bit */ + if(entry->virt != tlbvirt) { /* not my entry? overwrite it */ + if(entry->virt != 0) { + m->hashcoll++; + if (Debughash) + iprint("putstlb: hash collision: %#lx old virt " + "%#lux new virt %#lux page %#lux\n", + entry - m->stb, entry->virt, tlbvirt, + tlbvirt >> (PGSHIFT+1)); + } + entry->virt = tlbvirt; + entry->phys0 = 0; + entry->phys1 = 0; + } + + if(odd) + entry->phys1 = tlbphys; + else + entry->phys0 = tlbphys; + + if(entry->phys0 == 0 && entry->phys1 == 0) + entry->virt = 0; + + return entry; +} + +void +checkmmu(ulong, ulong) +{ +} + +void +countpagerefs(ulong*, int) +{ +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +ulong +cankaddr(ulong pa) +{ + if(pa >= KZERO || pa >= MEMSIZE) + return 0; + return MEMSIZE - pa; +} --- /sys/src/9/rb/notes/9rb.ms Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/notes/9rb.ms Wed Jul 24 00:25:12 2013 @@ -0,0 +1,189 @@ +.FP palatino +.TM +.TL +Plan 9 on the Mikrotik RB450G Routerboard +.AU +Geoff Collyer +.AI +.MH +.NH 1 +Motivation +.LP +I ported Plan 9 to the Routerboard mainly to verify +that Plan 9's MIPS-related code +(compiler, assembler, loader, +.CW libmach , +etc.) was still in working order and would +work on newer machines than the 1993-era ones that we last owned +(MIPS Magnum, SGI Challenge, Carrera and the like). +The verdict is that, +with a few surprising exceptions, the code still works on newish machines +(the MIPS 24K CPU in the Routerboard dates to about 2003 originally; +this revision is from about 2005). +So we now have a +machine on which to test MIPS executables. +.LP +The other reason I did the port was +as an incremental step toward +running Plan 9 on a MIPS64 machine (e.g., the dual-core, dual-issue +Cavium CN5020 in the Ubiquiti Edgerouter Lite 3). +.NH 1 +The new MIPS world +.LP +These newer MIPS systems are aimed at embedded applications, so they +typically lack FPUs and may also lack L2 caches or have small TLBs; +the MIPS 24K in the Atheros 7161 SoC lacks FPU and L2 cache, and has a +16-entry TLB. +It is a MIPS32R2 architecture system and lacks the 64-bit instructions +of the R4000. +These new MIPS systems are still big-endian, +so provide a useful test case to expose byte-ordering bugs. +.NH 1 +Plan 9 changes and additions +.NH 2 +CPU Bug Workarounds +.LP +The Linux MIPS people cite MIPS 24K erratum 48: +3 consecutive stores lose data. +MIPS only distribute their errata lists under NDA and to their +corporate partners, so we have only the Linux report to go on. +The fix requires +.I both +write-through data cache and +no more than two consecutive single-word stores in all executables. +I have made a crude optional change to +.I vl +to generate a NOP before every third consecutive store. +The fix could be better, in particular the technique for +keeping stores out of branch delay slots. +.NH 2 +Driver for Undocumented Ethernet Controller +.LP +The FreeBSD Atheros +.I arge +driver +(in +.CW /usr/src/sys/mips/atheros ) +provided inspiration for our Gigabit Ethernet driver, since the +hardware is otherwise largely undocumented. +I haven't got the second +Ethernet controller entirely working yet; +it's perhaps complicated by having a switch attached to it (the Atheros 8316). +At minimum, it probably needs MII or PHY initialisation. +.NH 2 +Floating-point Emulation +.LP +Floating-point emulation works but is +.I very +slow: +.I astro +takes about 8 seconds. +I added an +.CW fpemudebug +command to +.CW /dev/archctl ; +it +takes a number as argument corresponding to the +.CW Dbg* +bits in +.CW fpimips.c , +but requires the kernel to be compiled with +.CW FPEMUDEBUG +defined. +.NH 3 +\&... in Locking Code +.LP +The big surprises included that +.CW /sys/src/libc/mips/lock.c +read +.CW FCR0 +to +choose the locking style. +That's been broken out into +.CW c_fcr0.s +so that we can change it, but the kernel also emulates the +.CW MOVW +.CW FCR0,R1 +(and via a fast code path), to keep alive the possibility of running +old binaries from the dump. +.NH 2 +No 64-bit Instructions +.LP +The other big surprise was that +.CW /sys/src/libmp/mips/mpdigdiv.s +used 64-bit instructions (SLLV, SRLV, ADDVU, DIVVU). +For now I've resolved the problem by pushing it into a +subdirectory (\c +.CW r4k ) +and editing the +.CW mkfile s +to use the +.CW port +version +(and similarly in APE). +.br +.ne 8 +.NH 2 +Page Size vs TLB Faults +.LP +I started out with a 4K page size and reduced the number of TLB +entries reserved for the kernel to 2, leaving 14 for user programs, +but +.CW /dev/sysstat +was reporting 6 times as many TLB faults as page +faults, and the number increased at a furious rate. +.LP +So I switched to +a 16K page size, adjusted +.CW vl +.CW -H2 +accordingly and recompiled the +.CW /mips +world. +This reduced the TLB faults to just 10% more than the number of page faults. +(That number is now around 15% more, due to a better soft-TLB hash function +that makes the soft TLB more effective.) +16K pages also produce consecutive (even recursive) page faults +for the same address at the same PC +and the system runs at about 10% of its normal speed, +so 4K pages are currently the only sensible choice; +we'll just live with the absurdly-high number of TLB faults +(around 20k–30k per second). +It probably doesn't help that one 16K page is half of the L1 data cache +and one quarter of the L1 instruction cache. +.LP +Page size is controlled by +.CW BIGPAGES +in +.CW mem.h . +.NH 3 +Combined TLB Pool +.LP +I also changed +.CW mmu.c +to collapse the separate kernel and user TLB pools into one, +once user processes start running, +but that only helps to reduce TLB faults a little. +. +.br +.ne 8 +. +.NH 1 +Remaining Problems +.LP +Interrupt-driven UART output isn't quite right. +It can get stuck and then input makes it resume. +The UART is apparently connected via the APB and requires +interrupt unmasking in the APB (which we now do). +There's some kludgey stuff in +.CW uarti8250.c +that makes output work most of the time +(characters do sometimes get dropped). +.LP +The Ethernet driver currently does not +dig out the MAC addresses from the hardware, +so you'll need to edit the +.CW rb +configuration file for each Routerboard; the format should be obvious. +I don't have the stomach to dig the MAC address out of the hardware +via SPI or whatever vile interface it requires. --- /sys/src/9/rb/notes/erratum48 Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/notes/erratum48 Tue Jun 18 06:27:29 2013 @@ -0,0 +1,36 @@ +/* + * 24K Erratum 48: Lost Data on Stores During Refill. + + Problem: The FSB (fetch store buffer) acts as an intermediate buffer + for the data cache refills and store data. The following describes + the scenario where the store data could be lost. + + * A data cache miss, due to either a load or a store, causing fill + data to be supplied by the memory subsystem + * The first three doublewords of fill data are returned and written + into the cache + * A sequence of four stores occurs in consecutive cycles around the + final doubleword of the fill: + * Store A + * Store B + * Store C + * Zero, One or more instructions + * Store D + + The four stores A-D must be to different doublewords of the line that + is being filled. The fourth instruction in the sequence above permits + the fill of the final doubleword to be transferred from the FSB into + the cache. In the sequence above, the stores may be either integer + (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2, + swxc1, sdxc1, suxc1) stores, as long as the four stores are to + different doublewords on the line. If the floating point unit is + running in 1:2 mode, it is not possible to create the sequence above + using only floating point store instructions. In this case, the cache + line being filled is incorrectly marked invalid, thereby losing the + data from any store to the line that occurs between the original miss + and the completion of the five cycle sequence shown above. + + * Run the data cache in write-through mode. + * Insert a non-store instruction between + Store A and Store B or Store B and Store C. + */ --- /sys/src/9/rb/notes/pci.intr Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/notes/pci.intr Fri Jun 21 00:32:31 2013 @@ -0,0 +1,12 @@ + /* + * much of this is inspired by the freebsd driver, + * but is hard to understand due to lack of documentation. + * + * only pci irqs 0-2 are valid. + * 0 seems to yield INTR6. + * pci irqs 1-2 yield spurious ether intrs. + */ + i = 0; + *Pciintrsts = 0; + *Pciintrmask = 1 << i; + junk = *Pciintrmask; /* flush the write */ --- /sys/src/9/rb/rb Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/rb Thu Jul 4 01:32:59 2013 @@ -0,0 +1,58 @@ +# routerboard rb450g +dev + root + cons + arch + env + pipe + proc + mnt + srv + dup +# rtc + ssl + tls + cap + kprof + fs + + ether netif + ip arp chandial inferno ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum + + uart +link + loopbackmedium + ethermedium + +misc + uarti8250 +# emulated fp + fpi + fpimips + fpimem + ethermii + +ip + tcp + udp + ipifc + icmp + icmp6 + gre + ipmux + esp + +port + int cpuserver = 1; + uchar arge0mac[] = { 0xd4, 0xca, 0x6d, 0x7d, 0xf1, 0xce, }; + uchar arge1mac[] = { 0xd4, 0xca, 0x6d, 0x7d, 0xf1, 0xcf, }; +}; + +boot cpu + tcp + +bootdir + boot$CONF.out boot + /mips/bin/ip/ipconfig + /mips/bin/auth/factotum + nvram --- /sys/src/9/rb/rebootcode.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/rebootcode.c Thu Jul 18 19:57:31 2013 @@ -0,0 +1,81 @@ +/* + * mips reboot trampoline code + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define csr8r(r) (((ulong *)PHYSCONS)[r]) +#define csr8o(r, v) (((ulong *)PHYSCONS)[r] = (v)) + +enum { /* i8250 registers */ + Thr = 0, /* Transmitter Holding (WO) */ + Lsr = 5, /* Line Status */ +}; +enum { /* Lsr */ + Thre = 0x20, /* Thr Empty */ +}; + +void putc(int); + +/* + * Copy the new kernel to its correct location in physical memory, + * flush caches, ignore TLBs (we're in KSEG0 space), and jump to + * the start of the kernel. + */ +void +main(ulong aentry, ulong acode, ulong asize) +{ + void (*kernel)(void); + static ulong entry, code, size; + + putc('B'); putc('o'); putc('o'); putc('t'); + /* copy args to heap before moving stack to before a.out header */ + entry = aentry; + code = acode; + size = asize; + setsp(entry-0x20-4); + + memmove((void *)entry, (void *)code, size); + + cleancache(); + coherence(); + + /* + * jump to kernel entry point. + */ + putc(' '); + kernel = (void*)entry; + (*kernel)(); /* off we go - never to return */ + + putc('?'); + putc('!'); + for(;;) + ; +} + +void +putc(int c) +{ + int i; + + for(i = 0; !(csr8r(Lsr) & Thre) && i < 1000000; i++) + ; + csr8o(Thr, (uchar)c); + for(i = 0; !(csr8r(Lsr) & Thre) && i < 1000000; i++) + ; +} + +long +syscall(Ureg*) +{ + return -1; +} + +void +trap(Ureg *) +{ +} --- /sys/src/9/rb/trap.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/trap.c Tue Jul 23 03:51:54 2013 @@ -0,0 +1,1075 @@ +/* + * traps, exceptions, faults and interrupts on ar7161 + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include +#include "../port/error.h" + +#define setstatus(v) /* experiment: delete this to enable recursive traps */ + +typedef struct Handler Handler; + +struct Handler { + void (*handler)(void *); + void *arg; + Handler *next; /* at this interrupt level */ + ulong intrs; +}; + +ulong offintrs; +ulong intrcauses[ILmax+1]; + +int intr(Ureg*); +void kernfault(Ureg*, int); +void noted(Ureg*, Ureg**, ulong); +void rfnote(Ureg**); + +char *excname[] = +{ + "trap: external interrupt", + "trap: TLB modification (store to unwritable)", + "trap: TLB miss (load or fetch)", + "trap: TLB miss (store)", + "trap: address error (load or fetch)", + "trap: address error (store)", + "trap: bus error (fetch)", + "trap: bus error (data load or store)", + "trap: system call", + "breakpoint", + "trap: reserved instruction", + "trap: coprocessor unusable", + "trap: arithmetic overflow", + "trap: TRAP exception", + "trap: VCE (instruction)", + "trap: floating-point exception", + "trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */ + "trap: corextend unusable", + "trap: precise coprocessor 2 exception", + "trap: TLB read-inhibit", + "trap: TLB execute-inhibit", + "trap: undefined 21", + "trap: undefined 22", + "trap: WATCH exception", + "trap: machine checkcore", + "trap: undefined 25", + "trap: undefined 26", + "trap: undefined 27", + "trap: undefined 28", + "trap: undefined 29", + "trap: cache error", + "trap: VCE (data)", +}; + +char *fpcause[] = +{ + "inexact operation", + "underflow", + "overflow", + "division by zero", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*, uint); +#define FPEXPMASK (0x3f<<12) /* Floating exception bits in fcr31 */ + +struct { + char *name; + uint off; +} regname[] = { + "STATUS", Ureg_status, + "PC", Ureg_pc, + "SP", Ureg_sp, + "CAUSE",Ureg_cause, + "BADADDR", Ureg_badvaddr, + "TLBVIRT", Ureg_tlbvirt, + "HI", Ureg_hi, + "LO", Ureg_lo, + "R31", Ureg_r31, + "R30", Ureg_r30, + "R28", Ureg_r28, + "R27", Ureg_r27, + "R26", Ureg_r26, + "R25", Ureg_r25, + "R24", Ureg_r24, + "R23", Ureg_r23, + "R22", Ureg_r22, + "R21", Ureg_r21, + "R20", Ureg_r20, + "R19", Ureg_r19, + "R18", Ureg_r18, + "R17", Ureg_r17, + "R16", Ureg_r16, + "R15", Ureg_r15, + "R14", Ureg_r14, + "R13", Ureg_r13, + "R12", Ureg_r12, + "R11", Ureg_r11, + "R10", Ureg_r10, + "R9", Ureg_r9, + "R8", Ureg_r8, + "R7", Ureg_r7, + "R6", Ureg_r6, + "R5", Ureg_r5, + "R4", Ureg_r4, + "R3", Ureg_r3, + "R2", Ureg_r2, + "R1", Ureg_r1, +}; + +static Lock intrlock; +static Handler handlers[ILmax+1]; + +static char * +ptlb(ulong phys) +{ + static char buf[4][32]; + static int k; + char *p; + + k = (k+1)&3; + p = buf[k]; + p += snprint(p, sizeof buf[k] - (p - buf[k]), "(%#lux %lud ", + (phys<<6) & ~(BY2PG-1), (phys>>3)&7); + if(phys & 4) + *p++ = 'd'; + if(phys & 2) + *p++ = 'v'; + if(phys & 1) + *p++ = 'g'; + *p++ = ')'; + *p = 0; + return buf[k]; +} + +static void +kpteprint(Ureg *ur) +{ + ulong i, tlbstuff[3]; + KMap *k; + + i = (ur->badvaddr & ~(2*BY2PG-1)) | TLBPID(tlbvirt()); + print("tlbvirt=%#lux\n", i); + i = gettlbp(i, tlbstuff); + print("i=%lud v=%#lux p0=%s p1=%s\n", + i, tlbstuff[0], ptlb(tlbstuff[1]), ptlb(tlbstuff[2])); + + i = (ur->badvaddr & ~KMAPADDR)>>15; + if(i > KPTESIZE){ + print("kpte index = %lud ?\n", i); + return; + } + k = &kpte[i]; + print("i=%lud, &k=%#p, k={v=%#lux, p0=%s, p1=%s, pg=%#p}\n", + i, k, k->virt, ptlb(k->phys0), ptlb(k->phys1), k->pg); + print("pg={pa=%#lux, va=%#lux}\n", k->pg->pa, k->pg->va); +} + +void +kvce(Ureg *ur, int ecode) +{ + char c; + Pte **p; + Page **pg; + Segment *s; + ulong addr, soff; + + c = 'D'; + if(ecode == CVCEI) + c = 'I'; + print("Trap: VCE%c: addr=%#lux\n", c, ur->badvaddr); + if((ur->badvaddr & KSEGM) == KSEG3) { + kpteprint(ur); + return; + } + if(up && !(ur->badvaddr & KSEGM)) { + addr = ur->badvaddr; + s = seg(up, addr, 0); + if(s == 0){ + print("kvce: no seg for %#lux\n", addr); + for(;;) + ; + } + addr &= ~(BY2PG-1); + soff = addr - s->base; + p = &s->map[soff/PTEMAPMEM]; + if(*p){ + pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG]; + if(*pg) + print("kvce: pa=%#lux, va=%#lux\n", + (*pg)->pa, (*pg)->va); + else + print("kvce: no *pg\n"); + }else + print("kvce: no *p\n"); + } +} + +/* prepare to go to user space */ +void +kexit(Ureg*) +{ + Tos *tos; + + /* precise time accounting, kernel exit */ + tos = (Tos*)(USTKTOP-sizeof(Tos)); + tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry; + tos->pcycles = up->pcycles; + tos->pid = up->pid; +} + +void +trap(Ureg *ur) +{ + int ecode, clockintr, user, cop, x, fpchk; + ulong fpfcr31; + char buf[2*ERRMAX], buf1[ERRMAX], *fpexcep; + static int dumps; + + ecode = (ur->cause>>2)&EXCMASK; + user = ur->status&KUSER; + if (ur->cause & TS) + panic("trap: tlb shutdown"); + + fpchk = 0; + if(user){ + up->dbgreg = ur; + cycles(&up->kentry); + /* no fpu, so no fp state to save */ + } + + if (up && (char *)(ur) - up->kstack < 1024 && dumps++ == 0) { + iprint("trap: proc %ld kernel stack getting full\n", up->pid); + dumpregs(ur); + dumpstack(); + } + if (up == nil && + (char *)(ur) - (char *)m->stack < 1024 && dumps++ == 0) { + iprint("trap: cpu%d kernel stack getting full\n", m->machno); + dumpregs(ur); + dumpstack(); + } + +// splhi(); /* for the experiment: make it explicit */ + /* clear EXL in status */ + setstatus(getstatus() & ~EXL); + + clockintr = 0; + switch(ecode){ + case CINT: + clockintr = intr(ur); + break; + + case CFPE: + panic("FP exception but no FPU"); +#ifdef OLD_MIPS_EXAMPLE + fptrap(ur); + clrfpintr(); + fpchk = 1; +#endif + break; + + case CTLBM: + case CTLBL: + case CTLBS: + /* user tlb entries assumed not overwritten during startup */ + if(up == 0) + kernfault(ur, ecode); + + if(!user && (ur->badvaddr & KSEGM) == KSEG3) { + kfault(ur); + break; + } + x = up->insyscall; + up->insyscall = 1; + spllo(); + faultmips(ur, user, ecode); + up->insyscall = x; + break; + + case CVCEI: + case CVCED: + kvce(ur, ecode); + goto Default; + + case CWATCH: + if(!user) + panic("watchpoint trap from kernel mode pc=%#p", + ur->pc); + fpwatch(ur); + break; + + case CCPU: + cop = (ur->cause>>28)&3; + if(user && up && cop == 1) { + if(up->fpstate & FPillegal) { + /* someone used floating point in a note handler */ + postnote(up, 1, + "sys: floating point in note handler", + NDebug); + break; + } + /* no fpu, so we can only emulate fp ins'ns */ + if (fpuemu(ur) < 0) + postnote(up, 1, + "sys: fp instruction not emulated", + NDebug); + else + fpchk = 1; + break; + } + /* Fallthrough */ + + Default: + default: + if(user) { + spllo(); + snprint(buf, sizeof buf, "sys: %s", excname[ecode]); + postnote(up, 1, buf, NDebug); + break; + } + if (ecode == CADREL || ecode == CADRES) + iprint("kernel addr exception for va %#p pid %#ld %s\n", + ur->badvaddr, (up? up->pid: 0), + (up? up->text: "")); + print("cpu%d: kernel %s pc=%#lux\n", + m->machno, excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + if(fpchk) { + fpfcr31 = up->fpsave.fpstatus; + if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) { + spllo(); + fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1); + snprint(buf, sizeof buf, "sys: fp: %s", fpexcep); + postnote(up, 1, buf, NDebug); + } + } + + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && clockintr){ + sched(); + splhi(); + } + + if(user){ + notify(ur); + /* no fpu, so no fp state to restore */ + kexit(ur); + } + + /* restore EXL in status */ + setstatus(getstatus() | EXL); +} + +/* periodically zero all the interrupt counts */ +static void +resetcounts(void) +{ + int i; + Handler *hp; + + ilock(&intrlock); + for (i = 0; i < nelem(handlers); i++) + for (hp = &handlers[i]; hp != nil; hp = hp->next) + hp->intrs = 0; + iunlock(&intrlock); +} + +/* + * set handlers + */ +void +intrenable(int irq, void (*h)(void *), void *arg) +{ + Handler *hp; + static int resetclock; + + if (h == nil) + panic("intrenable: nil handler intr %d", irq); + if(irq < ILmin || irq >= nelem(handlers)) + panic("intrenable: bad handler intr %d %#p", irq, h); + + hp = &handlers[irq]; + ilock(&intrlock); + if (hp->handler != nil) { /* occupied? */ + /* add a new one at the end of the chain */ + for (; hp->next != nil; hp = hp->next) + ; + hp->next = smalloc(sizeof *hp); + hp = hp->next; + hp->next = nil; + } + hp->handler = h; + hp->arg = arg; + iunlock(&intrlock); + + if (irq == ILduart0) { /* special apb sub-interrupt */ + *Apbintrsts = 0; + *Apbintrmask = 1 << Apbintruart; /* enable, actually */ + coherence(); + } + intron(1 << (ILshift + irq)); + if (!resetclock) { + resetclock = 1; + addclock0link(resetcounts, 100); + } +} + +void +intrshutdown(void) +{ + introff(INTMASK); +} + +static void +jabberoff(Ureg *ur, int irq, ulong bit) +{ + introff(bit); /* interrupt off now ... */ + if (ur) + ur->status &= ~bit; /* ... and upon return */ + offintrs |= bit; + iprint("irq %d jabbering; shutting it down\n", irq); +} + +ulong +pollall(Ureg *ur, ulong cause) /* must be called splhi */ +{ + int i, intrs, sts; + ulong bit; + Handler *hp; + + /* exclude clock and sw intrs */ + intrs = cause & (INTR6|INTR5|INTR4|INTR3|INTR2) & getstatus(); + if(intrs == 0) + return cause; + + ilock(&intrlock); + for (i = ILmax; i >= ILmin; i--) { + bit = 1 << (ILshift + i); + if (!(intrs & bit)) + continue; + intrcauses[i]++; + for (hp = &handlers[i]; hp != nil; hp = hp->next) + if (hp->handler) { + if (i == ILduart0) { + sts = *Apbintrsts; + if((sts & (1 << Apbintruart)) == 0) + continue; + /* don't need to ack apb sub-intr */ + // *Apbintrsts &= ~(1 << Apbintruart); + } + (*hp->handler)(hp->arg); + splhi(); + if (++hp->intrs > 25000) { + jabberoff(ur, i, bit); + intrs &= ~bit; + hp->intrs = 0; + } + } else if (ur) + iprint("no handler for interrupt %d\n", i); + cause &= ~bit; + } + iunlock(&intrlock); + return cause; +} + +int +intr(Ureg *ur) +{ + int clockintr; + ulong cause; + + m->intr++; + clockintr = 0; + /* + * ignore interrupts that we have disabled, even if their cause bits + * are set. + */ + cause = ur->cause & ur->status & INTMASK; + cause &= ~(INTR1|INTR0); /* ignore sw interrupts */ + if (cause == 0) + print("spurious interrupt\n"); + if(cause & INTR7){ + clock(ur); + intrcauses[ILclock]++; + cause &= ~(1 << (ILclock + ILshift)); + clockintr = 1; + } + cause = pollall(ur, cause); + if(cause){ + print("intr: cause %#lux not handled\n", cause); + exit(1); + } + + /* preemptive scheduling */ + if(up && !clockintr) + preempted(); + /* if it was a clockintr, sched will be called at end of trap() */ + return clockintr; +} + +char* +fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + if(ur->cause & BD) /* branch delay */ + fppc += 4; + s = 0; + if(fcr31 & (1<<17)) + s = "unimplemented operation"; + else{ + fcr31 >>= 7; /* trap enable bits */ + fcr31 &= (fcr31>>5); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fcr31 & (1<badvaddr); + kpteprint(ur); + print("u=%#p status=%#lux pc=%#lux sp=%#lux\n", + up, ur->status, ur->pc, ur->sp); + delay(500); + panic("kfault"); +} + +static void +getpcsp(ulong *pc, ulong *sp) +{ + *pc = getcallerpc(&pc); + *sp = (ulong)&pc-4; +} + +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + memset(&ureg, 0, sizeof ureg); + getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp); + ureg.r31 = getcallerpc(&fn); + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong l, v, top, i; + extern ulong etext; + + if(up == 0) + return; + + print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", + ureg->pc, ureg->sp, ureg->r31); + top = (ulong)up->kstack + KSTACK; + i = 0; + for(l=ureg->sp; l < top; l += BY2WD) { + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)&etext) { + print("%.8lux=%.8lux ", l, v); + if((++i%4) == 0){ + print("\n"); + delay(200); + } + } + } + print("\n"); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +static ulong +R(Ureg *ur, int i) +{ + uchar *s; + + s = (uchar*)ur; + return *(ulong*)(s + regname[i].off - Uoffset); +} + +void +dumpregs(Ureg *ur) +{ + int i; + + if(up) + print("registers for %s %lud\n", up->text, up->pid); + else + print("registers for kernel\n"); + + for(i = 0; i < nelem(regname); i += 2) + print("%s\t%#.8lux\t%s\t%#.8lux\n", + regname[i].name, R(ur, i), + regname[i+1].name, R(ur, i+1)); +} + +int +notify(Ureg *ur) +{ + int l, s; + ulong sp; + Note *n; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + s = spllo(); + qlock(&up->debug); + up->fpstate |= FPillegal; + 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; + + seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#lux", ur->pc); + } + + if(n->flag != NUser && (up->notified || up->notify==0)) { + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splx(s); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + sp = ur->usp & ~(BY2V-1); + sp -= sizeof(Ureg); + + if(!okaddr((ulong)up->notify, BY2WD, 0) || + !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); /* push user regs */ + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + + sp -= BY2WD+ERRMAX; + memmove((char*)sp, up->note[0].msg, ERRMAX); /* push err string */ + + sp -= 3*BY2WD; + *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + /* + * arrange to resume at user's handler as if handler(ureg, errstr) + * were being called. + */ + ur->pc = (ulong)up->notify; + + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + +/* + * Check that status is OK to return from note. + */ +int +validstatus(ulong kstatus, ulong ustatus) +{ +// if((kstatus & (INTMASK|KX|SX|UX)) != (ustatus & (INTMASK|KX|SX|UX))) + if((kstatus & INTMASK) != (ustatus & INTMASK)) + return 0; + if((ustatus&(KSU|ERL|EXL|IE)) != (KUSER|EXL|IE)) + return 0; + if(ustatus & (0xFFFF0000&~CU1)) /* no CU3, CU2, CU0, RP, FR, RE, DS */ + return 0; + return 1; +} + +/* + * Return user to state before notify(); called from user's handler. + */ +void +noted(Ureg *kur, Ureg **urp, ulong arg0) +{ + Ureg *nur; + ulong oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + up->fpstate &= ~FPillegal; + + nur = up->ureg; + + oureg = (ulong)nur; + if((oureg & (BY2WD-1)) + || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad up->ureg in noted or call to noted() when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + if(!validstatus(kur->status, nur->status)) { + qunlock(&up->debug); + pprint("bad noted ureg status %#lux\n", nur->status); + pexit("Suicide", 0); + } + + memmove(*urp, up->ureg, sizeof(Ureg)); + switch(arg0) { + case NCONT: + case NRSTR: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); + qunlock(&up->debug); + splhi(); + /* + * the old challenge and carrera ports called rfnote here, + * but newer ports do not, and notes seem to work only + * without this call. + */ + // rfnote(urp); /* return from note with SP=urp */ + break; + + case NSAVE: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRMAX; + + splhi(); + (*urp)->sp = sp; + ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + (*urp)->r1 = oureg; /* arg 1 is ureg* */ + + // rfnote(urp); /* return from note with SP=urp */ + break; + + default: + pprint("unknown noted arg %#lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } +} + +#include "../port/systab.h" + +static Ref goodsyscall; +static Ref totalsyscall; + +static void +sctracesetup(ulong scallnr, ulong sp, uintptr pc, vlong *startnsp) +{ + if(up->procctl == Proc_tracesyscall){ + /* + * Redundant validaddr. Do we care? + * Tracing syscalls is not exactly a fast path... + * Beware, validaddr currently does a pexit rather + * than an error if there's a problem; that might + * change in the future. + */ + if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + syscallfmt(scallnr, pc, (va_list)(sp+BY2WD)); + up->procctl = Proc_stopme; + procctl(up); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + *startnsp = todget(nil); + } +} + +static void +sctracefinish(ulong scallnr, ulong sp, int ret, vlong startns) +{ + int s; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, + startns, todget(nil)); + s = splhi(); + procctl(up); + splx(s); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + } +} + +/* + * called directly from assembler, not via trap() + */ +long +syscall(Ureg *aur) +{ + int i; + volatile long ret; + ulong sp, scallnr; + vlong startns; + char *e; + Ureg *ur; + + cycles(&up->kentry); + + incref(&totalsyscall); + m->syscall++; + up->insyscall = 1; + ur = aur; + up->pc = ur->pc; + up->dbgreg = aur; + ur->cause = 16<<2; /* for debugging: system call is undef 16 */ + + scallnr = ur->r1; + up->scallnr = ur->r1; + sp = ur->sp; + sctracesetup(scallnr, sp, ur->pc, &startns); + + /* clear EXL in status */ + setstatus(getstatus() & ~EXL); + + /* no fpu, so no fp state to save */ + spllo(); + + up->nerrlab = 0; + ret = -1; + if(!waserror()) { + if(scallnr >= nsyscall || systab[scallnr] == 0){ + pprint("bad sys call number %ld pc %#lux\n", + scallnr, ur->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp & (BY2WD-1)){ + pprint("odd sp in sys call pc %#lux sp %#lux\n", + ur->pc, ur->sp); + postnote(up, 1, "sys: odd stack", 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(0 && up->pid == 1) + print("[%lud %s] syscall %lud: %s\n", + up->pid, up->text, scallnr, up->errstr); + } + if(up->nerrlab){ + print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab); + for(i = 0; i < NERR; i++) + print("sp=%#lux pc=%#lux\n", + up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + sctracefinish(scallnr, sp, ret, startns); + + ur->pc += 4; + ur->r1 = ret; + + up->psstate = 0; + up->insyscall = 0; + + if(scallnr == NOTED) { /* ugly hack */ + noted(ur, &aur, *(ulong*)(sp+BY2WD)); /* may return */ + ret = ur->r1; + } + incref(&goodsyscall); + splhi(); + if(scallnr!=RFORK && (up->procctl || up->nnote)){ + ur->r1 = ret; /* load up for noted() above */ + notify(ur); + } + /* if we delayed sched because we held a lock, sched now */ + if(up->delaysched) + sched(); + kexit(ur); + + /* restore EXL in status */ + setstatus(getstatus() | EXL); + + return ret; +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (ulong)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + + cur->pc += 4; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +static +void +linkproc(void) +{ + spllo(); + up->kpfun(up->kparg); + pexit("kproc exiting", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +/* set up user registers before return from exec() */ +long +execregs(ulong entry, ulong ssize, ulong nargs) +{ + Ureg *ur; + ulong *sp; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ur = (Ureg*)up->dbgreg; + ur->usp = (ulong)sp; + ur->pc = entry - 4; /* syscall advances it */ + up->fpsave.fpstatus = initfp.fpstatus; + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ +} + +ulong +userpc(void) +{ + Ureg *ur; + + ur = (Ureg*)up->dbgreg; + return ur->pc; +} + +/* + * This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* + * Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg *xp, Proc *p) +{ + xp->pc = p->sched.pc; + xp->sp = p->sched.sp; + xp->r24 = (ulong)p; /* up */ + xp->r31 = (ulong)sched; +} + +ulong +dbgpc(Proc *p) +{ + Ureg *ur; + + ur = p->dbgreg; + if(ur == 0) + return 0; + + return ur->pc; +} --- /sys/src/9/rb/uarti8250.c Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/uarti8250.c Tue Jul 23 02:25:37 2013 @@ -0,0 +1,854 @@ +/* + * 8250-like UART + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + CONSOLE = 0, /* first uart */ + Pollstuckoutput = 1, +}; + +enum { /* registers */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 0, /* Transmitter Holding (WO) */ + Ier = 1, /* Interrupt Enable */ + Iir = 2, /* Interrupt Identification (RO) */ + Fcr = 2, /* FIFO Control (WO) */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + + Scr = 7, /* Scratch Pad */ +// Mdr = 8, /* Mode Def'n (omap rw) */ + Usr = 31, /* Uart Status Register */ + Stickyend, + + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +enum { /* Usr */ + Busy = 0x01, +}; + +enum { /* Ier */ + Erda = 0x01, /* Enable Received Data Available */ + Ethre = 0x02, /* Enable Thr Empty */ + Erls = 0x04, /* Enable Receiver Line Status */ + Ems = 0x08, /* Enable Modem Status */ +}; + +enum { /* Iir */ + Ims = 0x00, /* Ms interrupt */ + Ip = 0x01, /* Interrupt Pending (not) */ + Ithre = 0x02, /* Thr Empty */ + Irda = 0x04, /* Received Data Available */ + Irls = 0x06, /* Receiver Line Status */ + Ictoi = 0x0C, /* Character Time-out Indication */ + IirMASK = 0x3F, + Ifena = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ +// FIFOdma = 0x08, + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* no longer in use */ + Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Transmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Carrier Detect */ +}; + +enum { /* Mdr */ + Modemask = 7, + Modeuart = 0, +}; + + +typedef struct Ctlr { + u32int* io; + int irq; + int tbdf; + int iena; + int poll; + + uchar sticky[Stickyend]; + + Lock; + int hasfifo; + int checkfifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[] = { +{ .io = (u32int*)PHYSCONS, + .irq = ILduart0, + .tbdf = -1, + .poll = 0, }, +}; + +static Uart i8250uart[] = { +{ .regs = &i8250ctlr[0], + .name = "cons", + .freq = 3686000, /* Not used, we use the global i8250freq */ + .phys = &i8250physuart, + .console= 1, + .next = nil, }, +}; + +#define csr8r(c, r) ((c)->io[r]) +#define csr8w(c, r, v) ((c)->io[r] = (uchar)((c)->sticky[r] | (v))) +#define csr8o(c, r, v) ((c)->io[r] = (uchar)(v)) + +static long +i8250status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = smalloc(READSTR); + mcr = ctlr->sticky[Mcr]; + msr = csr8r(ctlr, Msr); + ier = ctlr->sticky[Ier]; + lcr = ctlr->sticky[Lcr]; + 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, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & Ems) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +i8250fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + if(ctlr->hasfifo == 0) + return; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + * Some UARTs require FIFOena to be set before + * other bits can take effect, so set it twice. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +i8250dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Dtr; + else + ctlr->sticky[Mcr] &= ~Dtr; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250rts(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Rts; + else + ctlr->sticky[Mcr] &= ~Rts; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + ctlr->sticky[Ier] |= Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + ctlr->sticky[Ier] &= ~Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +i8250parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->parity = parity; + + return 0; +} + +static int +i8250stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~Stb; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->stop = stop; + + return 0; +} + +static int +i8250bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->bits = bits; + + return 0; +} + +static int +i8250baud(Uart* uart, int baud) +{ +#ifdef notdef /* don't change the speed */ + ulong bgc; + Ctlr *ctlr; + extern int i8250freq; /* In the config file */ + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(i8250freq == 0 || baud <= 0) + return -1; + bgc = (i8250freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + while(csr8r(ctlr, Usr) & Busy) + delay(1); + csr8w(ctlr, Lcr, Dlab); /* begin kludge */ + csr8o(ctlr, Dlm, bgc>>8); + csr8o(ctlr, Dll, bgc); + csr8w(ctlr, Lcr, 0); +#endif + uart->baud = baud; + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + if (up == nil) + panic("i8250break: nil up"); + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, 0); +} + +static void +emptyoutstage(Uart *uart, int n) +{ + _uartputs((char *)uart->op, n); + uart->op = uart->oe = uart->ostage; +} + +static void +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(/* uart->cts == 0 || */ uart->blocked) + return; + + if(!normalprint) { /* early */ + if (uart->op < uart->oe) + emptyoutstage(uart, uart->oe - uart->op); + while ((i = uartstageoutput(uart)) > 0) + emptyoutstage(uart, i); + return; + } + + /* nothing more to send? then disable xmit intr */ + ctlr = uart->regs; + if (uart->op >= uart->oe && qlen(uart->oq) == 0 && + (1 || csr8r(ctlr, Lsr) & Temt)) { /* could try ignoring Temt */ + ctlr->sticky[Ier] &= ~Ethre; + csr8w(ctlr, Ier, 0); + return; + } + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chip's output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + csr8o(ctlr, Thr, *uart->op++); /* start tx */ + ctlr->sticky[Ier] |= Ethre; + csr8w(ctlr, Ier, 0); /* intr when done */ + } +} + +void +serialkick(void) +{ + uartkick(&i8250uart[CONSOLE]); +} + +static Lock i8250intrlock; + +static void +i8250interrupt(void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + ctlr = uart->regs; + ilock(&i8250intrlock); + + /* force Ethre on. don't know why this is needed, but it is. */ + ctlr->sticky[Ier] |= Ethre; + csr8w(ctlr, Ier, 0); + /* this is probably optional. maybe it helps fast input. */ + ctlr->sticky[Mcr] |= Ie; + csr8w(ctlr, Mcr, 0); + + for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ + switch(iir & IirMASK){ + case Ims: /* Ms interrupt */ + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } + break; + case Ithre: /* Thr Empty */ + uartkick(uart); + break; + case Irda: /* Received Data Available */ + case Irls: /* Receiver Line Status */ + case Ictoi: /* Character Time-out Indication */ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + while((lsr = csr8r(ctlr, Lsr)) & Dr){ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + break; + + default: + iprint("weird uart interrupt type %#2.2uX\n", iir); + break; + } + } + iunlock(&i8250intrlock); +} + +static void +i8250disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + ctlr->sticky[Ier] = 0; + csr8w(ctlr, Ier, 0); + + if(ctlr->iena != 0){ + /* bad idea if the IRQ is shared */ +// introff(1 << (ILshift + ctlr->irq)); + ctlr->iena = 0; + } +} + +static void +i8250clock(void) +{ + i8250interrupt(&i8250uart[CONSOLE]); +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ctlr->sticky[Lcr] = Wls8; /* no parity */ + csr8w(ctlr, Lcr, 0); + + /* + * Check if there is a FIFO. + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + * Also, reading the Iir outwith i8250interrupt() + * can be dangerous, but this should only happen + * once, before interrupts are enabled. + */ + ilock(ctlr); + if(!ctlr->checkfifo){ + /* + * Wait until the transmitter is really empty. + */ + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + csr8w(ctlr, Fcr, FIFOena); + if(csr8r(ctlr, Iir) & Ifena) + ctlr->hasfifo = 1; + csr8w(ctlr, Fcr, 0); + ctlr->checkfifo = 1; + } + iunlock(ctlr); + + /* + * 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. + */ + if(ie){ + if(ctlr->iena == 0 && !ctlr->poll){ + intrenable(ctlr->irq, i8250interrupt, uart); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, 0); + csr8w(ctlr, Mcr, 0); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * During startup, the i8259 interrupt controller is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. The workaround is to call the + * interrupt handler to clear any pending interrupt events. + * Note: this must be done after setting Ier. + */ + if(ie) { + i8250interrupt(uart); + /* + * force output to resume if stuck. shouldn't be needed. + */ + if (Pollstuckoutput) + addclock0link(i8250clock, 10); + } +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +static int +i8250getc(Uart* uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr) & Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +i8250putc(Uart* uart, int c) +{ + int i, s; + Ctlr *ctlr; + + if (!normalprint) { /* too early; use brute force */ + s = splhi(); + while (!(((ulong *)PHYSCONS)[Lsr] & Thre)) + ; + ((ulong *)PHYSCONS)[Thr] = (uchar)c; + splx(s); + return; + } + + ctlr = uart->regs; + s = splhi(); + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 200; i++) + delay(5); + csr8o(ctlr, Thr, c); + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 200; i++) + delay(5); + splx(s); +} + +void +serialputc(int c) +{ + i8250putc(&i8250uart[CONSOLE], c); +} + +void +serialputs(char* s, int n) +{ + _uartputs(s, n); +} + +#ifdef PLAN9K +static void +i8250poll(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->iena || !ctlr->poll) + return; + i8250interrupt(uart); +} +#endif + +PhysUart i8250physuart = { + .name = "i8250", + .pnp = i8250pnp, + .enable = i8250enable, + .disable = i8250disable, + .kick = i8250kick, + .dobreak = i8250break, + .baud = i8250baud, + .bits = i8250bits, + .stop = i8250stop, + .parity = i8250parity, + .modemctl = i8250modemctl, + .rts = i8250rts, + .dtr = i8250dtr, + .status = i8250status, + .fifo = i8250fifo, + .getc = i8250getc, + .putc = i8250putc, +#ifdef PLAN9K + .poll = i8250poll, +#endif +}; + +static void +i8250dumpregs(Ctlr* ctlr) +{ + int dlm, dll; + int _uartprint(char*, ...); + + csr8w(ctlr, Lcr, Dlab); + dlm = csr8r(ctlr, Dlm); + dll = csr8r(ctlr, Dll); + csr8w(ctlr, Lcr, 0); + + _uartprint("dlm %#ux dll %#ux\n", dlm, dll); +} + +Uart* uartenable(Uart *p); + +/* must call this from a process's context */ +int +i8250console(void) +{ + Uart *uart; + + if (up == nil) + return -1; /* too early */ + + uart = &i8250uart[CONSOLE]; + if(uartenable(uart) != nil && uart->console){ + kbdq = uart->iq; + assert(kbdq); + serialoq = uart->oq; + assert(serialoq); + uart->putc = kbdcr2nl; + uart->opens++; + consuart = uart; + /* up wasn't set when chandevreset ran, so enable it now */ + i8250disable(uart); + i8250enable(uart, 1); + } + uartctl(uart, "b115200 l8 pn m0 s1 i128 w100"); + return 0; +} + +void +_uartputs(char* s, int n) +{ + char *e; + + for(e = s+n; s < e; s++){ + if(*s == '\n') + i8250putc(&i8250uart[CONSOLE], '\r'); + i8250putc(&i8250uart[CONSOLE], *s); + } +} + +int +_uartprint(char* fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + _uartputs(buf, n); + + return n; +} + +void (*lprint)(char *, int) = _uartputs; --- /sys/src/9/rb/words Thu Jan 1 01:00:00 1970 +++ /sys/src/9/rb/words Wed Jul 24 00:14:11 2013 @@ -0,0 +1,73 @@ +mikrotik rb450g routerboard + +atheros ar7161 cpu: mips 24Kc v3.5 rev 0: 30 dec 2005, + big-endian, 32-bit only, 8- or 9-stage pipeline; 8kb cache + option, but no l2 support, no relocatable reset exception + vector, no UserLocal register, no wait with interrupts + disabled, no errata fixes. +no fpu +256mb dram +mmu 16 tlbs +l1 i-cache 4 ways, 512 sets, 32 bytes per line = 64K +l1 d-cache 4 ways, 256 sets, 32 bytes per line = 32k +config1: 0x9ee3519e +config2: 0x80000000 (no l2 cache) +config3: 0x20 (VInt: vectored interrupts available) +config7: 0 (wait instruction not interruptible) +cause: 0x50008018 (original interrupt behaviour) +intctl: 0 (original interrupt behaviour) +srsctl, srsmap are 0 +perf ctl 0: 0x80000000 +perf ctl 1: 0x0 +ll/sc target memory must be cached. + +1 uart 8250 (actually 16550ish) +2 ethers arge[01] ar71xx, 2nd has 4 ports & a bridge +pci bus(es) +no video +no disk but has flash, alas + +addresses + +devices; access by KSEG1|addr +0x10000000 pci memory space +0x18020000 uart 16550 in apb space +0x19000000 ether arge1, phy0-3 +0x1a000000 ether arge0, phy4 +0x1b000000 ehci +0x1c000000 ochi +0x1f000000 spi + +0x80060000 virtual start addr (linux) +0xbfc00000 prom + +irqs +2 pci +3 ehci +4 arge1 ar71xx @ 0x19:: + ar8316 switch +5 arge0 ar71xx @ 0x1a:: +6 uart +7 clock + +apb intrs +0 timer +1 error +2 gpio +3 uart +4 watchdog +5 hwpmc +6 ohci +7 dma + +for previous mips assembler code, see + /1998/0101/sys/src/boot/carrera + /1998/0101/sys/src/brazil/carrera + /2000/0615/sys/src/9/carrera + /n/fornaxdump/1997/1201/sys/src/brazil/magnum + +for ether, see + /o/bsd/free/sys/mips/atheros/if_arge.c + /o/bsd/free/sys/mips/atheros/if_argevar.h + /o/bsd/free/sys/mips/atheros/ar71xxreg.h + /o/bsd/free/sys/dev/etherswitch/* + /o/openwrt/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx --- /sys/src/cmd/spell/local Sun Dec 12 02:13:52 1999 +++ /sys/src/cmd/spell/local Tue Jul 23 23:49:35 2013 @@ -51,8 +51,8 @@ ASR pc,nopref Assoc d,nopref Asthana pc,nopref -Atal n,nopref AT&TTJ pc,nopref +Atal n,nopref Audix pc,nopref autodecrement v,er autoincrement v,er @@ -157,6 +157,7 @@ coinbox n Coker n,nopref Colbry n,nopref +Collyer n,nopref Comm d,nopref compand n compandor n @@ -229,6 +230,7 @@ DPCM pc,nopref DSP n,nopref Dunellen pc,nopref +e.g n,nopref Earley n,nopref EBCDIC pc,nopref ECC n,nopref @@ -243,6 +245,8 @@ elementwise d Elko pc,nopref Emlin pc,nopref +endian a +executable n EOF n,nopref EOT n,nopref Erdos pc,nopref @@ -270,6 +274,7 @@ Florham d,nopref FOCS d,nopref Fostik n,nopref +FPU n Fredman n,nopref Freeny n,nopref Frobenius pc,nopref @@ -284,6 +289,7 @@ Geffrard n,nopref Gehani n,nopref GEI pc,nopref +Geoff n,nopref Gerardo pc,nopref Geri pc,nopref Gersho n,nopref @@ -333,6 +339,7 @@ Hwang pc,nopref hypertext n Hz pc,nopref +i.e n,nopref IC n,nopref Ichbiah pc,nopref ICL pc,nopref @@ -369,6 +376,7 @@ Jukl n,nopref Julesz n,nopref junctor n +K&R pc,nopref Kac pc,nopref Kadota n,nopref Kagi n,nopref @@ -396,12 +404,12 @@ Kleene pc,nopref Klinger n,nopref kludge pc,y +kludgey a Knuth pc,nopref Kolmogorov pc Korn pc Kornfeld n,nopref Kosaraju pc,nopref -K&R pc,nopref Krishan pc,nopref Krishnakumar n,nopref Kroon pc,nopref @@ -429,6 +437,10 @@ Lehmer pc,nopref Lempel pc,nopref Lesk n,nopref +libc n +libmach n +libmp n +libsec n Liberman n,nopref lightpen n Lincroft pc,nopref @@ -436,6 +448,7 @@ linewidth n Linhart n,nopref Linotron pc,nopref +linux n,nopref Liskov pc,nopref Litman n,nopref livelock n,ed @@ -492,6 +505,7 @@ MHCC pc,nopref MHz pc,nopref Mihalis pc,nopref +MII n Millburn pc,nopref Millington pc,nopref Milne pc,nopref @@ -500,6 +514,7 @@ MIPS pc,nopref Mitch pc,nopref Mitra n,nopref +mkfile n ML pc,nopref mm d MMU n,nopref @@ -509,6 +524,7 @@ Montville pc,nopref MOS pc,nopref Mosteller pc,nopref +MOVW n MTBF d,nopref MTS n,nopref Multics pc,nopref @@ -533,6 +549,7 @@ Netravali n,nopref newline n NMOS pc,nopref +NOP n NP pc,nopref NPA pc,nopref NPL pc,nopref @@ -544,12 +561,12 @@ nullary d Nutley pc,nopref NYNEX pc,nopref +O'Gorman pc,nopref OCLC pc,nopref OCR pc,nopref Odlyzko n,nopref OEM n,nopref Ogielski n,nopref -O'Gorman pc,nopref Oldwick pc,nopref ONR pc,nopref OOH pc,nopref @@ -586,6 +603,7 @@ Perlis pc,nopref Petajan n,nopref phreak n +PHY n Phys pc,nopref Pierrehumbert n,nopref Pinson n,nopref @@ -706,6 +724,7 @@ Sleator pc,nopref Slepian n,nopref Snobol pc,nopref +SoC n,nopref Softech pc,nopref Sondhi n,nopref Speakerphone pc,nopref @@ -746,6 +765,7 @@ Thu pc,nopref Tillman n,nopref Tishby n,nopref +TLB n TM s,nopref Toeplitz pc,nopref TOMS pc,nopref @@ -809,6 +829,7 @@ wirewrap n,v,ms Wirth pc,nopref Witsenhausen n,nopref +workaround n,nopref Wulf pc,nopref Wyk n,nopref Yannakakis n,nopref