diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/mtx |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/mtx')
-rwxr-xr-x | sys/src/9/mtx/clock.c | 112 | ||||
-rwxr-xr-x | sys/src/9/mtx/cycintr.c | 27 | ||||
-rwxr-xr-x | sys/src/9/mtx/dat.h | 213 | ||||
-rwxr-xr-x | sys/src/9/mtx/devarch.c | 401 | ||||
-rwxr-xr-x | sys/src/9/mtx/devether.c | 472 | ||||
-rwxr-xr-x | sys/src/9/mtx/devrtc.c | 438 | ||||
-rwxr-xr-x | sys/src/9/mtx/ether2114x.c | 1671 | ||||
-rwxr-xr-x | sys/src/9/mtx/etherif.h | 35 | ||||
-rwxr-xr-x | sys/src/9/mtx/fns.h | 110 | ||||
-rwxr-xr-x | sys/src/9/mtx/i8259.c | 213 | ||||
-rwxr-xr-x | sys/src/9/mtx/inb.s | 119 | ||||
-rwxr-xr-x | sys/src/9/mtx/initcode | 25 | ||||
-rwxr-xr-x | sys/src/9/mtx/io.h | 180 | ||||
-rwxr-xr-x | sys/src/9/mtx/kbd.c | 435 | ||||
-rwxr-xr-x | sys/src/9/mtx/l.s | 603 | ||||
-rwxr-xr-x | sys/src/9/mtx/main.c | 465 | ||||
-rwxr-xr-x | sys/src/9/mtx/mem.h | 195 | ||||
-rwxr-xr-x | sys/src/9/mtx/mkfile | 96 | ||||
-rwxr-xr-x | sys/src/9/mtx/mmu.c | 257 | ||||
-rwxr-xr-x | sys/src/9/mtx/mtx | 46 | ||||
-rwxr-xr-x | sys/src/9/mtx/mtxcpu | 46 | ||||
-rwxr-xr-x | sys/src/9/mtx/pci.c | 908 | ||||
-rwxr-xr-x | sys/src/9/mtx/random.c | 138 | ||||
-rwxr-xr-x | sys/src/9/mtx/raven.c | 153 | ||||
-rwxr-xr-x | sys/src/9/mtx/trap.c | 836 | ||||
-rwxr-xr-x | sys/src/9/mtx/uarti8250.c | 636 |
26 files changed, 8830 insertions, 0 deletions
diff --git a/sys/src/9/mtx/clock.c b/sys/src/9/mtx/clock.c new file mode 100755 index 000000000..dacef88ed --- /dev/null +++ b/sys/src/9/mtx/clock.c @@ -0,0 +1,112 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +static ulong clkreload; + +void +delayloopinit(void) +{ + ulong v; + uvlong x; + + m->loopconst = 5000; + v = getdec(); + delay(1000); + v -= getdec(); + + x = m->loopconst; + x *= m->dechz; + x /= v; + m->loopconst = x; +} + +void +clockinit(void) +{ + /* XXX this should not be hard coded! */ + m->cpuhz = 300000000; + m->bushz = 66666666; + + m->dechz = m->bushz/4; /* true for all 604e */ + m->tbhz = m->dechz; /* conjecture; manual doesn't say */ + + delayloopinit(); + + clkreload = m->dechz/HZ-1; + putdec(clkreload); +} + +void +clockintr(Ureg *ureg) +{ + long v; + + v = -getdec(); + if(v > clkreload/2){ + if(v > clkreload) + m->ticks += v/clkreload; + v = 0; + } + putdec(clkreload-v); + + timerintr(ureg, 0); +} + +void +timerset(Tval) +{ +} + +void +delay(int l) +{ + ulong i, j; + + j = m->loopconst; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->loopconst; + l += 500; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} + +ulong +µs(void) +{ + return fastticks2us(m->ticks); +} + +/* + * performance measurement ticks. must be low overhead. + * doesn't have to count over a second. + */ +ulong +perfticks(void) +{ + return m->ticks; +} diff --git a/sys/src/9/mtx/cycintr.c b/sys/src/9/mtx/cycintr.c new file mode 100755 index 000000000..d56edba80 --- /dev/null +++ b/sys/src/9/mtx/cycintr.c @@ -0,0 +1,27 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +int +havetimer(void) +{ + return 0; +} + +void +timeradd(Timer *) +{ +} + +void +timerdel(Timer *) +{ +} + +void +clockintrsched(void) +{ +} diff --git a/sys/src/9/mtx/dat.h b/sys/src/9/mtx/dat.h new file mode 100755 index 000000000..294bca8c5 --- /dev/null +++ b/sys/src/9/mtx/dat.h @@ -0,0 +1,213 @@ +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Notsave Notsave; +typedef struct Page Page; +typedef struct PCArch PCArch; +typedef struct Pcidev Pcidev; +typedef struct PMMU PMMU; +typedef struct Proc Proc; +typedef struct Sys Sys; +typedef struct Ureg Ureg; +typedef struct Vctl Vctl; +typedef long Tval; + +#pragma incomplete Ureg + +#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */ + +/* + * parameters for sysproc.c + */ +#define AOUT_MAGIC Q_MAGIC + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + Proc *p; + Mach *m; + ushort isilock; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + FPinit, + FPactive, + FPinactive, +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPsave +{ + double fpreg[32]; + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; +}; + +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 */ + int monitor; /* has display? */ + ulong ialloc; /* bytes available for interrupt time allocation */ + ulong pipeqsize; /* size in bytes of pipe queues */ +}; + +/* + * mmu goo in the Proc structure + */ +#define NCOLOR 1 +struct PMMU +{ + int mmupid; +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong UNUSED; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ +/* + * Fake kmap + */ +typedef void KMap; +#define VA(k) ((ulong)(k)) +#define kmap(p) (KMap*)((p)->pa|KZERO) +#define kunmap(k) + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor */ + ulong splpc; /* pc that called splhi() */ + Proc *proc; /* current process on this processor */ + + /* ordering from here on irrelevant */ + + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int inclockintr; + int cputype; + ulong loopconst; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + vlong cpuhz; + ulong bushz; + ulong dechz; + ulong tbhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + + ulong pcclast; + uvlong fastclock; + Perf perf; /* performance counters */ + + int tlbfault; /* only used by devproc; no access to tlb */ + int tlbpurge; /* ... */ + int pfault; + int cs; + int syscall; + int load; + int intr; + int flushmmu; /* make current proc flush it's mmu state */ + int ilockdepth; + + ulong ptabbase; /* start of page table in kernel virtual space */ + int slotgen; /* next pte (byte offset) when pteg is full */ + int mmupid; /* next mmu pid to use */ + int sweepcolor; + int trigcolor; + Rendez sweepr; + + ulong spuriousintr; + int lastintr; + + /* MUST BE LAST */ + int stack[1]; +}; + +struct +{ + Lock; + short machs; + short exiting; + short ispanic; +}active; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +#define MACHP(n) ((Mach *)((int)&mach0+n*BY2PG)) +extern Mach mach0; + +extern register Mach *m; +extern register Proc *up; + +extern FPsave initfp; diff --git a/sys/src/9/mtx/devarch.c b/sys/src/9/mtx/devarch.c new file mode 100755 index 000000000..cd5eea88f --- /dev/null +++ b/sys/src/9/mtx/devarch.c @@ -0,0 +1,401 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct IOMap IOMap; +struct IOMap +{ + IOMap *next; + char tag[13]; + ulong start; + ulong end; +}; + +static struct +{ + Lock; + IOMap *m; + IOMap *free; + IOMap maps[32]; // some initial free maps + + QLock ql; // lock for reading map +} iomap; + +enum { + Qdir = 0, + Qioalloc = 1, + Qiob, + Qiow, + Qiol, + Qbase, + + Qmax = 16, +}; + +typedef long Rdwrfn(Chan*, void*, long, vlong); + +static Rdwrfn *readfn[Qmax]; +static Rdwrfn *writefn[Qmax]; + +static Dirtab archdir[] = { + ".", { Qdir, 0, QTDIR }, 0, 0555, + "ioalloc", { Qioalloc, 0 }, 0, 0444, + "iob", { Qiob, 0 }, 0, 0660, + "iow", { Qiow, 0 }, 0, 0660, + "iol", { Qiol, 0 }, 0, 0660, +}; +Lock archwlock; /* the lock is only for changing archdir */ +int narchdir = Qbase; +int (*_pcmspecial)(char *, ISAConf *); +void (*_pcmspecialclose)(int); + +/* + * 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; i<narchdir; i++) + if(strcmp(archdir[i].name, name) == 0){ + unlock(&archwlock); + return nil; + } + + d.qid.path = narchdir; + archdir[narchdir] = d; + readfn[narchdir] = rdfn; + writefn[narchdir] = wrfn; + dp = &archdir[narchdir++]; + unlock(&archwlock); + + return dp; +} + +void +ioinit(void) +{ + int i; + + for(i = 0; i < nelem(iomap.maps)-1; i++) + iomap.maps[i].next = &iomap.maps[i+1]; + iomap.maps[i].next = nil; + iomap.free = iomap.maps; + + // a dummy entry at 2^17 + ioalloc(0x20000, 1, 0, "dummy"); +} + +// +// alloc some io port space and remember who it was +// alloced to. if port < 0, find a free region. +// +int +ioalloc(int port, int size, int align, char *tag) +{ + IOMap *m, **l; + int i; + + lock(&iomap); + if(port < 0){ + // find a free port above 0x400 and below 0x1000 + port = 0x400; + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + i = m->start - port; + if(i > size) + break; + if(align > 0) + port = ((port+align-1)/align)*align; + else + port = m->end; + } + if(*l == nil){ + unlock(&iomap); + return -1; + } + } else { + // see if the space clashes with previously allocated ports + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if(m->end <= port) + continue; + if(m->start >= port+size) + break; + unlock(&iomap); + return -1; + } + } + m = iomap.free; + if(m == nil){ + print("ioalloc: out of maps"); + unlock(&iomap); + return port; + } + iomap.free = m->next; + m->next = *l; + m->start = port; + m->end = port + size; + strncpy(m->tag, tag, sizeof(m->tag)); + m->tag[sizeof(m->tag)-1] = 0; + *l = m; + + archdir[0].qid.vers++; + + unlock(&iomap); + return m->start; +} + +void +iofree(int port) +{ + IOMap *m, **l; + + lock(&iomap); + for(l = &iomap.m; *l; l = &(*l)->next){ + if((*l)->start == port){ + m = *l; + *l = m->next; + m->next = iomap.free; + iomap.free = m; + break; + } + if((*l)->start > port) + break; + } + archdir[0].qid.vers++; + unlock(&iomap); +} + +int +iounused(int start, int end) +{ + IOMap *m; + + for(m = iomap.m; m; m = m->next){ + if(start >= m->start && start < m->end + || start <= m->start && end > m->start) + return 0; + } + return 1; +} + +static void +checkport(int start, int end) +{ + /* standard vga regs are OK */ + if(start >= 0x2b0 && end <= 0x2df+1) + return; + if(start >= 0x3c0 && end <= 0x3da+1) + return; + + if(iounused(start, end)) + return; + error(Eperm); +} + +static Chan* +archattach(char* spec) +{ + return devattach('P', spec); +} + +Walkqid* +archwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, archdir, narchdir, devgen); +} + +static int +archstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, archdir, narchdir, devgen); +} + +static Chan* +archopen(Chan* c, int omode) +{ + return devopen(c, omode, archdir, nelem(archdir), devgen); +} + +static void +archclose(Chan*) +{ +} + +enum +{ + Linelen= 31, +}; + +static long +archread(Chan *c, void *a, long n, vlong offset) +{ + char buf[Linelen+1], *p; + int port; + ushort *sp; + ulong *lp; + IOMap *m; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, a, n, archdir, nelem(archdir), devgen); + + case Qiob: + port = offset; + checkport(offset, offset+n); + for(p = a; port < offset+n; port++) + *p++ = inb(port); + return n; + + case Qiow: + if((n & 0x01) || (offset & 0x01)) + error(Ebadarg); + checkport(offset, offset+n+1); + n /= 2; + sp = a; + for(port = offset; port < offset+n; port += 2) + *sp++ = ins(port); + return n*2; + + case Qiol: + if((n & 0x03) || (offset & 0x03)) + error(Ebadarg); + checkport(offset, offset+n+3); + n /= 4; + lp = a; + for(port = offset; port < offset+n; port += 4) + *lp++ = inl(port); + return n*4; + + case Qioalloc: + break; + + default: + if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + + offset = offset/Linelen; + n = n/Linelen; + p = a; + lock(&iomap); + for(m = iomap.m; n > 0 && m != nil; m = m->next){ + if(offset-- > 0) + continue; + if(strcmp(m->tag, "dummy") == 0) + break; + sprint(buf, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag); + memmove(p, buf, Linelen); + p += Linelen; + n--; + } + unlock(&iomap); + + return p - (char*)a; +} + +static long +archwrite(Chan *c, void *a, long n, vlong offset) +{ + char *p; + int port; + ushort *sp; + ulong *lp; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qiob: + p = a; + checkport(offset, offset+n); + for(port = offset; port < offset+n; port++) + outb(port, *p++); + return n; + + case Qiow: + if((n & 01) || (offset & 01)) + error(Ebadarg); + checkport(offset, offset+n+1); + n /= 2; + sp = a; + for(port = offset; port < offset+n; port += 2) + outs(port, *sp++); + return n*2; + + case Qiol: + if((n & 0x03) || (offset & 0x03)) + error(Ebadarg); + checkport(offset, offset+n+3); + n /= 4; + lp = a; + for(port = offset; port < offset+n; port += 4) + outl(port, *lp++); + return n*4; + + default: + if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + return 0; +} + +Dev archdevtab = { + 'P', + "arch", + + devreset, + devinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +int +pcmspecial(char *idstr, ISAConf *isa) +{ + return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; +} + +void +pcmspecialclose(int a) +{ + if (_pcmspecialclose != nil) + _pcmspecialclose(a); +} diff --git a/sys/src/9/mtx/devether.c b/sys/src/9/mtx/devether.c new file mode 100755 index 000000000..73c8bac4e --- /dev/null +++ b/sys/src/9/mtx/devether.c @@ -0,0 +1,472 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->dev], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->dev], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->dev], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + qpass(f->in, xbp); + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + + if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){ + qnoblock(ether->oq, 1); + return n; + } + + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < 6; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[32], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + if(isaconfig("ether", ctlrno, ether) == 0) + continue; + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(strncmp(ether->opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + } + if(cards[n].reset(ether)) + break; + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI) + ether->irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + /* + * If ether->irq is <0, it is a hack to indicate no interrupt + * used by ethersink. + */ + if(ether->irq >= 0) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps >= 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 65*1024); + if(ether->oq == 0) + ether->oq = qopen(65*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + devshutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/sys/src/9/mtx/devrtc.c b/sys/src/9/mtx/devrtc.c new file mode 100755 index 000000000..5b6a2399c --- /dev/null +++ b/sys/src/9/mtx/devrtc.c @@ -0,0 +1,438 @@ +/* + * M48T59/559 Timekeeper + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +enum{ + STB0 = 0x74, + STB1 = 0x75, + Data = 0x77, + + NVOFF= 0, + NVLEN= 0x1ff0, /* length in bytes of NV RAM */ + + /* + * register offsets into time of day clock + */ + NVflags= 0x1ff0, + NVwatchdog= 0x1ff7, + NVctl= 0x1ff8, + NVsec, + NVmin, + NVhour, + NVday, /* (1 = Sun) */ + NVmday, /* (1-31) */ + NVmon, /* (1-12) */ + NVyear, /* (0-99) */ + + /* NVctl */ + RTwrite = (1<<7), + RTread = (1<<6), + RTsign = (1<<5), + RTcal = 0x1f, + + /* NVwatchdog */ + WDsteer = (1<<7), /* 0 -> intr, 1 -> reset */ + WDmult = (1<<2), /* 5 bits of multiplier */ + WDres0 = (0<<0), /* 1/16 sec resolution */ + WDres1 = (1<<0), /* 1/4 sec resolution */ + WDres2 = (2<<0), /* 1 sec resolution */ + WDres3 = (3<<0), /* 4 sec resolution */ + + Qdir = 0, + Qrtc, + Qnvram, +}; + +/* + * broken down time + */ +typedef struct +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +} Rtc; + +QLock rtclock; /* mutex on nvram operations */ + +static Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "rtc", {Qrtc, 0}, 0, 0644, + "nvram", {Qnvram, 0}, 0, 0600, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); +static void setrtc(Rtc*); +static void nvcksum(void); +static void nvput(int, uchar); +static uchar nvget(int); + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + case Qnvram: + if(strcmp(up->user, eve)!=0 || !cpuserver) + error(Eperm); + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong off) +{ + char *p; + ulong t; + int i; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + qlock(&rtclock); + t = rtctime(); + qunlock(&rtclock); + n = readnum(offset, buf, n, t, 12); + return n; + case Qnvram: + offset += NVOFF; + if(offset > NVLEN) + return 0; + if(n > NVLEN - offset) + n = NVLEN - offset; + p = buf; + qlock(&rtclock); + for(i = 0; i < n; i++) + p[i] = nvget(i+offset); + qunlock(&rtclock); + return n; + } + error(Egreg); + return -1; /* never reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + Rtc rtc; + ulong secs; + char *cp, *ep; + int i; + ulong offset = off; + + switch((ulong)c->qid.path){ + case Qrtc: + if(offset!=0) + error(Ebadarg); + /* + * read the time + */ + cp = ep = buf; + ep += n; + while(cp < ep){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + /* + * write it + */ + qlock(&rtclock); + setrtc(&rtc); + qunlock(&rtclock); + return n; + case Qnvram: + offset += NVOFF; + if(offset > NVLEN) + return 0; + if(n > NVLEN - offset) + n = NVLEN - offset; + qlock(&rtclock); + for(i = 0; i < n; i++) + nvput(i+offset, ((uchar*)buf)[i]); + nvcksum(); + qunlock(&rtclock); + return n; + } + error(Egreg); + return -1; /* never reached */ +} + +long +rtcbwrite(Chan *c, Block *bp, ulong offset) +{ + return devbwrite(c, bp, offset); +} + +Dev rtcdevtab = { + 'r', + "rtc", + + devreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +nvput(int offset, uchar val) +{ + outb(STB0, offset); + outb(STB1, offset>>8); + outb(Data, val); +} + +static uchar +nvget(int offset) +{ + outb(STB0, offset); + outb(STB1, offset>>8); + return inb(Data); +} + +static void +nvcksum(void) +{ +} + +void +watchreset(void) +{ + splhi(); + nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0); + for(;;); +} + +static int +getbcd(int bcd) +{ + return (bcd&0x0f) + 10 * (bcd>>4); +} + +static int +putbcd(int val) +{ + return (val % 10) | (((val/10) % 10) << 4); +} + +long +rtctime(void) +{ + int ctl; + Rtc rtc; + + /* + * convert from BCD + */ + ctl = nvget(NVctl); + ctl &= RTsign|RTcal; + nvput(NVctl, ctl|RTread); + + rtc.sec = getbcd(nvget(NVsec) & 0x7f); + rtc.min = getbcd(nvget(NVmin)); + rtc.hour = getbcd(nvget(NVhour)); + rtc.mday = getbcd(nvget(NVmday)); + rtc.mon = getbcd(nvget(NVmon)); + rtc.year = getbcd(nvget(NVyear)); + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + + nvput(NVctl, ctl); + + return rtc2sec(&rtc); +} + +static void +setrtc(Rtc *rtc) +{ + int ctl; + + ctl = nvget(NVctl); + ctl &= RTsign|RTcal; + nvput(NVctl, ctl|RTwrite); + + nvput(NVsec, putbcd(rtc->sec)); + nvput(NVmin, putbcd(rtc->min)); + nvput(NVhour, putbcd(rtc->hour)); + nvput(NVmday, putbcd(rtc->mday)); + nvput(NVmon, putbcd(rtc->mon)); + nvput(NVyear, putbcd(rtc->year % 100)); + + nvput(NVctl, ctl); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int y) +{ + + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; + + return; +} diff --git a/sys/src/9/mtx/ether2114x.c b/sys/src/9/mtx/ether2114x.c new file mode 100755 index 000000000..b5dbfff06 --- /dev/null +++ b/sys/src/9/mtx/ether2114x.c @@ -0,0 +1,1671 @@ +/* + * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. + * To do: + * thresholds; + * ring sizing; + * handle more error conditions; + * tidy setup packet mess; + * push initialisation back to attach; + * full SROM decoding. + */ +#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" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +enum { /* CRS0 - Bus Mode */ + Swr = 0x00000001, /* Software Reset */ + Bar = 0x00000002, /* Bus Arbitration */ + Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + Ble = 0x00000080, /* Big/Little Endian */ + Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + Cal = 0x0000C000, /* Cache Alignment (field) */ + Cal8 = 0x00004000, /* 8 longword boundary alignment */ + Cal16 = 0x00008000, /* 16 longword boundary alignment */ + Cal32 = 0x0000C000, /* 32 longword boundary alignment */ + Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ + Rml = 0x00200000, /* Read Multiple */ +}; + +enum { /* CSR[57] - Status and Interrupt Enable */ + Ti = 0x00000001, /* Transmit Interrupt */ + Tps = 0x00000002, /* Transmit Process Stopped */ + Tu = 0x00000004, /* Transmit buffer Unavailable */ + Tjt = 0x00000008, /* Transmit Jabber Timeout */ + Unf = 0x00000020, /* transmit UNderFlow */ + Ri = 0x00000040, /* Receive Interrupt */ + Ru = 0x00000080, /* Receive buffer Unavailable */ + Rps = 0x00000100, /* Receive Process Stopped */ + Rwt = 0x00000200, /* Receive Watchdog Timeout */ + Eti = 0x00000400, /* Early Transmit Interrupt */ + Gte = 0x00000800, /* General purpose Timer Expired */ + Fbe = 0x00002000, /* Fatal Bus Error */ + Ais = 0x00008000, /* Abnormal Interrupt Summary */ + Nis = 0x00010000, /* Normal Interrupt Summary */ + Rs = 0x000E0000, /* Receive process State (field) */ + Ts = 0x00700000, /* Transmit process State (field) */ + Eb = 0x03800000, /* Error bits */ +}; + +enum { /* CSR6 - Operating Mode */ + Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ + Sr = 0x00000002, /* Start/stop Receive */ + Ho = 0x00000004, /* Hash-Only filtering mode */ + Pb = 0x00000008, /* Pass Bad frames */ + If = 0x00000010, /* Inverse Filtering */ + Sb = 0x00000020, /* Start/stop Backoff counter */ + Pr = 0x00000040, /* Promiscuous Mode */ + Pm = 0x00000080, /* Pass all Multicast */ + Fd = 0x00000200, /* Full Duplex mode */ + Om = 0x00000C00, /* Operating Mode (field) */ + Fc = 0x00001000, /* Force Collision */ + St = 0x00002000, /* Start/stop Transmission Command */ + Tr = 0x0000C000, /* ThReshold control bits (field) */ + Tr128 = 0x00000000, + Tr256 = 0x00004000, + Tr512 = 0x00008000, + Tr1024 = 0x0000C000, + Ca = 0x00020000, /* CApture effect enable */ + Ps = 0x00040000, /* Port Select */ + Hbd = 0x00080000, /* HeartBeat Disable */ + Imm = 0x00100000, /* IMMediate mode */ + Sf = 0x00200000, /* Store and Forward */ + Ttm = 0x00400000, /* Transmit Threshold Mode */ + Pcs = 0x00800000, /* PCS function */ + Scr = 0x01000000, /* SCRambler mode */ + Mbo = 0x02000000, /* Must Be One */ + Ra = 0x40000000, /* Receive All */ + Sc = 0x80000000, /* Special Capture effect enable */ + + TrMODE = Tr512, /* default transmission threshold */ +}; + +enum { /* CSR9 - ROM and MII Management */ + Scs = 0x00000001, /* serial ROM chip select */ + Sclk = 0x00000002, /* serial ROM clock */ + Sdi = 0x00000004, /* serial ROM data in */ + Sdo = 0x00000008, /* serial ROM data out */ + Ss = 0x00000800, /* serial ROM select */ + Wr = 0x00002000, /* write */ + Rd = 0x00004000, /* read */ + + Mdc = 0x00010000, /* MII management clock */ + Mdo = 0x00020000, /* MII management write data */ + Mii = 0x00040000, /* MII management operation mode (W) */ + Mdi = 0x00080000, /* MII management data in */ +}; + +enum { /* CSR12 - General-Purpose Port */ + Gpc = 0x00000100, /* General Purpose Control */ +}; + +typedef struct Des { + int status; + int control; + ulong addr; + Block* bp; +} Des; + +enum { /* status */ + Of = 0x00000001, /* Rx: OverFlow */ + Ce = 0x00000002, /* Rx: CRC Error */ + Db = 0x00000004, /* Rx: Dribbling Bit */ + Re = 0x00000008, /* Rx: Report on MII Error */ + Rw = 0x00000010, /* Rx: Receive Watchdog */ + Ft = 0x00000020, /* Rx: Frame Type */ + Cs = 0x00000040, /* Rx: Collision Seen */ + Tl = 0x00000080, /* Rx: Frame too Long */ + Ls = 0x00000100, /* Rx: Last deScriptor */ + Fs = 0x00000200, /* Rx: First deScriptor */ + Mf = 0x00000400, /* Rx: Multicast Frame */ + Rf = 0x00000800, /* Rx: Runt Frame */ + Dt = 0x00003000, /* Rx: Data Type (field) */ + De = 0x00004000, /* Rx: Descriptor Error */ + Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ + Ff = 0x40000000, /* Rx: Filtering Fail */ + + Def = 0x00000001, /* Tx: DEFerred */ + Uf = 0x00000002, /* Tx: UnderFlow error */ + Lf = 0x00000004, /* Tx: Link Fail report */ + Cc = 0x00000078, /* Tx: Collision Count (field) */ + Hf = 0x00000080, /* Tx: Heartbeat Fail */ + Ec = 0x00000100, /* Tx: Excessive Collisions */ + Lc = 0x00000200, /* Tx: Late Collision */ + Nc = 0x00000400, /* Tx: No Carrier */ + Lo = 0x00000800, /* Tx: LOss of carrier */ + To = 0x00004000, /* Tx: Transmission jabber timeOut */ + + Es = 0x00008000, /* [RT]x: Error Summary */ + Own = 0x80000000, /* [RT]x: OWN bit */ +}; + +enum { /* control */ + Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ + Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ + + Ch = 0x01000000, /* [RT]x: second address CHained */ + Er = 0x02000000, /* [RT]x: End of Ring */ + + Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ + Dpd = 0x00800000, /* Tx: Disabled PaDding */ + Ac = 0x04000000, /* Tx: Add CRC disable */ + Set = 0x08000000, /* Tx: SETup packet */ + Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ + Fseg = 0x20000000, /* Tx: First SEGment */ + Lseg = 0x40000000, /* Tx: Last SEGment */ + Ic = 0x80000000, /* Tx: Interrupt on Completion */ +}; + +enum { /* PHY registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisment */ + Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ + Aner = 6, /* Auto-Negotiation Expansion */ +}; + +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + uchar* srom; + int sromsz; /* address size in bits */ + uchar* sromea; /* MAC address */ + uchar* leaf; + int sct; /* selected connection type */ + int k; /* info block count */ + uchar* infoblock[16]; + int sctk; /* sct block index */ + int curk; /* current block index */ + uchar* type5block; + + int phy[32]; /* logical to physical map */ + int phyreset; /* reset bitmap */ + int curphyad; + int fdx; + int ttm; + + uchar fd; /* option */ + int medium; /* option */ + + int csr6; /* CSR6 - operating mode */ + int mask; /* CSR[57] - interrupt mask */ + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Block* setupbp; + + ulong of; /* receive statistics */ + ulong ce; + ulong cs; + ulong tl; + ulong rf; + ulong de; + + ulong ru; + ulong rps; + ulong rwt; + + ulong uf; /* transmit statistics */ + ulong ec; + ulong lc; + ulong nc; + ulong lo; + ulong to; + + ulong tps; + ulong tu; + ulong tjt; + ulong unf; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (inl((c)->port+((r)*8))) +#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + if(on) + ctlr->csr6 |= Pr; + else + ctlr->csr6 &= ~Pr; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(!(ctlr->csr6 & Sr)){ + ctlr->csr6 |= Sr; + csr32w(ctlr, 6, ctlr->csr6); + } + iunlock(&ctlr->lock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->ce; + ether->frames = ctlr->rf+ctlr->cs; + ether->buffs = ctlr->de+ctlr->tl; + ether->overflows = ctlr->of; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); + l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); + l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); + l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); + l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); + l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); + l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); + l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); + l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); + l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); + l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); + l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", + ctlr->to); + l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), + ctlr->csr6); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int control; + + ctlr = ether->ctlr; + while(ctlr->ntq < (ctlr->ntdr-1)){ + if(ctlr->setupbp){ + bp = ctlr->setupbp; + ctlr->setupbp = 0; + control = Ic|Set|BLEN(bp); + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + control = Ic|Lseg|Fseg|BLEN(bp); + } + + ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + des->control |= control; + ctlr->ntq++; + coherence(); + des->status = Own; + csr32w(ctlr, 1, 0); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ + /* + * Acknowledge the interrupts and mask-out + * the ones that are implicitly handled. + */ + csr32w(ctlr, 5, status); + status &= (ctlr->mask & ~(Nis|Ti)); + + if(status & Ais){ + if(status & Tps) + ctlr->tps++; + if(status & Tu) + ctlr->tu++; + if(status & Tjt) + ctlr->tjt++; + if(status & Ru) + ctlr->ru++; + if(status & Rps) + ctlr->rps++; + if(status & Rwt) + ctlr->rwt++; + status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); + } + + /* + * Received packets. + */ + if(status & Ri){ + des = &ctlr->rdr[ctlr->rdrx]; + while(!(des->status & Own)){ + if(des->status & Es){ + if(des->status & Of) + ctlr->of++; + if(des->status & Ce) + ctlr->ce++; + if(des->status & Cs) + ctlr->cs++; + if(des->status & Tl) + ctlr->tl++; + if(des->status & Rf) + ctlr->rf++; + if(des->status & De) + ctlr->de++; + } + else if(bp = iallocb(Rbsz)){ + len = ((des->status & Fl)>>16)-4; + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + } + + des->control &= Er; + des->control |= Rbsz; + coherence(); + des->status = Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~Ri; + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Unf){ + ctlr->unf++; + ilock(&ctlr->lock); + csr32w(ctlr, 6, ctlr->csr6 & ~St); + switch(ctlr->csr6 & Tr){ + case Tr128: + len = Tr256; + break; + case Tr256: + len = Tr512; + break; + case Tr512: + len = Tr1024; + break; + default: + case Tr1024: + len = Sf; + break; + } + ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); + csr32w(ctlr, 5, Tps); + status &= ~(Unf|Tps); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + if(des->status & Own) + break; + + if(des->status & Es){ + if(des->status & Uf) + ctlr->uf++; + if(des->status & Ec) + ctlr->ec++; + if(des->status & Lc) + ctlr->lc++; + if(des->status & Nc) + ctlr->nc++; + if(des->status & Lo) + ctlr->lo++; + if(des->status & To) + ctlr->to++; + ether->oerrs++; + } + + freeb(des->bp); + des->control &= Er; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + /* + * Anything left not catered for? + */ + if(status) + panic("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des; + Block *bp; + int i; + uchar bi[Eaddrlen*2]; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side; + * create and post a setup packet to initialise + * the physical ethernet address. + */ + ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0); + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if(des->bp == nil) + panic("can't allocate ethernet receive ring\n"); + des->status = Own; + des->control = Rbsz; + des->addr = PCIWADDR(des->bp->rp); + } + ctlr->rdr[ctlr->nrdr-1].control |= Er; + ctlr->rdrx = 0; + csr32w(ctlr, 3, PCIWADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + ctlr->tdr[ctlr->ntdr-1].control |= Er; + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); + + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ + ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); + csr32w(ctlr, 7, ctlr->mask); + ctlr->csr6 |= St; + csr32w(ctlr, 6, ctlr->csr6); + + for(i = 0; i < Eaddrlen/2; i++){ + bi[i*4] = ether->ea[i*2]; + bi[i*4+1] = ether->ea[i*2+1]; + bi[i*4+2] = ether->ea[i*2+1]; + bi[i*4+3] = ether->ea[i*2]; + } + bp = iallocb(Eaddrlen*2*16); + if(bp == nil) + panic("can't allocate ethernet setup buffer\n"); + memset(bp->rp, 0xFF, sizeof(bi)); + for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) + memmove(bp->rp+i, bi, sizeof(bi)); + bp->wp += sizeof(bi)*16; + + ctlr->setupbp = bp; + ether->oq = qopen(256*1024, Qmsg, 0, 0); + transmit(ether); +} + +static void +csr9w(Ctlr* ctlr, int data) +{ + csr32w(ctlr, 9, data); + microdelay(1); +} + +static int +miimdi(Ctlr* ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, 9) & Mdi) + data |= (1<<i); + csr9w(ctlr, Mii|Mdc); + csr9w(ctlr, Mii); + } + csr9w(ctlr, 0); + + return data; +} + +static void +miimdo(Ctlr* ctlr, int bits, int n) +{ + int i, mdo; + + /* + * Write n bits to the MII Management Register. + */ + for(i = n-1; i >= 0; i--){ + if(bits & (1<<i)) + mdo = Mdo; + else + mdo = 0; + csr9w(ctlr, mdo); + csr9w(ctlr, mdo|Mdc); + csr9w(ctlr, mdo); + } +} + +static int +miir(Ctlr* ctlr, int phyad, int regad) +{ + int data, i; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int phyad, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); + miimdo(ctlr, data, 32); + csr9w(ctlr, Mdc); + csr9w(ctlr, 0); +} + +static int +sromr(Ctlr* ctlr, int r) +{ + int i, op, data, size; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + if(ctlr->sromsz == 0) + ctlr->sromsz = 6; + + return csr32r(ctlr, 9) & 0xFFFF; + } + + /* + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 7.4 of the 21140 Hardware Reference Manual. + */ +reread: + csr9w(ctlr, Rd|Ss); + csr9w(ctlr, Rd|Ss|Scs); + csr9w(ctlr, Rd|Ss|Sclk|Scs); + csr9w(ctlr, Rd|Ss); + + op = 0x06; + for(i = 3-1; i >= 0; i--){ + data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->sromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + microdelay(1); + if(!(csr32r(ctlr, 9) & Sdo)) + break; + } + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr9w(ctlr, Rd|Ss|Sclk|Scs); + if(csr32r(ctlr, 9) & Sdo) + data |= (1<<i); + csr9w(ctlr, Rd|Ss|Scs); + } + + csr9w(ctlr, 0); + + if(ctlr->sromsz == 0){ + ctlr->sromsz = 8-size; + goto reread; + } + + return data & 0xFFFF; +} + +static void +softreset(Ctlr* ctlr) +{ + /* + * Soft-reset the controller and initialise bus mode. + * Delay should be >= 50 PCI cycles (2×S @ 25MHz). + */ + csr32w(ctlr, 0, Swr); + microdelay(10); + csr32w(ctlr, 0, Rml|Cal16|Dbo); + delay(1); +} + +static int +type5block(Ctlr* ctlr, uchar* block) +{ + int csr15, i, len; + + /* + * Reset or GPR sequence. Reset should be once only, + * before the GPR sequence. + * Note 'block' is not a pointer to the block head but + * a pointer to the data in the block starting at the + * reset length value so type5block can be used for the + * sequences contained in type 1 and type 3 blocks. + * The SROM docs state the 21140 type 5 block is the + * same as that for the 21143, but the two controllers + * use different registers and sequence-element lengths + * so the 21140 code here is a guess for a real type 5 + * sequence. + */ + len = *block++; + if(ctlr->id != Tulip3){ + for(i = 0; i < len; i++){ + csr32w(ctlr, 12, *block); + block++; + } + return len; + } + + for(i = 0; i < len; i++){ + csr15 = *block++<<16; + csr15 |= *block++<<24; + csr32w(ctlr, 15, csr15); + debug("%8.8uX ", csr15); + } + return 2*len; +} + +static int +typephylink(Ctlr* ctlr, uchar*) +{ + int an, bmcr, bmsr, csr6, x; + + /* + * Fail if + * auto-negotiataion enabled but not complete; + * no valid link established. + */ + bmcr = miir(ctlr, ctlr->curphyad, Bmcr); + miir(ctlr, ctlr->curphyad, Bmsr); + bmsr = miir(ctlr, ctlr->curphyad, Bmsr); + debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); + if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) + return 0; + + if(bmcr & 0x1000){ + an = miir(ctlr, ctlr->curphyad, Anar); + an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; + debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", + miir(ctlr, ctlr->curphyad, Anar), + miir(ctlr, ctlr->curphyad, Anlpar), + an); + + if(an & 0x0100) + x = 0x4000; + else if(an & 0x0080) + x = 0x2000; + else if(an & 0x0040) + x = 0x1000; + else if(an & 0x0020) + x = 0x0800; + else + x = 0; + } + else if((bmcr & 0x2100) == 0x2100) + x = 0x4000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } + else if(bmcr & 0x0100) + x = 0x1000; + else + x = 0x0800; + + csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + if(ctlr->fdx & x) + csr6 |= Fd; + if(ctlr->ttm & x) + csr6 |= Ttm; + debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", + csr6, ctlr->csr6, csr32r(ctlr, 6)); + if(csr6 != ctlr->csr6){ + ctlr->csr6 = csr6; + csr32w(ctlr, 6, csr6); + } + + return 1; +} + +static int +typephymode(Ctlr* ctlr, uchar* block, int wait) +{ + uchar *p; + int len, mc, nway, phyx, timeo; + + if(DEBUG){ + int i; + + len = (block[0] & ~0x80)+1; + for(i = 0; i < len; i++) + debug("%2.2uX ", block[i]); + debug("\n"); + } + + if(block[1] == 1) + len = 1; + else if(block[1] == 3) + len = 2; + else + return -1; + + /* + * Snarf the media capabilities, nway advertisment, + * FDX and TTM bitmaps. + */ + p = &block[5+len*block[3]+len*block[4+len*block[3]]]; + mc = *p++; + mc |= *p++<<8; + nway = *p++; + nway |= *p++<<8; + ctlr->fdx = *p++; + ctlr->fdx |= *p++<<8; + ctlr->ttm = *p++; + ctlr->ttm |= *p<<8; + debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", + mc, nway, ctlr->fdx, ctlr->ttm); + USED(mc); + + phyx = block[2]; + ctlr->curphyad = ctlr->phy[phyx]; + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + if(!(ctlr->phyreset & (1<<phyx))){ + debug("reset seq: len %d: ", block[3]); + if(ctlr->type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<<phyx); + } + + /* + * GPR sequence. + */ + debug("gpr seq: len %d: ", block[3]); + type5block(ctlr, &block[3]); + debug("\n"); + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + /* + * Turn off auto-negotiation, set the auto-negotiation + * advertisment register then start the auto-negotiation + * process again. + */ + miiw(ctlr, ctlr->curphyad, Bmcr, 0); + miiw(ctlr, ctlr->curphyad, Anar, nway|1); + miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(typephylink(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +typesymmode(Ctlr *ctlr, uchar *block, int wait) +{ + uint gpmode, gpdata, command; + + USED(wait); + gpmode = block[3] | ((uint) block[4] << 8); + gpdata = block[5] | ((uint) block[6] << 8); + command = (block[7] | ((uint) block[8] << 8)) & 0x71; + if (command & 0x8000) { + print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n"); + return -1; + } + csr32w(ctlr, 15, gpmode); + csr32w(ctlr, 15, gpdata); + ctlr->csr6 = (command & 0x71) << 18; + csr32w(ctlr, 6, ctlr->csr6); + return 0; +} + +static int +type2mode(Ctlr* ctlr, uchar* block, int) +{ + uchar *p; + int csr6, csr13, csr14, csr15, gpc, gpd; + + csr6 = Sc|Mbo|Ca|Sb|TrMODE; + debug("type2mode: medium 0x%2.2uX\n", block[2]); + + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + } + + /* + * Operating mode programming values from the datasheet + * unless media specific data is explicitly given. + */ + p = &block[3]; + if(block[2] & 0x40){ + csr13 = (block[4]<<8)|block[3]; + csr14 = (block[6]<<8)|block[5]; + csr15 = (block[8]<<8)|block[7]; + p += 6; + } + else switch(block[2] & 0x3F){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0x00000001; + csr14 = 0x00007F3F; + csr15 = 0x00000008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x00000006; + break; + case 0x02: /* 10BASE-5 (AUI) */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x0000000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0x00000001; + csr14 = 0x00007F3D; + csr15 = 0x00000008; + break; + } + gpc = *p++<<16; + gpc |= *p++<<24; + gpd = *p++<<16; + gpd |= *p<<24; + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, gpc|csr15); + delay(10); + csr32w(ctlr, 15, gpd|csr15); + csr32w(ctlr, 13, csr13); + + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n", + csr13, csr14, csr15); + debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n", + gpc, gpd, csr6); + + return 0; +} + +static int +type0link(Ctlr* ctlr, uchar* block) +{ + int m, polarity, sense; + + m = (block[3]<<8)|block[2]; + sense = 1<<((m & 0x000E)>>1); + if(m & 0x0080) + polarity = sense; + else + polarity = 0; + + return (csr32r(ctlr, 12) & sense)^polarity; +} + +static int +type0mode(Ctlr* ctlr, uchar* block, int wait) +{ + int csr6, m, timeo; + + csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; +debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); + switch(block[0]){ + default: + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + break; + } + + m = (block[3]<<8)|block[2]; + if(m & 0x0001) + csr6 |= Ps; + if(m & 0x0010) + csr6 |= Ttm; + if(m & 0x0020) + csr6 |= Pcs; + if(m & 0x0040) + csr6 |= Scr; + + csr32w(ctlr, 12, block[1]); + microdelay(10); + csr32w(ctlr, 6, csr6); + ctlr->csr6 = csr6; + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(type0link(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +mediaxx(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + if(block[0] & 0x80){ + switch(block[1]){ + default: + return -1; + case 0: + if(ctlr->medium >= 0 && block[2] != ctlr->medium) + return 0; +/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) + return 0; + if(type0mode(ctlr, block+2, wait)) + return 0; + break; + case 1: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 2: + debug("type2: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(type2mode(ctlr, block, wait)) + return 0; + break; + case 3: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 4: + debug("type4: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(typesymmode(ctlr, block, wait)) + return 0; + break; + } + } + else{ + if(ctlr->medium >= 0 && block[0] != ctlr->medium) + return 0; +/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) + return 0; + if(type0mode(ctlr, block, wait)) + return 0; + } + + if(ctlr->csr6){ + if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) + return 10; + return 100; + } + + return 0; +} + +static int +media(Ether* ether, int wait) +{ + Ctlr* ctlr; + int k, mbps; + + ctlr = ether->ctlr; + for(k = 0; k < ctlr->k; k++){ + mbps = mediaxx(ether, wait); + if(mbps > 0) + return mbps; + if(ctlr->curk == 0) + ctlr->curk = ctlr->k-1; + else + ctlr->curk--; + } + + return 0; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static uchar en1207[] = { /* Accton EN1207-COMBO */ + 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x0B, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x1B, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ + + /* There is 10BASE-2 as well, but... */ +}; + +static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ + 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x3F, /* [6] general purpose control */ + 1, /* [7] block count */ + + 0x07, /* [8] media code (100BASE-FX) */ + 0x03, /* [9] general purpose port data */ + 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ +}; + +static uchar smc9332[] = { /* SMC 9332 */ + 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x00, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x09, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ +}; + +static uchar* leaf21140[] = { + en1207, /* Accton EN1207-COMBO */ + ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ + smc9332, /* SMC 9332 */ + nil, +}; + +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + +static int +srom(Ctlr* ctlr) +{ + int i, k, oui, phy, x; + uchar *p; + + /* + * This is a partial decoding of the SROM format described in + * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, + * 2-Mar-98'. Only the 2114[03] are handled, support for other + * controllers can be added as needed. + * Do a dummy read first to get the size and allocate ctlr->srom. + */ + sromr(ctlr, 0); + if(ctlr->srom == nil) + ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort)); + for(i = 0; i < (1<<ctlr->sromsz); i++){ + x = sromr(ctlr, i); + ctlr->srom[2*i] = x; + ctlr->srom[2*i+1] = x>>8; + } + + /* + * There are 2 SROM layouts: + * e.g. Digital EtherWORKS station address at offset 20; + * this complies with the 21140A SROM + * application note from Digital; + * e.g. SMC9332 station address at offset 0 followed by + * 2 additional bytes, repeated at offset + * 6; the 8 bytes are also repeated in + * reverse order at offset 8. + * To check which it is, read the SROM and check for the repeating + * patterns of the non-compliant cards; if that fails use the one at + * offset 20. + */ + ctlr->sromea = ctlr->srom; + for(i = 0; i < 8; i++){ + x = ctlr->srom[i]; + if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ + ctlr->sromea = &ctlr->srom[20]; + break; + } + } + + /* + * Fake up the SROM for the PNIC. + * It looks like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + + /* + * Next, try to find the info leaf in the SROM for media detection. + * If it's a non-conforming card try to match the vendor ethernet code + * and point p at a fake info leaf with compact 21140 entries. + */ + if(ctlr->sromea == ctlr->srom){ + p = nil; + for(i = 0; leaf21140[i] != nil; i++){ + if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ + p = &leaf21140[i][4]; + break; + } + } + if(p == nil) + return -1; + } + else + p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; + + /* + * Set up the info needed for later media detection. + * For the 21140, set the general-purpose mask in CSR12. + * The info block entries are stored in order of increasing + * precedence, so detection will work backwards through the + * stored indexes into ctlr->srom. + * If an entry is found which matches the selected connection + * type, save the index. Otherwise, start at the last entry. + * If any MII entries are found (type 1 and 3 blocks), scan + * for PHYs. + */ + ctlr->leaf = p; + ctlr->sct = *p++; + ctlr->sct |= *p++<<8; + if(ctlr->id != Tulip3){ + csr32w(ctlr, 12, Gpc|*p++); + delay(200); + } + ctlr->k = *p++; + if(ctlr->k >= nelem(ctlr->infoblock)) + ctlr->k = nelem(ctlr->infoblock)-1; + ctlr->sctk = ctlr->k-1; + phy = 0; + for(k = 0; k < ctlr->k; k++){ + ctlr->infoblock[k] = p; + /* + * The RAMIX PMC665 has a badly-coded SROM, + * hence the test for 21143 and type 3. + */ + if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ + *p |= 0x80; + if(*(p+1) == 1 || *(p+1) == 3) + phy = 1; + if(*(p+1) == 5) + ctlr->type5block = p; + p += (*p & ~0x80)+1; + } + else{ + debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + p[0], p[1], p[2], p[3]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + p += 4; + } + } + ctlr->curk = ctlr->sctk; + debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", + ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); + + if(phy){ + x = 0; + for(k = 0; k < nelem(ctlr->phy); k++){ + if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) + continue; + if(DEBUG){ + oui = (oui & 0x3FF)<<6; + oui |= miir(ctlr, k, 3)>>10; + miir(ctlr, k, 1); + debug("phy%d: index %d oui %uX reg1 %uX\n", + x, k, oui, miir(ctlr, k, 1)); + USED(oui); + } + ctlr->phy[x] = k; + } + } + + ctlr->fd = 0; + ctlr->medium = -1; + + return 0; +} + +static void +dec2114xpci(void) +{ + Ctlr *ctlr; + Pcidev *p; + int x; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Tulip3: /* 21143 */ + /* + * Exit sleep mode. + */ + x = pcicfgr32(p, 0x40); + x &= ~0xc0000000; + pcicfgw32(p, 0x40, x); + /*FALLTHROUGH*/ + + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case Tulip0: /* 21140 */ + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ + print("dec2114x: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + /* + * Some cards (e.g. ANA-6910FX) seem to need the Ps bit + * set or they don't always work right after a hardware + * reset. + */ + csr32w(ctlr, 6, Mbo|Ps); + softreset(ctlr); + + if(srom(ctlr)){ + iofree(ctlr->port); + free(ctlr); + continue; + } + + switch(ctlr->id){ + default: + break; + + case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); + break; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + dec2114xpci(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; +// ether->irq = ctlr->pcidev->intl; +ether->irq = 2; /* arrrrrgh */ + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + + /* + * Look for a medium override in case there's no autonegotiation + * (no MII) or the autonegotiation fails. + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i])) + continue; + ctlr->medium = x; + + switch(ctlr->medium){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + + ether->mbps = media(ether, 1); + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether2114xlink(void) +{ + addethercard("21140", reset); + addethercard("2114x", reset); +} diff --git a/sys/src/9/mtx/etherif.h b/sys/src/9/mtx/etherif.h new file mode 100755 index 000000000..34fa2cc02 --- /dev/null +++ b/sys/src/9/mtx/etherif.h @@ -0,0 +1,35 @@ +enum { + MaxEther = 24, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void *ctlr; + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/sys/src/9/mtx/fns.h b/sys/src/9/mtx/fns.h new file mode 100755 index 000000000..d88d0d636 --- /dev/null +++ b/sys/src/9/mtx/fns.h @@ -0,0 +1,110 @@ +#include "../port/portfns.h" + +ulong cankaddr(ulong); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockinit(void); +void clockintr(Ureg*); +void clockintrsched(void); +int cmpswap(long*, long, long); +#define coherence() eieio() +void cpuidprint(void); +#define cycles(x) do{}while(0) +void dcflush(void*, ulong); +void delay(int); +void dumpregs(Ureg*); +void delayloopinit(void); +void eieio(void); +void evenaddr(ulong); +void faultpower(Ureg*, ulong addr, int read); +void fprestore(FPsave*); +void fpsave(FPsave*); +char* getconf(char*); +ulong getdar(void); +ulong getdec(void); +ulong getdsisr(void); +ulong gethid0(void); +ulong gethid1(void); +ulong getmsr(void); +ulong getpvr(void); +void gotopc(ulong); +int havetimer(void); +void hwintrinit(void); +void i8250console(void); +void i8259init(void); +int i8259intack(void); +int i8259enable(Vctl*); +int i8259vecno(int); +int i8259disable(int); +void icflush(void*, ulong); +#define idlehands() /* nothing to do in the runproc */ +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 intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +int ioalloc(int, int, int, char*); +void iofree(int); +void ioinit(void); +int iprint(char*, ...); +int isaconfig(char*, int, ISAConf*); +void kbdinit(void); +#define kexit(a) +#define kmapinval() +void links(void); +void mmuinit(void); +void mmusweep(void*); +void mpicdisable(int); +void mpicenable(int, Vctl*); +int mpiceoi(int); +int mpicintack(void); +int newmmupid(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); +int pciscan(int, Pcidev **); +ulong pcibarsize(Pcidev *, int); +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 pcihinv(Pcidev*); +uchar pciipin(Pcidev *, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +#define procrestore(p) +void procsave(Proc*); +void procsetup(Proc*); +void putdec(ulong); +void puthid0(ulong); +void puthid1(ulong); +void putmsr(ulong); +void putsdr1(ulong); +void putsr(int, ulong); +void raveninit(void); +void sync(void); +int tas(void*); +void timeradd(Timer *); +void timerdel(Timer *); +void touser(void*); +void trapinit(void); +void trapvec(void); +void tlbflush(ulong); +void tlbflushall(void); +#define userureg(ur) (((ur)->status & MSR_PR) != 0) +void watchreset(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) diff --git a/sys/src/9/mtx/i8259.c b/sys/src/9/mtx/i8259.c new file mode 100755 index 000000000..605cd3fc0 --- /dev/null +++ b/sys/src/9/mtx/i8259.c @@ -0,0 +1,213 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +static Lock i8259lock; +static int i8259mask = 0xFFFF; /* disabled interrupts */ +int i8259elcr; /* mask of level-triggered interrupts */ + +void +i8259init(void) +{ + int x; + + ioalloc(Int0ctl, 2, 0, "i8259.0"); + ioalloc(Int1ctl, 2, 0, "i8259.1"); + ilock(&i8259lock); + + /* + * Set up the first 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */ + outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */ + outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + + /* + * Set up the second 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC+8. + * Set the 8259 as slave with edge triggered + * input with fully nested interrupts. + */ + outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */ + outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */ + outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + outb(Int1aux, (i8259mask>>8) & 0xFF); + + /* + * pass #2 8259 interrupts to #1 + */ + i8259mask &= ~0x04; + outb(Int0aux, i8259mask & 0xFF); + + /* + * Set Ocw3 to return the ISR when ctl read. + * After initialisation status read is set to IRR. + * Read IRR first to possibly deassert an outstanding + * interrupt. + */ + inb(Int0ctl); + outb(Int0ctl, Ocw3|0x03); + inb(Int1ctl); + outb(Int1ctl, Ocw3|0x03); + + /* + * Check for Edge/Level register. + * This check may not work for all chipsets. + * First try a non-intrusive test - the bits for + * IRQs 13, 8, 2, 1 and 0 must be edge (0). If + * that's OK try a R/W test. + */ + x = (inb(Elcr2)<<8)|inb(Elcr1); + if(!(x & 0x2107)){ + outb(Elcr1, 0); + if(inb(Elcr1) == 0){ + outb(Elcr1, 0x20); + if(inb(Elcr1) == 0x20) + i8259elcr = x; + outb(Elcr1, x & 0xFF); + print("ELCR: %4.4uX\n", i8259elcr); + } + } + iunlock(&i8259lock); +} + +int +i8259isr(int vno) +{ + int irq, isr; + + if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC) + return 0; + irq = vno-VectorPIC; + + /* + * tell the 8259 that we're done with the + * highest level interrupt (interrupts are still + * off at this point) + */ + ilock(&i8259lock); + isr = inb(Int0ctl); + outb(Int0ctl, EOI); + if(irq >= 8){ + isr |= inb(Int1ctl)<<8; + outb(Int1ctl, EOI); + } + iunlock(&i8259lock); + + return isr & (1<<irq); +} + +int +i8259enable(Vctl* v) +{ + int irq, irqbit; + + /* + * Given an IRQ, enable the corresponding interrupt in the i8259 + * and return the vector to be used. The i8259 is set to use a fixed + * range of vectors starting at VectorPIC. + */ + irq = v->irq; + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259enable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<<irq; + + ilock(&i8259lock); + if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){ + print("i8259enable: irq %d shared but not level\n", irq); + iunlock(&i8259lock); + return -1; + } + i8259mask &= ~irqbit; + if(irq < 8) + outb(Int0aux, i8259mask & 0xFF); + else + outb(Int1aux, (i8259mask>>8) & 0xFF); + + if(i8259elcr & irqbit) + v->eoi = i8259isr; + else + v->isr = i8259isr; + iunlock(&i8259lock); + + return VectorPIC+irq; +} + +int +i8259intack(void) +{ + int irq; + + outb(Int0ctl, Ocw3|0x07); /* intr ack on first 8259 */ + irq = inb(Int0ctl) & 7; + if(irq == 2) { /* cascade */ + outb(Int1ctl, Ocw3|0x07); /* intr ack on second 8259 */ + irq = (inb(Int1ctl) & 7) + 8; + } + return irq+VectorPIC; +} + +int +i8259vecno(int irq) +{ + return VectorPIC+irq; +} + +int +i8259disable(int irq) +{ + int irqbit; + + /* + * Given an IRQ, disable the corresponding interrupt + * in the 8259. + */ + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259disable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<<irq; + + ilock(&i8259lock); + if(!(i8259mask & irqbit)){ + i8259mask |= irqbit; + if(irq < 8) + outb(Int0aux, i8259mask & 0xFF); + else + outb(Int1aux, (i8259mask>>8) & 0xFF); + } + iunlock(&i8259lock); + return 0; +} diff --git a/sys/src/9/mtx/inb.s b/sys/src/9/mtx/inb.s new file mode 100755 index 000000000..9f32a8de9 --- /dev/null +++ b/sys/src/9/mtx/inb.s @@ -0,0 +1,119 @@ +#include "mem.h" + +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +TEXT inb(SB), $0 + OR $IOMEM, R3 + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $1, R4 +insb1: + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $IOMEM, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $IOMEM, R3 + EIEIO + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/sys/src/9/mtx/initcode b/sys/src/9/mtx/initcode new file mode 100755 index 000000000..1d846abe5 --- /dev/null +++ b/sys/src/9/mtx/initcode @@ -0,0 +1,25 @@ +#include "/sys/src/libc/9syscall/sys.h" + +/* + * we pass in the argument of the exec parameters as 0(FP) + */ + +TEXT main(SB),$8 + + MOVW $setSB(SB), R2 + MOVW $boot(SB), R3 + ADD $12, R1, R4 /* get a pointer to 0(FP) */ + MOVW R3, 4(R1) + MOVW R4, 8(R1) + MOVW $EXEC, R3 + SYSCALL + + /* should never get here */ +loop: + BR loop + +DATA boot+0(SB)/5,$"/boot" +DATA boot+5(SB)/5,$"/boot" +DATA bootv+0(SB)/4,$boot+6(SB) +GLOBL boot+0(SB),$11 +GLOBL bootv+0(SB),$8 diff --git a/sys/src/9/mtx/io.h b/sys/src/9/mtx/io.h new file mode 100755 index 000000000..59f13d815 --- /dev/null +++ b/sys/src/9/mtx/io.h @@ -0,0 +1,180 @@ +enum { + IrqCLOCK = 0, + IrqKBD = 1, + IrqUART1 = 3, + IrqUART0 = 4, + IrqPCMCIA = 5, + IrqFLOPPY = 6, + IrqLPT = 7, + IrqIRQ7 = 7, + IrqAUX = 12, /* PS/2 port */ + IrqIRQ13 = 13, /* coprocessor on 386 */ + IrqATA0 = 14, + IrqATA1 = 15, + MaxIrqPIC = 15, + + VectorPIC = 32, + MaxVectorPIC = VectorPIC+MaxIrqPIC, +}; + +typedef 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 */ +} Vctl; + +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 BUSDF(tbdf) ((tbdf)&0x000FF00) +#define BUSBDF(tbdf) ((tbdf)&0x0FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 and 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 */ +}; + +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 */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + 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; + ulong pcr; +}; + +#define PCIWINDOW 0x80000000 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) diff --git a/sys/src/9/mtx/kbd.c b/sys/src/9/mtx/kbd.c new file mode 100755 index 000000000..fe96fc345 --- /dev/null +++ b/sys/src/9/mtx/kbd.c @@ -0,0 +1,435 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= 0xF000, /* function key (begin Unicode private space) */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= KF|17, + Right= KF|18, + End= '\r', + Down= View, + Pgdown= KF|19, + Ins= KF|20, + Del= 0x7F, + Scroll= KF|21, +}; + +/* + * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard. + * A 'standard' keyboard doesn't produce anything above 0x58. + */ +Rune kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, View, No, Up, No, No, No, No, +}; + +Rune kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, Up, No, No, No, No, +}; + +Rune kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, No, No, No, No, No, +}; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cauxdis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cauxint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +static Lock i8042lock; +static uchar ccc; +static void (*auxputc)(int, int); + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = KADDR(0x472); + int i, x; + + *s = 0x1234; /* BIOS warm-boot flag */ + + /* + * newer reset the machine command + */ + outready(); + outb(Cmd, 0xFE); + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, x); /* toggle reset */ + delay(100); + } +} + +int +i8042auxcmd(int cmd) +{ + unsigned int c; + int tries; + + c = 0; + tries = 0; + + ilock(&i8042lock); + do{ + if(tries++ > 2) + break; + if(outready() < 0) + break; + outb(Cmd, 0xD4); + if(outready() < 0) + break; + outb(Data, cmd); + if(outready() < 0) + break; + if(inready() < 0) + break; + c = inb(Data); + } while(c == 0xFE || c == 0); + iunlock(&i8042lock); + + if(c != 0xFA){ + print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd); + return -1; + } + return 0; +} + +/* + * keyboard interrupt + */ +static void +i8042intr(Ureg*, void*) +{ + int s, c, i; + static int esc1, esc2; + static int alt, caps, ctl, num, shift; + static int collecting, nk; + static Rune kc[5]; + int keyup; + + /* + * get status + */ + lock(&i8042lock); + s = inb(Status); + if(!(s&Inready)){ + unlock(&i8042lock); + return; + } + + /* + * get the character + */ + c = inb(Data); + unlock(&i8042lock); + + /* + * if it's the aux port... + */ + if(s & Minready){ + if(auxputc != nil) + auxputc(c, shift); + return; + } + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return; + } else if(c == 0xe1){ + esc2 = 2; + return; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + c |= keyup; + if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ + print("unknown key %ux\n", c); + return; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + alt = 0; + break; + case Shift: + shift = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return; + } + + /* + * normal character + */ + if(!(c & (Spec|KF))){ + if(ctl){ + if(alt && c == Del) + exit(0); + c &= 0x1f; + } + if(!collecting){ + kbdputc(kbdq, c); + return; + } + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + kbdputc(kbdq, c); + else /* dump characters */ + for(i=0; i<nk; i++) + kbdputc(kbdq, kc[i]); + nk = 0; + collecting = 0; + return; + } else { + switch(c){ + case Caps: + caps ^= 1; + return; + case Num: + num ^= 1; + return; + case Shift: + shift = 1; + return; + case Latin: + alt = 1; + collecting = 1; + nk = 0; + return; + case Ctrl: + ctl = 1; + return; + } + } + kbdputc(kbdq, c); +} + +void +i8042auxenable(void (*putc)(int, int)) +{ + char *err = "i8042: aux init failed\n"; + + /* enable kbd/aux xfers and interrupts */ + ccc &= ~Cauxdis; + ccc |= Cauxint; + + ilock(&i8042lock); + if(outready() < 0) + print(err); + outb(Cmd, 0x60); /* write control register */ + if(outready() < 0) + print(err); + outb(Data, ccc); + if(outready() < 0) + print(err); + outb(Cmd, 0xA8); /* auxilliary device enable */ + if(outready() < 0){ + iunlock(&i8042lock); + return; + } + auxputc = putc; + intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux"); + iunlock(&i8042lock); +} + +void +kbdinit(void) +{ + int c; + + kbdq = qopen(4*1024, 0, 0, 0); + if(kbdq == nil) + panic("kbdinit"); + qnoblock(kbdq, 1); + + ioalloc(Data, 1, 0, "kbd"); + ioalloc(Cmd, 1, 0, "kbd"); + + intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd"); + + /* wait for a quiescent controller */ + while((c = inb(Status)) & (Outbusy | Inready)) + if(c & Inready) + inb(Data); + + /* get current controller command byte */ + outb(Cmd, 0x20); + if(inready() < 0){ + print("kbdinit: can't read ccc\n"); + ccc = 0; + } else + ccc = inb(Data); + + /* enable kbd xfers and interrupts */ + /* disable mouse */ + ccc &= ~Ckbddis; + ccc |= Csf | Ckbdint | Cscs1; + if(outready() < 0) + print("kbd init failed\n"); + outb(Cmd, 0x60); + if(outready() < 0) + print("kbd init failed\n"); + outb(Data, ccc); + outready(); +} diff --git a/sys/src/9/mtx/l.s b/sys/src/9/mtx/l.s new file mode 100755 index 000000000..c880c5390 --- /dev/null +++ b/sys/src/9/mtx/l.s @@ -0,0 +1,603 @@ +#include "mem.h" + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +#define TLBIA WORD $((31<<26)|(307<<1)) +#define TLBSYNC WORD $((31<<26)|(566<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +#define UREGSPACE (UREGSIZE+8) + + TEXT start(SB), $-4 + + /* + * setup MSR + * turn off interrupts + * use 0x000 as exception prefix + * enable machine check + */ + MOVW MSR, R3 + MOVW $(MSR_EE|MSR_IP), R4 + ANDN R4, R3 + OR $(MSR_ME), R3 + ISYNC + MOVW R3, MSR + MSRSYNC + + /* except during trap handling, R0 is zero from now on */ + MOVW $0, R0 + + /* setup SB for pre mmu */ + MOVW $setSB(SB), R2 + MOVW $KZERO, R3 + ANDN R3, R2 + + BL mmuinit0(SB) + + /* running with MMU on!! */ + + /* set R2 to correct value */ + MOVW $setSB(SB), R2 + + /* debugger sets R1 to top of usable memory +1 */ + MOVW R1, memsize(SB) + + BL kfpinit(SB) + + /* set up Mach */ + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 /* set stack */ + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + + BL main(SB) + + RETURN /* not reached */ + +GLOBL mach0(SB), $(MAXMACH*BY2PG) +GLOBL memsize(SB), $4 + +/* + * on return from this function we will be running in virtual mode. + * We set up the Block Address Translation (BAT) registers thus: + * 1) first 3 BATs are 256M blocks, starting from KZERO->0 + * 2) remaining BAT maps last 256M directly + */ +TEXT mmuinit0(SB), $0 + /* reset all the tlbs */ + MOVW $64, R3 + MOVW R3, CTR + MOVW $0, R4 +tlbloop: + TLBIE R4 + ADD $BIT(19), R4 + BDNZ tlbloop + TLBSYNC + + /* KZERO -> 0 */ + MOVW $(KZERO|(0x7ff<<2)|2), R3 + MOVW $(PTEVALID|PTEWRITE), R4 + MOVW R3, SPR(IBATU(0)) + MOVW R4, SPR(IBATL(0)) + MOVW R3, SPR(DBATU(0)) + MOVW R4, SPR(DBATL(0)) + + /* KZERO+256M -> 256M */ + ADD $(1<<28), R3 + ADD $(1<<28), R4 + MOVW R3, SPR(IBATU(1)) + MOVW R4, SPR(IBATL(1)) + MOVW R3, SPR(DBATU(1)) + MOVW R4, SPR(DBATL(1)) + + /* KZERO+512M -> 512M */ + ADD $(1<<28), R3 + ADD $(1<<28), R4 + MOVW R3, SPR(IBATU(2)) + MOVW R4, SPR(IBATL(2)) + MOVW R3, SPR(DBATU(2)) + MOVW R4, SPR(DBATL(2)) + + /* direct map last block, uncached, (?guarded) */ + MOVW $((0xf<<28)|(0x7ff<<2)|2), R3 + MOVW $((0xf<<28)|PTE1_I|PTE1_G|PTE1_RW), R4 + MOVW R3, SPR(DBATU(3)) + MOVW R4, SPR(DBATL(3)) + + /* IBAT 3 unused */ + MOVW R0, SPR(IBATU(3)) + MOVW R0, SPR(IBATL(3)) + + /* enable MMU */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR), R4 + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + + RETURN + +TEXT kfpinit(SB), $0 + MOVFL $0,FPSCR(7) + MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FMOVD $4503601774854144.0, F27 + FMOVD $0.5, F29 + FSUB F29, F29, F28 + FADD F29, F29, F30 + FADD F30, F30, F31 + FMOVD F28, F0 + FMOVD F28, F1 + FMOVD F28, F2 + FMOVD F28, F3 + FMOVD F28, F4 + FMOVD F28, F5 + FMOVD F28, F6 + FMOVD F28, F7 + FMOVD F28, F8 + FMOVD F28, F9 + FMOVD F28, F10 + FMOVD F28, F11 + FMOVD F28, F12 + FMOVD F28, F13 + FMOVD F28, F14 + FMOVD F28, F15 + FMOVD F28, F16 + FMOVD F28, F17 + FMOVD F28, F18 + FMOVD F28, F19 + FMOVD F28, F20 + FMOVD F28, F21 + FMOVD F28, F22 + FMOVD F28, F23 + FMOVD F28, F24 + FMOVD F28, F25 + FMOVD F28, F26 + RETURN + +TEXT splhi(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splx(SB), $0 + /* fall though */ + +TEXT splxpc(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MOVW MSR, R3 + OR $MSR_EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +TEXT touser(SB), $-4 + MOVW $(UTZERO+32), R5 /* header appears in text */ + MOVW $(MSR_EE|MSR_PR|MSR_ME|MSR_IR|MSR_DR|MSR_RI), R4 + MOVW R4, SPR(SRR1) + MOVW R3, R1 + MOVW R5, SPR(SRR0) + RFI + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $CACHELINESZ, R5 + BDNZ icf0 + ISYNC + RETURN + +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 +dcf1: + SYNC + RETURN + +TEXT tas(SB), $0 + SYNC + MOVW R3, R4 + MOVW $0xdead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT _xinc(SB),$0 /* void _xinc(long *); */ + MOVW R3, R4 +xincloop: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $1, R3 + STWCCC R3, (R4) + BNE xincloop + RETURN + +TEXT _xdec(SB),$0 /* long _xdec(long *); */ + MOVW R3, R4 +xdecloop: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + ADD $-1, R3 + STWCCC R3, (R4) + BNE xdecloop + RETURN + +TEXT cmpswap(SB),$0 /* int cmpswap(long*, long, long) */ + MOVW R3, R4 /* addr */ + MOVW old+4(FP), R5 + MOVW new+8(FP), R6 + DCBF (R4) /* fix for 603x bug? */ + LWAR (R4), R3 + CMP R3, R5 + BNE fail + STWCCC R6, (R4) + BNE fail + MOVW $1, R3 + RETURN +fail: + MOVW $0, R3 + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +TEXT getdar(SB), $0 + MOVW SPR(DAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT putsdr1(SB), $0 + MOVW R3, SPR(SDR1) + RETURN + +TEXT putsr(SB), $0 + MOVW 4(FP), R4 + MOVW R4, SEG(R3) + RETURN + +TEXT gethid0(SB), $0 + MOVW SPR(HID0), R3 + RETURN + +TEXT gethid1(SB), $0 + MOVW SPR(HID1), R3 + RETURN + +TEXT puthid0(SB), $0 + MOVW R3, SPR(HID0) + RETURN + +TEXT puthid1(SB), $0 + MOVW R3, SPR(HID1) + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT sync(SB), $0 + SYNC + RETURN + +TEXT tlbflushall(SB), $0 + MOVW $64, R3 + MOVW R3, CTR + MOVW $0, R4 +tlbflushall0: + TLBIE R4 + ADD $BIT(19), R4 + BDNZ tlbflushall0 + EIEIO + TLBSYNC + SYNC + RETURN + +TEXT tlbflush(SB), $0 + TLBIE R3 + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +/* + * traps force memory mapping off. + * the following code has been executed at the exception + * vector location + * MOVW R0, SPR(SAVER0) + * MOVW LR, R0 + * MOVW R0, SPR(SAVELR) + * bl trapvec(SB) + */ +TEXT trapvec(SB), $-4 + MOVW LR, R0 + MOVW R1, SPR(SAVER1) + MOVW R0, SPR(SAVEXX) /* vector */ + + /* did we come from user space */ + MOVW SPR(SRR1), R0 + MOVW CR, R1 + MOVW R0, CR + BC 4,17,ktrap + + /* switch to kernel stack */ + MOVW R1, CR + MOVW R2, R0 + MOVW $setSB(SB), R2 + RLWNM $0, R2, $~KZERO, R2 /* PADDR(setSB) */ + MOVW $mach0(SB), R1 /* m-> */ + RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->) */ + MOVW 8(R1), R1 /* m->proc */ + RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc) */ + MOVW 8(R1), R1 /* m->proc->kstack */ + RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc->kstack) */ + ADD $(KSTACK-UREGSIZE), R1 + MOVW R0, R2 + BL saveureg(SB) + BL trap(SB) + BR restoreureg +ktrap: + MOVW R1, CR + MOVW SPR(SAVER1), R1 + RLWNM $0, R1, $~KZERO, R1 /* PADDR(R1) */ + SUB $UREGSPACE, R1 + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + RLWNM $0, R2, $~KZERO, R2 /* PADDR(setSB) */ + MOVW $mach0(SB), R(MACH) + RLWNM $0, R(MACH), $~KZERO, R(MACH) /* PADDR(m->) */ + MOVW 8(R(MACH)), R(USER) + MOVW $mach0(SB), R(MACH) + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + MOVW SPR(SRR0), R0 + MOVW R0, 16(R1) /* old PC */ + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) /* old status */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + OR $KZERO, R3 /* fix ureg */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(MSR_IR|MSR_DR|MSR_FP|MSR_RI), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + OR $KZERO, R1 /* fix stack pointer */ + RFI /* returns to trap handler */ + +/* + * restore state from Ureg and return from trap/interrupt + */ +TEXT forkret(SB), $0 + BR restoreureg + +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, LR + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVER0), R0 + RFI + +TEXT fpsave(SB), $0 + FMOVD F0, (0*8)(R3) + FMOVD F1, (1*8)(R3) + FMOVD F2, (2*8)(R3) + FMOVD F3, (3*8)(R3) + FMOVD F4, (4*8)(R3) + FMOVD F5, (5*8)(R3) + FMOVD F6, (6*8)(R3) + FMOVD F7, (7*8)(R3) + FMOVD F8, (8*8)(R3) + FMOVD F9, (9*8)(R3) + FMOVD F10, (10*8)(R3) + FMOVD F11, (11*8)(R3) + FMOVD F12, (12*8)(R3) + FMOVD F13, (13*8)(R3) + FMOVD F14, (14*8)(R3) + FMOVD F15, (15*8)(R3) + FMOVD F16, (16*8)(R3) + FMOVD F17, (17*8)(R3) + FMOVD F18, (18*8)(R3) + FMOVD F19, (19*8)(R3) + FMOVD F20, (20*8)(R3) + FMOVD F21, (21*8)(R3) + FMOVD F22, (22*8)(R3) + FMOVD F23, (23*8)(R3) + FMOVD F24, (24*8)(R3) + FMOVD F25, (25*8)(R3) + FMOVD F26, (26*8)(R3) + FMOVD F27, (27*8)(R3) + FMOVD F28, (28*8)(R3) + FMOVD F29, (29*8)(R3) + FMOVD F30, (30*8)(R3) + FMOVD F31, (31*8)(R3) + MOVFL FPSCR, F0 + FMOVD F0, (32*8)(R3) + RETURN + +TEXT fprestore(SB), $0 + FMOVD (32*8)(R3), F0 + MOVFL F0, FPSCR + FMOVD (0*8)(R3), F0 + FMOVD (1*8)(R3), F1 + FMOVD (2*8)(R3), F2 + FMOVD (3*8)(R3), F3 + FMOVD (4*8)(R3), F4 + FMOVD (5*8)(R3), F5 + FMOVD (6*8)(R3), F6 + FMOVD (7*8)(R3), F7 + FMOVD (8*8)(R3), F8 + FMOVD (9*8)(R3), F9 + FMOVD (10*8)(R3), F10 + FMOVD (11*8)(R3), F11 + FMOVD (12*8)(R3), F12 + FMOVD (13*8)(R3), F13 + FMOVD (14*8)(R3), F14 + FMOVD (15*8)(R3), F15 + FMOVD (16*8)(R3), F16 + FMOVD (17*8)(R3), F17 + FMOVD (18*8)(R3), F18 + FMOVD (19*8)(R3), F19 + FMOVD (20*8)(R3), F20 + FMOVD (21*8)(R3), F21 + FMOVD (22*8)(R3), F22 + FMOVD (23*8)(R3), F23 + FMOVD (24*8)(R3), F24 + FMOVD (25*8)(R3), F25 + FMOVD (26*8)(R3), F26 + FMOVD (27*8)(R3), F27 + FMOVD (28*8)(R3), F28 + FMOVD (29*8)(R3), F29 + FMOVD (30*8)(R3), F30 + FMOVD (31*8)(R3), F31 + RETURN diff --git a/sys/src/9/mtx/main.c b/sys/src/9/mtx/main.c new file mode 100755 index 000000000..eebad4444 --- /dev/null +++ b/sys/src/9/mtx/main.c @@ -0,0 +1,465 @@ +#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" + +Conf conf; +FPsave initfp; + +void +main(void) +{ + memset(edata, 0, (ulong)end-(ulong)edata); + conf.nmach = 1; + machinit(); + ioinit(); + i8250console(); + quotefmtinstall(); + print("\nPlan 9\n"); + confinit(); + xinit(); + raveninit(); + trapinit(); + printinit(); + cpuidprint(); + mmuinit(); + hwintrinit(); + clockinit(); + kbdinit(); + procinit0(); + initseg(); + timersinit(); + links(); + chandevreset(); + pageinit(); + swapinit(); + fpsave(&initfp); + initfp.fpscr = 0; + userinit(); + schedinit(); +} + +void +machinit(void) +{ + memset(m, 0, sizeof(Mach)); + m->cputype = getpvr()>>16; + + /* + * For polled uart output at boot, need + * a default delay constant. 100000 should + * be enough for a while. Cpuidentify will + * calculate the real value later. + */ + m->loopconst = 100000; + + /* turn on caches */ + puthid0(gethid0() | BIT(16) | BIT(17)); + + active.machs = 1; + active.exiting = 0; +} + +void +cpuidprint(void) +{ + char *id; + + id = "unknown PowerPC"; + switch(m->cputype) { + case 9: + id = "PowerPC 604e"; + break; + } + print("cpu0: %s\n", id); +} + +static struct +{ + char *name; + char *val; +} +plan9ini[] = +{ + { "console", "0" }, + { "ether0", "type=2114x" }, +}; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nelem(plan9ini); i++) + if(cistrcmp(name, plan9ini[i].name) == 0) + return plan9ini[i].val; + return nil; +} + +void +init0(void) +{ +// char **p, *q, name[KNAMELEN]; +// int n; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + chandevinit(); + + if(!waserror()){ + snprint(buf, sizeof(buf), "power %s mtx", conffile); + ksetenv("terminal", buf, 0); + ksetenv("cputype", "power", 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + +/* + for(p = confenv; *p; p++) { + q = strchr(p[0], '='); + if(q == 0) + continue; + n = q-p[0]; + if(n >= KNAMELEN) + n = KNAMELEN-1; + memmove(name, p[0], n); + name[n] = 0; + if(name[0] != '*') + ksetenv(name, q+1, 0); + ksetenv(name, q+1, 1); + } +*/ + poperror(); + } + kproc("alarm", alarmkproc, 0); + kproc("mmusweep", mmusweep, 0); + touser((void*)(USTKTOP-8)); +} + +void +userinit(void) +{ + Proc *p; + Segment *s; + KMap *k; + Page *pg; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + p->fpstate = FPinit; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); + + /* + * User Stack + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + segpage(s, pg); + + /* + * Text + */ + s = newseg(SG_TEXT, UTZERO, 1); + s->flushme++; + p->seg[TSEG] = s; + pg = newpage(1, 0, UTZERO); + memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); + segpage(s, pg); + k = kmap(s->map[0]->pages[0]); + memmove((ulong*)VA(k), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +/* still to do */ +void +reboot(void*, void*, ulong) +{ + exit(0); +} + +void +exit(int ispanic) +{ + int ms, once; + + lock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<<m->machno); + active.machs &= ~(1<<m->machno); + active.exiting = 1; + unlock(&active); + + if(once) + print("cpu%d: exiting\n", m->machno); + spllo(); + for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ + delay(TK2MS(2)); + if(active.machs == 0 && consactive() == 0) + break; + } + + if(active.ispanic && m->machno == 0){ + if(cpuserver) + delay(10000); + else if(conf.monitor) + for(;;); + } + else + delay(1000); + + watchreset(); +} + +/* + * set up floating point for a new process + */ +void +procsetup(Proc *p) +{ + p->fpstate = FPinit; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + if(p->fpstate == FPactive){ + if(p->state != Moribund) + fpsave(&up->fpsave); + p->fpstate = FPinactive; + } +} + +void +confinit(void) +{ + char *p; + int userpcnt; + ulong pa, kpages; + extern ulong memsize; /* passed in from ROM monitor */ + + if(p = getconf("*kernelpercent")) + userpcnt = 100 - strtol(p, 0, 0); + else + userpcnt = 0; + + pa = PGROUND(PADDR(end)); + + conf.mem[0].npage = memsize/BY2PG; + conf.mem[0].base = pa; + conf.npage = conf.mem[0].npage; + + conf.nmach = 1; + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + if(cpuserver) + conf.nproc *= 3; + if(conf.nproc > 2000) + conf.nproc = 2000; + conf.nimage = 200; + conf.nswap = conf.nproc*80; + conf.nswppo = 4096; + conf.copymode = 0; /* copy on write */ + + if(cpuserver) { + if(userpcnt < 10) + userpcnt = 70; + kpages = conf.npage - (conf.npage*userpcnt)/100; + + /* + * Hack for the big boys. Only good while physmem < 4GB. + * Give the kernel a max. of 16MB + enough to allocate the + * page pool. + * This is an overestimate as conf.upages < conf.npages. + * The patch of nimage is a band-aid, scanning the whole + * page list in imagereclaim just takes too long. + */ + if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){ + kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG; + conf.nimage = 2000; + kpages += (conf.nproc*KSTACK)/BY2PG; + } + } else { + if(userpcnt < 10) { + if(conf.npage*BY2PG < 16*MB) + userpcnt = 40; + else + userpcnt = 60; + } + kpages = conf.npage - (conf.npage*userpcnt)/100; + + /* + * Make sure terminals with low memory get at least + * 4MB on the first Image chunk allocation. + */ + if(conf.npage*BY2PG < 16*MB) + imagmem->minarena = 4*1024*1024; + } + conf.upages = conf.npage - kpages; + conf.ialloc = (kpages/2)*BY2PG; + + /* + * Guess how much is taken by the large permanent + * datastructures. Mntcache and Mntrpc are not accounted for + * (probably ~300KB). + */ + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page); + mainmem->maxsize = kpages; + if(!cpuserver){ + /* + * give terminals lots of image memory, too; the dynamic + * allocation will balance the load properly, hopefully. + * be careful with 32-bit overflow. + */ + imagmem->maxsize = kpages; + } + +// conf.monitor = 1; /* BUG */ +} + +static int +getcfields(char* lp, char** fields, int n, char* sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp && strchr(sep, *lp) != 0) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && strchr(sep, *lp) == 0){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + + return i; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + int i; + char cc[KNAMELEN], *p; + + sprint(cc, "%s%d", class, ctlrno); + + p = getconf(cc); + if(p == 0) + return 0; + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + } + return 1; +} + +int +cistrcmp(char *a, char *b) +{ + int ac, bc; + + for(;;){ + ac = *a++; + bc = *b++; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + return 0; +} + +int +cistrncmp(char *a, char *b, int n) +{ + unsigned ac, bc; + + while(n > 0){ + ac = *a++; + bc = *b++; + n--; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + + return 0; +} diff --git a/sys/src/9/mtx/mem.h b/sys/src/9/mtx/mem.h new file mode 100755 index 000000000..05c01ff8a --- /dev/null +++ b/sys/src/9/mtx/mem.h @@ -0,0 +1,195 @@ +/* + * 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 BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 4 +#define CACHELINESZ (1<<CACHELINELOG) +#define BLOCKALIGN CACHELINESZ + +#define MHz 1000000 + +#define BY2PTE 8 /* bytes per pte entry */ +#define BY2PTEG 64 /* bytes per pte group */ + +#define MAXMACH 1 /* max # cpus system can run */ +#define MACHSIZE BY2PG +#define KSTACK 4096 /* Size of kernel stack */ + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Standard PPC Special Purpose Registers (OEA and VEA) + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SDR1 25 +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define ASR 280 /* Address Space Register */ +#define EAR 282 /* External Access Register (optional) */ +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 284 /* Time base Upper/Lower (Writing) */ +#define TBWL 285 +#define PVR 287 /* Processor Version */ +#define IABR 1010 /* Instruction Address Breakpoint Register (optional) */ +#define DABR 1013 /* Data Address Breakpoint Register (optional) */ +#define FPECR 1022 /* Floating-Point Exception Cause Register (optional) */ +#define PIR 1023 /* Processor Identification Register (optional) */ + +#define IBATU(i) (528+2*(i)) /* Instruction BAT register (upper) */ +#define IBATL(i) (529+2*(i)) /* Instruction BAT register (lower) */ +#define DBATU(i) (536+2*(i)) /* Data BAT register (upper) */ +#define DBATL(i) (537+2*(i)) /* Data BAT register (lower) */ + +/* + * PPC604e-specific Special Purpose Registers (OEA) + */ +#define HID0 1008 /* Hardware Implementation Dependant Register 0 */ +#define HID1 1009 /* Hardware Implementation Dependant Register 1 */ +#define PMC1 953 /* Performance Monitor Counter 1 */ +#define PMC2 954 /* Performance Monitor Counter 2 */ +#define PMC3 957 /* Performance Monitor Counter 3 */ +#define PMC4 958 /* Performance Monitor Counter 4 */ +#define MMCR0 952 /* Monitor Control Register 0 */ +#define MMCR1 956 /* Monitor Control Register 0 */ +#define SIA 955 /* Sampled Instruction Address */ +#define SDA 959 /* Sampled Data Address */ + +#define BIT(i) (1<<(31-(i))) /* Silly backwards register bit numbering scheme */ + +/* + * Bit encodings for Machine State Register (MSR) + */ +#define MSR_POW BIT(13) /* Enable Power Management */ +#define MSR_ILE BIT(15) /* Interrupt Little-Endian enable */ +#define MSR_EE BIT(16) /* External Interrupt enable */ +#define MSR_PR BIT(17) /* Supervisor/User privelege */ +#define MSR_FP BIT(18) /* Floating Point enable */ +#define MSR_ME BIT(19) /* Machine Check enable */ +#define MSR_FE0 BIT(20) /* Floating Exception mode 0 */ +#define MSR_SE BIT(21) /* Single Step (optional) */ +#define MSR_BE BIT(22) /* Branch Trace (optional) */ +#define MSR_FE1 BIT(23) /* Floating Exception mode 1 */ +#define MSR_IP BIT(25) /* Exception prefix 0x000/0xFFF */ +#define MSR_IR BIT(26) /* Instruction MMU enable */ +#define MSR_DR BIT(27) /* Data MMU enable */ +#define MSR_PM BIT(29) /* Performance Monitor marked mode (604e specific) */ +#define MSR_RI BIT(30) /* Recoverable Exception */ +#define MSR_LE BIT(31) /* Little-Endian enable */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +#define CFPU 0x08 +#define CDEC 0x09 +#define CSYSCALL 0x0C +#define CTRACE 0x0D /* optional */ +#define CFPA 0x0E /* optional */ + +/* PPC604e-specific: */ +#define CPERF 0x0F /* performance monitoring */ +#define CIBREAK 0x13 +#define CSMI 0x14 + +/* + * Magic registers + */ + +#define MACH 30 /* R30 is m-> */ +#define USER 29 /* R29 is up-> */ + + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 +#define SSEGMAPSIZE 16 +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * First pte word + */ +#define PTE0(v, vsid, h, va) (((v)<<31)|((vsid)<<7)|((h)<<6)|(((va)>>22)&0x3f)) + +/* + * Second pte word; WIMG & PP(RW/RO) common to page table and BATs + */ +#define PTE1_W BIT(25) +#define PTE1_I BIT(26) +#define PTE1_M BIT(27) +#define PTE1_G BIT(28) + +#define PTE1_RW BIT(30) +#define PTE1_RO BIT(31) + +/* + * PTE bits for fault.c. These belong to the second PTE word. Validity is + * implied for putmmu(), and we always set PTE0_V. PTEVALID is used + * here to set cache policy bits on a global basis. + */ +#define PTEVALID 0 +#define PTEWRITE PTE1_RW +#define PTERONLY PTE1_RO +#define PTEUNCACHED PTE1_I + +/* + * Address spaces + */ + +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */ +#define TSTKTOP KZERO /* top of temporary stack */ +#define TSTKSIZ 100 +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO (KZERO+0x4000) /* first address in kernel text */ +#define USTKSIZE (4*1024*1024) /* size of user stack */ +#define UREGSIZE ((8+32)*4) + +#define PCIMEM0 0xf0000000 +#define PCISIZE0 0x0e000000 +#define PCIMEM1 0xc0000000 +#define PCISIZE1 0x30000000 +#define IOMEM 0xfe000000 +#define IOSIZE 0x00800000 +#define FALCON 0xfef80000 +#define RAVEN 0xfeff0000 +#define FLASHA 0xff000000 +#define FLASHB 0xff800000 +#define FLASHAorB 0xfff00000 + +#define isphys(x) (((ulong)x&KZERO)!=0) + +#define getpgcolor(a) 0 diff --git a/sys/src/9/mtx/mkfile b/sys/src/9/mtx/mkfile new file mode 100755 index 000000000..af7f50f02 --- /dev/null +++ b/sys/src/9/mtx/mkfile @@ -0,0 +1,96 @@ +CONF=mtx +CONFLIST=mtx mtxcpu + +objtype=power +</$objtype/mkfile +p=9 + +DEVS=`{rc ../port/mkdevlist $CONF} + +PORT=\ + alarm.$O\ + alloc.$O\ + allocb.$O\ + auth.$O\ + cache.$O\ + chan.$O\ + dev.$O\ + edf.$O\ + fault.$O\ + latin1.$O\ + log.$O\ + mul64fract.$O\ + rebootcmd.$O\ + page.$O\ + parse.$O\ + pgrp.$O\ + portclock.$O\ + print.$O\ + proc.$O\ + qio.$O\ + qlock.$O\ + rdb.$O\ + segment.$O\ + swap.$O\ + sysfile.$O\ + sysproc.$O\ + taslock.$O\ + tod.$O\ + xalloc.$O\ + +OBJ=\ + l.$O\ + inb.$O\ + clock.$O\ + i8259.$O\ + kbd.$O\ + main.$O\ + mmu.$O\ + random.$O\ + raven.$O\ + trap.$O\ + $CONF.root.$O\ + $CONF.rootc.$O\ + $DEVS\ + $PORT\ + +LIB=\ + /$objtype/lib/libmemlayer.a\ + /$objtype/lib/libmemdraw.a\ + /$objtype/lib/libdraw.a\ + /$objtype/lib/libip.a\ + /$objtype/lib/libc.a\ + /$objtype/lib/libsec.a\ + +ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'} +VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'} +SDEV=`{echo devsd.c sd*.c | sed 's/\.c/.'$O'/g'} + +loadaddr = 0x80004020 + +$p$CONF: $CONF.c $OBJ $LIB + $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c + $LD -R4 -o $target -T$loadaddr -l $OBJ $CONF.$O $LIB + ls -l $target + +install:V: $p$CONF + cp $p$CONF /$objtype/$p$CONF + +<../boot/bootmkfile +<../port/portmkfile +<|../port/mkbootrules $CONF + +clock.$O: /$objtype/include/ureg.h +devether.$O: /$objtype/include/ureg.h +main.$O: /$objtype/include/ureg.h errstr.h init.h +trap.$O: /$objtype/include/ureg.h + +$ETHER: etherif.h ../port/netif.h + +init.h: initcode /sys/src/libc/9syscall/sys.h + $AS initcode + $LD -l -s -R4 -o init.out initcode.$O -lc + {echo 'uchar initcode[]={' + xd -r -1x init.out | + sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' + echo '};'} > init.h diff --git a/sys/src/9/mtx/mmu.c b/sys/src/9/mtx/mmu.c new file mode 100755 index 000000000..c43a1a7dd --- /dev/null +++ b/sys/src/9/mtx/mmu.c @@ -0,0 +1,257 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * We have one page table per processor. + * + * Different processes are distinguished via the VSID field in + * the segment registers. As flushing the entire page table is an + * expensive operation, we implement an aging algorithm for + * mmu pids, with a background kproc to purge stale pids en mass. + * + * This needs modifications to run on a multiprocessor. + */ + +static ulong ptabsize; /* number of bytes in page table */ +static ulong ptabmask; /* hash mask */ + +/* + * VSID is 24 bits. 3 are required to distinguish segments in user + * space (kernel space only uses the BATs). pid 0 is reserved. + * The top 2 bits of the pid are used as a `color' for the background + * pid reclaimation algorithm. + */ + +enum { + PIDBASE = 1, + PIDBITS = 21, + COLBITS = 2, + PIDMAX = ((1<<PIDBITS)-1), + COLMASK = ((1<<COLBITS)-1), +}; + +#define VSID(pid, i) (((pid)<<3)|i) +#define PIDCOLOR(pid) ((pid)>>(PIDBITS-COLBITS)) +#define PTECOL(color) PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0) + +void +mmuinit(void) +{ + int lhash, mem; + extern ulong memsize; /* passed in from ROM monitor */ + + if(ptabsize == 0) { + /* heuristically size the hash table */ + lhash = 10; + mem = (1<<23); + while(mem < memsize) { + lhash++; + mem <<= 1; + } + ptabsize = (1<<(lhash+6)); + ptabmask = (1<<lhash)-1; + } + + m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize); + putsdr1(PADDR(m->ptabbase) | (ptabmask>>10)); + m->mmupid = PIDBASE; + m->sweepcolor = 0; + m->trigcolor = COLMASK; +} + +static int +work(void*) +{ + return PIDCOLOR(m->mmupid) == m->trigcolor; +} + +void +mmusweep(void*) +{ + Proc *p; + int i, x, sweepcolor; + ulong *ptab, *ptabend, ptecol; + + for(;;) { + if(PIDCOLOR(m->mmupid) != m->trigcolor) + sleep(&m->sweepr, work, nil); + + sweepcolor = m->sweepcolor; + x = splhi(); + p = proctab(0); + for(i = 0; i < conf.nproc; i++, p++) + if(PIDCOLOR(p->mmupid) == sweepcolor) + p->mmupid = 0; + splx(x); + + ptab = (ulong*)m->ptabbase; + ptabend = (ulong*)(m->ptabbase+ptabsize); + ptecol = PTECOL(sweepcolor); + while(ptab < ptabend) { + if((*ptab & PTECOL(3)) == ptecol) + *ptab = 0; + ptab += 2; + } + tlbflushall(); + + m->sweepcolor = (sweepcolor+1) & COLMASK; + m->trigcolor = (m->trigcolor+1) & COLMASK; + } +} + +int +newmmupid(void) +{ + int pid, newcolor; + + pid = m->mmupid++; + if(m->mmupid > PIDMAX) + m->mmupid = PIDBASE; + newcolor = PIDCOLOR(m->mmupid); + if(newcolor != PIDCOLOR(pid)) { + if(newcolor == m->sweepcolor) { + /* desperation time. can't block here. punt to fault/putmmu */ + print("newmmupid: %uld: no free mmu pids\n", up->pid); + if(m->mmupid == PIDBASE) + m->mmupid = PIDMAX; + else + m->mmupid--; + pid = 0; + } + else if(newcolor == m->trigcolor) + wakeup(&m->sweepr); + } + up->mmupid = pid; + return pid; +} + +void +flushmmu(void) +{ + int x; + + x = splhi(); + up->newtlb = 1; + mmuswitch(up); + splx(x); +} + +/* + * called with splhi + */ +void +mmuswitch(Proc *p) +{ + int i, mp; + + if(p->kp) { + for(i = 0; i < 8; i++) + putsr(i<<28, 0); + return; + } + + if(p->newtlb) { + p->mmupid = 0; + p->newtlb = 0; + } + mp = p->mmupid; + if(mp == 0) + mp = newmmupid(); + + for(i = 0; i < 8; i++) + putsr(i<<28, VSID(mp, i)|BIT(1)|BIT(2)); +} + +void +mmurelease(Proc* p) +{ + p->mmupid = 0; +} + +void +putmmu(ulong va, ulong pa, Page *pg) +{ + int mp; + char *ctl; + ulong *p, *ep, *q, pteg; + ulong vsid, ptehi, x, hash; + + /* + * If mmupid is 0, mmuswitch/newmmupid was unable to assign us + * a pid, hence we faulted. Keep calling sched() until the mmusweep + * proc catches up, and we are able to get a pid. + */ + while((mp = up->mmupid) == 0) + sched(); + + vsid = VSID(mp, va>>28); + hash = (vsid ^ (va>>12)&0xffff) & ptabmask; + ptehi = PTE0(1, vsid, 0, va); + + pteg = m->ptabbase + BY2PTEG*hash; + p = (ulong*)pteg; + ep = (ulong*)(pteg+BY2PTEG); + q = nil; + tlbflush(va); + while(p < ep) { + x = p[0]; + if(x == ptehi) { + q = p; + break; + } + if(q == nil && (x & BIT(0)) == 0) + q = p; + p += 2; + } + if(q == nil) { + q = (ulong*)(pteg+m->slotgen); + m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1); + } + q[0] = ptehi; + q[1] = pa; + sync(); + + ctl = &pg->cachectl[m->machno]; + switch(*ctl) { + case PG_NEWCOL: + default: + panic("putmmu: %d\n", *ctl); + break; + case PG_NOFLUSH: + break; + case PG_TXTFLUSH: + dcflush((void*)pg->va, BY2PG); + icflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + } +} + +void +checkmmu(ulong, ulong) +{ +} + +void +countpagerefs(ulong*, int) +{ +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +ulong +cankaddr(ulong pa) +{ + ulong kzero; + + kzero = -KZERO; + if(pa >= kzero) + return 0; + return kzero - pa; +} diff --git a/sys/src/9/mtx/mtx b/sys/src/9/mtx/mtx new file mode 100755 index 000000000..47c2441f5 --- /dev/null +++ b/sys/src/9/mtx/mtx @@ -0,0 +1,46 @@ +dev + root + cons + arch + pnp pci + env + pipe + proc + mnt + srv + dup + ssl + cap + kprof + uart + rtc + + ether netif + ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno + +link + ether2114x pci + ethermedium + netdevmedium + +misc + uarti8250 + +ip + tcp + udp + ipifc + icmp + icmp6 + +port + int cpuserver = 0; + +boot + tcp + +bootdir + bootmtx.out boot + /power/bin/ip/ipconfig + /power/bin/auth/factotum + diff --git a/sys/src/9/mtx/mtxcpu b/sys/src/9/mtx/mtxcpu new file mode 100755 index 000000000..b64090725 --- /dev/null +++ b/sys/src/9/mtx/mtxcpu @@ -0,0 +1,46 @@ +dev + root + cons + arch + pnp pci + env + pipe + proc + mnt + srv + dup + ssl + cap + kprof + uart + rtc + + ether netif + ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno + +link + ether2114x pci + ethermedium + netdevmedium + +misc + uarti8250 + +ip + tcp + udp + ipifc + icmp + icmp6 + +port + int cpuserver = 1; + +boot cpu + tcp + +bootdir + bootmtxcpu.out boot + ipconfig.hack ipconfig + factotum.hack factotum + diff --git a/sys/src/9/mtx/pci.c b/sys/src/9/mtx/pci.c new file mode 100755 index 000000000..73c4d641e --- /dev/null +++ b/sys/src/9/mtx/pci.c @@ -0,0 +1,908 @@ +/* + * PCI support code. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(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); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + PciADDR = 0xCF8, /* CONFIG_ADDRESS */ + PciDATA = 0xCFC, /* CONFIG_DATA */ + + /* configuration mechanism #2 */ + PciCSE = 0xCF8, /* configuration space enable */ + PciFORWARD = 0xCFA, /* which bus */ + + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static QLock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +static char* bustypes[] = { + "CBUSI", + "CBUSII", + "EISA", + "FUTURE", + "INTERN", + "ISA", + "MBI", + "MBII", + "MCA", + "MPI", + "MPSA", + "NUBUS", + "PCI", + "PCMCIA", + "TC", + "VL", + "VME", + "XPRESS", +}; + +#pragma varargck type "T" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru == 0x04 && p->bridge != nil) { + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + } + if((pcicfgr8(p, PciHDT)&0x7f) != 0) + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciIBR, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciIBR, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, PciIUBR, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciMBR, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + v = 0xFFFF0000 | IOen | MEMen | MASen; + pcicfgrw32(p->tbdf, PciPCR, v, 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x06: /* bridge device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + qlock(&pcicfginitlock); + ubn = pcilscan(bno, list); + qunlock(&pcicfginitlock); + return ubn; +} + +static void +pcicfginit(void) +{ + char *p; + int bno; + Pcidev **list; + ulong mema, ioa; + + qlock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + /* + * Try to determine which PCI configuration mode is implemented. + * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses + * a DWORD at 0xCF8 and another at 0xCFC and will pass through + * any non-DWORD accesses as normal I/O cycles. There shouldn't be + * a device behind these addresses so if Mode2 accesses fail try + * for Mode1 (which is preferred, Mode2 is deprecated). + */ + outb(PciCSE, 0); + if(inb(PciCSE) == 0){ + pcicfgmode = 2; + pcimaxdno = 15; + } + else { + outl(PciADDR, 0); + if(inl(PciADDR) == 0){ + pcicfgmode = 1; + pcimaxdno = 31; + } + } + + if(pcicfgmode < 0) + goto out; + + fmtinstall('T', tbdffmt); + + if(p = getconf("*pcimaxbno")) + pcimaxbno = strtoul(p, 0, 0); + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + +out: + qunlock(&pcicfginitlock); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x03; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inb(PciDATA+o); + else + outb(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x02; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = ins(PciDATA+o); + else + outs(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inl(PciDATA); + else + outl(PciDATA, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + qlock(&pcicfginitlock); + pcilhinv(p); + qunlock(&pcicfginitlock); +} + +void +pcireset(void) +{ + Pcidev *p; + int pcr; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + pcr = pcicfgr16(p, PciPCR); + pcr &= ~0x0004; + pcicfgw16(p, PciPCR, pcr); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= MASen; + pcicfgw16(p, PciPCR, pcr); +} + +void +pciclrbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr &= ~MASen; + pcicfgw16(p, PciPCR, pcr); +} diff --git a/sys/src/9/mtx/random.c b/sys/src/9/mtx/random.c new file mode 100755 index 000000000..3c204b8d1 --- /dev/null +++ b/sys/src/9/mtx/random.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +struct Rb +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar wakeme; + ushort bits; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.rp - rb.wp; + return i != 1 && i != (1 - sizeof(rb.buf)); +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + up->basepri = PriNormal; + up->priority = up->basepri; + + for(;;){ + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + if(rb.randomcount == 0 || !rbnotfull(0)) + return; + + rb.bits = (rb.bits<<2) ^ rb.randomcount; + rb.randomcount = 0; + + rb.next++; + if(rb.next != 8/2) + return; + rb.next = 0; + + *rb.wp ^= rb.bits; + if(rb.wp+1 == rb.ep) + rb.wp = rb.buf; + else + rb.wp = rb.wp+1; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + addclock0link(randomclock, 1000/HZ); + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; + kproc("genrandom", genrandom, 0); +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + uchar *e, *p; + ulong x; + + p = xp; + + if(waserror()){ + qunlock(&rb); + nexterror(); + } + + qlock(&rb); + for(e = p + n; p < e; ){ + if(rb.wp == rb.rp){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + + /* + * beating clocks will be precictable if + * they are synchronized. Use a cheap pseudo + * random number generator to obscure any cycles. + */ + x = rb.randn*1103515245 ^ *rb.rp; + *p++ = rb.randn = x; + + if(rb.rp+1 == rb.ep) + rb.rp = rb.buf; + else + rb.rp = rb.rp+1; + } + qunlock(&rb); + poperror(); + + wakeup(&rb.producer); + + return n; +} diff --git a/sys/src/9/mtx/raven.c b/sys/src/9/mtx/raven.c new file mode 100755 index 000000000..d6fcb0179 --- /dev/null +++ b/sys/src/9/mtx/raven.c @@ -0,0 +1,153 @@ +/* + * ``Nevermore!'' + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Raven Raven; +struct Raven +{ + ushort vid; + ushort did; + uchar _pad4; + uchar rev; + uchar _pad6[2]; + ushort gcsr; + ushort feat; + uchar _padC[7]; + uchar padj; + uchar _pad14[12]; + ushort errtst; + ushort erren; + uchar _pad24[3]; + uchar errst; + ulong errad; + uchar _pad2C[2]; + ushort errat; + ulong piack; + uchar _pad34[12]; + + struct { + ushort start; + ushort end; + ushort off; + uchar _pad; + uchar attr; + } map[4]; + + struct { + ulong cntl; + uchar _pad[3]; + uchar stat; + } wdt[2]; + + ulong gpr[4]; +}; + +enum { + /* map[] attr bits */ + Iom = (1<<0), + Mem = (1<<1), + Wpen = (1<<4), + Wen = (1<<6), + Ren = (1<<7), +}; + +static Raven *raven = (Raven*)RAVEN; +static ulong mpic; + +static void +setmap(int i, ulong addr, ulong len, ulong busaddr, int attr) +{ + raven->map[i].start = addr>>16; + raven->map[i].end = (addr+len-1)>>16; + raven->map[i].off = (busaddr-addr)>>16; + raven->map[i].attr = attr; +} + +static ulong +swap32(ulong x) +{ + return (x>>24)|((x>>8)&0xff00)|((x<<8)&0xff0000)|(x<<24); +} + +static ulong +mpic32r(int rno) +{ + return swap32(*(ulong*)(mpic+rno)); +} + +static void +mpic32w(int rno, ulong x) +{ + *(ulong*)(mpic+rno) = swap32(x); + eieio(); +} + +void +raveninit(void) +{ + int i; + Pcidev *p; + + if(raven->vid != 0x1057 || raven->did !=0x4801) + panic("raven not found"); + + /* set up a sensible hardware memory/IO map */ + setmap(0, PCIMEM0, PCISIZE0, 0, Wen|Ren|Mem); + setmap(1, KZERO, IOSIZE, 0, Wen|Ren); /* keeps PPCbug happy */ + setmap(2, PCIMEM1, PCISIZE1, PCISIZE0, Wen|Ren|Mem); + setmap(3, IOMEM, IOSIZE, 0, Wen|Ren); /* I/O must be slot 3 for PCI cfg space */ + + p = pcimatch(nil, 0x1057, 0x4801); + if(p == nil) + panic("raven PCI regs not found"); + mpic = (p->mem[1].bar+PCIMEM0); + + /* ensure all interrupts are off, and routed to cpu 0 */ + for(i = 0; i < 16; i++) { + mpic32w(0x10000+0x20*i, (1<<31)); /* mask */ + mpic32w(0x10010+0x20*i, 1); /* route to cpu 0 */ + } + + mpic32w(0x20080, 1); /* cpu 0 task pri */ +// mpic32w(0x21080, 1); /* cpu 1 task pri */ + mpic32w(0x1020, (1<<29)); /* Mixed mode (8259 & Raven intrs both available) */ +} + +void +mpicenable(int vec, Vctl *v) +{ + ulong x; + + x = (1<<22)|(15<<16)|vec; + if(vec == 0) + x |= (1<<23); + mpic32w(0x10000+0x20*vec, x); + if(vec != 0) + v->eoi = mpiceoi; +} + +void +mpicdisable(int vec) +{ + mpic32w(0x10000+0x20*vec, (1<<31)); +} + +int +mpicintack(void) +{ + return mpic32r(0x200A0 + (m->machno<<12)); +} + +int +mpiceoi(int vec) +{ + USED(vec); + mpic32w(0x200B0 + (m->machno<<12), 0); + return 0; +} diff --git a/sys/src/9/mtx/trap.c b/sys/src/9/mtx/trap.c new file mode 100755 index 000000000..767e96c3b --- /dev/null +++ b/sys/src/9/mtx/trap.c @@ -0,0 +1,836 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "tos.h" +#include "../port/error.h" + +static Lock vctllock; +static Vctl *vctl[256]; + +void +hwintrinit(void) +{ + i8259init(); + mpicenable(0, nil); /* 8259 interrupts are routed through MPIC intr 0 */ +} + +static int +hwintrenable(Vctl *v) +{ + int vec, irq; + + irq = v->irq; + if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */ + if(irq > 15) { + print("intrenable: pci irq %d out of range\n", v->irq); + return -1; + } + vec = irq; + mpicenable(vec, v); + } + else { + if(irq > MaxIrqPIC) { + print("intrenable: irq %d out of range\n", v->irq); + return -1; + } + vec = irq+VectorPIC; + if(i8259enable(v) == -1) + return -1; + } + return vec; +} + +static int +hwintrdisable(Vctl *v) +{ + int vec, irq; + + irq = v->irq; + if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */ + if(irq > 15) { + print("intrdisable: pci irq %d out of range\n", v->irq); + return -1; + } + vec = irq; + mpicdisable(vec); + } + else { + if(irq > MaxIrqPIC) { + print("intrdisable: irq %d out of range\n", v->irq); + return -1; + } + vec = irq+VectorPIC; + if(i8259disable(irq) == -1) + return -1; + } + return vec; +} + +static int +hwvecno(int irq, int tbdf) +{ + if(BUSTYPE(tbdf) == BusPCI) /* MPIC? */ + return irq; + else + return irq+VectorPIC; +} + +void +intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) +{ + int vno; + Vctl *v; + + if(f == nil){ + print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n", + irq, tbdf, name); + return; + } + + v = xalloc(sizeof(Vctl)); + v->isintr = 1; + v->irq = irq; + v->tbdf = tbdf; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN-1); + v->name[KNAMELEN-1] = 0; + + ilock(&vctllock); + vno = hwintrenable(v); + if(vno == -1){ + iunlock(&vctllock); + print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n", + irq, tbdf, v->name); + xfree(v); + return; + } + if(vctl[vno]){ + if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi) + panic("intrenable: handler: %s %s %#p %#p %#p %#p", + vctl[vno]->name, v->name, + vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); + v->next = vctl[vno]; + } + vctl[vno] = v; + iunlock(&vctllock); +} + +void +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) +{ + Vctl **pv, *v; + int vno; + + vno = hwvecno(irq, tbdf); + ilock(&vctllock); + pv = &vctl[vno]; + while (*pv && + ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || + strcmp((*pv)->name, name))) + pv = &((*pv)->next); + assert(*pv); + + v = *pv; + *pv = (*pv)->next; /* Link out the entry */ + + if(vctl[vno] == nil) + hwintrdisable(v); + iunlock(&vctllock); + xfree(v); +} + +void syscall(Ureg*); +void noted(Ureg*, ulong); +static void _dumpstack(Ureg*); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "reserved A", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "reserved 10", + "reserved 11", + "reserved 12", + "instruction address breakpoint", + "system management interrupt", +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +trap(Ureg *ureg) +{ + ulong dsisr; + int ecode, user; + char buf[ERRMAX], *s; + + ecode = (ureg->cause >> 8) & 0xff; + user = (ureg->srr1 & MSR_PR) != 0; + if(user) + up->dbgreg = ureg; + + if(ureg->status & MSR_RI == 0) + print("double fault?: ecode = %d\n", ecode); + + switch(ecode) { + case CEI: + intr(ureg); + break; + case CDEC: + clockintr(ureg); + break; + case CSYSCALL: + if(!user) + panic("syscall in kernel: srr1 0x%4.4luX", ureg->srr1); + syscall(ureg); + return; /* syscall() calls notify itself, don't do it again */ + case CFPU: + if(!user || up == nil) { + dumpregs(ureg); + panic("floating point in kernel"); + } + switch(up->fpstate){ + case FPinit: + fprestore(&initfp); + up->fpstate = FPactive; + break; + case FPinactive: + fprestore(&up->fpsave); + up->fpstate = FPactive; + break; + default: + panic("fpstate"); + } + ureg->srr1 |= MSR_FP; + break; + case CISI: + faultpower(ureg, ureg->pc, 1); + break; + case CDSI: + dsisr = getdsisr(); + if(dsisr & BIT(6)) + faultpower(ureg, getdar(), 0); + else + faultpower(ureg, getdar(), 1); + break; + case CPROG: + if(ureg->status & (1<<19)) + s = "floating point exception"; + else if(ureg->status & (1<<18)) + s = "illegal instruction"; + else if(ureg->status & (1<<17)) + s = "privileged instruction"; + else + s = "undefined program exception"; + if(user){ + spllo(); + sprint(buf, "sys: trap: %s", s); + postnote(up, 1, buf, NDebug); + break; + } + dumpregs(ureg); + panic(s); + break; + default: + if(ecode < nelem(excname) && user){ + spllo(); + sprint(buf, "sys: trap: %s", excname[ecode]); + postnote(up, 1, buf, NDebug); + break; + } + dumpregs(ureg); + if(ecode < nelem(excname)) + panic("%s", excname[ecode]); + panic("unknown trap/intr: %d", ecode); + } + + /* restoreureg must execute at high IPL */ + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && ecode == CDEC){ + sched(); + splhi(); + } + + if(user) { + notify(ureg); + if(up->fpstate == FPinactive) + ureg->srr1 &= ~MSR_FP; + kexit(ureg); + } +} + +void +faultpower(Ureg *ureg, ulong addr, int read) +{ + int user, insyscall, n; + char buf[ERRMAX]; + + user = (ureg->srr1 & MSR_PR) != 0; + insyscall = up->insyscall; + up->insyscall = 1; + n = fault(addr, read); + if(n < 0){ + if(!user){ + dumpregs(ureg); + panic("fault: 0x%lux", addr); + } + sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr); + postnote(up, 1, buf, NDebug); + } + up->insyscall = insyscall; +} + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +trapinit(void) +{ + int i; + + /* + * set all exceptions to trap + */ + for(i = 0; i < 0x2000; i += 0x100) + sethvec(i, trapvec); + + putmsr(getmsr() & ~MSR_IP); +} + +void +intr(Ureg *ureg) +{ + int vno; + Vctl *ctl, *v; + + vno = mpicintack(); + if(vno == 0) { /* 8259, wired through MPIC vec 0 */ + vno = i8259intack(); + mpiceoi(0); + } + + if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) { + panic("spurious intr %d", vno); + return; + } + + if(ctl->isr) + ctl->isr(vno); + for(v = ctl; v != nil; v = v->next){ + if(v->f) + v->f(ureg, v->a); + } + if(ctl->eoi) + ctl->eoi(vno); + + if(up) + preempted(); +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<<i)) + s = fpcause[i]; + if(s == 0) + return "no floating point exception"; + sprint(buf, "%s fppc=0x%lux", s, fppc); + return buf; +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ + +static void +getpcsp(ulong *pc, ulong *sp) +{ + *pc = getcallerpc(&pc); + *sp = (ulong)&pc-4; +} + +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp); + ureg.lr = getcallerpc(&fn); + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong l, sl, el, v; + int i; + + l = (ulong)&l; + if(up == 0){ + el = (ulong)m+BY2PG; + sl = el-KSTACK; + } + else{ + sl = (ulong)up->kstack; + el = sl + KSTACK; + } + if(l > el || l < sl){ + el = (ulong)m+BY2PG; + sl = el-KSTACK; + } + if(l > el || l < sl) + return; + print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr); + i = 0; + for(; l < el; l += 4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%.8lux=%.8lux ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->srr1 & MSR_PR == 0) + if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + print("dsisr\t%.8lux\tdar\t%.8lux\n", getdsisr(), getdar()); + l = &ur->cause; + for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2) + print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]); +} + +static void +linkproc(void) +{ + spllo(); + (*up->kpfun)(up->kparg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +/* + * called in sysfile.c + */ +void +evenaddr(ulong addr) +{ + if(addr & 3){ + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} + +long +execregs(ulong entry, ulong ssize, ulong nargs) +{ + ulong *sp; + Ureg *ureg; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ureg = up->dbgreg; + ureg->usp = (ulong)sp; + ureg->pc = entry; + ureg->srr1 &= ~MSR_FP; + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (ulong)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + cur->r3 = 0; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +ulong +userpc(void) +{ + Ureg *ureg; + + ureg = (Ureg*)up->dbgreg; + return ureg->pc; +} + + +/* This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg* ureg, Proc* p) +{ + ureg->pc = p->sched.pc; + ureg->sp = p->sched.sp+4; +} + +ulong +dbgpc(Proc *p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} + +/* + * system calls + */ +#include "../port/systab.h" + +/* TODO: make this trap a separate asm entry point, like on other RISC architectures */ +void +syscall(Ureg* ureg) +{ + int i; + char *e; + long ret; + ulong sp, scallnr; + + m->syscall++; + up->insyscall = 1; + up->pc = ureg->pc; + up->dbgreg = ureg; + + scallnr = ureg->r3; + up->scallnr = ureg->r3; + spllo(); + + sp = ureg->usp; + up->nerrlab = 0; + ret = -1; + if(!waserror()){ + if(scallnr >= nsyscall || systab[scallnr] == nil){ + pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + up->s = *((Sargs*)(sp+BY2WD)); + up->psstate = sysctab[scallnr]; + + ret = systab[scallnr](up->s.args); + poperror(); + }else{ + /* failure: save the error buffer for errstr */ + e = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = e; + } + if(up->nerrlab){ + print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab); + print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr); + for(i = 0; i < NERR; i++) + print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + + up->insyscall = 0; + up->psstate = 0; + + /* + * Put return value in frame. On the x86 the syscall is + * just another trap and the return value from syscall is + * ignored. On other machines the return value is put into + * the results register by caller of syscall. + */ + ureg->r3 = ret; + + if(scallnr == NOTED) + noted(ureg, *(ulong*)(sp+BY2WD)); + + /* restoreureg must execute at high IPL */ + splhi(); + if(scallnr!=RFORK) + notify(ureg); + if(up->fpstate == FPinactive) + ureg->srr1 &= ~MSR_FP; +} + +/* + * Call user, if necessary, with note. + * Pass user the Ureg struct and the note on his stack. + */ +int +notify(Ureg* ur) +{ + int l; + ulong s, sp; + Note *n; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + s = spllo(); + qlock(&up->debug); + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0){ + l = strlen(n->msg); + if(l > ERRMAX-15) /* " pc=0x12345678\0" */ + l = ERRMAX-15; + sprint(n->msg+l, " pc=0x%.8lux", ur->pc); + } + + if(n->flag!=NUser && (up->notified || up->notify==0)){ + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splhi(); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + sp = ur->usp & ~(BY2V-1); + sp -= sizeof(Ureg); + + if(!okaddr((ulong)up->notify, BY2WD, 0) || + !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + sp -= BY2WD+ERRMAX; + memmove((char*)sp, up->note[0].msg, ERRMAX); + sp -= 3*BY2WD; + *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + ur->pc = (ulong)up->notify; + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + + +/* + * Return user to state before notify() + */ +void +noted(Ureg* ureg, ulong arg0) +{ + Ureg *nureg; + ulong oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + nureg = up->ureg; /* pointer to user returned Ureg struct */ + + /* sanity clause */ + oureg = (ulong)nureg; + if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad ureg in noted or call to noted when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove(ureg, nureg, sizeof(Ureg)); + + switch(arg0){ + case NCONT: + case NRSTR: + if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); + qunlock(&up->debug); + break; + + case NSAVE: + if(!okaddr(nureg->pc, BY2WD, 0) + || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRMAX; + splhi(); + ureg->sp = sp; + ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + break; + + default: + pprint("unknown noted arg 0x%lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } +} diff --git a/sys/src/9/mtx/uarti8250.c b/sys/src/9/mtx/uarti8250.c new file mode 100755 index 000000000..ce68e6868 --- /dev/null +++ b/sys/src/9/mtx/uarti8250.c @@ -0,0 +1,636 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * 8250 UART and compatibles. + */ +enum { + Uart0 = 0x3F8, /* COM1 */ + Uart0IRQ = 4, + Uart1 = 0x2F8, /* COM2 */ + Uart1IRQ = 3, + + UartFREQ = 1843200, +}; + +enum { /* I/O ports */ + 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 */ + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +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, + Ife = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + 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 */ + 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, /* Tramsmitter 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, /* Data Set Ready */ +}; + +typedef struct Ctlr { + int io; + int irq; + int tbdf; + int iena; + + uchar sticky[8]; + + Lock; + int fifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[2] = { +{ .io = Uart0, + .irq = Uart0IRQ, + .tbdf = BUSUNKNOWN, }, + +{ .io = Uart1, + .irq = Uart1IRQ, + .tbdf = BUSUNKNOWN, }, +}; + +static Uart i8250uart[2] = { +{ .regs = &i8250ctlr[0], + .name = "COM1", + .freq = UartFREQ, + .phys = &i8250physuart, + .special=0, + .next = &i8250uart[1], }, + +{ .regs = &i8250ctlr[1], + .name = "COM2", + .freq = UartFREQ, + .phys = &i8250physuart, + .special=0, + .next = nil, }, +}; + +#define csr8r(c, r) inb((c)->io+(r)) +#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(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 = malloc(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)%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, + (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 on) +{ + int i; + Ctlr *ctlr; + + /* + * Toggle FIFOs: + * if none, do nothing; + * reset the Rx and Tx FIFOs; + * empty the Rx buffer and clear any interrupt conditions; + * if enabling, try to turn them on. + */ + ctlr = uart->regs; + + ilock(ctlr); + if(!ctlr->fifo){ + csr8w(ctlr, Fcr, FIFOtclr|FIFOrclr); + for(i = 0; i < 16; i++){ + csr8r(ctlr, Iir); + csr8r(ctlr, Rbr); + } + + ctlr->fena = 0; + if(on){ + csr8w(ctlr, Fcr, FIFO4|FIFOena); + if(!(csr8r(ctlr, Iir) & Ife)) + ctlr->fifo = 1; + ctlr->fena = 1; + } + } + 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': + default: + break; + } + 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) +{ + ulong bgc; + Ctlr *ctlr; + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(uart->freq == 0 || baud <= 0) + return -1; + bgc = (uart->freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Dlab); + outb(ctlr->io+Dlm, bgc>>8); + outb(ctlr->io+Dll, bgc); + csr8w(ctlr, Lcr, 0); + + uart->baud = baud; + + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + /* + * 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 +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + 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 + */ + ctlr = uart->regs; + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + outb(ctlr->io+Thr, *(uart->op++)); + } +} + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + + ctlr = uart->regs; + 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 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 & 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 0x%2.2uX\n", iir); + break; + } + } +} + +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); +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *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. + */ + ctlr = uart->regs; + if(ie){ + if(ctlr->iena == 0){ + intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Ethre|Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + csr8w(ctlr, Mcr, ctlr->sticky[Mcr]); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); +} + +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; + Ctlr *ctlr; + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); + outb(ctlr->io+Thr, c); + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); +} + +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, +}; + +void +i8250console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + switch(n){ + default: + return; + case 0: + uart = &i8250uart[0]; + break; + case 1: + uart = &i8250uart[1]; + break; + } + + uartctl(uart, "b9600 l8 pn s1"); + if(*cmd != '\0') + uartctl(uart, cmd); + (*uart->phys->enable)(uart, 0); + + consuart = uart; + uart->console = 1; +} |