From f322baa2e764a610c48ec19a242d4f4503054f59 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 9 Jan 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-01-09 --- alphapc/devarch.c | 3 +- alphapc/devether.c | 1 + alphapc/devvga.c | 3 +- alphapc/main.c | 20 +- bitsy/devether.c | 1 + bitsy/devflash.c | 1 + bitsy/devpcmcia.c | 1 + bitsy/devpenmouse.c | 1 + bitsy/devsac.c | 1 + bitsy/devuda1341.c | 1 + "bitsy/dev\302\265c.c" | 1 + bitsy/main.c | 16 +- boot/boot.h | 9 +- boot/local.c | 6 - boot/rc.c | 17 + boot/sac.c | 9 - ip/devip.c | 10 +- mtx/devarch.c | 1 + mtx/devether.c | 467 +++++++++++ mtx/ether2114x.c | 1671 ++++++++++++++++++++++++++++++++++++++++ mtx/etherif.h | 36 + mtx/main.c | 14 +- mtx/trap.c | 3 + pc/apbootstrap.s | 9 +- pc/apm.c | 14 - pc/dat.h | 2 +- pc/devarch.c | 1 + pc/devether.c | 20 + pc/devfloppy.c | 1 + pc/devi82365.c | 1 + pc/devlml.c | 3 +- pc/devlpt.c | 1 + pc/devpccard.c | 3 +- pc/devrtc.c | 1 + pc/devusb.c | 1 + pc/devvga.c | 1 + pc/ether82543gc.c | 8 + pc/ether82557.c | 13 + pc/etherif.h | 1 + pc/floppy.h | 1 - pc/fns.h | 4 +- pc/initcode.s | 23 + pc/l.s | 61 +- pc/main.c | 215 ++++-- pc/mem.h | 10 +- pc/memory.c | 58 +- pc/mmu.c | 24 +- pc/mp.c | 3 - pc/pci.c | 7 +- pc/reboot.h | 7 + pc/rebootcode.s | 54 ++ pc/uarti8250.c | 21 +- port/chan.c | 12 + port/dev.c | 5 + port/devaudio.c | 1 + port/devbridge.c | 1 + port/devcap.c | 1 + port/devcons.c | 51 +- port/devdraw.c | 1 + port/devdup.c | 1 + port/devenv.c | 99 ++- port/devkprof.c | 1 + port/devloopback.c | 1 + port/devmnt.c | 2 +- port/devmouse.c | 1 + port/devpipe.c | 1 + port/devpnp.c | 1 + port/devproc.c | 1 + port/devroot.c | 1 + port/devsd.c | 1 + port/devsdp.c | 1 + port/devsrv.c | 3 +- port/devssl.c | 1 + port/devtls.c | 1 + port/devuart.c | 1 + port/portdat.h | 1 + port/portfns.h | 7 +- port/rebootcmd.c | 100 +++ 78 files changed, 2905 insertions(+), 253 deletions(-) create mode 100644 boot/rc.c create mode 100644 mtx/devether.c create mode 100644 mtx/ether2114x.c create mode 100644 mtx/etherif.h create mode 100644 pc/initcode.s create mode 100644 pc/reboot.h create mode 100644 pc/rebootcode.s create mode 100644 port/rebootcmd.c diff --git a/alphapc/devarch.c b/alphapc/devarch.c index 0653091e63faedbf03de70f8428ab28ee0e44bb6..8ef79ab9fa13ac35f3192e8b8c1edf77c9b06451 100644 --- a/alphapc/devarch.c +++ b/alphapc/devarch.c @@ -372,7 +372,8 @@ Dev archdevtab = { "arch", devreset, - devinit, + devinit, + devshutdown, archattach, archwalk, archstat, diff --git a/alphapc/devether.c b/alphapc/devether.c index 333d1bf1b76b41593ba6c98d4db622f4167090ea..410b2ce8c23ddf898ac0753b8d80f95abc4119e1 100644 --- a/alphapc/devether.c +++ b/alphapc/devether.c @@ -431,6 +431,7 @@ Dev etherdevtab = { etherreset, devinit, + devshutdown, etherattach, etherwalk, etherstat, diff --git a/alphapc/devvga.c b/alphapc/devvga.c index b21b8b075f8ee1d47e4d848db674e8f7875c7a9d..c3be69f18aa94740404ad7f471e432827c6349a9 100644 --- a/alphapc/devvga.c +++ b/alphapc/devvga.c @@ -393,7 +393,8 @@ Dev vgadevtab = { "vga", vgareset, - devinit, + devinit, + devshutdown, vgaattach, vgawalk, vgastat, diff --git a/alphapc/main.c b/alphapc/main.c index 85d604d24bd1f48567d7a33199d87d9b859b9cfa..5d4d5c8af576eb79f959d4f4312af1c2e3d1e937 100644 --- a/alphapc/main.c +++ b/alphapc/main.c @@ -145,16 +145,16 @@ init0(void) chandevinit(); if(!waserror()){ - ksetenv("cputype", "alpha"); + ksetenv("cputype", "alpha", 0); sprint(tstr, "alpha %s alphapc", conffile); - ksetenv("terminal", tstr); + ksetenv("terminal", tstr, 0); if(cpuserver) - ksetenv("service", "cpu"); + ksetenv("service", "cpu", 0); else - ksetenv("service", "terminal"); + ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++) - if(confname[i] && confname[i][0] != '*') - ksetenv(confname[i], confval[i]); + if(confname[i]) + ksetenv(confname[i], confval[i], 1); poperror(); } @@ -258,6 +258,14 @@ procsave(Proc *p) */ } +/* still too do */ +void +reboot(void*, void*, ulong) +{ + exit(0); +} + + void exit(int) { diff --git a/bitsy/devether.c b/bitsy/devether.c index 1f93800d378d2d115d842e7c40ba6674629f9598..b083600248386bcd7b1038dfc01c9c446b57d82b 100644 --- a/bitsy/devether.c +++ b/bitsy/devether.c @@ -445,6 +445,7 @@ Dev etherdevtab = { etherreset, devinit, + devshutdown, etherattach, etherwalk, etherstat, diff --git a/bitsy/devflash.c b/bitsy/devflash.c index 69cd5437e9f8d6a798d42a711f9f1a8ba35350fe..c87943ea2b3b69228defb008b889fa7cd4171295 100644 --- a/bitsy/devflash.c +++ b/bitsy/devflash.c @@ -617,6 +617,7 @@ Dev flashdevtab = { devreset, flashinit, + devshutdown, flashattach, flashwalk, flashstat, diff --git a/bitsy/devpcmcia.c b/bitsy/devpcmcia.c index 036af58a9810b001bbec36116cf03ed245b45d16..8ff095c623610171bef225ad5ba9ce188499469e 100644 --- a/bitsy/devpcmcia.c +++ b/bitsy/devpcmcia.c @@ -386,6 +386,7 @@ Dev pcmciadevtab = { pcmciareset, devinit, + devshutdown, pcmciaattach, pcmciawalk, pcmciastat, diff --git a/bitsy/devpenmouse.c b/bitsy/devpenmouse.c index b2db6451667d0868efbbaaac9a9f67cdcf868dcf..54f903203b8aaaf3158b9ce1e295d92fad730bb3 100644 --- a/bitsy/devpenmouse.c +++ b/bitsy/devpenmouse.c @@ -430,6 +430,7 @@ Dev penmousedevtab = { penmousereset, penmouseinit, + devshutdown, penmouseattach, penmousewalk, penmousestat, diff --git a/bitsy/devsac.c b/bitsy/devsac.c index 5109cdd6a9e6b5cd20491e9803f40f0dc1f5d004..ca5048c1750a26cb350616e8068be38d6a35b653 100644 --- a/bitsy/devsac.c +++ b/bitsy/devsac.c @@ -576,6 +576,7 @@ Dev sacdevtab = { devreset, sacinit, + devshutdown, sacattach, sacwalk, sacstat, diff --git a/bitsy/devuda1341.c b/bitsy/devuda1341.c index f9d4ff07c9acc509af692af97c7385ef83aaaf6d..bfe9d63cff4a7583c11c0cd547f26241e2e5ebf4 100644 --- a/bitsy/devuda1341.c +++ b/bitsy/devuda1341.c @@ -1382,6 +1382,7 @@ Dev uda1341devtab = { devreset, audioinit, + devshutdown, audioattach, audiowalk, audiostat, diff --git "a/bitsy/dev\302\265c.c" "b/bitsy/dev\302\265c.c" index 5eb70b65f929f782a6f95f1c5292796d0d140f7a..b340de95826600d4968c865067ad8064766e61d0 100644 --- "a/bitsy/dev\302\265c.c" +++ "b/bitsy/dev\302\265c.c" @@ -417,6 +417,7 @@ Dev µcdevtab = { devreset, µcinit, + devshutdown, µcattach, µcwalk, µcstat, diff --git a/bitsy/main.c b/bitsy/main.c index 54dcca450c456b5db163bc778ce7aaf45f17f7b0..bd7c9a9076e6682f00b865cf5666e5b4ca15fa9e 100644 --- a/bitsy/main.c +++ b/bitsy/main.c @@ -51,6 +51,14 @@ main(void) schedinit(); } +/* need to do better */ +void +reboot(void*, void*, ulong) +{ + exit(0); +} + + /* * exit kernel either on a panic or user request */ @@ -94,12 +102,12 @@ init0(void) chandevinit(); if(!waserror()){ - ksetenv("terminal", "bitsy"); - ksetenv("cputype", "arm"); + ksetenv("terminal", "bitsy", 0); + ksetenv("cputype", "arm", 0); if(cpuserver) - ksetenv("service", "cpu"); + ksetenv("service", "cpu", 0); else - ksetenv("service", "terminal"); + ksetenv("service", "terminal", 0); poperror(); } kproc("alarm", alarmkproc, 0); diff --git a/boot/boot.h b/boot/boot.h index f17024336247d26aed673238be0f10099244faf9..972176531cb1a5fa345e95567289c90fe468e2c2 100644 --- a/boot/boot.h +++ b/boot/boot.h @@ -53,24 +53,19 @@ extern int parsefields(char*, char**, int, char*); /* methods */ extern void configil(Method*); -extern int authil(void); extern int connectil(void); extern void configtcp(Method*); -extern int authtcp(void); extern int connecttcp(void); extern void configlocal(Method*); -extern int authlocal(void); extern int connectlocal(void); extern void configsac(Method*); -extern int authsac(void); extern int connectsac(void); -extern void configpaq(Method*); -extern int authpaq(void); -extern int connectpaq(void); +extern void configrc(Method*); +extern int connectrc(void); /* hack for passing authentication address */ extern char *authaddr; diff --git a/boot/local.c b/boot/local.c index 61f918587ab05e121dbd6df0a3bd632018025e56..85356bfba7cf8bc6abef111d895001b4878f0e1a 100644 --- a/boot/local.c +++ b/boot/local.c @@ -48,12 +48,6 @@ configlocal(Method *mp) USED(mp); } -int -authlocal(void) -{ - return -1; -} - int connectlocal(void) { diff --git a/boot/rc.c b/boot/rc.c new file mode 100644 index 0000000000000000000000000000000000000000..811d91c05acaa4bf1d1424e1d57865c2725d20bf --- /dev/null +++ b/boot/rc.c @@ -0,0 +1,17 @@ +#include +#include +#include <../boot/boot.h> + +void +configrc(Method *) +{ + execl("/rc", "/rc", "-m", "/rcmain", "-i", 0); + fatal("rc"); +} + +int +connectrc(void) +{ + // does not get here + return -1; +} diff --git a/boot/sac.c b/boot/sac.c index b281b9d94c258116826be63d68aa280cb360233b..cb3aa0134d8bf4ddc56b4a53847c2f4046a46587 100644 --- a/boot/sac.c +++ b/boot/sac.c @@ -2,8 +2,6 @@ #include #include <../boot/boot.h> -void configip(char* dev); - // HACK - take over from boot since file system is not // available on a pipe @@ -42,13 +40,6 @@ configsac(Method *mp) fatal(cmd); } -int -authsac(void) -{ - // does not get here - return -1; -} - int connectsac(void) { diff --git a/ip/devip.c b/ip/devip.c index 40861a26754c8d89d22e69ae38bff0e22ad69c96..6de129aecd3cab52e1800e9f24d5a880bf3c5056 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -259,11 +259,7 @@ ipreset(void) { nullmediumlink(); pktmediumlink(); -} -static void -ipinit(void) -{ fmtinstall('i', eipconv); fmtinstall('I', eipconv); fmtinstall('E', eipconv); @@ -1079,9 +1075,6 @@ ipwrite(Chan* ch, void *v, long n, vlong off) free(cb); nexterror(); } - if(cb->nf < 1) - error("short control request"); - ct = lookupcmd(cb, ipctlmsg, nelem(ipctlmsg)); switch(ct->index){ case CMconnect: @@ -1171,7 +1164,8 @@ Dev ipdevtab = { "ip", ipreset, - ipinit, + devinit, + devshutdown, ipattach, ipwalk, ipstat, diff --git a/mtx/devarch.c b/mtx/devarch.c index e80d8bc427c16868537e7f0a90deac9eb83f2a09..cd5eea88f310acc017a11c396ac21cc8801e01ef 100644 --- a/mtx/devarch.c +++ b/mtx/devarch.c @@ -372,6 +372,7 @@ Dev archdevtab = { devreset, devinit, + devshutdown, archattach, archwalk, archstat, diff --git a/mtx/devether.c b/mtx/devether.c new file mode 100644 index 0000000000000000000000000000000000000000..437ed3b0d7cc47f9a602b82c76cdbabd93f74f5a --- /dev/null +++ b/mtx/devether.c @@ -0,0 +1,467 @@ +#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 multcast 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(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) + 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 %lud", + 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, 1, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 65*1024); + if(ether->oq == 0) + ether->oq = qopen(65*1024, 1, 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/mtx/ether2114x.c b/mtx/ether2114x.c new file mode 100644 index 0000000000000000000000000000000000000000..c788af956abc3c77632a95b0a97598ddb8076fe6 --- /dev/null +++ b/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 (1) +#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 Bit 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; + +print("ether intr\n"); + 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, 1, 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<= 0; i--){ + if(bits & (1<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<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); + 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<type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<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<sromsz)*sizeof(ushort)); + for(i = 0; i < (1<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->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/mtx/etherif.h b/mtx/etherif.h new file mode 100644 index 0000000000000000000000000000000000000000..5af59e14629aa7a477e37d4e7114b5c506400fc5 --- /dev/null +++ b/mtx/etherif.h @@ -0,0 +1,36 @@ +enum { + MaxEther = 24, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int mbps; /* Mbps */ + 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/mtx/main.c b/mtx/main.c index c837d1a5f771dfea8c14a183015d56f4919442f3..f90f318a3f68a966a0804e569dbf75f96c9d86bc 100644 --- a/mtx/main.c +++ b/mtx/main.c @@ -80,6 +80,7 @@ static struct plan9ini[] = { { "console", "0" }, + { "ether0", "type=2114x" }, }; char* @@ -115,11 +116,11 @@ init0(void) chandevinit(); if(!waserror()){ - ksetenv("cputype", "power"); + ksetenv("cputype", "power", 0); if(cpuserver) - ksetenv("service", "cpu"); + ksetenv("service", "cpu", 0); else - ksetenv("service", "terminal"); + ksetenv("service", "terminal", 0); /* for(p = confenv; *p; p++) { @@ -195,6 +196,13 @@ userinit(void) ready(p); } +/* still to do */ +void +reboot(void*, void*, ulong) +{ + exit(0); +} + void exit(int ispanic) { diff --git a/mtx/trap.c b/mtx/trap.c index ebc7270f2398c92da1b74401e966da71111b7c0f..3dacd293a3dad8929bd98554cc80d9b883b6a782 100644 --- a/mtx/trap.c +++ b/mtx/trap.c @@ -24,6 +24,7 @@ hwintrenable(Vctl *v) irq = v->irq; if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */ +print("add PCI intr %d\n", irq); if(irq > 15) { print("intrenable: pci irq %d out of range\n", v->irq); return -1; @@ -32,6 +33,7 @@ hwintrenable(Vctl *v) mpicenable(vec, v); } else { +print("add pic intr %d\n", irq); if(irq > MaxIrqPIC) { print("intrenable: irq %d out of range\n", v->irq); return -1; @@ -348,6 +350,7 @@ intr(Ureg *ureg) vno = i8259intack(); mpiceoi(0); } +else print("non-pic %d\n", vno); if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) { panic("spurious intr %d", vno); diff --git a/pc/apbootstrap.s b/pc/apbootstrap.s index eb6567e63fad15dcf52a390c4b5421d5bae88554..3887d71de0795e1fd8eaf0e5b1cd7bea32b591dd 100644 --- a/pc/apbootstrap.s +++ b/pc/apbootstrap.s @@ -15,8 +15,6 @@ #define INVD BYTE $0x0F; BYTE $0x08 #define WBINVD BYTE $0x0F; BYTE $0x09 -#define PADDR(a) ((a) & ~KZERO) - /* * Macros for calculating offsets within the page directory base * and page tables. Note that these are assembler-specific hence @@ -64,9 +62,8 @@ TEXT _apbootstrap(SB), $0 /* address APBOOTSTRAP+0x14 */ FARJUMP32(SELECTOR(2, SELGDT, 0), _ap32-KZERO(SB)) /* - * There's a little mystery here - the Pentium Pro appears to need an identity - * mmu map for the switch to virtual mode. The manual doesn't say this is necessary - * and it isn't required on the Pentium. + * For Pentiums and higher, the code that enables paging must come from + * pages that are identity mapped. * To this end double map KZERO at virtual 0 and undo the mapping once virtual * nirvana has been obtained. */ @@ -109,5 +106,5 @@ TEXT gdt(SB), $0 LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) TEXT gdtptr(SB), $0 - WORD $(3*8) + WORD $(3*8-1) LONG $gdt-KZERO(SB) diff --git a/pc/apm.c b/pc/apm.c index 39e6eb8e8982be65ec7ef15f276c4b04b76324ed..2ea18b4d88f18303af37706a2901141c26df981d 100644 --- a/pc/apm.c +++ b/pc/apm.c @@ -64,19 +64,6 @@ setgdt(int sel, ulong base, ulong limit, int flag) ((base>>16)&0xFF) | SEGP | SEGPL(0) | flag; } -static void -loadgdt(void) -{ - ulong x; - ushort ptr[3]; - - ptr[0] = sizeof(m->gdt); - x = (ulong)m->gdt; - ptr[1] = x & 0xFFFF; - ptr[2] = (x>>16) & 0xFFFF; - lgdt(ptr); -} - static ulong ax, cx, dx, di, ebx, esi; static Ureg apmu; static long @@ -154,7 +141,6 @@ apmlink(void) setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD); setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR); setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD); - loadgdt(); addarchfile("apm", 0660, apmread, apmwrite); diff --git a/pc/dat.h b/pc/dat.h index 75de7f804723bd184975ecbf5ec881e1745f8a85..cefd292c7bd762e7913f7dfaae04e37edb5f9f69 100644 --- a/pc/dat.h +++ b/pc/dat.h @@ -84,7 +84,7 @@ struct Conf ulong copymode; /* 0 is copy on write, 1 is copy on reference */ ulong ialloc; /* max interrupt time allocation in bytes */ ulong pipeqsize; /* size in bytes of pipe queues */ - int nuart; /* number of uart devices */ + int nuart; /* number of uart devices */ }; /* diff --git a/pc/devarch.c b/pc/devarch.c index 997c78ef825b56eadf8de3c26e6d357b8cee19c6..668b6071eec4e57f9b7023860cfe31a7b0684107 100644 --- a/pc/devarch.c +++ b/pc/devarch.c @@ -461,6 +461,7 @@ Dev archdevtab = { devreset, devinit, + devshutdown, archattach, archwalk, archstat, diff --git a/pc/devether.c b/pc/devether.c index be33331a7c0de34c6805c7785fe9cb091d503b5a..ee8de484f49d1392e4876db1e053189e9217a04c 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -425,6 +425,25 @@ etherreset(void) free(ether); } +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown fuction\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + #define POLY 0xedb88320 /* really slow 32 bit crc for ethers */ @@ -451,6 +470,7 @@ Dev etherdevtab = { etherreset, devinit, + ethershutdown, etherattach, etherwalk, etherstat, diff --git a/pc/devfloppy.c b/pc/devfloppy.c index 55470ed369a384b66fb956de0fe4a2041d532865..56e863e977b5c63719eeb9a9616d3ce0cdbdd61c 100644 --- a/pc/devfloppy.c +++ b/pc/devfloppy.c @@ -1061,6 +1061,7 @@ Dev floppydevtab = { floppyreset, devinit, + devshutdown, floppyattach, floppywalk, floppystat, diff --git a/pc/devi82365.c b/pc/devi82365.c index 261f9f17d08f64104895450da9eb3ab8f3d89b98..b863306ab36b5e1da931a659210d8d04ad9fef38 100644 --- a/pc/devi82365.c +++ b/pc/devi82365.c @@ -870,6 +870,7 @@ Dev i82365devtab = { devreset, devinit, + devshutdown, i82365attach, i82365walk, i82365stat, diff --git a/pc/devlml.c b/pc/devlml.c index 9b723c5826b78603421152cfc3ed9b3de3e11fd2..a2ff8e0c6ada46dcc9f06da4b2689bc9f20ef396 100644 --- a/pc/devlml.c +++ b/pc/devlml.c @@ -318,7 +318,8 @@ Dev lmldevtab = { "video", lmlreset, - devinit, + devinit, + devshutdown, lmlattach, lmlwalk, lmlstat, diff --git a/pc/devlpt.c b/pc/devlpt.c index febb8eb366fbce8c44e6d82dd65008f080f9afe5..2fbc2abadfcc1c03daa0fc1c440abb6aaf979d3d 100644 --- a/pc/devlpt.c +++ b/pc/devlpt.c @@ -229,6 +229,7 @@ Dev lptdevtab = { devreset, devinit, + devshutdown, lptattach, lptwalk, lptstat, diff --git a/pc/devpccard.c b/pc/devpccard.c index 2f616e51142d07865bf2fa401469f782c537f0a6..75390afb8e4471101b9ac894fc8665d7941dfa43 100644 --- a/pc/devpccard.c +++ b/pc/devpccard.c @@ -1367,7 +1367,8 @@ Dev pccarddevtab = { "cardbus", devreset, - devinit, + devinit, + devshutdown, pccardattach, pccardwalk, pccardstat, diff --git a/pc/devrtc.c b/pc/devrtc.c index 205932a33cb4232f8aa76fbcb1bcf7c4e113d728..5d092066f02a15065364ccf22e49702a7c8f0765 100644 --- a/pc/devrtc.c +++ b/pc/devrtc.c @@ -308,6 +308,7 @@ Dev rtcdevtab = { devreset, rtcinit, + devshutdown, rtcattach, rtcwalk, rtcstat, diff --git a/pc/devusb.c b/pc/devusb.c index 36a631014ce45b410b7f0a69de444112e426ca4d..cbe3217072c145ad5bb0a0bdcc66bc8e8ea2c9ab 100644 --- a/pc/devusb.c +++ b/pc/devusb.c @@ -2307,6 +2307,7 @@ Dev usbdevtab = { usbreset, usbinit, + devshutdown, usbattach, usbwalk, usbstat, diff --git a/pc/devvga.c b/pc/devvga.c index 42176638d035406e140a7ad9b30387787df0117a..13a6f8473659bf63b0b7b622b313fd84df777c38 100644 --- a/pc/devvga.c +++ b/pc/devvga.c @@ -447,6 +447,7 @@ Dev vgadevtab = { vgareset, devinit, + devshutdown, vgaattach, vgawalk, vgastat, diff --git a/pc/ether82543gc.c b/pc/ether82543gc.c index 6261513d2d59f7db240a79ed258bc6933407d0a5..9910351a7f67fd9bfa7448808e9a7e0916417043 100644 --- a/pc/ether82543gc.c +++ b/pc/ether82543gc.c @@ -861,6 +861,13 @@ gc82543detach(Ctlr* ctlr) ; } +static void +gc82543shutdown(Ether* ether) +{ +print("gc82543shutdown\n"); + gc82543detach(ether->ctlr); +} + static int gc82543reset(Ctlr* ctlr) { @@ -994,6 +1001,7 @@ gc82543pnp(Ether* edev) edev->transmit = gc82543transmit; edev->interrupt = gc82543interrupt; edev->ifstat = gc82543ifstat; + edev->shutdown = gc82543shutdown; edev->arg = edev; edev->promiscuous = nil; diff --git a/pc/ether82557.c b/pc/ether82557.c index 4789c7311770cbb828ddeb4ed7493924ce1e7b12..5601004b5b2c3ce5515d96410e08f34957bb18a6 100644 --- a/pc/ether82557.c +++ b/pc/ether82557.c @@ -973,6 +973,18 @@ scanphy(Ctlr* ctlr) return -1; } +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether82557 shutting down\n"); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); +} + + static int reset(Ether* ether) { @@ -1247,6 +1259,7 @@ reset(Ether* ether) ether->transmit = transmit; ether->interrupt = interrupt; ether->ifstat = ifstat; + ether->shutdown = shutdown; ether->promiscuous = promiscuous; ether->multicast = multicast; diff --git a/pc/etherif.h b/pc/etherif.h index 5af59e14629aa7a477e37d4e7114b5c506400fc5..f1d724c0028b88245a62ca814cf504ff3f7e0752 100644 --- a/pc/etherif.h +++ b/pc/etherif.h @@ -19,6 +19,7 @@ struct Ether { void (*interrupt)(Ureg*, void*); long (*ifstat)(Ether*, void*, long, ulong); long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ void *ctlr; Queue* oq; diff --git a/pc/floppy.h b/pc/floppy.h index 86c4c4dd12ffb76db5858d6424e3a531a51fd3d1..898ab74742791532a6616d1096a2a7f56906c86b 100644 --- a/pc/floppy.h +++ b/pc/floppy.h @@ -164,7 +164,6 @@ floppysetup1(FController *fl) fl->d[1].dt = equip & 0xf; floppysetdef(&fl->d[1]); } - intrenable(IrqFLOPPY, pcfloppyintr, fl, BUSUNKNOWN, "floppy"); } diff --git a/pc/fns.h b/pc/fns.h index 08403a57eae518a517bcf084254d0296a3688df5..b7dd73c4542e98a86cbc3800a049a00bb2c39fdf 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -1,7 +1,6 @@ #include "../port/portfns.h" void aamloop(int); -void addconf(char*, char*); Dirtab* addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong)); //void addscsilink(char*, Scsiio (*)(int, ISAConf*)); void archinit(void); @@ -74,8 +73,9 @@ void lgdt(ushort[3]); void lidt(ushort[3]); void links(void); void ltr(ulong); +void mach0init(void); void mathinit(void); -void meminit(ulong); +void meminit(void); #define mmuflushtlb(pdb) putcr3(pdb) void mmuinit(void); ulong mmukmap(ulong, ulong, int); diff --git a/pc/initcode.s b/pc/initcode.s new file mode 100644 index 0000000000000000000000000000000000000000..55018f9ee811bf087e27c00076a8e3b9d95e3db3 --- /dev/null +++ b/pc/initcode.s @@ -0,0 +1,23 @@ +#include "/sys/src/libc/9syscall/sys.h" + +TEXT main(SB),$0 + + /* + * exec("/boot", bootv) + */ + LEAL 4(SP),AX + PUSHL AX + LEAL boot(SB),AX + PUSHL AX + PUSHL $0 + MOVL $EXEC,AX + INT $64 + + /* + * should never get here + */ +here: + JMP here + +GLOBL boot+0(SB),$6 +DATA boot+0(SB)/5,$"/boot" diff --git a/pc/l.s b/pc/l.s index b359ec68dea5595fa701b195fe5c80d0e786968a..4241162c147ca58959dae6886e78d61c07b8541f 100644 --- a/pc/l.s +++ b/pc/l.s @@ -24,29 +24,29 @@ #define PTO(a) (((((a))>>12) & 0x03FF)<<2) /* - * Entered here from the bootstrap programme possibly via a jump to 0x00100020, so - * need to make another jump to set the correct virtual address. - * In protected mode with paging turned on, the first 4MB of physical memory mapped - * to KZERO and up. + * For backwards compatiblity with 9load - should go away when 9load is changed + * 9load currently sets up the mmu, however the first 16MB of memory is identity + * mapped, so behave as if the mmu was not setup */ -TEXT _start0x00100020(SB),$0 - CLI - MOVL $_start0x80100020(SB), AX +TEXT _start0x80100020(SB),$0 + MOVL $_start0x00100020(SB), AX + ANDL $~KZERO, AX JMP* AX - /* - * First check if the bootstrap programme left the first 4MB nicely mapped, otherwise - * make the basic page tables for processor 0. Four pages are needed for the basic set: - * a page directory, a page table for mapping the first 4MB of physical memory, and - * virtual and physical pages for mapping the Mach structure. + * In protected mode with paging turned off and segment registers setup to linear map all memory. + * Entered via a jump to 0x00100020, the physical address of the virtual kernel entry point of 0x80100020 + * Make the basic page tables for processor 0. Four pages are needed for the basic set: + * a page directory, a page table for mapping the first 4MB of physical memory to KZERO, + * and virtual and physical pages for mapping the Mach structure. * The remaining PTEs will be allocated later when memory is sized. + * An identity mmu map is also needed for the switch to virtual mode. This + * identity mapping is removed once the MMU is going and the JMP has been made + * to virtual memory. */ -TEXT _start0x80100020(SB), $0 - MOVL CR3, AX /* check the page directory base */ - CMPL AX, $PADDR(CPU0PDB) - JEQ _clearbss +TEXT _start0x00100020(SB),$0 + CLI /* make sure interrupts are off */ - MOVL $CPU0PDB, DI /* clear 4 pages for the tables etc. */ + MOVL $PADDR(CPU0PDB), DI /* clear 4 pages for the tables etc. */ XORL AX, AX MOVL $(4*BY2PG), CX SHRL $2, CX @@ -54,13 +54,13 @@ TEXT _start0x80100020(SB), $0 CLD REP; STOSL - MOVL $CPU0PDB, AX + MOVL $PADDR(CPU0PDB), AX ADDL $PDO(KZERO), AX /* page directory offset for KZERO */ MOVL $PADDR(CPU0PTE), (AX) /* PTE's for 0x80000000 */ MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ ORL BX, (AX) - MOVL $CPU0PTE, AX /* first page of page table */ + MOVL $PADDR(CPU0PTE), AX /* first page of page table */ MOVL $1024, CX /* 1024 pages in 4MB */ _setpte: MOVL BX, (AX) @@ -68,7 +68,7 @@ _setpte: ADDL $4, AX LOOP _setpte - MOVL $CPU0PTE, AX + MOVL $PADDR(CPU0PTE), AX ADDL $PTO(MACHADDR), AX /* page table entry offset for MACHADDR */ MOVL $PADDR(CPU0MACH), (AX) /* PTE for Mach */ MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ @@ -76,18 +76,12 @@ _setpte: /* * Now ready to use the new map. Make sure the processor options are what is wanted. - * It is necessary on some processors to follow mode switching with a JMP instruction + * It is necessary on some processors to immediately follow mode switching with a JMP instruction * to clear the prefetch queues. - * There's a little mystery here - the Pentium Pro appears to need an identity - * mmu map for the switch to virtual mode. The manual doesn't say this is necessary - * and it isn't required on the Pentium. - * To this end double map KZERO at virtual 0 and undo the mapping once virtual - * nirvana has been attained. */ MOVL $PADDR(CPU0PDB), CX /* load address of page directory */ - MOVL CX, BX - MOVL (PDO(KZERO))(BX), DX /* double-map KZERO at 0 */ - MOVL DX, (PDO(0))(BX) + MOVL (PDO(KZERO))(CX), DX /* double-map KZERO at 0 */ + MOVL DX, (PDO(0))(CX) MOVL CX, CR3 DELAY /* JMP .+2 */ @@ -95,9 +89,9 @@ _setpte: ORL $0x80010000, DX /* PG|WP */ ANDL $~0x6000000A, DX /* ~(CD|NW|TS|MP) */ - MOVL $_startpg(SB), AX + MOVL $_startpg(SB), AX /* this is a virtual address */ MOVL DX, CR0 /* turn on paging */ - JMP* AX + JMP* AX /* jump to the virtual nirvana */ /* * Basic machine environment set, can clear BSS and create a stack. @@ -107,9 +101,7 @@ _setpte: * be initialised here. */ TEXT _startpg(SB), $0 - MOVL CX, AX /* physical address of PDB */ - ORL $KZERO, AX - MOVL $0, (PDO(0))(AX) /* undo double-map of KZERO at 0 */ + MOVL $0, (PDO(0))(CX) /* undo double-map of KZERO at 0 */ MOVL CX, CR3 /* load and flush the mmu */ _clearbss: @@ -126,6 +118,7 @@ _clearbss: MOVL SP, m(SB) /* initialise global Mach pointer */ MOVL $0, 0(SP) /* initialise m->machno */ + ADDL $(MACHSIZE-4), SP /* initialise stack */ /* diff --git a/pc/main.c b/pc/main.c index 6d923a6b56109e58dd00531ace978a3ffb76f12c..a32dbf186db13f59634ef8b18d54b13b2a8f02b5 100644 --- a/pc/main.c +++ b/pc/main.c @@ -7,27 +7,28 @@ #include "ureg.h" #include "init.h" #include "pool.h" +#include "reboot.h" Mach *m; -static uchar *sp; /* stack pointer for /boot */ - /* * Where configuration info is left for the loaded programme. * This will turn into a structure as more is done by the boot loader * (e.g. why parse the .ini file twice?). - * There are XXX bytes available at CONFADDR. + * There are 3584 bytes available at CONFADDR. */ #define BOOTLINE ((char*)CONFADDR) #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) #define BOOTARGSLEN (4096-0x200-BOOTLINELEN) -#define MAXCONF 32 +#define MAXCONF 64 char bootdisk[KNAMELEN]; +Conf conf; char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; +uchar *sp; /* user stack of init proc */ static void options(void) @@ -71,18 +72,16 @@ options(void) void main(void) { - conf.nmach = 1; - MACHP(0) = (Mach*)CPU0MACH; - m->pdb = (ulong*)CPU0PDB; - machinit(); - active.machs = 1; - active.exiting = 0; + mach0init(); options(); ioinit(); i8250console(); + print("\nPlan 9\n"); + cpuidentify(); screeninit(); + meminit(); confinit(); archinit(); xinit(); @@ -107,6 +106,19 @@ conf.monitor = 1; schedinit(); } +void +mach0init(void) +{ + conf.nmach = 1; + MACHP(0) = (Mach*)CPU0MACH; + m->pdb = (ulong*)CPU0PDB; + + machinit(); + + active.machs = 1; + active.exiting = 0; +} + void machinit(void) { @@ -128,20 +140,11 @@ machinit(void) m->loopconst = 100000; } -void -ksetterm(char *f) -{ - char buf[2*KNAMELEN]; - - sprint(buf, f, conffile); - ksetenv("terminal", buf); -} - void init0(void) { int i; - char tstr[32]; + char buf[2*KNAMELEN]; up->nerrlab = 0; @@ -159,17 +162,15 @@ init0(void) chandevinit(); if(!waserror()){ - strcpy(tstr, arch->id); - strcat(tstr, " %s"); - ksetterm(tstr); - ksetenv("cputype", "386"); + snprint(buf, sizeof(buf), "%s %s", arch->id, conffile); + ksetenv("terminal", buf, 0); + ksetenv("cputype", "386", 0); if(cpuserver) - ksetenv("service", "cpu"); + ksetenv("service", "cpu", 0); else - ksetenv("service", "terminal"); + ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++) - if(confname[i] && confname[i][0] != '*') - ksetenv(confname[i], confval[i]); + ksetenv(confname[i], confval[i], 1); poperror(); } kproc("alarm", alarmkproc, 0); @@ -260,6 +261,8 @@ bootargs(ulong base) ac = 0; av[ac++] = pusharg("/386/9dos"); + + /* when boot is changed to only use rc, this code can go away */ cp[BOOTLINELEN-1] = 0; buf[0] = 0; if(strncmp(cp, "fd", 2) == 0){ @@ -270,13 +273,6 @@ bootargs(ulong base) av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); - if(buf[0]){ - cp = strchr(buf, '!'); - if(cp){ - strcpy(bootdisk, cp+1); - addconf("bootdisk", bootdisk); - } - } /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); @@ -290,18 +286,6 @@ bootargs(ulong base) sp += (USTKTOP - BY2PG) - base - sizeof(ulong); } -Conf conf; - -void -addconf(char *name, char *val) -{ - if(nconf >= MAXCONF) - return; - confname[nconf] = name; - confval[nconf] = val; - nconf++; -} - char* getconf(char *name) { @@ -313,24 +297,47 @@ getconf(char *name) return 0; } +static void +writeconf(void) +{ + char *p, *q; + int n; + + p = getconfenv(); + + if(waserror()) { + free(p); + nexterror(); + } + + /* convert to name=value\n format */ + for(q=p; *q; q++) { + q += strlen(q); + *q = '='; + q += strlen(q); + *q = '\n'; + } + n = q - p + 1; + if(n >= BOOTARGSLEN) + error("kernel configuration too large"); + memset(BOOTLINE, 0, BOOTLINELEN); + memmove(BOOTARGS, p, n); + poperror(); + free(p); +} + void confinit(void) { char *p; int userpcnt; - ulong kpages, maxmem; + ulong kpages; - if(p = getconf("*maxmem")) - maxmem = strtoul(p, 0, 0); - else - maxmem = 0; if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; - meminit(maxmem); - conf.npage = conf.npage0 + conf.npage1; conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; @@ -556,8 +563,8 @@ procsave(Proc *p) mmuflushtlb(PADDR(m->pdb)); } -void -exit(int ispanic) +static void +shutdown(int ispanic) { int ms, once; @@ -588,7 +595,56 @@ exit(int ispanic) } else delay(1000); +} + +void +reboot(void *entry, void *code, ulong size) +{ + void (*f)(ulong, ulong, ulong); + ulong *pdb; + + writeconf(); + + shutdown(0); + + /* + * should be the only processor running now + */ + print("shutting down...\n"); + delay(200); + + splhi(); + + /* turn off buffered serial console */ + serialoq = nil; + + /* shutdown devices */ + chandevshutdown(); + + /* + * Modify the machine page table to directly map the low 4MB of memory + * This allows the reboot code to turn off the page mapping + */ + pdb = m->pdb; + pdb[PDX(0)] = pdb[PDX(KZERO)]; + mmuflushtlb(PADDR(pdb)); + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; + memmove(f, rebootcode, sizeof(rebootcode)); + + print("rebooting...\n"); + + /* off we go - never to return */ + (*f)(PADDR(entry), PADDR(code), size); +} + + +void +exit(int ispanic) +{ + shutdown(ispanic); arch->reset(); } @@ -596,33 +652,32 @@ int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[32], *p; - int i, n; + int i; snprint(cc, sizeof cc, "%s%d", class, ctlrno); - for(n = 0; n < nconf; n++){ - if(cistrcmp(confname[n], cc) != 0) - continue; - isa->nopt = tokenize(confval[n], 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; + p = getconf(cc); + if(p == nil) + 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 0; + return 1; } int diff --git a/pc/mem.h b/pc/mem.h index 382c086f10c2ef2323d2b5a884be4ddfc384c105..6a6b88306a6896d2792729b51d6c1224391984ae 100644 --- a/pc/mem.h +++ b/pc/mem.h @@ -31,6 +31,7 @@ * Fundamental addresses */ #define IDTADDR 0x80000800 /* idt */ +#define REBOOTADDR 0x00001000 /* reboot code - physical address */ #define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ #define CONFADDR 0x80001200 /* info passed from boot loader */ #define CPU0PDB 0x80002000 /* bootstrap processor PDB */ @@ -75,8 +76,8 @@ #define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) #define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) -#define KESEL SELECTOR(KESEG, SELGDT, 0) #define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) #define UESEL SELECTOR(UESEG, SELGDT, 3) #define UDSEL SELECTOR(UDSEG, SELGDT, 3) #define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) @@ -126,4 +127,11 @@ #define PTEUSER (1<<2) #define PTESIZE (1<<7) +/* + * Macros for calculating offsets within the page directory base + * and page tables. + */ +#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) +#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) + #define getpgcolor(a) 0 diff --git a/pc/memory.c b/pc/memory.c index 92a066eb8e45ad4ae24171e045e072e1a1ca520e..df86e096aade94efbcc3fc136990c1ee654e2848 100644 --- a/pc/memory.c +++ b/pc/memory.c @@ -11,9 +11,6 @@ #define MEMDEBUG 0 -#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) -#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) - enum { MemUPA = 0, /* unbacked physical address */ MemRAM = 1, /* physical memory */ @@ -77,9 +74,19 @@ static RMap rmapumbrw = { }; void -memdebug(void) +mapprint(RMap *rmap) { Map *mp; + + print("%s\n", rmap->name); + for(mp = rmap->map; mp->size; mp++) + print("\t%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); +} + + +void +memdebug(void) +{ ulong maxpa, maxpa1, maxpa2; if(MEMDEBUG == 0) @@ -91,14 +98,10 @@ memdebug(void) print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n", maxpa, MB+maxpa*KB, maxpa1, maxpa2); - for(mp = rmapram.map; mp->size; mp++) - print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); - for(mp = rmapumb.map; mp->size; mp++) - print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); - for(mp = rmapumbrw.map; mp->size; mp++) - print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); - for(mp = rmapupa.map; mp->size; mp++) - print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); + mapprint(&rmapram); + mapprint(&rmapumb); + mapprint(&rmapumbrw); + mapprint(&rmapupa); } void @@ -263,10 +266,23 @@ umbscan(void) } } +int +touch(uchar *p, int n) +{ + int x; + int i; + + x = 0; + for(i=0; ipdb)); -// memset(va, 0, MB); + /* memset(va, 0, MB); so damn slow to memset all of memory */ } else if(pa < 16*MB){ nvalid[MemUMB] += MB/BY2PG; @@ -418,10 +437,17 @@ ramscan(ulong maxmem) } void -meminit(ulong maxmem) +meminit(void) { Map *mp, *xmp; ulong pa, *pte; + ulong maxmem; + char *p; + + if(p = getconf("*maxmem")) + maxmem = strtoul(p, 0, 0); + else + maxmem = 0; /* * Set special attributes for memory between 640KB and 1MB: diff --git a/pc/mmu.c b/pc/mmu.c index 198ca9cb77ff956972a91e56e3b4c911d62d37c3..ebfb224dac34983eba721c158fd97cae92fa0cb6 100644 --- a/pc/mmu.c +++ b/pc/mmu.c @@ -10,7 +10,7 @@ #define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\ ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } -Segdesc gdt[6] = +Segdesc gdt[NGDT] = { [NULLSEG] { 0, 0}, /* null descriptor */ [KDSEG] DATASEGM(0), /* kernel data/stack */ @@ -20,9 +20,6 @@ Segdesc gdt[6] = [TSSSEG] TSSSEGM(0,0), /* tss segment */ }; -#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) -#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) - static void taskswitch(ulong pdb, ulong stack) { @@ -39,6 +36,21 @@ taskswitch(ulong pdb, ulong stack) putcr3(pdb); } +void +gdtinit(void) +{ + ulong x; + ushort ptr[3]; + + memmove(m->gdt, gdt, sizeof(m->gdt)); + + ptr[0] = sizeof(m->gdt)-1; + x = (ulong)m->gdt; + ptr[1] = x & 0xFFFF; + ptr[2] = (x>>16) & 0xFFFF; + lgdt(ptr); +} + void mmuinit(void) { @@ -53,13 +65,13 @@ mmuinit(void) m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; - ptr[0] = sizeof(m->gdt); + ptr[0] = sizeof(m->gdt)-1; x = (ulong)m->gdt; ptr[1] = x & 0xFFFF; ptr[2] = (x>>16) & 0xFFFF; lgdt(ptr); - ptr[0] = sizeof(Segdesc)*256; + ptr[0] = sizeof(Segdesc)*256-1; x = IDTADDR; ptr[1] = x & 0xFFFF; ptr[2] = (x>>16) & 0xFFFF; diff --git a/pc/mp.c b/pc/mp.c index ffc74ab2851c154b07c4782e933e939a9d65370d..d702b4c19b33f1f2a532e2774f71f3138ef4ff45 100644 --- a/pc/mp.c +++ b/pc/mp.c @@ -348,9 +348,6 @@ checkmtrr(void) } } -#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) -#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) - static void squidboy(Apic* apic) { diff --git a/pc/pci.c b/pc/pci.c index 7d855a9d89762ed2ec5a6c9591eee07d7c7366e1..f6aeadce6d4f539ddf01b208e0a9529cc7969e57 100644 --- a/pc/pci.c +++ b/pc/pci.c @@ -888,6 +888,7 @@ pcicfginit(void) out: qunlock(&pcicfginitlock); + } static int @@ -1170,8 +1171,12 @@ pcireset(void) if(pcicfgmode == -1) pcicfginit(); - for(p = pcilist; p != nil; p = p->list) + for(p = pcilist; p != nil; p = p->list) { + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; pciclrbme(p); + } } void diff --git a/pc/reboot.h b/pc/reboot.h new file mode 100644 index 0000000000000000000000000000000000000000..30c2ab499979b9ea9fc6ce0b08dccfb3d4cd5a36 --- /dev/null +++ b/pc/reboot.h @@ -0,0 +1,7 @@ +uchar rebootcode[]={ +0x8b,0x7c,0x24,0x04,0x89,0xf8,0x8b,0x74,0x24,0x08,0x8b,0x4c,0x24,0x0c,0x0f,0x20, +0xc2,0x81,0xe2,0xff,0xff,0xff,0x7f,0x0f,0x22,0xc2,0x31,0xd2,0x0f,0x22,0xda,0x39, +0xfe,0x7f,0x08,0x89,0xf2,0x01,0xca,0x39,0xfa,0x7f,0x07,0xfc,0xf3,0xa4,0x09,0xc0, +0xff,0xe0,0x01,0xcf,0x01,0xce,0x4f,0x4e,0xfd,0xf3,0xa4,0xeb,0xf1, + +}; diff --git a/pc/rebootcode.s b/pc/rebootcode.s new file mode 100644 index 0000000000000000000000000000000000000000..34ee4ca3636b83075882ef06303de5d5cee556b3 --- /dev/null +++ b/pc/rebootcode.s @@ -0,0 +1,54 @@ +#include "mem.h" + +/* + * Turn off MMU, then memmory the new kernel to its correct location + * in physical memory. Then jumps the to start of the kernel. + */ + +TEXT main(SB),$0 + MOVL p1+0(FP), DI /* destination */ + MOVL DI, AX /* entry point */ + MOVL p2+4(FP), SI /* source */ + MOVL n+8(FP), CX /* byte count */ + +/* + * disable paging + */ + MOVL CR0, DX + ANDL $~0x80000000, DX /* ~(PG) */ + MOVL DX, CR0 + MOVL $0, DX + MOVL DX, CR3 + +/* + * the source and destination may overlap. + * determine whether to copy forward or backwards + */ + CMPL SI, DI + JGT _forward + MOVL SI, DX + ADDL CX, DX + CMPL DX, DI + JGT _back + +_forward: + CLD + REP; MOVSB + JMP _startkernel + +_back: + ADDL CX, DI + ADDL CX, SI + SUBL $1, DI + SUBL $1, SI + STD + REP; MOVSB + JMP _startkernel +/* + * JMP to kernel entry point. Note the true kernel entry point is + * the virtual address KZERO|AX, but this must wait until + * the MMU is enabled by the kernel in l.s + */ +_startkernel: + ORL AX, AX /* NOP: avoid link bug */ + JMP* AX diff --git a/pc/uarti8250.c b/pc/uarti8250.c index 4a2bd2faae8032cf9391f36f363f0e4e02a11d1e..a255d2844791388c1f2d98d54f6ece13908b71a6 100644 --- a/pc/uarti8250.c +++ b/pc/uarti8250.c @@ -165,7 +165,7 @@ i8250status(Uart* uart, void* buf, long n, long offset) 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" + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n" "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n", uart->baud, @@ -178,6 +178,7 @@ i8250status(Uart* uart, void* buf, long n, long offset) (mcr & Rts) != 0, (lcr & Stb) ? 2: 1, ctlr->fena, + ier, uart->dev, uart->type, @@ -269,13 +270,13 @@ i8250modemctl(Uart* uart, int on) ilock(&uart->tlock); if(on){ ctlr->sticky[Ier] |= Ems; - csr8w(ctlr, Ier, 0); + csr8w(ctlr, Ier, ctlr->sticky[Ier]); uart->modem = 1; uart->cts = csr8r(ctlr, Msr) & Cts; } else{ ctlr->sticky[Ier] &= ~Ems; - csr8w(ctlr, Ier, 0); + csr8w(ctlr, Ier, ctlr->sticky[Ier]); uart->modem = 0; uart->cts = 1; } @@ -525,7 +526,7 @@ i8250disable(Uart* uart) ctlr = uart->regs; ctlr->sticky[Ier] = 0; - csr8w(ctlr, Ier, 0); + csr8w(ctlr, Ier, ctlr->sticky[Ier]); if(ctlr->iena != 0){ intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name); @@ -562,6 +563,18 @@ i8250enable(Uart* uart, int ie) (*uart->phys->dtr)(uart, 1); (*uart->phys->rts)(uart, 1); + + + /* + * During startup, the i8259 interrupt controler is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. To work around this we call the + * interrupt handler to clear any pending inerrupt events. + * Note this must be donw after setting Ier. + */ + if(ie) + i8250interrupt(nil, uart); } void* diff --git a/port/chan.c b/port/chan.c index a49da35b59beb16423e377d3ceac2b5671831ce2..c2bae6e7c93c04c72ba76ccdebdac4e5a182e50c 100644 --- a/port/chan.c +++ b/port/chan.c @@ -208,6 +208,18 @@ chandevinit(void) devtab[i]->init(); } +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + Chan* newchan(void) { diff --git a/port/dev.c b/port/dev.c index bc5ed0f162febb63cea4584c45b52d5fe96ffb0c..9d33988a28a1a46ab8904570fb2dbe6fdb1090b5 100644 --- a/port/dev.c +++ b/port/dev.c @@ -78,6 +78,11 @@ devinit(void) { } +void +devshutdown(void) +{ +} + Chan* devattach(int tc, char *spec) { diff --git a/port/devaudio.c b/port/devaudio.c index 5c74856e2b1b981bacb4ae2c9276b829b99292d0..67f8c2453172b75bd0bdb9fd2443aca0ca6c6ec9 100644 --- a/port/devaudio.c +++ b/port/devaudio.c @@ -1221,6 +1221,7 @@ Dev audiodevtab = { devreset, audioinit, + devshutdown, audioattach, audiowalk, audiostat, diff --git a/port/devbridge.c b/port/devbridge.c index 91176800f06d381c0a5b1c54a30cd58de4a13bd4..0ff308fd8dc64742472db64705df48a9414b6815 100644 --- a/port/devbridge.c +++ b/port/devbridge.c @@ -1190,6 +1190,7 @@ Dev bridgedevtab = { devreset, bridgeinit, + devshutdown, bridgeattach, bridgewalk, bridgestat, diff --git a/port/devcap.c b/port/devcap.c index a4e6f59b0f4686fa75a440c64f4a46c4f1e9c930..4ed100a6b4bc935f4989407026fc00991c75f212 100644 --- a/port/devcap.c +++ b/port/devcap.c @@ -251,6 +251,7 @@ Dev capdevtab = { devreset, devinit, + devshutdown, capattach, capwalk, capstat, diff --git a/port/devcons.c b/port/devcons.c index d0ce3265709baa4d00ef4be00fb692caa0d85834..b93aea0c431216a2f8c0b06d09f465f4756987f8 100644 --- a/port/devcons.c +++ b/port/devcons.c @@ -50,6 +50,18 @@ static int readbintime(char*, int); static int writetime(char*, int); static int writebintime(char*, int); +enum +{ + CMreboot, + CMpanic, +}; + +Cmdtab reboottbl[] = +{ + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + void printinit(void) { @@ -868,6 +880,8 @@ conswrite(Chan *c, void *va, long n, vlong off) int id, fd; Chan *swc; ulong offset = off; + Cmdbuf *cb; + Cmdtab *ct; switch((ulong)c->qid.path){ case Qcons: @@ -940,21 +954,22 @@ conswrite(Chan *c, void *va, long n, vlong off) case Qreboot: if(!iseve()) error(Eperm); - if(strncmp(a, "reboot", 6) == 0){ - print("conswrite: reboot\n"); - exit(0); - } - if(strncmp(a, "malloc", 6) == 0){ /* rsc bug */ - a = malloc(2); - strcpy(a, "hi"); - free(a); - a = malloc(2); - strcpy(a, "helo"); - free(a); - panic("not reached conswrite"); + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); } - if(strncmp(a, "panic", 5) == 0) + ct = lookupcmd(cb, reboottbl, nelem(reboottbl)); + switch(ct->index) { + case CMreboot: + rebootcmd(cb->nf-1, cb->f+1); + break; + case CMpanic: panic("/dev/reboot"); + } + poperror(); + free(cb); break; case Qsysstat: @@ -1009,21 +1024,13 @@ conswrite(Chan *c, void *va, long n, vlong off) return n; } -void -setterm(char *f) -{ - char buf[64]; - - snprint(buf, sizeof buf, f, conffile); - ksetenv("terminal", buf); -} - Dev consdevtab = { 'c', "cons", devreset, consinit, + devshutdown, consattach, conswalk, consstat, diff --git a/port/devdraw.c b/port/devdraw.c index 2d553fb78d9988041e44d2f94ecc18f1fd4b196c..8ce6ee3c25be4d518bb06c0835b9d3a037752cd2 100644 --- a/port/devdraw.c +++ b/port/devdraw.c @@ -1925,6 +1925,7 @@ Dev drawdevtab = { devreset, devinit, + devshutdown, drawattach, drawwalk, drawstat, diff --git a/port/devdup.c b/port/devdup.c index d9c6e3dd90e80585177b6437e19c9baa2fef6ea8..ed8c5de633ad8caf38928e822c3375fe3c5039d7 100644 --- a/port/devdup.c +++ b/port/devdup.c @@ -130,6 +130,7 @@ Dev dupdevtab = { devreset, devinit, + devshutdown, dupattach, dupwalk, dupstat, diff --git a/port/devenv.c b/port/devenv.c index 55123835168b94008a5ead547740f1a803f1afa2..f695aa3c8425a0fbafd8db26d21c814f9d60f484 100644 --- a/port/devenv.c +++ b/port/devenv.c @@ -5,12 +5,16 @@ #include "fns.h" #include "../port/error.h" - enum { Maxenvsize = 16300, }; +static Egrp *envgrp(Chan *c); +static int envwriteable(Chan *c); + +static Egrp confegrp; /* global environment group containing the kernel configuration */ + static int envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) { @@ -22,7 +26,7 @@ envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) return 1; } - eg = up->egrp; + eg = envgrp(c); qlock(eg); for(e = eg->entries; e && s; e = e->link) @@ -53,7 +57,19 @@ envlookup(Egrp *eg, char *name, ulong qidpath) static Chan* envattach(char *spec) { - return devattach('e', spec); + Chan *c; + Egrp *egrp = nil; + + if(spec && *spec) { + if(strcmp(spec, "c") == 0) + egrp = &confegrp; + if(egrp == nil) + error(Ebadarg); + } + + c = devattach('e', spec); + c->aux = egrp; + return c; } static Walkqid* @@ -66,7 +82,7 @@ static int envstat(Chan *c, uchar *db, int n) { if(c->qid.type & QTDIR) - c->qid.vers = up->egrp->vers; + c->qid.vers = envgrp(c)->vers; return devstat(c, db, n, 0, 0, envgen); } @@ -76,12 +92,14 @@ envopen(Chan *c, int omode) Egrp *eg; Evalue *e; - eg = up->egrp; + eg = envgrp(c); if(c->qid.type & QTDIR) { if(omode != OREAD) error(Eperm); } else { + if(omode != OREAD && !envwriteable(c)) + error(Eperm); qlock(eg); e = envlookup(eg, nil, c->qid.path); if(e == 0) { @@ -112,7 +130,7 @@ envcreate(Chan *c, char *name, int omode, ulong) error(Eperm); omode = openmode(omode); - eg = up->egrp; + eg = envgrp(c); qlock(eg); if(waserror()) { @@ -151,7 +169,7 @@ envremove(Chan *c) if(c->qid.type & QTDIR) error(Eperm); - eg = up->egrp; + eg = envgrp(c); qlock(eg); l = &eg->entries; for(e = *l; e; e = e->link) { @@ -196,7 +214,7 @@ envread(Chan *c, void *a, long n, vlong off) if(c->qid.type & QTDIR) return devdirread(c, a, n, 0, 0, envgen); - eg = up->egrp; + eg = envgrp(c); qlock(eg); e = envlookup(eg, nil, c->qid.path); if(e == 0) { @@ -230,7 +248,7 @@ envwrite(Chan *c, void *a, long n, vlong off) if(vend > Maxenvsize) error(Etoobig); - eg = up->egrp; + eg = envgrp(c); qlock(eg); e = envlookup(eg, nil, c->qid.path); if(e == 0) { @@ -260,6 +278,7 @@ Dev envdevtab = { devreset, devinit, + devshutdown, envattach, envwalk, envstat, @@ -314,17 +333,73 @@ closeegrp(Egrp *eg) } } +static Egrp* +envgrp(Chan *c) +{ + if(c->aux == nil) + return up->egrp; + return c->aux; +} + +static int +envwriteable(Chan *c) +{ + return iseve() || c->aux == nil; +} + /* * to let the kernel set environment variables */ void -ksetenv(char *ename, char *eval) +ksetenv(char *ename, char *eval, int conf) { Chan *c; char buf[2*KNAMELEN]; - - sprint(buf, "#e/%s", ename); + + snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename); c = namec(buf, Acreate, OWRITE, 0600); devtab[c->type]->write(c, eval, strlen(eval), 0); cclose(c); } + +/* + * Return a copy of configuration environment as a sequence of strings. + * The strings alternate between name and value. A zero length name string + * indicates the end of the list + */ +char * +getconfenv(void) +{ + Egrp *eg = &confegrp; + Evalue *e; + char *p, *q; + int n; + + qlock(eg); + if(waserror()) { + qunlock(eg); + nexterror(); + } + + /* determine size */ + n = 0; + for(e=eg->entries; e; e=e->link) + n += strlen(e->name) + e->len + 2; + p = malloc(n + 1); + if(p == nil) + error(Enomem); + q = p; + for(e=eg->entries; e; e=e->link) { + strcpy(q, e->name); + q += strlen(q) + 1; + memmove(q, e->value, e->len); + q[e->len] = 0; + /* move up to the first null */ + q += strlen(q) + 1; + } + *q = 0; + + poperror(); + qunlock(eg); + return p; +} diff --git a/port/devkprof.c b/port/devkprof.c index 2a3a06f03638a7bc1746fd2f4da3a102ce52b8af..85ca17a58817dbc6557be2e320b8e2cc38277c05 100644 --- a/port/devkprof.c +++ b/port/devkprof.c @@ -176,6 +176,7 @@ Dev kprofdevtab = { devreset, kprofinit, + devshutdown, kprofattach, kprofwalk, kprofstat, diff --git a/port/devloopback.c b/port/devloopback.c index 02468b2e38a875fd71330c8e51c2c3991618eb6c..53d5263ee4fa24fa75ce4df6301720c7fe33a6d6 100644 --- a/port/devloopback.c +++ b/port/devloopback.c @@ -748,6 +748,7 @@ Dev loopbackdevtab = { devreset, loopbackinit, + devshutdown, loopbackattach, loopbackwalk, loopbackstat, diff --git a/port/devmnt.c b/port/devmnt.c index ade2edce84ef27bbe86c15f1f0898fa3c5a46c44..46e485168ade4ecb4d4d2b2bed3f36d6b20508d6 100644 --- a/port/devmnt.c +++ b/port/devmnt.c @@ -450,7 +450,6 @@ mntwalk(Chan *c, Chan *nc, char **name, int nname) wq->qid[i] = r->reply.wqid[i]; Return: - poperror(); mntfree(r); poperror(); @@ -1142,6 +1141,7 @@ Dev mntdevtab = { mntreset, devinit, + devshutdown, mntattach, mntwalk, mntstat, diff --git a/port/devmouse.c b/port/devmouse.c index 10b79d25a51d3ac10742286e7dd4a35b6cd225bb..5ba4c242e8b6b2423ae68f40c2ab600cb56c4ee4 100644 --- a/port/devmouse.c +++ b/port/devmouse.c @@ -435,6 +435,7 @@ Dev mousedevtab = { mousereset, mouseinit, + devshutdown, mouseattach, mousewalk, mousestat, diff --git a/port/devpipe.c b/port/devpipe.c index 890e4eaa98038bfe937cf5bde76f28b8aaff9862..1a710d3a16eaf1c2e9095dc6a4c9baf37f0730f8 100644 --- a/port/devpipe.c +++ b/port/devpipe.c @@ -375,6 +375,7 @@ Dev pipedevtab = { devreset, pipeinit, + devshutdown, pipeattach, pipewalk, pipestat, diff --git a/port/devpnp.c b/port/devpnp.c index 7d60a719efd00c5777dd37604c87869f5757e682..315339d7bb4f2476534d10bc14607c4d68ef80ac 100644 --- a/port/devpnp.c +++ b/port/devpnp.c @@ -624,6 +624,7 @@ Dev pnpdevtab = { pnpreset, devinit, + devshutdown, pnpattach, pnpwalk, pnpstat, diff --git a/port/devproc.c b/port/devproc.c index 4d7d28d9348567a63d1b5ca9a33758353031bb9f..b95339a7047b731f093a46c966026d3f0dbb3dc0 100644 --- a/port/devproc.c +++ b/port/devproc.c @@ -920,6 +920,7 @@ Dev procdevtab = { devreset, procinit, + devshutdown, procattach, procwalk, procstat, diff --git a/port/devroot.c b/port/devroot.c index 56f88d7afc2ff7b9f07b3e81e6e682e219fa9253..0379bb25b70a31b4f46a3cec41a460aefc0c0999 100644 --- a/port/devroot.c +++ b/port/devroot.c @@ -156,6 +156,7 @@ Dev rootdevtab = { rootreset, devinit, + devshutdown, rootattach, rootwalk, rootstat, diff --git a/port/devsd.c b/port/devsd.c index 10f53400d4447a4889124c32f90b1663295f4182..3c6419f3e66386c97b032e658b59859aba6e260e 100644 --- a/port/devsd.c +++ b/port/devsd.c @@ -1448,6 +1448,7 @@ Dev sddevtab = { sdreset, devinit, + devshutdown, sdattach, sdwalk, sdstat, diff --git a/port/devsdp.c b/port/devsdp.c index 7c0329e3c82331effea71de56dbe10a373c60733..8a02c7f3b3dbb77af26de0bb85ef82645ce079a6 100644 --- a/port/devsdp.c +++ b/port/devsdp.c @@ -935,6 +935,7 @@ Dev sdpdevtab = { devreset, sdpinit, + devshutdown, sdpattach, sdpwalk, sdpstat, diff --git a/port/devsrv.c b/port/devsrv.c index 731f9086846ad5998bd8f7d79d1be0fff217e591..5d459b424e567daebdc3c4b0c9bceac983a1d903 100644 --- a/port/devsrv.c +++ b/port/devsrv.c @@ -316,7 +316,8 @@ Dev srvdevtab = { "srv", devreset, - srvinit, + srvinit, + devshutdown, srvattach, srvwalk, srvstat, diff --git a/port/devssl.c b/port/devssl.c index ea0dc95eb7ae091b38ffd9eabc2935abd8dbbda4..75ec63374ba1cbc91d5613f6b90c28b68d97813c 100644 --- a/port/devssl.c +++ b/port/devssl.c @@ -1229,6 +1229,7 @@ Dev ssldevtab = { devreset, sslinit, + devshutdown, sslattach, sslwalk, sslstat, diff --git a/port/devtls.c b/port/devtls.c index 16d17b64e38dc90e17a96883922ce311faf76d29..f111a64bad90bc3a9c398c2727b53f48d557cf85 100644 --- a/port/devtls.c +++ b/port/devtls.c @@ -1666,6 +1666,7 @@ Dev tlsdevtab = { devreset, tlsinit, + devshutdown, tlsattach, tlswalk, tlsstat, diff --git a/port/devuart.c b/port/devuart.c index 942c316b2c473b201b9b5cc0725ebbadc7889943..b6c5c9f76cf39b816df614851e840ba036f2ef8a 100644 --- a/port/devuart.c +++ b/port/devuart.c @@ -509,6 +509,7 @@ Dev uartdevtab = { uartreset, devinit, + devshutdown, uartattach, uartwalk, uartstat, diff --git a/port/portdat.h b/port/portdat.h index b5f117130d6d32d6bbb27d07408e57934c4cb357..5f80971e228a81e6fa311dd50f8810cefe1c7dfd 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -185,6 +185,7 @@ struct Dev void (*reset)(void); void (*init)(void); + void (*shutdown)(void); Chan* (*attach)(char*); Walkqid* (*walk)(Chan*, Chan*, char**, int); int (*stat)(Chan*, uchar*, int); diff --git a/port/portfns.h b/port/portfns.h index 21017790d17b8f48acee763e078001f0ddc28303..26123b93e25b82f3bedf6dbd6b2a1ad3640ca28e 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -25,6 +25,7 @@ int canqlock(QLock*); int canrlock(RWlock*); void chandevinit(void); void chandevreset(void); +void chandevshutdown(void); void chanfree(Chan*); void chanrec(Mnt*); void checkalarms(void); @@ -73,6 +74,7 @@ void devpermcheck(char*, ulong, int); void devpower(int); void devremove(Chan*); void devreset(void); +void devshutdown(void); int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); int devwstat(Chan*, uchar*, int); @@ -114,6 +116,7 @@ void getcolor(ulong, ulong*, ulong*, ulong*); ulong getmalloctag(void*); ulong getrealloctag(void*); void gotolabel(Label*); +char* getconfenv(void); int haswaitq(void*); long hostdomainwrite(char*, int); long hostownerwrite(char*, int); @@ -142,7 +145,7 @@ int kprint(char*, ...); void kproc(char*, void(*)(void*), void*); void kprocchild(Proc*, void (*)(void*), void*); void (*kproftimer)(ulong); -void ksetenv(char*, char*); +void ksetenv(char*, char*, int); void kstrcpy(char*, char*, int); void kstrdup(char**, char*); long latin1(Rune*, int); @@ -270,6 +273,8 @@ void rdb(void); int readnum(ulong, char*, ulong, ulong, int); int readstr(ulong, char*, ulong, char*); void ready(Proc*); +void rebootcmd(int, char**); +void reboot(void*, void*, ulong); void relocateseg(Segment*, ulong); void renameuser(char*, char*); void resched(char*); diff --git a/port/rebootcmd.c b/port/rebootcmd.c new file mode 100644 index 0000000000000000000000000000000000000000..5918649039f7e6f24cd790aea884b8d16e9062b7 --- /dev/null +++ b/port/rebootcmd.c @@ -0,0 +1,100 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "a.out.h" + +static ulong +l2be(long l) +{ + uchar *cp; + + cp = (uchar*)&l; + return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3]; +} + + +static void +readn(Chan *c, void *vp, long n) +{ + char *p = vp; + long nn; + + while(n > 0) { + nn = devtab[c->type]->read(c, p, n, c->offset); + if(nn == 0) + error(Eshort); + c->offset += nn; + p += nn; + n -= nn; + } +} + +static void +setbootcmd(int argc, char *argv[]) +{ + char *buf, *p, *ep; + int i; + + buf = malloc(1024); + if(buf == nil) + error(Enomem); + p = buf; + ep = buf + 1024; + for(i=0; i