diff --git a/sys/src/9/sgi/bootfs.proto b/sys/src/9/sgi/bootfs.proto new file mode 100644 index 000000000..53d8aac86 --- /dev/null +++ b/sys/src/9/sgi/bootfs.proto @@ -0,0 +1,37 @@ +$objtype + bin + awk + auth + secstore + aux + kbdfs + bind + cat + dd + echo + grep + ip + ipconfig + ls + mkdir + mntgen + mount + mv + ndb + dnsgetip + ps + rc + rm + sed + sleep + srv + test + unmount + xd +rc + lib + rcmain + net.rc 555 sys sys ../boot/net.rc + bin + bootrc 555 sys sys ../boot/bootrc +tmp diff --git a/sys/src/9/sgi/clock.c b/sys/src/9/sgi/clock.c new file mode 100644 index 000000000..8a026520a --- /dev/null +++ b/sys/src/9/sgi/clock.c @@ -0,0 +1,167 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +enum { + Cyccntres = 2, /* counter advances at ½ clock rate */ + Basetickfreq = 150*Mhz / Cyccntres, /* sgi/indy */ + + Instrs = 10*Mhz, +}; + +static long +issue1loop(void) +{ + register int i; + long st; + + i = Instrs; + st = perfticks(); + do { + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; + --i; --i; --i; --i; --i; + /* omit 3 (--i) to account for conditional branch, nop & jump */ + i -= 1+3; /* --i plus 3 omitted (--i) instructions */ + } while(--i >= 0); + return perfticks() - st; +} + +/* estimate instructions/s. */ +static int +guessmips(long (*loop)(void), char *) +{ + int s; + long cyc; + + do { + s = splhi(); + cyc = loop(); + splx(s); + if (cyc < 0) + iprint("again..."); + } while (cyc < 0); + /* + * Instrs instructions took cyc cycles @ Basetickfreq Hz. + * round the result. + */ + return (((vlong)Basetickfreq * Instrs) / cyc + Mhz/2) / Mhz; +} + +void +clockinit(void) +{ + int mips; + + /* + * calibrate fastclock + */ + mips = guessmips(issue1loop, "single"); + + /* + * m->delayloop should be the number of delay loop iterations + * needed to consume 1 ms, assuming 2 instr'ns in the delay loop. + */ + m->delayloop = mips*Mhz / (1000 * 2); + if(m->delayloop == 0) + m->delayloop = 1; + + m->speed = mips; + m->hz = m->speed*Mhz; + + m->maxperiod = Basetickfreq / HZ; + m->minperiod = Basetickfreq / (100*HZ); + wrcompare(rdcount()+m->maxperiod); + + intron(INTR7); +} + +void +clock(Ureg *ur) +{ + wrcompare(rdcount()+m->maxperiod); /* side-effect: dismiss intr */ + timerintr(ur, 0); +} + +void +microdelay(int n) +{ + ulong now; + now = µs(); + while(µs() - now < n); +} + +void +delay(int n) +{ + while(--n >= 0) + microdelay(1000); +} + +ulong +µs(void) +{ + return fastticks2us(fastticks(nil)); +} + +uvlong +fastticks(uvlong *hz) +{ + int x; + ulong delta, count; + + if(hz) + *hz = Basetickfreq; + + /* avoid reentry on interrupt or trap, to prevent recursion */ + x = splhi(); + count = rdcount(); + if(rdcompare() - count > m->maxperiod) + wrcompare(count+m->maxperiod); + if (count < m->lastcount) /* wrapped around? */ + delta = count + ((1ull<<32) - m->lastcount); + else + delta = count - m->lastcount; + m->lastcount = count; + m->fastticks += delta; + splx(x); + + return m->fastticks; +} + +ulong +perfticks(void) +{ + return rdcount(); +} + +void +timerset(Tval next) +{ + int x; + long period; + + if(next == 0) + return; + x = splhi(); /* don't let us get scheduled */ + period = next - fastticks(nil); + if(period > m->maxperiod - m->minperiod) + period = m->maxperiod; + else if(period < m->minperiod) + period = m->minperiod; + wrcompare(rdcount()+period); + splx(x); + +} diff --git a/sys/src/9/sgi/dat.h b/sys/src/9/sgi/dat.h new file mode 100644 index 000000000..9e3acb98c --- /dev/null +++ b/sys/src/9/sgi/dat.h @@ -0,0 +1,211 @@ +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct KMap KMap; +typedef struct Lance Lance; +typedef struct Lancemem Lancemem; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct MMU MMU; +typedef struct Notsave Notsave; +typedef struct PMMU PMMU; +typedef struct Softtlb Softtlb; +typedef struct Ureg Ureg; +typedef struct Proc Proc; +typedef uvlong Tval; + +#define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */ + +/* + * parameters for sysproc.c and rebootcmd.c + */ +#define AOUT_MAGIC V_MAGIC || magic==M_MAGIC +/* r3k or r4k boot images */ +#define BOOT_MAGIC (0x160<<16) || magic == ((0x160<<16)|3) + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Lock +{ + ulong key; /* semaphore (non-zero = locked) */ + ulong sr; + ulong pc; + Proc *p; + Mach *m; + ushort isilock; +}; + +struct Label +{ + uintptr sp; + uintptr pc; +}; + +struct Confmem +{ + ulong base; + ulong npage; + ulong kbase; + ulong klimit; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + Confmem mem[4]; + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* bytes available for interrupt-time allocation */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +/* + * floating point registers + */ +enum +{ + /* floating point state */ + FPinit, + FPactive, + FPinactive, + FPemu, + + /* bit meaning floating point illegal */ + FPillegal= 0x100, +}; + +enum { + Nfpregs = 32, /* floats; half as many doubles */ +}; + +/* + * emulated floating point (mips32r2 with ieee fp regs) + * fpstate is separate, kept in Proc + */ +struct FPsave +{ + /* /dev/proc expects the registers to be first in FPsave */ + ulong reg[Nfpregs]; /* the canonical bits */ + union { + ulong fpstatus; /* both are fcr31 */ + ulong fpcontrol; + }; + + int fpdelayexec; /* executing delay slot of branch */ + uintptr fpdelaypc; /* pc to resume at after */ + ulong fpdelaysts; /* save across user-mode delay-slot execution */ + + /* stuck-fault detection */ + uintptr fppc; /* addr of last fault */ + int fpcnt; /* how many consecutive at that addr */ +}; + +/* + * mmu goo in the Proc structure + */ +struct PMMU +{ + int pidonmach[MAXMACH]; +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong nonempty; +}; + +#include "../port/portdat.h" + +struct Mach +{ + /* the following are all known by l.s and cannot be moved */ + int machno; /* physical id of processor */ + Softtlb*stb; + Proc* proc; /* process on this processor */ + ulong splpc; /* pc that called splhi() */ + ulong tlbfault; + + /* the following is safe to move */ + ulong tlbpurge; + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + void* alarm; /* alarms bound to this clock */ + int lastpid; /* last pid allocated on this machine */ + Proc* pidproc[NTLBPID]; /* proc that owns tlbpid on this mach */ + KMap* kactive; /* active on this machine */ + int knext; + uchar ktlbx[NTLB]; /* tlb index used for kmap */ + uchar ktlbnext; + int speed; /* cpu speed */ + ulong delayloop; /* for the delay() routine */ + ulong fairness; /* for runproc */ + int flushmmu; + int inclockintr; + int ilockdepth; + Perf perf; /* performance counters */ + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + + /* for per-processor timers */ + ulong lastcount; + uvlong fastticks; + ulong hz; + ulong maxperiod; + ulong minperiod; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + int pfault; + int cs; + int syscall; + int load; + int intr; + int hashcoll; /* soft-tlb hash collisions */ + int paststartup; /* for putktlb */ + + int stack[1]; +}; + +struct KMap +{ + Ref; + ulong virt; + ulong phys0; + ulong phys1; + KMap* next; + KMap* konmach[MAXMACH]; + Page* pg; + ulong pc; /* of caller to kmap() */ +}; + +#define VA(k) ((k)->virt) +#define PPN(x) ((ulong)(x)>>6) + +struct Softtlb +{ + ulong virt; + ulong phys0; + ulong phys1; +}; + +struct +{ + Lock; + long machs; /* bitmap of processors */ + short exiting; + int ispanic; +}active; + +extern register Mach *m; +extern register Proc *up; diff --git a/sys/src/9/sgi/devether.c b/sys/src/9/sgi/devether.c new file mode 100644 index 000000000..0902953cf --- /dev/null +++ b/sys/src/9/sgi/devether.c @@ -0,0 +1,511 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "pool.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); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->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 Chan* +ethercreate(Chan*, char*, int, ulong) +{ + error(Eperm); + return 0; +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) { + // print("soverflow for f->in\n"); + ether->soverflows++; + } + } + else { + // print("soverflow iallocb\n"); + ether->soverflows++; + } + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) { + // print("soverflow for fx->in\n"); + ether->soverflows++; + } + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback; + 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) + if(etheriq(ether, bp, loopback) == 0) + return len; + + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(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 < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static Ether* +etherprobe(int cardno, int ctlrno) +{ + int i, lg; + ulong mb, bsz; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + if(ether == nil){ + print("etherprobe: no memory for Ether\n"); + return nil; + } + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(cardno >= MaxEther || cards[cardno].type == nil){ + free(ether); + return nil; + } + if(cards[cardno].reset(ether) < 0){ + free(ether); + return nil; + } + + snprint(name, sizeof(name), "ether%d", ctlrno); + + intrenable(ether->irqlevel, ether->interrupt, ether); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, cards[cardno].type, ether->mbps, ether->port, ether->irq); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + /* compute log10(ether->mbps) into lg */ + for(lg = 0, mb = ether->mbps; mb >= 10; lg++) + mb /= 10; + if (lg > 0) + lg--; + if (lg > 14) /* 2^(14+17) = 2³¹ */ + lg = 14; + /* allocate larger output queues for higher-speed interfaces */ + bsz = 1UL << (lg + 17); /* 2¹⁷ = 128K, bsz = 2ⁿ × 128K */ + while (bsz > mainmem->maxsize / 8 && bsz > 128*1024) + bsz /= 2; + + netifinit(ether, name, Ntypes, bsz); + if(ether->oq == nil) { + ether->oq = qopen(bsz, Qmsg, 0, 0); + ether->limit = bsz; + } + if(ether->oq == nil) + panic("etherreset %s: can't allocate output queue of %ld bytes", name, bsz); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + return ether; +} + +static void +etherreset(void) +{ + Ether *ether; + int cardno, ctlrno; + + cardno = ctlrno = 0; + while(cards[cardno].type != nil && ctlrno < MaxEther){ + if(etherxx[ctlrno] != nil){ + ctlrno++; + continue; + } + if((ether = etherprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + etherxx[ctlrno] = ether; + ctlrno++; + } +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown function\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#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, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/sys/src/9/sgi/etherif.h b/sys/src/9/sgi/etherif.h new file mode 100644 index 000000000..035dd64cb --- /dev/null +++ b/sys/src/9/sgi/etherif.h @@ -0,0 +1,40 @@ +enum { + MaxEther = 1, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + + int ctlrno; + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + int irq, irqlevel; + uintptr port; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/sys/src/9/sgi/etherseeq.c b/sys/src/9/sgi/etherseeq.c new file mode 100644 index 000000000..4f9d930ac --- /dev/null +++ b/sys/src/9/sgi/etherseeq.c @@ -0,0 +1,415 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" +#include "etherif.h" + +typedef struct Hio Hio; +typedef struct Desc Desc; +typedef struct Ring Ring; +typedef struct Ctlr Ctlr; + +/* + * SEEQ 8003 interfaced to HPC3 (very different from IP20) + */ +struct Hio +{ + ulong unused0[20480]; + ulong crbp; /* current receive buf desc ptr */ + ulong nrbdp; /* next receive buf desc ptr */ + ulong unused1[1022]; + ulong rbc; /* receive byte count */ + ulong rstat; /* receiver status */ + ulong rgio; /* receive gio fifo ptr */ + ulong rdev; /* receive device fifo ptr */ + ulong unused2; + ulong ctl; /* interrupt, channel reset, buf oflow */ + ulong dmacfg; /* dma configuration */ + ulong piocfg; /* pio configuration */ + ulong unused3[1016]; + ulong cxbdp; /* current xmit buf desc ptr */ + ulong nxbdp; /* next xmit buffer desc. pointer */ + ulong unused4[1022]; + ulong xbc; /* xmit byte count */ + ulong xstat; + ulong xgio; /* xmit gio fifo ptr */ + ulong xdev; /* xmit device fifo ptr */ + ulong unused5[1020]; + ulong crbdp; /* current receive descriptor ptr */ + ulong unused6[2047]; + ulong cpfxbdp; /* current/previous packet 1st xmit */ + ulong ppfxbdp; /* desc ptr */ + ulong unused7[59390]; + ulong eaddr[6]; /* seeq station address wo */ + ulong csr; /* seeq receiver cmd/status reg */ + ulong csx; /* seeq transmitter cmd/status reg */ +}; + +enum +{ /* ctl */ + Cover= 0x08, /* receive buffer overflow */ + Cnormal=0x00, /* 1=normal, 0=loopback */ + Cint= 0x02, /* interrupt (write 1 to clear) */ + Creset= 0x01, /* ethernet channel reset */ + + /* xstat */ + Xdma= 0x200, /* dma active */ + Xold= 0x080, /* register has been read */ + Xok= 0x008, /* transmission was successful */ + Xmaxtry=0x004, /* transmission failed after 16 attempts */ + Xcoll= 0x002, /* transmission collided */ + Xunder= 0x001, /* transmitter underflowed */ + + /* csx */ + Xreg0= 0x00, /* access reg bank 0 incl station addr */ + XIok= 0x08, + XImaxtry=0x04, + XIcoll= 0x02, + XIunder=0x01, + + /* rstat */ + Rlshort=0x800, /* [small len in received frame] */ + Rdma= 0x200, /* dma active */ + Rold= 0x80, /* register has been read */ + Rok= 0x20, /* received good frame */ + Rend= 0x10, /* received end of frame */ + Rshort= 0x08, /* received short frame */ + Rdrbl= 0x04, /* dribble error */ + Rcrc= 0x02, /* CRC error */ + Rover= 0x01, /* overflow error */ + + /* csr */ + Rsmb= 0xc0, /* receive station/broadcast/multicast frames */ + Rsb= 0x80, /* receive station/broadcast frames */ + Rprom= 0x40, /* receive all frames */ + RIok= 0x20, /* interrupt on good frame */ + RIend= 0x10, /* interrupt on end of frame */ + RIshort=0x08, /* interrupt on short frame */ + RIdrbl= 0x04, /* interrupt on dribble error */ + RIcrc= 0x02, /* interrupt on CRC error */ + RIover= 0x01, /* interrupt on overflow error */ + + HPC_MODNORM= 0x0, /* mode: 0=normal, 1=loopback */ + HPC_FIX_INTR= 0x8000, /* start timeout counter after */ + HPC_FIX_EOP= 0x4000, /* rcv_eop_intr/eop_in_chip is set */ + HPC_FIX_RXDC= 0x2000, /* clear eop status upon rxdc */ +}; + +struct Desc +{ + ulong addr; /* addr */ + ulong count; /* eox / eop / busy / xie / count:13 */ + ulong next; + uchar* base; +}; + +struct Ring +{ + Rendez; + int size; + uchar* base; + Desc* head; + Desc* tail; +}; + +enum +{ + Eor= 1<<31, /* end of ring */ + Eop= 1<<30, + Ie= 1<<29, + Busy= 1<<24, + Empty= 1<<14, /* no data here */ +}; + +enum { + Rbsize = ETHERMAXTU+3, +}; + +struct Ctlr +{ + int attach; + + Hio *io; + + Ring rx; + Ring tx; +}; + +static ulong dummy; + +static void +interrupt(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *ctlr; + Hio *io; + uint s; + + edev = arg; + ctlr = edev->ctlr; + io = ctlr->io; + s = io->ctl; + if(s & Cover) + io->ctl = Cnormal | Cover; + if(s & Cint) { + io->ctl = Cnormal | Cint; + wakeup(&ctlr->rx); + } +} + +static int +notempty(void *arg) +{ + Ctlr *ctlr = arg; + Hio *io; + + io = ctlr->io; + dummy = io->piocfg; + if((io->rstat & Rdma) == 0) + return 1; + return (IO(Desc, ctlr->rx.head->next)->count & Empty) == 0; +} + +static void +rxproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr; + Hio *io; + Block *b; + Desc *p; + int n; + + while(waserror()) + ; + + ctlr = edev->ctlr; + io = ctlr->io; + for(p = IO(Desc, ctlr->rx.head->next);; p = IO(Desc, p->next)){ + while((p->count & Empty) != 0){ + io->rstat = Rdma; + tsleep(&ctlr->rx, notempty, ctlr, 500); + } + n = Rbsize - (p->count & 0x3fff)-3; + if(n >= ETHERMINTU){ + if((p->base[n+2] & Rok) != 0){ + b = allocb(n); + b->wp += n; + memmove(b->rp, p->base+2, n); + etheriq(edev, b, 1); + } + } + p->addr = PADDR(p->base); + p->count = Ie|Empty|Rbsize; + ctlr->rx.head = p; + } +} + +static void +txproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr; + Hio *io; + Block *b; + Desc *p; + int clean, n; + + while(waserror()) + ; + + ctlr = edev->ctlr; + io = ctlr->io; + clean = ctlr->tx.size / 2; + for(p = IO(Desc, ctlr->tx.tail->next); (b = qbread(edev->oq, 1000000)) != nil; p = IO(Desc, p->next)){ + while(!clean){ + splhi(); + p = ctlr->tx.head; + dummy = io->piocfg; + ctlr->tx.head = IO(Desc, io->nxbdp & ~0xf); + spllo(); + while(p != ctlr->tx.head){ + if((p->count & Busy) == 0) + break; + clean++; + p->count = Eor|Eop; + p = IO(Desc, p->next); + } + + p = IO(Desc, ctlr->tx.tail->next); + if(clean) + break; + + io->xstat = Xdma; + tsleep(&ctlr->tx, return0, nil, 10); + } + clean--; + + n = BLEN(b); + if(n > ETHERMAXTU) + n = ETHERMAXTU; + memmove(p->base, b->rp, n); + + p->addr = PADDR(p->base); + p->count = Eor|Eop|Busy|n; + + ctlr->tx.tail->count &= ~Eor; + ctlr->tx.tail = p; + + io->xstat = Xdma; + + freeb(b); + } +} + +static void +allocring(Ring *r, int n) +{ + uchar *b; + Desc *p; + int m; + + r->size = n; + + m = n*BY2PG/2; + b = xspanalloc(m, BY2PG, 0); + dcflush(b, m); + b = IO(uchar, b); + memset(b, 0, m); + r->base = b; + + m = n*sizeof(Desc); + p = xspanalloc(m, BY2PG, 0); + dcflush(p, m); + p = IO(Desc, p); + memset(p, 0, m); + r->head = r->tail = p; + + for(m=0; mbase = b; + p->next = PADDR(p+1); + } + p[-1].next = PADDR(r->head); +} + +static int +init(Ether *edev) +{ + Ctlr *ctlr; + Desc *p; + Hio *io; + int i; + + io = IO(Hio, edev->port); + ctlr = edev->ctlr; + ctlr->io = io; + + io->csx = Xreg0; + allocring(&ctlr->rx, 256); + allocring(&ctlr->tx, 64); + + io->rstat = 0; + io->xstat = 0; + io->ctl = Cnormal | Creset | Cint; + delay(10); + io->ctl = Cnormal; + io->csx = 0; + io->csr = 0; + + io->dmacfg |= HPC_FIX_INTR | HPC_FIX_EOP | HPC_FIX_RXDC; + + p = ctlr->rx.head; + do { + p->addr = PADDR(p->base); + p->count = Ie|Empty|Rbsize; + p = IO(Desc, p->next); + } while(p != ctlr->rx.head); + io->crbdp = PADDR(p); + io->nrbdp = p->next; + + p = ctlr->tx.tail; + do { + p->addr = 0; + p->count = Eor|Eop; + p = IO(Desc, p->next); + } while(p != ctlr->tx.tail); + ctlr->tx.head = IO(Desc, p->next); + io->cxbdp = PADDR(p); + io->nxbdp = p->next; + + for(i=0; i<6; i++) + io->eaddr[i] = edev->ea[i]; + + io->csx = 0; /* XIok | XImaxtry | XIcoll | XIunder; -- no interrupts needed */ + io->csr = Rprom | RIok|RIend|RIshort|RIdrbl|RIcrc; + + return 0; +} + +/* + * do nothing for promiscuous() and multicast() as we + * are always in promisc mode. + */ +static void +promiscuous(void*, int) +{ +} +static void +multicast(void*, uchar*, int) +{ +} + +static void +attach(Ether *edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->attach) + return; + ctlr->attach = 1; + kproc("#0rx", rxproc, edev); + kproc("#0tx", txproc, edev); +} + +static int +pnp(Ether *edev) +{ + static Ctlr ct; + char *s; + + /* only one controller */ + if(edev->ctlrno != 0) + return -1; + + /* get mac address from nvram */ + if((s = getconf("eaddr")) != nil) + parseether(edev->ea, s); + + edev->ctlr = &ct; + edev->port = HPC3_ETHER; + edev->irq = IRQENET; + edev->irqlevel = hpc3irqlevel(edev->irq); + edev->ctlr = &ct; + edev->promiscuous = promiscuous; + edev->multicast = multicast; + edev->interrupt = interrupt; + edev->attach = attach; + edev->arg = edev; + edev->mbps = 10; + edev->link = 1; + if(init(edev) < 0){ + edev->ctlr = nil; + return -1; + } + return 0; +} + +void +etherseeqlink(void) +{ + addethercard("seeq", pnp); +} diff --git a/sys/src/9/sgi/faultmips.c b/sys/src/9/sgi/faultmips.c new file mode 100644 index 000000000..0635596df --- /dev/null +++ b/sys/src/9/sgi/faultmips.c @@ -0,0 +1,248 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "io.h" + +enum { + Debug = 0, +}; + +typedef struct Fault Fault; +struct Fault { + uintptr va; + ulong pid; + uintptr pc; + int cnt; + char *prog; + int code; +}; + +extern char *excname[]; + +static Fault lflt, maxflt; + +ulong* +reg(Ureg *ur, int regno) +{ + ulong *l; + + switch(regno) { + case 31: return &ur->r31; + case 30: return &ur->r30; + case 29: return &ur->sp; + default: + l = &ur->r1; + return &l[regno-1]; + } +} + +/* + * Ask if the instruction at EPC could have cause this badvaddr + */ +int +tstbadvaddr(Ureg *ur) +{ + int rn; + ulong iw, off, ea; + + iw = ur->pc; + if(ur->cause & BD) + iw += 4; + + if(seg(up, iw, 0) == 0) + return 0; + + iw = *(ulong*)iw; + +/* print("iw: %#lux\n", iw); /**/ + + switch((iw>>26) & 0x3f) { + default: + return 1; + case 0x20: /* LB */ + case 0x24: /* LBU */ + /* LD */ + case 0x35: + case 0x36: + case 0x37: /* LDCz */ + case 0x1A: /* LDL */ + case 0x1B: /* LDR */ + case 0x21: /* LH */ + case 0x25: /* LHU */ + case 0x30: /* LL */ + case 0x34: /* LLD */ + case 0x23: /* LW */ + case 0x31: + case 0x32: /* LWCz possible 0x33 */ + case 0x27: /* LWU */ + case 0x22: /* LWL */ + case 0x26: /* LWR */ + break; + + case 0x28: /* SB */ + case 0x38: /* SC */ + case 0x3C: /* SCD */ + case 0x3D: + case 0x3E: + case 0x3F: /* SDCz */ + case 0x2C: /* SDL */ + case 0x2D: /* SDR */ + case 0x29: /* SH */ + case 0x2B: /* SW */ + case 0x39: + case 0x3A: /* SWCz */ + case 0x2A: /* SWL */ + case 0x2E: /* SWR */ + break; + } + + off = iw & 0xffff; + if(off & 0x8000) + off |= ~0xffff; + + rn = (iw>>21) & 0x1f; + ea = *reg(ur, rn); + if(rn == 0) + ea = 0; + ea += off; + + /* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/ + + if(ur->badvaddr == ea) + return 0; + + return 1; +} + +/* + * we think we get consecutive page faults from unlucky combinations of + * scheduling and stlb hashes, and they only happen with 16K pages. + * however, we also get page faults while servicing the exact same fault. + * more than 5 consecutive faults is unusual, now that we have a better + * hash function. + * + * this can be helpful during mmu and cache debugging. + */ +static int +ckfaultstuck(Ureg *ur, int read, int code) +{ + uintptr pc, va; + + va = ur->badvaddr; + pc = ur->pc; + if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc || + code != lflt.code) { + /* at least one address or cause is different from last time */ + lflt.cnt = 1; + lflt.va = va; + lflt.pid = up->pid; + lflt.pc = pc; + lflt.code = code; + return 0; + } + ++lflt.cnt; + if (lflt.cnt >= 1000) /* fixfault() isn't fixing underlying cause? */ + panic("fault: %d consecutive faults for va %#p", lflt.cnt, va); + if (lflt.cnt > maxflt.cnt) { + maxflt.cnt = lflt.cnt; + maxflt.va = va; + maxflt.pid = up->pid; + maxflt.pc = pc; + kstrdup(&maxflt.prog, up->text); + } + + /* we're servicing that fault now! */ + /* adjust the threshold and program name to suit */ + if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0) + return 0; + iprint("%d consecutive faults for va %#p at pc %#p in %s " + "pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid); + iprint("\t%s: %s%s r31 %#lux tlbvirt %#lux\n", + excname[code], va == pc? "[instruction] ": "", + (read? "read": "write"), ur->r31, tlbvirt()); + return 0; +} + +char * +faultsprint(char *p, char *ep) +{ + if (Debug) + p = seprint(p, ep, + "max consecutive faults %d for va %#p in %s\n", + maxflt.cnt, maxflt.va, maxflt.prog); + return p; +} + +/* + * find out fault address and type of access. + * Call common fault handler. + */ +void +faultmips(Ureg *ur, int user, int code) +{ + int read; + ulong addr; + char *p, buf[ERRMAX]; + + addr = ur->badvaddr; + addr &= ~(BY2PG-1); + + read = !(code==CTLBM || code==CTLBS); + +/* print("fault: %s code %d va %#p pc %#p r31 %#lux tlbvirt %#lux\n", + up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/ + + if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0) + return; + + if(user) { + p = "store"; + if(read) + p = "load"; + snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#lux r31=%#lux", + p, ur->badvaddr, ur->r31); + postnote(up, 1, buf, NDebug); + return; + } + + splhi(); + serialoq = nil; + print("kernel %s vaddr=%#lux\n", excname[code], ur->badvaddr); + print("st=%#lux pc=%#lux r31=%#lux sp=%#lux\n", + ur->status, ur->pc, ur->r31, ur->sp); + dumpregs(ur); + panic("fault"); +} + +/* + * called in syscallfmt.c, sysfile.c, sysproc.c + */ +void +validalign(uintptr addr, unsigned align) +{ + /* + * Plan 9 is a 32-bit O/S, and the hardware it runs on + * does not usually have instructions which move 64-bit + * quantities directly, synthesizing the operations + * with 32-bit move instructions. Therefore, the compiler + * (and hardware) usually only enforce 32-bit alignment, + * if at all. + * + * Take this out if the architecture warrants it. + */ + if(align == sizeof(vlong)) + align = sizeof(long); + + /* + * Check align is a power of 2, then addr alignment. + */ + if((align != 0 && !(align & (align-1))) && !(addr & (align-1))) + return; + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + /*NOTREACHED*/ +} diff --git a/sys/src/9/sgi/fns.h b/sys/src/9/sgi/fns.h new file mode 100644 index 000000000..1c40cbfce --- /dev/null +++ b/sys/src/9/sgi/fns.h @@ -0,0 +1,68 @@ +#include "../port/portfns.h" + +ulong arcs(ulong, ...); +void arcsconsinit(void); +void arcsproc(void*); +void arcsputc(char); +int argcgetc(void); +ulong cankaddr(ulong); +void clock(Ureg*); +void clockinit(void); +int cmpswap(long*, long, long); +void coherence(void); +void cycles(uvlong *); +void dcflush(void*, ulong); +void evenaddr(uintptr); +void faultmips(Ureg*, int, int); +ulong fcr31(void); +void fptrap(Ureg*); +char* getconf(char*); +ulong getpagemask(void); +ulong getrandom(void); +int gettlbp(ulong, ulong*); +ulong gettlbvirt(int); +int hpc3irqlevel(int); +void icflush(void *, ulong); +void idlehands(void); +void introff(int); +void intron(int); +void kfault(Ureg*); +KMap* kmap(Page*); +void kmapinit(void); +void kmapinval(void); +void kunmap(KMap*); +void links(void); +ulong prid(void); +void procfork(Proc *); +void procrestore(Proc *); +void procsave(Proc *); +void procsetup(Proc *); +void purgetlb(int); +void puttlbx(int, ulong, ulong, ulong, int); +ulong rdcompare(void); +ulong rdcount(void); +ulong* reg(Ureg*, int); +void restfpregs(FPsave*, ulong); +void intrenable(int, void(*)(Ureg *, void *), void *); +void setpagemask(ulong); +void setwired(ulong); +ulong stlbhash(ulong); +void syscall(Ureg*); +int tas(ulong*); +void tlbinit(void); +ulong tlbvirt(void); +void touser(void*); +#define userureg(ur) ((ur)->status & KUSER) +void validalign(uintptr, unsigned); +void wrcompare(ulong); +void wrcount(ulong); + +#define PTR2UINT(p) ((uintptr)(p)) +#define UINT2PTR(i) ((void*)(i)) + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) + +#define KADDR(a) ((void*)((ulong)(a)|KSEG0)) +#define PADDR(a) ((ulong)(a)&~KSEGM) + +#define KSEG1ADDR(a) ((void*)((ulong)(a)|KSEG1)) diff --git a/sys/src/9/sgi/fptrap.c b/sys/src/9/sgi/fptrap.c new file mode 100644 index 000000000..82c61bbd3 --- /dev/null +++ b/sys/src/9/sgi/fptrap.c @@ -0,0 +1,268 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + +enum /* op */ +{ + ABS = 5, + ADD = 0, + CVTD = 33, + CVTS = 32, + CVTW = 36, + DIV = 3, + MOV = 6, + MUL = 2, + NEG = 7, + SUB = 1, +}; + +static int fpunimp(ulong); +static ulong branch(Ureg*, ulong); + +void +fptrap(Ureg *ur) +{ + ulong iw, npc; + + if((up->fpsave.fpstatus&(1<<17)) == 0) + return; + + if(ur->cause & (1<<31)) + iw = *(ulong*)(ur->pc+4); + else + iw = *(ulong*)ur->pc; + + if(fpunimp(iw) == 0) + return; + + if(ur->cause & (1<<31)){ + npc = branch(ur, up->fpsave.fpstatus); + if(npc == 0) + return; + ur->pc = npc; + } + else + ur->pc += 4; + + up->fpsave.fpstatus &= ~(1<<17); +} + +static void +unpack(FPsave *f, int fmt, int reg, int *sign, int *exp) +{ + *sign = 1; + if(f->reg[reg] & 0x80000000) + *sign = -1; + + switch(fmt){ + case 0: + *exp = ((f->reg[reg]>>23)&0xFF) - ((1<<7)-2); + break; + case 1: + if(reg & 1) /* shouldn't happen */ + reg &= ~1; + *exp = ((f->reg[reg]>>20)&0x7FF) - ((1<<10)-2); + break; + } +} + +static void +zeroreg(FPsave *f, int fmt, int reg, int sign) +{ + int size; + + size = 0; + switch(fmt){ + case 0: + size = 4; + break; + case 1: + if(reg & 1) + reg &= ~1; + size = 8; + break; + } + memset(&f->reg[reg], 0, size); + if(sign < 0) + f->reg[reg] |= 0x80000000; +} + +static int +fpunimp(ulong iw) +{ + int ss, st, sd; + int es, et, ed; + int maxe, maxm; + ulong op, fmt, ft, fs, fd; + + if((iw>>25) != 0x23) + return 0; + op = iw & ((1<<6)-1); + fmt = (iw>>21) & ((1<<4)-1); + ft = (iw>>16) & ((1<<5)-1); + fs = (iw>>11) & ((1<<5)-1); + fd = (iw>>6) & ((1<<5)-1); + unpack(&up->fpsave, fmt, fs, &ss, &es); + unpack(&up->fpsave, fmt, ft, &st, &et); + ed = 0; + maxe = 0; + maxm = 0; + switch(fmt){ + case 0: + maxe = 1<<7; + maxm = 24; + break; + case 1: + maxe = 1<<10; + maxm = 53; + break; + } + switch(op){ + case ABS: + up->fpsave.reg[fd] &= ~0x80000000; + return 1; + + case NEG: + up->fpsave.reg[fd] ^= 0x80000000; + return 1; + + case SUB: + st = -st; + case ADD: + if(es<-(maxe-maxm) && et<-(maxe-maxm)) + ed = -maxe; + if(es > et) + sd = es; + else + sd = et; + break; + + case DIV: + et = -et; + case MUL: + sd = 1; + if(ss != st) + sd = -1; + ed = es + et; + break; + + case CVTS: + if(fmt != 1) + return 0; + fmt = 0; /* convert FROM double TO single */ + maxe = 1<<7; + ed = es; + sd = ss; + break; + + default: /* probably a compare */ + return 0; + } + if(ed <= -(maxe-5)){ /* guess: underflow */ + zeroreg(&up->fpsave, fmt, fd, sd); + /* Set underflow exception and sticky */ + up->fpsave.fpstatus |= (1<<3)|(1<<13); + return 1; + } + return 0; +} + +static ulong +branch(Ureg *ur, ulong fcr31) +{ + ulong iw, npc, rs, rt, rd, offset; + + iw = *(ulong*)ur->pc; + rs = (iw>>21) & 0x1F; + if(rs) + rs = *reg(ur, rs); + rt = (iw>>16) & 0x1F; + if(rt) + rt = *reg(ur, rt); + offset = iw & ((1<<16)-1); + if(offset & (1<<15)) /* sign extend */ + offset |= ~((1<<16)-1); + offset <<= 2; + /* + * Integer unit jumps first + */ + switch(iw>>26){ + case 0: /* SPECIAL: JR or JALR */ + switch(iw&0x3F){ + case 0x09: /* JALR */ + rd = (iw>>11) & 0x1F; + if(rd) + *reg(ur, rd) = ur->pc+8; + /* fall through */ + case 0x08: /* JR */ + return rs; + default: + return 0; + } + case 1: /* BCOND */ + switch((iw>>16) & 0x1F){ + case 0x10: /* BLTZAL */ + ur->r31 = ur->pc + 8; + /* fall through */ + case 0x00: /* BLTZ */ + if((long)rs < 0) + return ur->pc+4 + offset; + return ur->pc + 8; + case 0x11: /* BGEZAL */ + ur->r31 = ur->pc + 8; + /* fall through */ + case 0x01: /* BGEZ */ + if((long)rs >= 0) + return ur->pc+4 + offset; + return ur->pc + 8; + default: + return 0; + } + case 3: /* JAL */ + ur->r31 = ur->pc+8; + /* fall through */ + case 2: /* JMP */ + npc = iw & ((1<<26)-1); + npc <<= 2; + return npc | (ur->pc&0xF0000000); + case 4: /* BEQ */ + if(rs == rt) + return ur->pc+4 + offset; + return ur->pc + 8; + case 5: /* BNE */ + if(rs != rt) + return ur->pc+4 + offset; + return ur->pc + 8; + case 6: /* BLEZ */ + if((long)rs <= 0) + return ur->pc+4 + offset; + return ur->pc + 8; + case 7: /* BGTZ */ + if((long)rs > 0) + return ur->pc+4 + offset; + return ur->pc + 8; + } + /* + * Floating point unit jumps + */ + if((iw>>26) == 0x11) /* COP1 */ + switch((iw>>16) & 0x3C1){ + case 0x101: /* BCT */ + case 0x181: /* BCT */ + if(fcr31 & (1<<23)) + return ur->pc+4 + offset; + return ur->pc + 8; + case 0x100: /* BCF */ + case 0x180: /* BCF */ + if(!(fcr31 & (1<<23))) + return ur->pc+4 + offset; + return ur->pc + 8; + } + /* shouldn't get here */ + return 0; +} diff --git a/sys/src/9/sgi/indy b/sys/src/9/sgi/indy new file mode 100644 index 000000000..df8f76a61 --- /dev/null +++ b/sys/src/9/sgi/indy @@ -0,0 +1,48 @@ +dev + root + cons + uart + mnt + srv + shr + proc + env + pipe + dup + ether netif + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno + ssl + tls + cap + kprof +# sd +# draw screen +# mouse + +link + etherseeq + ethermedium + loopbackmedium + +misc + uartarcs + +ip + tcp + udp + rudp + ipifc + icmp + icmp6 + gre + ipmux + esp + +port + int cpuserver = 0; + +bootdir + /$objtype/bin/paqfs + /$objtype/bin/auth/factotum + boot + bootfs.paq diff --git a/sys/src/9/sgi/init9.s b/sys/src/9/sgi/init9.s new file mode 100644 index 000000000..6698f397c --- /dev/null +++ b/sys/src/9/sgi/init9.s @@ -0,0 +1,8 @@ +TEXT _main(SB), $8 + MOVW $setR30(SB), R30 + MOVW $boot(SB), R1 + ADDU $12, R29, R2 /* get a pointer to 0(FP) */ + MOVW R1, 4(R29) + MOVW R2, 8(R29) + JAL startboot(SB) + diff --git a/sys/src/9/sgi/io.h b/sys/src/9/sgi/io.h new file mode 100644 index 000000000..70d9d5540 --- /dev/null +++ b/sys/src/9/sgi/io.h @@ -0,0 +1,47 @@ +enum { + Mhz = 1000*1000, +}; + +#define IO(t,x) ((t*)(KSEG1|((ulong)x))) + +/* Interrupts */ +#define IRQGIO0 0 +#define IRQSCSI 1 +#define IRQSCSI1 2 +#define IRQENET 3 +#define IRQGDMA 4 +#define IRQPLP 5 +#define IRQGIO1 6 +#define IRQLCL2 7 +#define IRQISDN_ISAC 8 +#define IRQPOWER 9 +#define IRQISDN_HSCX 10 +#define IRQLCL3 11 +#define IRQHPCDMA 12 +#define IRQACFAIL 13 +#define IRQVIDEO 14 +#define IRQGIO2 15 +#define IRQEISA 19 +#define IRQKBDMS 20 +#define IRQDUART 21 +#define IRQDRAIN0 22 +#define IRQDRAIN1 23 +#define IRQGIOEXP0 22 +#define IRQGIOEXP1 23 + +/* + * Local Interrupt registers (INT2) + */ +#define INT2_IP20 0x1fb801c0 +#define INT2_IP22 0x1fbd9000 +#define INT2_IP24 0x1fbd9880 + +#define INT2_BASE INT2_IP24 /* indy */ + +#define LIO_0_ISR (INT2_BASE+0x3) +#define LIO_0_MASK (INT2_BASE+0x7) + +#define HPC3_ETHER 0x1fb80000 + +#define MEMCFG0 0x1fa000c4 /* mem. size config. reg. 0 (w, rw) */ +#define MEMCFG1 0x1fa000cc /* mem. size config. reg. 1 (w, rw) */ diff --git a/sys/src/9/sgi/l.s b/sys/src/9/sgi/l.s new file mode 100644 index 000000000..04b8652a4 --- /dev/null +++ b/sys/src/9/sgi/l.s @@ -0,0 +1,834 @@ +#include "mem.h" + +#define SP R29 + +#define NOOP NOR R0, R0, R0 +#define WAIT NOOP; NOOP +#define RETURN RET; NOOP +#define CONST(i, v) MOVW $((i) & 0xffff0000), v; OR $((i) & 0xffff), v; +#define GETMACH(r) CONST(MACHADDR, r) + +/* + * R4000 instructions + */ +#define ERET WORD $0x42000018 +#define LL(base, rt) WORD $((060<<26)|((base)<<21)|((rt)<<16)) +#define SC(base, rt) WORD $((070<<26)|((base)<<21)|((rt)<<16)) + +#define MFC0(src,sel,dst) WORD $(0x40000000|((src)<<11)|((dst)<<16)|(sel)) +#define MTC0(src,dst,sel) WORD $(0x40800000|((dst)<<11)|((src)<<16)|(sel)) +#define RDHWR(hwr, r) WORD $(0x7c00003b|((hwr)<<11)|((r)<<16)) + +/* + * cache manipulation + */ +#define CACHE BREAK /* overloaded op-code */ + +#define PI R((0 /* primary I cache */ +#define PD R((1 /* primary D cache */ +#define SD R((3 /* secondary combined I/D cache */ + +#define IWBI (0<<2))) /* index write-back invalidate */ +#define ILT (1<<2))) /* index load tag */ +#define IST (2<<2))) /* index store tag */ +#define CDE (3<<2))) /* create dirty exclusive */ +#define HINV (4<<2))) /* hit invalidate */ +#define HWBI (5<<2))) /* hit write back invalidate */ +#define HWB (6<<2))) /* hit write back */ +#define HSV (7<<2))) /* hit set virtual */ + + NOSCHED + +/* + * Boot only processor + */ + +TEXT start(SB), $-4 + MOVW $setR30(SB), R30 + + MOVW $CU1, R1 + MOVW R1, M(STATUS) + WAIT + + MOVW $(0x1C<<7), R1 + MOVW R1, FCR31 /* permit only inexact and underflow */ + NOOP + MOVD $0.5, F26 + SUBD F26, F26, F24 + ADDD F26, F26, F28 + ADDD F28, F28, F30 + + MOVD F24, F0 + MOVD F24, F2 + MOVD F24, F4 + MOVD F24, F6 + MOVD F24, F8 + MOVD F24, F10 + MOVD F24, F12 + MOVD F24, F14 + MOVD F24, F16 + MOVD F24, F18 + MOVD F24, F20 + MOVD F24, F22 + + MOVW $MACHADDR, R(MACH) + ADDU $(MACHSIZE-BY2V), R(MACH), SP + + MOVW R(MACH), R1 +clrmach: + MOVW R0, (R1) + ADDU $BY2WD, R1 + BNE R1, SP, clrmach + NOOP + + MOVW $edata(SB), R1 + MOVW $end(SB), R2 +clrbss: + MOVB R0, (R1) + ADDU $1, R1 + BNE R1, R2, clrbss + NOOP + + MOVW R0, 0(R(MACH)) /* m->machno = 0 */ + MOVW R0, R(USER) /* up = nil */ + + JAL main(SB) + NOOP + +TEXT arcs(SB), $256 + MOVW R24, 0x80(SP) + MOVW R25, 0x84(SP) + MOVW R26, 0x88(SP) + MOVW R27, 0x8C(SP) + + MOVW $SPBADDR, R4 + MOVW 0x20(R4), R5 + ADDU R1, R5 + MOVW (R5), R2 + + MOVW 16(FP), R7 + MOVW 12(FP), R6 + MOVW 8(FP), R5 + MOVW 4(FP), R4 + + JAL (R2) + NOOP + + MOVW $setR30(SB), R30 + + MOVW 0x80(SP), R24 + MOVW 0x84(SP), R25 + MOVW 0x88(SP), R26 + MOVW 0x8C(SP), R27 + + MOVW R2, R1 + RETURN + +/* + * Take first processor into user mode + * - argument is stack pointer to user + */ + +TEXT touser(SB), $-4 + MOVW M(STATUS), R4 + MOVW $(UTZERO+32), R2 /* header appears in text */ + MOVW R2, M(EPC) + MOVW R1, SP + AND $(~KMODEMASK), R4 + OR $(KUSER|IE|EXL), R4 /* switch to user mode, intrs on, exc */ + MOVW R4, M(STATUS) /* " */ + WAIT + ERET /* clears EXL */ + NOOP + +/* + * manipulate interrupts + */ + +/* enable an interrupt; bit is in R1 */ +TEXT intron(SB), $0 + MOVW M(STATUS), R2 + WAIT + OR R1, R2 + MOVW R2, M(STATUS) + WAIT + RETURN + +/* disable an interrupt; bit is in R1 */ +TEXT introff(SB), $0 + MOVW M(STATUS), R2 + WAIT + XOR $-1, R1 + AND R1, R2 + MOVW R2, M(STATUS) + WAIT + RETURN + +TEXT splhi(SB), $0 + MOVW R31, 12(R(MACH)) /* save PC in m->splpc */ + MOVW M(STATUS), R1 + WAIT + AND $~IE, R1, R2 + MOVW R2, M(STATUS) + WAIT + RETURN + +TEXT splx(SB), $0 + MOVW R31, 12(R(MACH)) /* save PC in m->splpc */ + MOVW M(STATUS), R2 + WAIT + AND $IE, R1 + AND $~IE, R2 + OR R2, R1 + MOVW R1, M(STATUS) + WAIT + RETURN + +TEXT spllo(SB), $0 + MOVW M(STATUS), R1 + WAIT + OR $IE, R1, R2 + MOVW R2, M(STATUS) + WAIT + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW M(STATUS), R1 + WAIT + AND $IE, R1 + RETURN + +TEXT coherence(SB), $-4 + RETURN + +/* + * process switching + */ + +TEXT setlabel(SB), $-4 + MOVW SP, 0(R1) + MOVW R31, 4(R1) + MOVW R0, R1 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 0(R1), SP + MOVW 4(R1), R31 + MOVW $1, R1 + RETURN + +/* + * the tlb routines need to be called at splhi. + */ + +TEXT getwired(SB),$0 + MOVW M(WIRED), R1 + RETURN + +TEXT setwired(SB),$0 + MOVW R1, M(WIRED) + RETURN + +TEXT getrandom(SB),$0 + MOVW M(RANDOM), R1 + RETURN + +TEXT getpagemask(SB),$0 + MOVW M(PAGEMASK), R1 + RETURN + +TEXT setpagemask(SB),$0 + MOVW R1, M(PAGEMASK) + MOVW R0, R1 /* prevent accidents */ + RETURN + +TEXT puttlbx(SB), $0 /* puttlbx(index, virt, phys0, phys1, pagemask) */ + MOVW 4(FP), R2 + MOVW 8(FP), R3 + MOVW 12(FP), R4 + MOVW $((2*BY2PG-1) & ~0x1fff), R5 + MOVW R2, M(TLBVIRT) + MOVW R3, M(TLBPHYS0) + MOVW R4, M(TLBPHYS1) + MOVW R5, M(PAGEMASK) + MOVW R1, M(INDEX) + NOOP + NOOP + TLBWI + NOOP + RETURN + +TEXT tlbvirt(SB), $0 + MOVW M(TLBVIRT), R1 + NOOP + RETURN + +TEXT gettlbx(SB), $0 /* gettlbx(index, &entry) */ + MOVW 4(FP), R4 + MOVW R1, M(INDEX) + NOOP + NOOP + TLBR + NOOP + NOOP + NOOP + MOVW M(TLBVIRT), R1 + MOVW M(TLBPHYS0), R2 + MOVW M(TLBPHYS1), R3 + NOOP + MOVW R1, 0(R4) + MOVW R2, 4(R4) + MOVW R3, 8(R4) + RETURN + +TEXT gettlbp(SB), $0 /* gettlbp(tlbvirt, &entry) */ + MOVW 4(FP), R5 + MOVW R1, M(TLBVIRT) + NOOP + NOOP + NOOP + TLBP + NOOP + NOOP + MOVW M(INDEX), R1 + NOOP + BLTZ R1, gettlbp1 + TLBR + NOOP + NOOP + NOOP + MOVW M(TLBVIRT), R2 + MOVW M(TLBPHYS0), R3 + MOVW M(TLBPHYS1), R4 + NOOP + MOVW R2, 0(R5) + MOVW R3, 4(R5) + MOVW R4, 8(R5) +gettlbp1: + RETURN + +TEXT gettlbvirt(SB), $0 /* gettlbvirt(index) */ + MOVW R1, M(INDEX) + NOOP + NOOP + TLBR + NOOP + NOOP + NOOP + MOVW M(TLBVIRT), R1 + NOOP + RETURN + +/* + * compute stlb hash index. + * + * M(TLBVIRT) [page & asid] in arg, result in arg. + * stir in swizzled asid; we get best results with asid in both high & low bits. + */ +#define STLBHASH(arg, tmp) \ + AND $0xFF, arg, tmp; \ + SRL $(PGSHIFT+1), arg; \ + XOR tmp, arg; \ + SLL $(STLBLOG-8), tmp; \ + XOR tmp, arg; \ + CONST (STLBSIZE-1, tmp); \ + AND tmp, arg + +TEXT stlbhash(SB), $0 /* for mmu.c */ + STLBHASH(R1, R2) + RETURN + +TEXT utlbmiss(SB), $-4 + GETMACH (R26) + MOVW R27, 12(R26) /* m->splpc = R27 */ + + MOVW 16(R26), R27 + ADDU $1, R27 + MOVW R27,16(R26) /* m->tlbfault++ */ + + MOVW M(TLBVIRT), R27 + NOOP + STLBHASH(R27, R26) + + /* scale to a byte index (multiply by 12) */ + SLL $1, R27, R26 /* × 2 */ + ADDU R26, R27 /* × 3 */ + SLL $2, R27 /* × 12 */ + + GETMACH (R26) + MOVW 4(R26), R26 + ADDU R26, R27 /* R27 = &m->stb[hash] */ + + MOVW M(BADVADDR), R26 + NOOP + AND $BY2PG, R26 + + BNE R26, utlbodd /* odd page? */ + NOOP + +utlbeven: + MOVW 4(R27), R26 /* R26 = m->stb[hash].phys0 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + NOOP + MOVW R26, M(TLBPHYS0) + MOVW 8(R27), R26 /* R26 = m->stb[hash].phys1 */ + JMP utlbcom + MOVW R26, M(TLBPHYS1) /* branch delay slot */ + +utlbodd: + MOVW 8(R27), R26 /* R26 = m->stb[hash].phys1 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + NOOP + MOVW R26, M(TLBPHYS1) + MOVW 4(R27), R26 /* R26 = m->stb[hash].phys0 */ + MOVW R26, M(TLBPHYS0) + +utlbcom: + WAIT + MOVW M(TLBVIRT), R26 + MOVW 0(R27), R27 /* R27 = m->stb[hash].virt */ + BEQ R27, stlbm /* nothing cached? do it the hard way */ + NOOP + /* is the stlb entry for the right virtual address? */ + BNE R26, R27, stlbm /* M(TLBVIRT) != m->stb[hash].virt? */ + NOOP + + /* if an entry exists, overwrite it, else write a random one */ + CONST (PGSZ, R27) + MOVW R27, M(PAGEMASK) /* select page size */ + TLBP /* probe tlb */ + NOOP + NOOP + MOVW M(INDEX), R26 + NOOP + BGEZ R26, utlbindex /* if tlb entry found, rewrite it */ + NOOP + MOVW M(RANDOM), R26 + MOVW R26, M(INDEX) +utlbindex: + NOOP + NOOP + TLBWI /* write indexed tlb entry */ + NOOP + +utlbret: + GETMACH (R26) + MOVW 12(R26), R27 /* R27 = m->splpc */ + MOVW M(EPC), R26 + JMP (R27) + NOOP + +stlbm: + GETMACH (R26) + MOVW 12(R26), R27 /* R27 = m->splpc */ + + /* fall through */ + +TEXT gevector(SB), $-4 + MOVW M(STATUS), R26 + WAIT + AND $KUSER, R26 + + BNE R26, wasuser + MOVW SP, R26 /* delay slot, old SP in R26 */ + +waskernel: + JMP dosave + SUBU $UREGSIZE, SP /* delay slot, allocate frame on kernel stack */ + +wasuser: /* get kernel stack for this user process */ + GETMACH (SP) + MOVW 8(SP), SP /* m->proc */ + MOVW 8(SP), SP /* m->proc->kstack */ + ADDU $(KSTACK-UREGSIZE), SP + +dosave: + MOVW R31, 0x28(SP) + + JAL saveregs(SB) + MOVW R26, 0x10(SP) /* delay slot, save old SP */ + + GETMACH (R(MACH)) + MOVW 8(R(MACH)), R(USER) /* R24 = m->proc */ + MOVW $setR30(SB), R30 + + BEQ R26, dosys /* set by saveregs() */ + NOOP + +dotrap: + MOVW $forkret(SB), R31 + JMP trap(SB) + MOVW 4(SP), R1 /* delay slot, first arg to trap() */ + +dosys: + JAL syscall(SB) + MOVW 4(SP), R1 /* delay slot, first arg to syscall() */ + + /* fall through */ + +TEXT forkret(SB), $-4 + JAL restregs(SB) /* restores old PC in R26 */ + MOVW 0x14(SP), R1 /* delay slot, CAUSE */ + + MOVW 0x28(SP), R31 + + JMP (R27) + MOVW 0x10(SP), SP /* delay slot */ + +/* + * SP-> 0x00 --- (spill R31) + * 0x04 --- (trap()/syscall() arg1) + * 0x08 status + * 0x0C pc + * 0x10 sp/usp + * 0x14 cause + * 0x18 badvaddr + * 0x1C tlbvirt + * 0x20 hi + * 0x24 lo + * 0x28 r31 + * ..... + * 0x9c r1 + */ + +TEXT saveregs(SB), $-4 + MOVW R1, 0x9C(SP) + MOVW R2, 0x98(SP) + MOVW M(STATUS), R2 + ADDU $8, SP, R1 + MOVW R1, 0x04(SP) /* arg to base of regs */ + MOVW $~KMODEMASK, R1 + AND R2, R1 + MOVW R1, M(STATUS) /* so we can take another trap */ + MOVW R2, 0x08(SP) + MOVW M(EPC), R2 + MOVW M(CAUSE), R1 + MOVW R2, 0x0C(SP) + MOVW R1, 0x14(SP) + AND $(EXCMASK<<2), R1 + SUBU $(CSYS<<2), R1, R26 + + BEQ R26, notsaved /* is syscall? */ + MOVW R27, 0x34(SP) /* delay slot */ + + MOVW M(BADVADDR), R1 + MOVW M(TLBVIRT), R2 + MOVW R1, 0x18(SP) + MOVW R2, 0x1C(SP) + + MOVW HI, R1 + MOVW LO, R2 + MOVW R1, 0x20(SP) + MOVW R2, 0x24(SP) + + MOVW R25, 0x3C(SP) + MOVW R24, 0x40(SP) + MOVW R23, 0x44(SP) + MOVW R22, 0x48(SP) + MOVW R21, 0x4C(SP) + MOVW R20, 0x50(SP) + MOVW R19, 0x54(SP) + MOVW R18, 0x58(SP) + MOVW R17, 0x5C(SP) + MOVW R16, 0x60(SP) + MOVW R15, 0x64(SP) + MOVW R14, 0x68(SP) + MOVW R13, 0x6C(SP) + MOVW R12, 0x70(SP) + MOVW R11, 0x74(SP) + MOVW R10, 0x78(SP) + MOVW R9, 0x7C(SP) + MOVW R8, 0x80(SP) + MOVW R7, 0x84(SP) + MOVW R6, 0x88(SP) + MOVW R5, 0x8C(SP) + MOVW R4, 0x90(SP) + MOVW R3, 0x94(SP) + +notsaved: + MOVW R30, 0x2C(SP) + + RET + MOVW R28, 0x30(SP) /* delay slot */ + +TEXT restregs(SB), $-4 + AND $(EXCMASK<<2), R1 + SUBU $(CSYS<<2), R1, R26 + + BEQ R26, notrestored /* is syscall? */ + MOVW 0x34(SP), R27 /* delay slot */ + + MOVW 0x3C(SP), R25 + MOVW 0x40(SP), R24 + MOVW 0x44(SP), R23 + MOVW 0x48(SP), R22 + MOVW 0x4C(SP), R21 + MOVW 0x50(SP), R20 + MOVW 0x54(SP), R19 + MOVW 0x58(SP), R18 + MOVW 0x5C(SP), R17 + MOVW 0x60(SP), R16 + MOVW 0x64(SP), R15 + MOVW 0x68(SP), R14 + MOVW 0x6C(SP), R13 + MOVW 0x70(SP), R12 + MOVW 0x74(SP), R11 + MOVW 0x78(SP), R10 + MOVW 0x7C(SP), R9 + MOVW 0x80(SP), R8 + MOVW 0x84(SP), R7 + MOVW 0x88(SP), R6 + MOVW 0x8C(SP), R5 + MOVW 0x90(SP), R4 + MOVW 0x94(SP), R3 + + MOVW 0x24(SP), R2 + MOVW 0x20(SP), R1 + MOVW R2, LO + MOVW R1, HI + + MOVW 0x98(SP), R2 + +notrestored: + MOVW 0x08(SP), R1 + MOVW R1, M(STATUS) + MOVW 0x0C(SP), R26 /* old PC */ + MOVW R26, M(EPC) + + MOVW 0x30(SP), R28 + MOVW 0x2C(SP), R30 + + RET + MOVW 0x9C(SP), R1 /* delay slot */ + +/* + * hardware interrupt vectors + */ + +TEXT vector0(SB), $-4 + WAIT + CONST (SPBADDR+0x18, R26) + MOVW $eret(SB), R27 + MOVW (R26), R26 + JMP (R26) + NOOP + +TEXT vector180(SB), $-4 + WAIT + CONST (SPBADDR+0x14, R26) + MOVW $eret(SB), R27 + MOVW (R26), R26 + JMP (R26) + NOOP + +TEXT eret(SB), $-4 + ERET + NOOP + +/* + * floating-point stuff + */ + +TEXT clrfpintr(SB), $0 + MOVW M(STATUS), R3 + WAIT + OR $CU1, R3 + MOVW R3, M(STATUS) + NOOP + NOOP + NOOP + + MOVW FCR31, R1 + MOVW R1, R2 + AND $~(0x3F<<12), R2 + MOVW R2, FCR31 + + AND $~CU1, R3 + MOVW R3, M(STATUS) + WAIT + RETURN + +TEXT savefpregs(SB), $0 + MOVW FCR31, R2 + MOVW M(STATUS), R3 + WAIT + AND $~(0x3F<<12), R2, R4 + MOVW R4, FCR31 + + MOVD F0, 0x00(R1) + MOVD F2, 0x08(R1) + MOVD F4, 0x10(R1) + MOVD F6, 0x18(R1) + MOVD F8, 0x20(R1) + MOVD F10, 0x28(R1) + MOVD F12, 0x30(R1) + MOVD F14, 0x38(R1) + MOVD F16, 0x40(R1) + MOVD F18, 0x48(R1) + MOVD F20, 0x50(R1) + MOVD F22, 0x58(R1) + MOVD F24, 0x60(R1) + MOVD F26, 0x68(R1) + MOVD F28, 0x70(R1) + MOVD F30, 0x78(R1) + + MOVW R2, 0x80(R1) + AND $~CU1, R3 + MOVW R3, M(STATUS) + WAIT + RETURN + +TEXT restfpregs(SB), $0 + MOVW M(STATUS), R3 + WAIT + OR $CU1, R3 + MOVW R3, M(STATUS) + WAIT + MOVW fpstat+4(FP), R2 + NOOP + + MOVD 0x00(R1), F0 + MOVD 0x08(R1), F2 + MOVD 0x10(R1), F4 + MOVD 0x18(R1), F6 + MOVD 0x20(R1), F8 + MOVD 0x28(R1), F10 + MOVD 0x30(R1), F12 + MOVD 0x38(R1), F14 + MOVD 0x40(R1), F16 + MOVD 0x48(R1), F18 + MOVD 0x50(R1), F20 + MOVD 0x58(R1), F22 + MOVD 0x60(R1), F24 + MOVD 0x68(R1), F26 + MOVD 0x70(R1), F28 + MOVD 0x78(R1), F30 + + MOVW R2, FCR31 + AND $~CU1, R3 + MOVW R3, M(STATUS) + WAIT + RETURN + +TEXT fcr31(SB), $0 /* fp csr */ + MOVW FCR31, R1 + RETURN + +/* + * Emulate 68020 test and set: load linked / store conditional + */ + +TEXT tas(SB), $0 + MOVW R1, R2 /* address of key */ +tas1: + MOVW $1, R3 + LL(2, 1) + NOOP + SC(2, 3) + NOOP + BEQ R3, tas1 + NOOP + RETURN + +/* used by the semaphore implementation */ +TEXT cmpswap(SB), $0 + MOVW R1, R2 /* address of key */ + MOVW old+4(FP), R3 /* old value */ + MOVW new+8(FP), R4 /* new value */ + LL(2, 1) /* R1 = (R2) */ + NOOP + BNE R1, R3, fail + NOOP + MOVW R4, R1 + SC(2, 1) /* (R2) = R1 if (R2) hasn't changed; R1 = success */ + NOOP + RETURN +fail: + MOVW R0, R1 + RETURN + +/* + * cache manipulation + */ + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW M(STATUS), R10 + WAIT + MOVW 4(FP), R9 + MOVW $0, M(STATUS) + WAIT + ADDU R1, R9 /* R9 = last address */ + MOVW $(~0x3f), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDU $0x3f, R9 + AND $(~0x3f), R9 /* round last address up */ + SUBU R8, R9 /* R9 = revised count */ +icflush1: /* primary cache line size is 16 bytes */ + CACHE PD+HWB, 0x00(R8) + CACHE PI+HINV, 0x00(R8) + CACHE PD+HWB, 0x10(R8) + CACHE PI+HINV, 0x10(R8) + CACHE PD+HWB, 0x20(R8) + CACHE PI+HINV, 0x20(R8) + CACHE PD+HWB, 0x30(R8) + CACHE PI+HINV, 0x30(R8) + SUBU $0x40, R9 + ADDU $0x40, R8 + BGTZ R9, icflush1 + MOVW R10, M(STATUS) + WAIT + RETURN + +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + MOVW M(STATUS), R10 + WAIT + MOVW 4(FP), R9 + MOVW $0, M(STATUS) + WAIT + ADDU R1, R9 /* R9 = last address */ + MOVW $(~0x3f), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDU $0x3f, R9 + AND $(~0x3f), R9 /* round last address up */ + SUBU R8, R9 /* R9 = revised count */ +dcflush1: /* primary cache line size is 16 bytes */ + CACHE PD+HWB, 0x00(R8) + CACHE PD+HWB, 0x10(R8) + CACHE PD+HWB, 0x20(R8) + CACHE PD+HWB, 0x30(R8) + SUBU $0x40, R9 + ADDU $0x40, R8 + BGTZ R9, dcflush1 + MOVW R10, M(STATUS) + WAIT + RETURN + +/* + * access to CP0 registers + */ + +TEXT prid(SB), $0 + MOVW M(PRID), R1 + WAIT + RETURN + +TEXT rdcount(SB), $0 + MOVW M(COUNT), R1 + RETURN + +TEXT wrcount(SB), $0 + MOVW R1, M(COUNT) + RETURN + +TEXT wrcompare(SB), $0 + MOVW R1, M(COMPARE) + RETURN + +TEXT rdcompare(SB), $0 + MOVW M(COMPARE), R1 + RETURN + + SCHED diff --git a/sys/src/9/sgi/main.c b/sys/src/9/sgi/main.c new file mode 100644 index 000000000..ba0a0481e --- /dev/null +++ b/sys/src/9/sgi/main.c @@ -0,0 +1,487 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "init.h" +#include "pool.h" +#include "../ip/ip.h" +#include +#include <../port/error.h> + +enum { + /* space for syscall args, return PC, top-of-stack struct */ + Stkheadroom = sizeof(Sargs) + sizeof(uintptr) + sizeof(Tos), +}; + +static uchar *sp; /* XXX - must go - user stack of init proc */ +static FPsave initfp; + +/* + * software tlb simulation + */ +static Softtlb stlb[MAXMACH][STLBSIZE]; + +Conf conf; + +char* +getconf(char *name) +{ + return (char*)arcs(0x78, name); +} + +static void +fmtinit(void) +{ + printinit(); + quotefmtinstall(); + /* ipreset installs these when chandevreset runs */ + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +static int +ckpagemask(ulong mask, ulong size) +{ + int s; + ulong pm; + + s = splhi(); + setpagemask(mask); + pm = getpagemask(); + splx(s); + if(pm != mask){ + iprint("page size %ldK not supported on this cpu; " + "mask %#lux read back as %#lux\n", size/1024, mask, pm); + return -1; + } + return 0; +} + +void +addmem(uintptr base, uintptr top) +{ + uintptr s, e; + ulong *m; + int i; + + if(base >= top) + return; + + /* exclude kernel */ + s = 0; + e = PADDR(PGROUND((uintptr)end)); + if(s < top && e > base){ + if(s > base) + addmem(base, s); + if(e < top) + addmem(e, top); + return; + } + + /* exclude reserved firmware memory regions */ + m = nil; + while((m = (ulong*)arcs(0x48, m)) != nil){ + s = m[1]<<12; + e = s + (m[2]<<12); + switch(m[0]){ + case 2: /* FreeMemory */ + case 3: /* BadMemory */ + continue; + } + if(s < top && e > base){ + if(s > base) + addmem(base, s); + if(e < top) + addmem(e, top); + return; + } + } + for(i=0; i> 16; + case 1: + return *(ulong *)(KSEG1|MEMCFG0) & 0xffff; + case 2: + return *(ulong *)(KSEG1|MEMCFG1) >> 16; + case 3: + return *(ulong *)(KSEG1|MEMCFG1) & 0xffff; + } + return 0; +} + +void +meminit(void) +{ + uintptr base, size, top; + ulong mconf; + int i; + + /* + * divide memory twixt user pages and kernel. + */ + conf.npage = 0; + for(i=0; i<4; i++){ + mconf = bank_conf(i); + if(!(mconf & 0x2000)) + continue; + base = (mconf & 0xff) << 22; + size = ((mconf & 0x1f00) + 0x0100) << 14; + top = base + size; + addmem(base, top); + } +} + +void +main(void) +{ + savefpregs(&initfp); + + arcsconsinit(); + + meminit(); + confinit(); + machinit(); /* calls clockinit */ + active.exiting = 0; + active.machs = 1; + print("\nPlan 9\n"); + + kmapinit(); + xinit(); + timersinit(); + fmtinit(); + + ckpagemask(PGSZ, BY2PG); + tlbinit(); + pageinit(); + procinit0(); + initseg(); + links(); + chandevreset(); + + swapinit(); + userinit(); + schedinit(); + panic("schedinit returned"); +} + +/* + * initialize a processor's mach structure. each processor does this + * for itself. + */ +void +machinit(void) +{ + extern void gevector(void); /* l.s */ + extern void utlbmiss(void); + extern void vector0(void); + extern void vector180(void); + + void **sbp = (void*)SPBADDR; + + m->stb = stlb[m->machno]; + + /* install exception handlers */ + sbp[0x18/4] = utlbmiss; + sbp[0x14/4] = gevector; + + /* we could install our own vectors directly, but we'll try to play nice */ + if(0){ + memmove((void*)(KSEG0+0x0), (void*)vector0, 0x80); + memmove((void*)(KSEG0+0x180), (void*)vector180, 0x80); + icflush((void*)(KSEG0+0x0), 0x80); + icflush((void*)(KSEG0+0x180), 0x80); + } + + /* Ensure CU1 is off */ + clrfpintr(); + clockinit(); +} + +void +init0(void) +{ + char buf[128]; + + up->nerrlab = 0; + + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "mips", 0); + snprint(buf, sizeof buf, "mips %s", conffile); + ksetenv("terminal", buf, 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + + ksetenv("bootargs", "tcp", 0); + + /* make kbdfs attach to /dev/eia0 arcs console */ + ksetenv("console", "0", 0); + + /* no usb */ + ksetenv("usbwait", "0", 0); + ksetenv("nousbrc", "1", 0); + + poperror(); + } + + /* process input for arcs console */ + kproc("arcs", arcsproc, 0); + + kproc("alarm", alarmkproc, 0); + touser(sp); +} + +static uchar * +pusharg(char *p) +{ + int n; + + n = strlen(p) + 1; + sp -= n; + memmove(sp, p, n); + return sp; +} + +static void +bootargs(uintptr base) +{ int i, ac; + uchar *av[32]; + uchar **lsp; + + sp = (uchar *) base + BY2PG - sizeof(Tos); + + ac = 0; + av[ac++] = pusharg("boot"); + sp = (uchar *) ((ulong) sp & ~7); + sp -= ROUND((ac + 1) * sizeof(sp), 8) + 4; + lsp = (uchar **) sp; + for(i = 0; i < ac; i++) + lsp[i] = av[i] + ((USTKTOP - BY2PG) - (ulong) base); + lsp[i] = 0; + sp += (USTKTOP - BY2PG) - (ulong) base; +} + +void +userinit(void) +{ + Proc *p; + KMap *k; + Page *pg; + Segment *s; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + procsetup(p); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-Stkheadroom; + p->sched.sp = STACKALIGN(p->sched.sp); + + /* + * User Stack + * + * Technically, newpage can't be called here because it + * should only be called when in a user context as it may + * try to sleep if there are no pages available, but that + * shouldn't be the case here. + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + segpage(s, pg); + k = kmap(pg); + bootargs(VA(k)); + kunmap(k); + + /* + * Text + */ + s = newseg(SG_TEXT, UTZERO, 1); + s->flushme++; + p->seg[TSEG] = s; + pg = newpage(1, 0, UTZERO); + pg->txtflush = ~0; + segpage(s, pg); + k = kmap(s->map[0]->pages[0]); + memset((void *)VA(k), 0, BY2PG); + memmove((ulong*)VA(k), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +void +exit(int ispanic) +{ + splhi(); + while(ispanic); + arcs(0x18); /* reboot */ +} + +void +reboot(void *, void *, ulong) +{ +} + +void +evenaddr(uintptr va) +{ + if((va & 3) != 0){ + dumpstack(); + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} + +void +procsetup(Proc *p) +{ + p->fpstate = FPinit; + p->fpsave = initfp; + + cycles(&p->kentry); + p->pcycles = -p->kentry; +} + +void +procfork(Proc *p) +{ + int s; + + p->kentry = up->kentry; + p->pcycles = -p->kentry; + + s = splhi(); + switch(up->fpstate & ~FPillegal){ + case FPactive: + savefpregs(&up->fpsave); + up->fpstate = FPinactive; + /* wet floor */ + case FPinactive: + p->fpsave = up->fpsave; + p->fpstate = FPinactive; + } + splx(s); +} + +void +procsave(Proc *p) +{ + uvlong t; + + if(p->fpstate == FPactive){ + if(p->state != Moribund) { + savefpregs(&p->fpsave); + p->fpstate = FPinactive; + } + } + + cycles(&t); + p->pcycles += t; +} + +void +procrestore(Proc *p) +{ + uvlong t; + + if(p->kp) + return; + cycles(&t); + p->pcycles -= t; +} + +void +idlehands(void) +{ +} + +void +confinit(void) +{ + ulong kpages; + + /* + * set up CPU's mach structure + * cpu0's was zeroed in l.s and our stack is in Mach, so don't zero it. + */ + m->machno = 0; + m->speed = 150; /* initial guess at MHz */ + m->hz = m->speed * Mhz; + conf.nmach = 1; + + /* set up other configuration parameters */ + conf.nproc = 2000; + conf.nswap = 262144; + conf.nswppo = 4096; + conf.nimage = 200; + + conf.copymode = 0; /* copy on write */ + + kpages = conf.npage - (conf.npage*80)/100; + if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ + kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; + kpages += (conf.nproc*KSTACK)/BY2PG; + } + conf.upages = conf.npage - kpages; + conf.ialloc = (kpages/2)*BY2PG; + + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page*); + mainmem->maxsize = kpages; +// mainmem->flags |= POOL_PARANOIA; +} diff --git a/sys/src/9/sgi/mem.h b/sys/src/9/sgi/mem.h new file mode 100644 index 000000000..37506f93f --- /dev/null +++ b/sys/src/9/sgi/mem.h @@ -0,0 +1,277 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +#define MIN(a, b) ((a) < (b)? (a): (b)) +#define MAX(a, b) ((a) > (b)? (a): (b)) + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ + +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) +#define PGROUND(s) ROUND(s, BY2PG) + +#define MAXBY2PG (16*1024) /* rounding for UTZERO in executables; see mkfile */ +#define UTROUND(t) ROUNDUP((t), MAXBY2PG) + +#ifndef BIGPAGES +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log2(BY2PG) */ +#define PGSZ PGSZ4K +#else +/* 16K pages work very poorly */ +#define BY2PG (16*1024) /* bytes per page */ +#define PGSHIFT 14 /* log2(BY2PG) */ +#define PGSZ PGSZ16K +#endif + +#define KSTACK (8*1024) /* Size of kernel stack */ +#define MACHSIZE (BY2PG+KSTACK) +#define WD2PG (BY2PG/BY2WD) /* words per page */ + +#define MAXMACH 1 /* max # cpus system can run; see active.machs */ +#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */ +#define BLOCKALIGN 16 +#define CACHELINESZ 32 /* mips24k */ +#define ICACHESIZE (64*1024) /* rb450g */ +#define DCACHESIZE (32*1024) /* rb450g */ + +#define MASK(w) FMASK(0, w) + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * CP0 registers + */ + +#define INDEX 0 +#define RANDOM 1 +#define TLBPHYS0 2 /* aka ENTRYLO0 */ +#define TLBPHYS1 3 /* aka ENTRYLO1 */ +#define CONTEXT 4 +#define PAGEMASK 5 +#define WIRED 6 +#define BADVADDR 8 +#define COUNT 9 +#define TLBVIRT 10 /* aka ENTRYHI */ +#define COMPARE 11 +#define STATUS 12 +#define CAUSE 13 +#define EPC 14 +#define PRID 15 +#define CONFIG 16 +#define LLADDR 17 +#define WATCHLO 18 +#define WATCHHI 19 +#define DEBUGREG 23 +#define DEPC 24 +#define PERFCOUNT 25 +#define CACHEECC 26 +#define CACHEERR 27 +#define TAGLO 28 +#define TAGHI 29 +#define ERROREPC 30 +#define DESAVE 31 + +/* + * M(STATUS) bits + */ +#define KMODEMASK 0x0000001f +#define IE 0x00000001 /* master interrupt enable */ +#define EXL 0x00000002 /* exception level */ +#define ERL 0x00000004 /* error level */ +#define KSUPER 0x00000008 +#define KUSER 0x00000010 +#define KSU 0x00000018 +#define UX 0x00000020 +#define SX 0x00000040 +#define KX 0x00000080 +#define INTMASK 0x0000ff00 +#define SW0 0x00000100 +#define SW1 0x00000200 +#define INTR0 0x00000100 /* interrupt enable bits */ +#define INTR1 0x00000200 +#define INTR2 0x00000400 +#define INTR3 0x00000800 +#define INTR4 0x00001000 +#define INTR5 0x00002000 +#define INTR6 0x00004000 +#define INTR7 0x00008000 +#define DE 0x00010000 +#define TS 0x00200000 /* tlb shutdown; on 24k at least */ +#define BEV 0x00400000 /* bootstrap exception vectors */ +#define RE 0x02000000 /* reverse-endian in user mode */ +#define FR 0x04000000 /* enable 32 FP regs */ +#define CU0 0x10000000 +#define CU1 0x20000000 /* FPU enable */ + +/* + * M(CONFIG) bits + */ + +#define CFG_K0 7 /* kseg0 cachability */ +#define CFG_MM (1<<18) /* write-through merging enabled */ + +/* + * M(CAUSE) bits + */ + +#define BD (1<<31) /* last excep'n occurred in branch delay slot */ + +/* + * Exception codes + */ +#define EXCMASK 0x1f /* mask of all causes */ +#define CINT 0 /* external interrupt */ +#define CTLBM 1 /* TLB modification: store to unwritable page */ +#define CTLBL 2 /* TLB miss (load or fetch) */ +#define CTLBS 3 /* TLB miss (store) */ +#define CADREL 4 /* address error (load or fetch) */ +#define CADRES 5 /* address error (store) */ +#define CBUSI 6 /* bus error (fetch) */ +#define CBUSD 7 /* bus error (data load or store) */ +#define CSYS 8 /* system call */ +#define CBRK 9 /* breakpoint */ +#define CRES 10 /* reserved instruction */ +#define CCPU 11 /* coprocessor unusable */ +#define COVF 12 /* arithmetic overflow */ +#define CTRAP 13 /* trap */ +#define CVCEI 14 /* virtual coherence exception (instruction) */ +#define CFPE 15 /* floating point exception */ +#define CTLBRI 19 /* tlb read-inhibit */ +#define CTLBXI 20 /* tlb execute-inhibit */ +#define CWATCH 23 /* watch exception */ +#define CMCHK 24 /* machine checkcore */ +#define CCACHERR 30 /* cache error */ +#define CVCED 31 /* virtual coherence exception (data) */ + +/* + * M(CACHEECC) a.k.a. ErrCtl bits + */ +#define PE (1<<31) +#define LBE (1<<25) +#define WABE (1<<24) + +/* + * Trap vectors + */ + +#define UTLBMISS (KSEG0+0x000) +#define XEXCEPTION (KSEG0+0x080) +#define CACHETRAP (KSEG0+0x100) +#define EXCEPTION (KSEG0+0x180) + +/* + * Magic registers + */ + +#define USER 24 /* R24 is up-> */ +#define MACH 25 /* R25 is m-> */ + +#define UREGSIZE 0xA0 /* sizeof(Ureg)+8 */ + +/* + * MMU + */ +#define PGSZ4K (0x00<<13) +#define PGSZ16K (0x03<<13) /* on 24k */ +#define PGSZ64K (0x0F<<13) +#define PGSZ256K (0x3F<<13) +#define PGSZ1M (0xFF<<13) +#define PGSZ4M (0x3FF<<13) +#define PGSZ8M (0x7FF<<13) /* not on 24k */ +#define PGSZ16M (0xFFF<<13) +#define PGSZ64M (0x3FFF<<13) /* on 24k */ +#define PGSZ256M (0xFFFF<<13) /* on 24k */ + +/* mips address spaces, tlb-mapped unless marked otherwise */ +#define KUSEG 0x00000000 /* user process */ +#define KSEG0 0x80000000 /* kernel (direct mapped, cached) */ +#define KSEG1 0xA0000000 /* kernel (direct mapped, uncached: i/o) */ +#define KSEG2 0xC0000000 /* kernel, used for TSTKTOP */ +#define KSEG3 0xE0000000 /* kernel, used by kmap */ +#define KSEGM 0xE0000000 /* mask to check which seg */ + +/* + * Fundamental addresses + */ + +#define REBOOTADDR KADDR(0x1000) /* just above vectors */ +#define MACHADDR 0x88014000 /* Mach structures */ +#define MACHP(n) ((Mach *)(MACHADDR+(n)*MACHSIZE)) +#define KMAPADDR 0xE0000000 /* kmap'd addresses */ +#define SPBADDR 0x80001000 + +#define PIDXSHFT 12 +#ifndef BIGPAGES +#define NCOLOR 8 +#define PIDX ((NCOLOR-1)<>PIDXSHFT) % NCOLOR) +#else +/* no cache aliases are possible with pages of 16K or larger */ +#define NCOLOR 1 +#define PIDX 0 +#define getpgcolor(a) 0 +#endif +#define KMAPSHIFT 15 + +#define PTEGLOBL (1<<0) +#define PTEVALID (1<<1) +#define PTEWRITE (1<<2) +#define PTERONLY 0 +#define PTEALGMASK (7<<3) +#define PTENONCOHERWT (0<<3) /* cached, write-through (slower) */ +#define PTEUNCACHED (2<<3) +#define PTENONCOHERWB (3<<3) /* cached, write-back */ +#define PTEUNCACHEDACC (7<<3) +/* rest are reserved on 24k */ +#define PTECOHERXCL (4<<3) +#define PTECOHERXCLW (5<<3) +#define PTECOHERUPDW (6<<3) + +/* how much faster is it? mflops goes from about .206 (WT) to .37 (WB) */ +// #define PTECACHABILITY PTENONCOHERWT /* 24k erratum 48 disallows WB */ +#define PTECACHABILITY PTENONCOHERWB + +#define PTEPID(n) (n) +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 512 +#define SSEGMAPSIZE 16 + +#define STLBLOG 15 +#define STLBSIZE (1< init.h + +faultmips.$O mmu.$O syscall.$O trap.$O: /$objtype/include/ureg.h +main.$O: /$objtype/include/ureg.h errstr.h init.h + +%.clean:V: + rm -f $stem.c [9bz]$stem [9bz]$stem.gz boot$stem.* init.h diff --git a/sys/src/9/sgi/mmu.c b/sys/src/9/sgi/mmu.c new file mode 100644 index 000000000..8af2956e2 --- /dev/null +++ b/sys/src/9/sgi/mmu.c @@ -0,0 +1,479 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" + +/* + * tlb entry 0 is used only by mmuswitch() to set the current tlb pid. + * + * It is apparently assumed that user tlb entries are not + * overwritten during start-up, so ... + * During system start-up (before up first becomes non-nil), + * Kmap entries start at tlb index 1 and work their way up until + * kmapinval() removes them. They then restart at 1. As long as there + * are few kmap entries they will not pass tlbroff (the WIRED tlb entry + * limit) and interfere with user tlb entries. + * Once start-up is over, we combine the kernel and user tlb pools into one, + * in the hope of making better use of the tlb on systems with small ones. + * + * All invalidations of the tlb are via indexed entries. The virtual + * address used is always 'KZERO | (x<<(PGSHIFT+1) | currentpid' where + * 'x' is the index into the tlb. This ensures that the current pid doesn't + * change and that no two invalidated entries have matching virtual + * addresses just in case SGI/MIPS ever makes a chip that cares (as + * they keep threatening). These entries should never be used in + * lookups since accesses to KZERO addresses don't go through the tlb + * (actually only true of KSEG0 and KSEG1; KSEG2 and KSEG3 do go + * through the tlb). + */ + +#define TLBINVAL(x, pid) puttlbx(x, KZERO|((x)<<(PGSHIFT+1))|(pid), 0, 0, PGSZ) + +enum { + Debugswitch = 0, + Debughash = 0, +}; + +static ulong ktime[8]; /* only for first 8 cpus */ + +void +tlbinit(void) +{ + int i; + + for(i=0; inext) + i++; + if(inext = k+1; + k->next = 0; + unlock(&kmaplock); + + m->ktlbnext = TLBOFF; +} + +void +kmapdump(void) +{ + int i; + + for(i=0; ivirt & ~BY2PG | TLBPID(tlbvirt()); + x = gettlbp(virt, tlbent); + if (!m->paststartup) + if (up) { /* startup just ended? */ + tlbroff = 1; + setwired(tlbroff); /* share all-but-one entries */ + m->paststartup = 1; + } else if (x < 0) { /* no such entry? use next */ + x = m->ktlbnext++; + if(m->ktlbnext >= tlbroff) + m->ktlbnext = TLBOFF; + } + if (x < 0) x = getrandom(); /* no entry for va? overwrite random one */ + puttlbx(x, virt, k->phys0, k->phys1, PGSZ); + m->ktlbx[x] = 1; + return x; +} + +/* + * Arrange that the KMap'd virtual address will hit the same + * primary cache line as pg->va by making bits 14...12 of the + * tag the same as virtual address. These bits are the index + * into the primary cache and are checked whenever accessing + * the secondary cache through the primary. Violation causes + * a VCE trap. + */ +KMap * +kmap(Page *pg) +{ + int s, printed = 0; + ulong pte, virt; + KMap *k; + + s = splhi(); + lock(&kmaplock); + + if(kmapfree == 0) { +retry: + unlock(&kmaplock); + kmapinval(); /* try and free some */ + lock(&kmaplock); + if(kmapfree == 0){ + unlock(&kmaplock); + splx(s); + if(printed++ == 0){ + /* using iprint here we get mixed up with other prints */ + print("%d KMAP RETRY %#lux ktime %ld %ld %ld %ld %ld %ld %ld %ld\n", + m->machno, getcallerpc(&pg), + ktime[0], ktime[1], ktime[2], ktime[3], + ktime[4], ktime[5], ktime[6], ktime[7]); + delay(200); + } + splhi(); + lock(&kmaplock); + goto retry; + } + } + + k = kmapfree; + kmapfree = k->next; + + k->pg = pg; + /* + * One for the allocation, + * One for kactive + */ + k->pc = getcallerpc(&pg); + k->ref = 2; + k->konmach[m->machno] = m->kactive; + m->kactive = k; + + virt = pg->va; + /* bits 14..12 form the secondary-cache virtual index */ + virt &= PIDX; + virt |= KMAPADDR | ((k-kpte)<virt = virt; + pte = PPN(pg->pa)|PTECACHABILITY|PTEGLOBL|PTEWRITE|PTEVALID; + if(virt & BY2PG) { + k->phys0 = PTEGLOBL | PTECACHABILITY; + k->phys1 = pte; + } + else { + k->phys0 = pte; + k->phys1 = PTEGLOBL | PTECACHABILITY; + } + + putktlb(k); + unlock(&kmaplock); + + splx(s); + return k; +} + +void +kunmap(KMap *k) +{ + int s; + + s = splhi(); + if(decref(k) == 0) { + k->virt = 0; + k->phys0 = 0; + k->phys1 = 0; + k->pg = 0; + + lock(&kmaplock); + k->next = kmapfree; + kmapfree = k; +//nfree(); + unlock(&kmaplock); + } + splx(s); +} + +void +kfault(Ureg *ur) /* called from trap() */ +{ + ulong index, addr; + KMap *k, *f; + + addr = ur->badvaddr; + index = (addr & ~KSEGM) >> KMAPSHIFT; + if(index >= KPTESIZE) + panic("kmapfault: va=%#lux", addr); + + k = &kpte[index]; + if(k->virt == 0) + panic("kmapfault: unmapped %#lux", addr); + + for(f = m->kactive; f; f = f->konmach[m->machno]) + if(f == k) + break; + if(f == 0) { + incref(k); + k->konmach[m->machno] = m->kactive; + m->kactive = k; + } + putktlb(k); +} + +void +kmapinval(void) +{ + int mno, i, curpid; + KMap *k, *next; + uchar *ktlbx; + + if(m->machno < nelem(ktime)) + ktime[m->machno] = MACHP(0)->ticks; + if(m->kactive == 0) + return; + + curpid = PTEPID(TLBPID(tlbvirt())); + ktlbx = m->ktlbx; + for(i = 0; i < NTLB; i++, ktlbx++){ + if(*ktlbx == 0) + continue; + TLBINVAL(i, curpid); + *ktlbx = 0; + } + + mno = m->machno; + for(k = m->kactive; k; k = next) { + next = k->konmach[mno]; + kunmap(k); + } + + m->kactive = 0; + m->ktlbnext = TLBOFF; +} + +/* + * Process must be splhi + */ +static int +newtlbpid(Proc *p) +{ + int i, s; + Proc **h; + + i = m->lastpid; + h = m->pidproc; + for(s = 0; s < NTLBPID; s++) { + i++; + if(i >= NTLBPID) + i = 1; + if(h[i] == 0) + break; + } + + if(h[i]) + purgetlb(i); + if(h[i] != 0) + panic("newtlb"); + + m->pidproc[i] = p; + p->pidonmach[m->machno] = i; + m->lastpid = i; + + return i; +} + +void +mmuswitch(Proc *p) +{ + int tp; + static char lasttext[32]; + + if(Debugswitch && !p->kp){ + if(strncmp(lasttext, p->text, sizeof lasttext) != 0) + iprint("[%s]", p->text); + strncpy(lasttext, p->text, sizeof lasttext); + } + + if(p->newtlb) { + memset(p->pidonmach, 0, sizeof p->pidonmach); + p->newtlb = 0; + } + tp = p->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(p); + puttlbx(0, KZERO|PTEPID(tp), 0, 0, PGSZ); +} + +void +mmurelease(Proc *p) +{ + memset(p->pidonmach, 0, sizeof p->pidonmach); +} + + +/* tlbvirt also has TLBPID() in its low byte as the asid */ +static Softtlb* +putstlb(ulong tlbvirt, ulong tlbphys) +{ + int odd; + Softtlb *entry; + + /* identical calculation in l.s/utlbmiss */ + entry = &m->stb[stlbhash(tlbvirt)]; + odd = tlbvirt & BY2PG; /* even/odd bit */ + tlbvirt &= ~BY2PG; /* zero even/odd bit */ + if(entry->virt != tlbvirt) { /* not my entry? overwrite it */ + if(entry->virt != 0) { + m->hashcoll++; + if (Debughash) + iprint("putstlb: hash collision: %#lx old virt " + "%#lux new virt %#lux page %#lux\n", + entry - m->stb, entry->virt, tlbvirt, + tlbvirt >> (PGSHIFT+1)); + } + entry->virt = tlbvirt; + entry->phys0 = 0; + entry->phys1 = 0; + } + + if(odd) + entry->phys1 = tlbphys; + else + entry->phys0 = tlbphys; + + if(entry->phys0 == 0 && entry->phys1 == 0) + entry->virt = 0; + + return entry; +} + +void +putmmu(ulong tlbvirt, ulong tlbphys, Page *pg) +{ + short tp; + ulong tlbent[3]; + Softtlb *entry; + int s, x; + + s = splhi(); + tp = up->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(up); + + tlbvirt |= PTEPID(tp); + if((tlbphys & PTEALGMASK) != PTEUNCACHED) { + tlbphys &= ~PTEALGMASK; + tlbphys |= PTECACHABILITY; + } + + entry = putstlb(tlbvirt, tlbphys); + x = gettlbp(tlbvirt, tlbent); + if(x < 0) x = getrandom(); + puttlbx(x, entry->virt, entry->phys0, entry->phys1, PGSZ); + if(pg->txtflush & (1<machno)){ + icflush((void*)pg->va, BY2PG); + pg->txtflush &= ~(1<machno); + } + splx(s); +} + +void +purgetlb(int pid) +{ + int i, mno; + Proc *sp, **pidproc; + Softtlb *entry, *etab; + + m->tlbpurge++; + + /* + * find all pid entries that are no longer used by processes + */ + mno = m->machno; + pidproc = m->pidproc; + for(i=1; ipidonmach[mno] != i) + pidproc[i] = 0; + } + + /* + * shoot down the one we want + */ + sp = pidproc[pid]; + if(sp != 0) + sp->pidonmach[mno] = 0; + pidproc[pid] = 0; + + /* + * clean out all dead pids from the stlb; + */ + entry = m->stb; + for(etab = &entry[STLBSIZE]; entry < etab; entry++) + if(pidproc[TLBPID(entry->virt)] == 0) + entry->virt = 0; + + /* + * clean up the hardware + */ + for(i=tlbroff; inewtlb = 1; + mmuswitch(up); + splx(s); +} + +void +checkmmu(ulong, ulong) +{ +} + +void +countpagerefs(ulong*, int) +{ +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +ulong +cankaddr(ulong pa) +{ + if(pa >= KZERO) + return 0; + return -KZERO - pa; +} diff --git a/sys/src/9/sgi/trap.c b/sys/src/9/sgi/trap.c new file mode 100644 index 000000000..d9a6ae204 --- /dev/null +++ b/sys/src/9/sgi/trap.c @@ -0,0 +1,906 @@ +/* + * traps, exceptions, faults and interrupts on ar7161 + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include +#include "../port/error.h" + +typedef struct Handler Handler; + +struct Handler { + void (*handler)(Ureg*, void *); + void *arg; + Handler *next; /* at this interrupt level */ +}; + +int intr(Ureg*); +void kernfault(Ureg*, int); +void noted(Ureg*, ulong); +void rfnote(Ureg**); + +char *excname[] = +{ + "trap: external interrupt", + "trap: TLB modification (store to unwritable)", + "trap: TLB miss (load or fetch)", + "trap: TLB miss (store)", + "trap: address error (load or fetch)", + "trap: address error (store)", + "trap: bus error (fetch)", + "trap: bus error (data load or store)", + "trap: system call", + "breakpoint", + "trap: reserved instruction", + "trap: coprocessor unusable", + "trap: arithmetic overflow", + "trap: TRAP exception", + "trap: VCE (instruction)", + "trap: floating-point exception", + "trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */ + "trap: corextend unusable", + "trap: precise coprocessor 2 exception", + "trap: TLB read-inhibit", + "trap: TLB execute-inhibit", + "trap: undefined 21", + "trap: undefined 22", + "trap: WATCH exception", + "trap: machine checkcore", + "trap: undefined 25", + "trap: undefined 26", + "trap: undefined 27", + "trap: undefined 28", + "trap: undefined 29", + "trap: cache error", + "trap: VCE (data)", +}; + +char *fpcause[] = +{ + "inexact operation", + "underflow", + "overflow", + "division by zero", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*, uint); +#define FPEXPMASK (0x3f<<12) /* Floating exception bits in fcr31 */ + +struct { + char *name; + uint off; +} regname[] = { + "STATUS", offsetof(Ureg, status), + "PC", offsetof(Ureg, pc), + "SP", offsetof(Ureg, sp), + "CAUSE",offsetof(Ureg, cause), + "BADADDR", offsetof(Ureg, badvaddr), + "TLBVIRT", offsetof(Ureg, tlbvirt), + "HI", offsetof(Ureg, hi), + "LO", offsetof(Ureg, lo), + "R31", offsetof(Ureg, r31), + "R30", offsetof(Ureg, r30), + "R28", offsetof(Ureg, r28), + "R27", offsetof(Ureg, r27), + "R26", offsetof(Ureg, r26), + "R25", offsetof(Ureg, r25), + "R24", offsetof(Ureg, r24), + "R23", offsetof(Ureg, r23), + "R22", offsetof(Ureg, r22), + "R21", offsetof(Ureg, r21), + "R20", offsetof(Ureg, r20), + "R19", offsetof(Ureg, r19), + "R18", offsetof(Ureg, r18), + "R17", offsetof(Ureg, r17), + "R16", offsetof(Ureg, r16), + "R15", offsetof(Ureg, r15), + "R14", offsetof(Ureg, r14), + "R13", offsetof(Ureg, r13), + "R12", offsetof(Ureg, r12), + "R11", offsetof(Ureg, r11), + "R10", offsetof(Ureg, r10), + "R9", offsetof(Ureg, r9), + "R8", offsetof(Ureg, r8), + "R7", offsetof(Ureg, r7), + "R6", offsetof(Ureg, r6), + "R5", offsetof(Ureg, r5), + "R4", offsetof(Ureg, r4), + "R3", offsetof(Ureg, r3), + "R2", offsetof(Ureg, r2), + "R1", offsetof(Ureg, r1), +}; + +static Handler handlers[8]; + +void +kvce(Ureg *ur, int ecode) +{ + char c; + Pte **p; + Page **pg; + Segment *s; + ulong addr, soff; + + c = 'D'; + if(ecode == CVCEI) + c = 'I'; + print("Trap: VCE%c: addr=%#lux\n", c, ur->badvaddr); + if(up && !(ur->badvaddr & KSEGM)) { + addr = ur->badvaddr; + s = seg(up, addr, 0); + if(s == nil){ + print("kvce: no seg for %#lux\n", addr); + for(;;); + } + addr &= ~(BY2PG-1); + soff = addr - s->base; + p = &s->map[soff/PTEMAPMEM]; + if(*p){ + pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG]; + if(*pg) + print("kvce: pa=%#lux, va=%#lux\n", + (*pg)->pa, (*pg)->va); + else + print("kvce: no *pg\n"); + }else + print("kvce: no *p\n"); + } +} + +/* prepare to go to user space */ +void +kexit(Ureg *ur) +{ + Tos *tos; + + /* replicate fpstate to ureg status */ + if(up->fpstate != FPactive) + ur->status &= ~CU1; + + /* precise time accounting, kernel exit */ + tos = (Tos*)(USTKTOP-sizeof(Tos)); + tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry; + tos->pcycles = up->pcycles; + tos->pid = up->pid; +} + +void +trap(Ureg *ur) +{ + int ecode, clockintr, user, cop, x, fpchk; + ulong fpfcr31; + char buf[2*ERRMAX], buf1[ERRMAX], *fpexcep; + static int dumps; + + if (up && (char *)(ur) - up->kstack < 1024 && dumps++ == 0) { + iprint("trap: proc %ld kernel stack getting full\n", up->pid); + dumpregs(ur); + dumpstack(); + for(;;); + } + if (up == nil && + (char *)(ur) - (char *)m->stack < 1024 && dumps++ == 0) { + iprint("trap: cpu%d kernel stack getting full\n", m->machno); + dumpregs(ur); + dumpstack(); + for(;;); + } + + ecode = (ur->cause>>2)&EXCMASK; + user = userureg(ur); + if (ur->cause & TS) + panic("trap: tlb shutdown"); + + fpchk = 0; + if(user){ + up->dbgreg = ur; + cycles(&up->kentry); + } + + clockintr = 0; + switch(ecode){ + case CINT: + clockintr = intr(ur); + break; + + case CFPE: + if(!user) + goto Default; + if(up->fpstate == FPactive){ + savefpregs(&up->fpsave); + up->fpstate = FPinactive; + } + clrfpintr(); + fptrap(ur); + fpchk = 1; + break; + + case CTLBM: + case CTLBL: + case CTLBS: + if(up == nil || !user && (ur->badvaddr & KSEGM) == KSEG3) { + kfault(ur); + break; + } + x = up->insyscall; + up->insyscall = 1; + spllo(); + faultmips(ur, user, ecode); + up->insyscall = x; + break; + + case CVCEI: + case CVCED: + kvce(ur, ecode); + goto Default; + + case CWATCH: + if(!user) + panic("watchpoint trap from kernel mode pc=%#p", + ur->pc); + // fpwatch(ur); + break; + + case CCPU: + cop = (ur->cause>>28)&3; + if(user && up && cop == 1) { + if(up->fpstate & FPillegal) { + /* someone used floating point in a note handler */ + postnote(up, 1, + "sys: floating point in note handler", + NDebug); + break; + } + if(up->fpstate == FPinit || up->fpstate == FPinactive){ + restfpregs(&up->fpsave, up->fpsave.fpstatus&~FPEXPMASK); + up->fpstate = FPactive; + ur->status |= CU1; + break; + } + fpchk = 1; + break; + } + /* Fallthrough */ + + Default: + default: + if(user) { + spllo(); + snprint(buf, sizeof buf, "sys: %s", excname[ecode]); + postnote(up, 1, buf, NDebug); + break; + } + if (ecode == CADREL || ecode == CADRES) + iprint("kernel addr exception for va %#p pid %#ld %s\n", + ur->badvaddr, (up? up->pid: 0), + (up? up->text: "")); + print("cpu%d: kernel %s pc=%#lux\n", + m->machno, excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + if(fpchk) { + fpfcr31 = up->fpsave.fpstatus; + if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) { + spllo(); + fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1); + snprint(buf, sizeof buf, "sys: fp: %s", fpexcep); + postnote(up, 1, buf, NDebug); + } + } + + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && clockintr){ + sched(); + splhi(); + } + + if(user){ + notify(ur); + kexit(ur); + } +} + +/* map HPC3 irq to INTR2 */ +int +hpc3irqlevel(int irq) +{ + *IO(uchar, LIO_0_MASK) |= 1 << (irq & 7); + return 2 + irq/8; +} + +/* + * set handlers + */ +void +intrenable(int level, void (*h)(Ureg*, void *), void *arg) +{ + Handler *hp; + + hp = &handlers[level]; + if (hp->handler != nil) { /* occupied? */ + /* add a new one at the end of the chain */ + for (; hp->next != nil; hp = hp->next) + ; + if((hp->next = xalloc(sizeof *hp)) == nil) + panic("intrenable: out of memory"); + hp = hp->next; + } + hp->arg = arg; + hp->handler = h; + + intron(INTR0 << level); +} + +int +intr(Ureg *ur) +{ + ulong cause, mask; + int clockintr; + Handler *hh, *hp; + + m->intr++; + clockintr = 0; + /* + * ignore interrupts that we have disabled, even if their cause bits + * are set. + */ + cause = ur->cause & ur->status & INTMASK; + cause &= ~(INTR1|INTR0); /* ignore sw interrupts */ + if(cause & INTR7){ + clock(ur); + cause &= ~INTR7; + clockintr = 1; + } + hh = &handlers[2]; + for(mask = INTR2; cause != 0 && mask < INTR7; mask <<= 1){ + if(cause & mask){ + for(hp = hh; hp != nil; hp = hp->next){ + if(hp->handler != nil){ + (*hp->handler)(ur, hp->arg); + cause &= ~mask; + } + } + } + hh++; + } + if(cause != 0) + iprint("unhandled interrupts %lux\n", cause); + + /* preemptive scheduling */ + if(up != nil && !clockintr) + preempted(); + /* if it was a clockintr, sched will be called at end of trap() */ + return clockintr; +} + +char* +fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + if(ur->cause & BD) /* branch delay */ + fppc += 4; + s = 0; + if(fcr31 & (1<<17)) + s = "unimplemented operation"; + else{ + fcr31 >>= 7; /* trap enable bits */ + fcr31 &= (fcr31>>5); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fcr31 & (1<pc, ureg->sp, ureg->r31); + if(up == nil) + top = (ulong)MACHADDR + MACHSIZE; + else + top = (ulong)up->kstack + KSTACK; + i = 0; + for(l=ureg->sp; l < top; l += BY2WD) { + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)&etext) { + print("%.8lux=%.8lux ", l, v); + if((++i%4) == 0){ + print("\n"); + delay(200); + } + } + } + print("\n"); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +static ulong +R(Ureg *ur, int i) +{ + uchar *s; + + s = (uchar*)ur; + return *(ulong*)(s + regname[i].off); +} + +void +dumpregs(Ureg *ur) +{ + int i; + + if(up) + print("registers for %s %lud\n", up->text, up->pid); + else + print("registers for kernel\n"); + + for(i = 0; i < nelem(regname); i += 2) + print("%s\t%#.8lux\t%s\t%#.8lux\n", + regname[i].name, R(ur, i), + regname[i+1].name, R(ur, i+1)); +} + +int +notify(Ureg *ur) +{ + int l, s; + ulong sp; + Note *n; + + if(up->procctl) + procctl(); + if(up->nnote == 0) + return 0; + + if(up->fpstate == FPactive){ + savefpregs(&up->fpsave); + up->fpstate = FPinactive; + } + up->fpstate |= FPillegal; + + s = spllo(); + qlock(&up->debug); + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0) { + l = strlen(n->msg); + if(l > ERRMAX-15) /* " pc=0x12345678\0" */ + l = ERRMAX-15; + + seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#lux", ur->pc); + } + + if(n->flag != NUser && (up->notified || up->notify==0)) { + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splx(s); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + sp = ur->usp & ~(BY2V-1); + sp -= sizeof(Ureg); + + if(!okaddr((ulong)up->notify, BY2WD, 0) || + !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); /* push user regs */ + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + + sp -= BY2WD+ERRMAX; + memmove((char*)sp, up->note[0].msg, ERRMAX); /* push err string */ + + sp -= 3*BY2WD; + *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + /* + * arrange to resume at user's handler as if handler(ureg, errstr) + * were being called. + */ + ur->pc = (ulong)up->notify; + + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + +/* + * Check that status is OK to return from note. + */ +int +validstatus(ulong kstatus, ulong ustatus) +{ +// if((kstatus & (INTMASK|KX|SX|UX)) != (ustatus & (INTMASK|KX|SX|UX))) + if((kstatus & INTMASK) != (ustatus & INTMASK)) + return 0; + if((ustatus&(KSU|ERL|EXL|IE)) != (KUSER|EXL|IE)) + return 0; + if(ustatus & (0xFFFF0000&~CU1)) /* no CU3, CU2, CU0, RP, FR, RE, DS */ + return 0; + return 1; +} + +/* + * Return user to state before notify(); called from user's handler. + */ +void +noted(Ureg *kur, ulong arg0) +{ + Ureg *nur; + ulong oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + up->fpstate &= ~FPillegal; + + nur = up->ureg; + + oureg = (ulong)nur; + if((oureg & (BY2WD-1)) + || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad up->ureg in noted or call to noted() when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + if(0 && !validstatus(kur->status, nur->status)) { + qunlock(&up->debug); + pprint("bad noted ureg status %#lux\n", nur->status); + pexit("Suicide", 0); + } + + memmove(kur, up->ureg, sizeof(Ureg)); + switch(arg0) { + case NCONT: + case NRSTR: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); + qunlock(&up->debug); + splhi(); + break; + + case NSAVE: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRMAX; + splhi(); + kur->sp = sp; + kur->r1 = oureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + break; + + default: + pprint("unknown noted arg %#lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } +} + +#include "../port/systab.h" + +static void +sctracesetup(ulong scallnr, ulong sp, uintptr pc, vlong *startnsp) +{ + if(up->procctl == Proc_tracesyscall){ + /* + * Redundant validaddr. Do we care? + * Tracing syscalls is not exactly a fast path... + * Beware, validaddr currently does a pexit rather + * than an error if there's a problem; that might + * change in the future. + */ + if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + syscallfmt(scallnr, pc, (va_list)(sp+BY2WD)); + up->procctl = Proc_stopme; + procctl(); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + *startnsp = todget(nil); + } +} + +static void +sctracefinish(ulong scallnr, ulong sp, int ret, vlong startns) +{ + int s; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, + startns, todget(nil)); + s = splhi(); + procctl(); + splx(s); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + } +} + +/* + * called directly from assembler, not via trap() + */ +void +syscall(Ureg *ur) +{ + int i; + volatile long ret; + ulong sp, scallnr; + vlong startns; + char *e; + + cycles(&up->kentry); + + m->syscall++; + up->insyscall = 1; + up->pc = ur->pc; + up->dbgreg = ur; + ur->cause = 16<<2; /* for debugging: system call is undef 16 */ + + scallnr = ur->r1; + up->scallnr = ur->r1; + sp = ur->sp; + sctracesetup(scallnr, sp, ur->pc, &startns); + + /* no fpu, so no fp state to save */ + spllo(); + + up->nerrlab = 0; + ret = -1; + if(!waserror()) { + if(scallnr >= nsyscall || systab[scallnr] == 0){ + pprint("bad sys call number %ld pc %#lux\n", + scallnr, ur->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp & (BY2WD-1)){ + pprint("odd sp in sys call pc %#lux sp %#lux\n", + ur->pc, ur->sp); + postnote(up, 1, "sys: odd stack", NDebug); + error(Ebadarg); + } + + if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + up->s = *((Sargs*)(sp+BY2WD)); + up->psstate = sysctab[scallnr]; + + ret = systab[scallnr]((va_list)up->s.args); + poperror(); + }else{ + /* failure: save the error buffer for errstr */ + e = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = e; + if(0 && up->pid == 1) + print("[%lud %s] syscall %lud: %s\n", + up->pid, up->text, scallnr, up->errstr); + } + if(up->nerrlab){ + print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab); + for(i = 0; i < NERR; i++) + print("sp=%#lux pc=%#lux\n", + up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + sctracefinish(scallnr, sp, ret, startns); + + ur->pc += 4; + ur->r1 = ret; + + up->psstate = 0; + up->insyscall = 0; + + if(scallnr == NOTED) /* ugly hack */ + noted(ur, *(ulong*)(sp+BY2WD)); /* may return */ + splhi(); + if(scallnr!=RFORK && (up->procctl || up->nnote)) + notify(ur); + /* if we delayed sched because we held a lock, sched now */ + if(up->delaysched) + sched(); + kexit(ur); +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (ulong)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + + cur->status &= ~CU1; /* FPU off when returning */ + + cur->r1 = 0; + cur->pc += 4; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +static +void +linkproc(void) +{ + spllo(); + up->kpfun(up->kparg); + pexit("kproc exiting", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +/* set up user registers before return from exec() */ +uintptr +execregs(ulong entry, ulong ssize, ulong nargs) +{ + Ureg *ur; + ulong *sp; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ur = (Ureg*)up->dbgreg; + ur->usp = (ulong)sp; + ur->pc = entry - 4; /* syscall advances it */ + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ +} + +ulong +userpc(void) +{ + Ureg *ur; + + ur = (Ureg*)up->dbgreg; + return ur->pc; +} + +/* + * This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* + * Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg *xp, Proc *p) +{ + xp->pc = p->sched.pc; + xp->sp = p->sched.sp; + xp->r24 = (ulong)p; /* up */ + xp->r31 = (ulong)sched; +} + +ulong +dbgpc(Proc *p) +{ + Ureg *ur; + + ur = p->dbgreg; + if(ur == 0) + return 0; + + return ur->pc; +} diff --git a/sys/src/9/sgi/uartarcs.c b/sys/src/9/sgi/uartarcs.c new file mode 100644 index 000000000..128aa6a32 --- /dev/null +++ b/sys/src/9/sgi/uartarcs.c @@ -0,0 +1,208 @@ +/* + * ARCS console. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +extern PhysUart arcsphysuart; + +static Uart arcsuart = { + .name = "arcs", + .freq = 1843200, + .phys = &arcsphysuart, +}; + +static Lock arcslock; + +void +arcsputc(char c) +{ + int r; + + r = 0; + ilock(&arcslock); + arcs(0x6c, 1, &c, 1, &r); + iunlock(&arcslock); +} + +int +arcsgetc(void) +{ + int c, r; + uchar b; + + r = 0; + c = -1; + ilock(&arcslock); + if(arcs(0x68, 0) == 0) + if(arcs(0x64, 0, &b, 1, &r) == 0) + if(r == 1) + c = b; + iunlock(&arcslock); + return c; +} + +void +arcsproc(void*) +{ + int c; + + while(waserror()) + ; + for(;;){ + //sched(); + tsleep(&up->sleep, return0, nil, 50); + c = arcsgetc(); + if(c < 0) + continue; + uartrecv(&arcsuart, c); + } +} + +/* + * Send queued output to console + */ +static void +kick(Uart *uart) +{ + int n; + + for(n=0; uart->op < uart->oe || uartstageoutput(uart) > 0; uart->op += n){ + n = uart->oe - uart->op; + if(n <= 0 || !canlock(&arcslock)) + break; + if(arcs(0x6c, 1, uart->op, n, &n) != 0) + n = -1; + unlock(&arcslock); + if(n <= 0) + break; + } +} + +static void +interrupt(Ureg*, void *) +{ +} + +static Uart* +pnp(void) +{ + return &arcsuart; +} + +static void +enable(Uart*, int) +{ +} + +static void +disable(Uart*) +{ +} + +static void +donothing(Uart*, int) +{ +} + +static int +donothingint(Uart*, int) +{ + return 0; +} + +static int +baud(Uart *uart, int n) +{ + if(n <= 0) + return -1; + + uart->baud = n; + return 0; +} + +static int +bits(Uart *uart, int n) +{ + switch(n){ + case 7: + case 8: + break; + default: + return -1; + } + + uart->bits = n; + return 0; +} + +static int +stop(Uart *uart, int n) +{ + if(n != 1) + return -1; + uart->stop = n; + return 0; +} + +static int +parity(Uart *uart, int n) +{ + if(n != 'n') + return -1; + uart->parity = n; + return 0; +} + +static long +status(Uart *, void *, long, long) +{ + return 0; +} + +void +uartarcsputc(Uart*, int c) +{ + arcsputc(c); +} + +int +uartarcsgetc(Uart*) +{ + return arcsgetc(); +} + +PhysUart arcsphysuart = { + .name = "arcsuart", + + .pnp = pnp, + .enable = enable, + .disable = disable, + .kick = kick, + .dobreak = donothing, + .baud = baud, + .bits = bits, + .stop = stop, + .parity = parity, + .modemctl = donothing, + .rts = donothing, + .dtr = donothing, + .status = status, + .fifo = donothing, + + .getc = uartarcsgetc, + .putc = uartarcsputc, +}; + +void +arcsconsinit(void) +{ + consuart = &arcsuart; + consuart->console = 1; +}