summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/9/sgi/bootfs.proto37
-rw-r--r--sys/src/9/sgi/clock.c167
-rw-r--r--sys/src/9/sgi/dat.h211
-rw-r--r--sys/src/9/sgi/devether.c511
-rw-r--r--sys/src/9/sgi/etherif.h40
-rw-r--r--sys/src/9/sgi/etherseeq.c415
-rw-r--r--sys/src/9/sgi/faultmips.c248
-rw-r--r--sys/src/9/sgi/fns.h68
-rw-r--r--sys/src/9/sgi/fptrap.c268
-rw-r--r--sys/src/9/sgi/indy48
-rw-r--r--sys/src/9/sgi/init9.s8
-rw-r--r--sys/src/9/sgi/io.h47
-rw-r--r--sys/src/9/sgi/l.s834
-rw-r--r--sys/src/9/sgi/main.c487
-rw-r--r--sys/src/9/sgi/mem.h277
-rw-r--r--sys/src/9/sgi/mkfile93
-rw-r--r--sys/src/9/sgi/mmu.c479
-rw-r--r--sys/src/9/sgi/trap.c906
-rw-r--r--sys/src/9/sgi/uartarcs.c208
19 files changed, 5352 insertions, 0 deletions
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 = &ether->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; m<n; m++, p++, b += (BY2PG/2)){
+ p->base = 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 <tos.h>
+#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<nelem(conf.mem); i++){
+ if(conf.mem[i].npage == 0){
+ conf.mem[i].base = base;
+ conf.mem[i].npage = (top - base)/BY2PG;
+
+ conf.npage += conf.mem[i].npage;
+ return;
+ }
+ }
+ print("conf.mem[] too small\n");
+}
+
+/*
+ * get memory configuration word for a bank
+ */
+ulong
+bank_conf(int bank)
+{
+ switch(bank){
+ case 0:
+ return *(ulong *)(KSEG1|MEMCFG0) >> 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)
+#define getpgcolor(a) (((ulong)(a)>>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<<STLBLOG) /* entries in the soft TLB */
+/* page # bits that don't fit in STLBLOG bits */
+#define HIPFNBITS (BI2WD - (PGSHIFT+1) - STLBLOG)
+#define KPTELOG 8
+#define KPTESIZE (1<<KPTELOG) /* entries in the kfault soft TLB */
+
+#define TLBPID(n) ((n)&0xFF)
+#define NTLBPID 256 /* # of pids (affects size of Mach) */
+#define NTLB 48 /* # of entries (r4k) */
+#define TLBOFF 1 /* first tlb entry (0 used within mmuswitch) */
+#define NKTLB 2 /* # of initial kfault tlb entries */
+#define TLBROFF (TLBOFF+NKTLB) /* first large IO window tlb entry */
+
+/*
+ * Address spaces
+ */
+#define UZERO KUSEG /* base of user address space */
+#define UTZERO (UZERO+MAXBY2PG) /* 1st user text address; see mkfile */
+#define USTKTOP (KZERO-BY2PG) /* byte just beyond user stack */
+#define USTKSIZE (8*1024*1024) /* size of user stack */
+#define TSTKTOP (KSEG2+USTKSIZE-BY2PG) /* top of temporary stack */
+#define TSTKSIZ (1024*1024/BY2PG) /* can be at most UTSKSIZE/BY2PG */
+#define KZERO KSEG0 /* base of kernel address space */
+#define KTZERO (KZERO+0x08020000) /* first address in kernel text */
diff --git a/sys/src/9/sgi/mkfile b/sys/src/9/sgi/mkfile
new file mode 100644
index 000000000..1e6faede7
--- /dev/null
+++ b/sys/src/9/sgi/mkfile
@@ -0,0 +1,93 @@
+CONF=indy
+CONFLIST=indy
+
+objtype=mips
+</$objtype/mkfile
+p=9
+# must match mem.h
+# KTZERO=0x88020000
+KTZERO=0x88020000
+# must match mem.h
+UTZERO=0x4020
+
+# CFLAGS=$CFLAGS -DFPEMUDEBUG
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+ alarm.$O\
+ alloc.$O\
+ allocb.$O\
+ auth.$O\
+ cache.$O\
+ chan.$O\
+ clock.$O\
+ dev.$O\
+ edf.$O\
+ fault.$O\
+ fptrap.$O\
+ mul64fract.$O\
+ page.$O\
+ parse.$O\
+ pgrp.$O\
+ portclock.$O\
+ print.$O\
+ proc.$O\
+ qio.$O\
+ qlock.$O\
+ rdb.$O\
+ rebootcmd.$O\
+ segment.$O\
+ swap.$O\
+ syscallfmt.$O\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+
+OBJ=\
+ l.$O\
+ faultmips.$O\
+ main.$O\
+ mmu.$O\
+ random.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $CONF.rootc.$O\
+ $DEVS\
+ $PORT\
+
+LIB=\
+ /mips/lib/libauth.a\
+ /mips/lib/libsec.a\
+ /mips/lib/libmp.a\
+ /mips/lib/libip.a\
+ /mips/lib/libc.a\
+
+$p$CONF: $OBJ $CONF.c $LIB
+ $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+ $LD -o $target -l -R8 -H3 -T$KTZERO $OBJ $CONF.$O $LIB
+ size $target
+
+install:V: $p$CONF
+ cp $p$CONF /$objtype/
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+init.h: init9.s ../port/initcode.c /sys/src/libc/9syscall/sys.h
+ va init9.s
+ vc ../port/initcode.c
+ vl -T$UTZERO -R4 -o init.out init9.$O initcode.$O
+ {echo 'uchar initcode[]={'
+ xd -r -1x init.out |
+ sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+ echo '};'} > init.h
+
+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; i<NTLB; i++)
+ TLBINVAL(i, 0);
+}
+
+Lock kmaplock;
+KMap kpte[KPTESIZE];
+KMap* kmapfree;
+
+static int minfree = KPTESIZE;
+static int lastfree;
+static int tlbroff = TLBROFF;
+
+static void
+nfree(void)
+{
+ int i;
+ KMap *k;
+
+ i = 0;
+ for(k=kmapfree; k; k=k->next)
+ i++;
+ if(i<minfree){
+ iprint("%d free\n", i);
+ minfree = i;
+ }
+ lastfree = i;
+}
+
+void
+kmapinit(void)
+{
+ KMap *k, *klast;
+
+ lock(&kmaplock);
+ kmapfree = kpte;
+ klast = &kpte[KPTESIZE-1];
+ for(k=kpte; k<klast; k++)
+ k->next = k+1;
+ k->next = 0;
+ unlock(&kmaplock);
+
+ m->ktlbnext = TLBOFF;
+}
+
+void
+kmapdump(void)
+{
+ int i;
+
+ for(i=0; i<KPTESIZE; i++)
+ iprint("%d: %lud pc=%#lux\n", i, kpte[i].ref, kpte[i].pc);
+}
+
+static int
+putktlb(KMap *k)
+{
+ int x;
+ ulong virt;
+ ulong tlbent[3];
+
+ virt = k->virt & ~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)<<KMAPSHIFT);
+
+ k->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<<m->machno)){
+ icflush((void*)pg->va, BY2PG);
+ pg->txtflush &= ~(1<<m->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; i<NTLBPID; i++) {
+ sp = pidproc[i];
+ if(sp && sp->pidonmach[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; i<NTLB; i++)
+ if(pidproc[TLBPID(gettlbvirt(i))] == 0)
+ TLBINVAL(i, pid);
+}
+
+void
+flushmmu(void)
+{
+ int s;
+
+ s = splhi();
+ up->newtlb = 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 <tos.h>
+#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<<i))
+ s = fpcause[i];
+ }
+
+ if(s == 0)
+ return "no floating point exception";
+
+ snprint(buf, size, "%s fppc=%#lux", s, fppc);
+ return buf;
+}
+
+static void
+getpcsp(ulong *pc, ulong *sp)
+{
+ *pc = getcallerpc(&pc);
+ *sp = (ulong)&pc-4;
+}
+
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+
+ memset(&ureg, 0, sizeof ureg);
+ getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
+ ureg.r31 = getcallerpc(&fn);
+ fn(&ureg);
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong l, v, top, i;
+ extern ulong etext;
+
+ print("ktrace /kernel/path %.8lux %.8lux %.8lux\n",
+ ureg->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;
+}