summaryrefslogtreecommitdiff
path: root/os/cerf405/trap.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/cerf405/trap.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/cerf405/trap.c')
-rw-r--r--os/cerf405/trap.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/os/cerf405/trap.c b/os/cerf405/trap.c
new file mode 100644
index 00000000..846cc097
--- /dev/null
+++ b/os/cerf405/trap.c
@@ -0,0 +1,581 @@
+#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
+{
+ Maxhandler= MaxVector /* max number of interrupt handlers, assuming none shared */
+};
+
+enum {
+ /* UIC registers (p. 10-4) */
+ Usr = 0xC0, /* status */
+ Uer = 0xC2, /* enable */
+ Ucr = 0xC3, /* critical interrupts */
+ Upr = 0xC4, /* priority */
+ Utr = 0xC5, /* trigger */
+ Umsr = 0xC6, /* masked status */
+ Uvr = 0xC7, /* vector */
+ Uvcr = 0xC8, /* vector configuration */
+};
+
+typedef struct Handler Handler;
+struct Handler
+{
+ void (*r)(Ureg*, void*);
+ void *arg;
+ char name[KNAMELEN];
+ Handler *next;
+ int edge;
+ ulong nintr;
+ ulong ticks;
+ int maxtick;
+};
+
+static Lock veclock;
+
+static struct
+{
+ Handler *ivec[MaxVector];
+ Handler h[Maxhandler];
+ int free;
+ Handler* freelist;
+} halloc;
+
+Instr BREAK = 0x7fe00008;
+int (*breakhandler)(Ureg*, Proc*);
+
+void kernfault(Ureg*, int);
+
+char *excname[] =
+{
+ "reserved 0",
+ "system reset",
+ "machine check",
+ "data access",
+ "instruction access",
+ "external interrupt",
+ "alignment",
+ "program exception",
+ "floating-point unavailable",
+ "decrementer",
+ "i/o controller interface error",
+ "reserved B",
+ "system call",
+ "trace trap",
+ "floating point assist",
+ "reserved F",
+ "software emulation",
+ "ITLB miss",
+ "DTLB miss",
+ "ITLB error",
+ "DTLB error",
+ "reserved 15",
+ "reserved 16",
+ "reserved 17",
+ "reserved 18",
+ "reserved 19",
+ "reserved 1A",
+ "reserved 1B",
+ "data breakpoint",
+ "instruction breakpoint",
+ "peripheral breakpoint",
+ "development port",
+ /* the following are made up on a program exception */
+ "floating point exception", /* 20: FPEXC */
+ "illegal instruction", /* 21 */
+ "privileged instruction", /* 22 */
+ "trap", /* 23 */
+ "illegal operation", /* 24 */
+ "breakpoint", /* 25 */
+};
+
+char *fpcause[] =
+{
+ "inexact operation",
+ "division by zero",
+ "underflow",
+ "overflow",
+ "invalid operation",
+};
+char *fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */
+
+char *regname[]={
+ "CAUSE", "SRR1",
+ "PC", "GOK",
+ "LR", "CR",
+ "XER", "CTR",
+ "R0", "R1",
+ "R2", "R3",
+ "R4", "R5",
+ "R6", "R7",
+ "R8", "R9",
+ "R10", "R11",
+ "R12", "R13",
+ "R14", "R15",
+ "R16", "R17",
+ "R18", "R19",
+ "R20", "R21",
+ "R22", "R23",
+ "R24", "R25",
+ "R26", "R27",
+ "R28", "R29",
+ "R30", "R31",
+};
+
+void
+sethvec(int v, void (*r)(void))
+{
+ ulong *vp, pa, o;
+
+ vp = (ulong*)KADDR(v);
+ vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */
+ vp[1] = 0x7c0802a6; /* MOVW LR, R0 */
+ vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */
+ pa = PADDR(r);
+ o = pa >> 25;
+ if(o != 0 && o != 0x7F){
+ /* a branch too far: running from ROM */
+ vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */
+ vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */
+ vp[5] = 0x7c0803a6; /* MOVW R0, LR */
+ vp[6] = 0x4e800021; /* BL (LR) */
+ }else
+ vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */
+ dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+sethvec2(int v, void (*r)(void))
+{
+ ulong *vp;
+
+ vp = (ulong*)KADDR(v);
+ vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */
+ dcflush(vp, sizeof(*vp));
+}
+
+static void
+faultpower(Ureg *ur, ulong addr, int read)
+{
+ char buf[ERRMAX];
+
+ if(up == nil){
+ dumpregs(ur);
+ panic("kernel fault");
+ }
+
+ up->dbgreg = ur; /* For remote ACID */
+ spllo();
+
+ sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux",
+ read? "read": "write", ur->pc, addr);
+ if(up->type == Interp){
+ if(addr == ~0)
+ disfault(ur, "dereference of nil");
+ disfault(ur, buf);
+ }
+ dumpregs(ur);
+ panic("fault: %s\n", buf);
+}
+
+void
+trap(Ureg *ur)
+{
+ int ecode, s;
+ ulong w, esr;
+ char buf[ERRMAX];
+
+ ecode = ur->cause >> 8;
+ if(ecode < 0 || ecode >= 0x1F)
+ ecode = 0x1F;
+ esr = getesr();
+ putesr(0);
+ switch(ecode){
+ case CPIT:
+ clockintr(ur);
+ preemption(1);
+ break;
+
+ case CMCHECK:
+ if(esr & ESR_MCI){
+ faultpower(ur, ur->pc, 1);
+ break;
+ }
+ /* FALL THROUGH */
+ case CDSI:
+ faultpower(ur, getdear(), !(esr&ESR_DST));
+ break;
+
+ case CDMISS:
+ faultpower(ur, getdear(), 1);
+ break;
+
+ case CISI:
+ case CIMISS:
+ faultpower(ur, ur->pc, 1);
+ break;
+
+ case CPROG:
+ if(esr & ESR_PIL){
+ if(up == nil)
+ goto Default;
+ if((ulong)(ur+1) != ur->r1)
+ panic("fp emu stack");
+ spllo();
+ if(waserror()){
+ if(up->type == Interp)
+ disfault(ur, up->env->errstr);
+ panic("%s", up->env->errstr);
+ }
+ if(fpipower(ur) == 0){
+ splhi();
+ poperror();
+ print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); /* temporary */
+ goto Default;
+ }
+ poperror();
+ break;
+ }
+ /* TO DO: 4xx variant for the following */
+ if(ur->status & (1<<19)) {
+ ecode = 0x20;
+ w = ur->pc;
+ if(ur->status & (1<<16))
+ w += 4;
+ if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */
+ if(breakhandler){
+ s = (*breakhandler)(ur, up);
+ if(s == BrkSched){
+ if(up){
+ up->preempted = 0;
+ sched();
+ splhi();
+ }
+ }else if(s == BrkNoSched){
+ if(up){
+ up->preempted = 1; /* stop it being preempted until next instruction */
+ up->dbgreg = 0;
+ }
+ }
+ break;
+ }
+ ecode = 0x1D; /* breakpoint */
+ }
+ }
+ if(ur->status & (1<<18))
+ ecode = 0x21;
+ if(ur->status & (1<<17))
+ ecode = 0x22;
+ /* FALL THROUGH */
+
+ Default:
+ default:
+ if(up && up->type == Interp) {
+ spllo();
+ snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc);
+ error(buf);
+ break;
+ }
+ print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc);
+ dumpregs(ur);
+ dumpstack();
+ if(m->machno == 0)
+ spllo();
+ exit(1);
+ }
+
+ splhi();
+}
+
+void
+trapinit(void)
+{
+ int i;
+
+ putdcr(Uer, 0); /* none enabled */
+ putdcr(Ucr, 0); /* none are critical by default */
+ putdcr(Upr, ~IBIT(VectorPCISERR)); /* default is active high (except PCISERR) */
+ putdcr(Utr, 0); /* all are level sensitive by default */
+ putdcr(Usr, getdcr(Usr)); /* reset interrupts */
+ putdcr(Uvcr, 0); /* 31 is highest priority */
+ eieio();
+
+ /*
+ * set all exceptions to trap
+ */
+ for(i = 0x0; i < 0x3000; i += 0x100)
+ sethvec(i, trapvec);
+
+ /* on the 405, several traps are critical interrupts with different SRRs */
+ sethvec(0x0100, trapcvec);
+ sethvec(0x0200, trapcvec);
+
+ sethvec(CEI<<8, intrvec);
+ /* TO DO: FIT and WDT */
+ //sethvec2(CIMISS<<8, itlbmiss);
+ //sethvec2(CDMISS<<8, dtlbmiss);
+
+ putevpr(0); /* use our vectors */
+}
+
+void
+intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+ Handler *h;
+ ulong w, f, bit;
+
+ f = v;
+ v &= IRQmask;
+ bit = IBIT(v);
+ if(v < 0 || v >= nelem(halloc.ivec))
+ panic("intrenable(%d)", v);
+ ilock(&veclock);
+ if((h = halloc.freelist) == nil){
+ if(halloc.free >= Maxhandler){
+ iunlock(&veclock);
+ panic("out of interrupt handlers");
+ }
+ h = &halloc.h[halloc.free++];
+ }else
+ halloc.freelist = h->next;
+ h->r = r;
+ h->arg = arg;
+ strncpy(h->name, name, KNAMELEN-1);
+ h->name[KNAMELEN-1] = 0;
+ h->next = halloc.ivec[v];
+ halloc.ivec[v] = h;
+
+ /*
+ * enable corresponding interrupt in UIC
+ */
+ w = getdcr(Ucr);
+ if(f & IRQcritical)
+ putdcr(Ucr, w | bit);
+ else
+ putdcr(Ucr, w & ~bit);
+ if(v >= VectorIRQ){
+ /* (only) these have got choice of polarity, etc. */
+ w = getdcr(Utr);
+ h->edge = (f & IRQedge) != 0;
+ if(h->edge)
+ putdcr(Utr, w | bit);
+ else
+ putdcr(Utr, w & ~bit);
+ w = getdcr(Upr);
+ if(f & IRQactivelow)
+ putdcr(Upr, w | bit);
+ else
+ putdcr(Upr, w & ~bit);
+ }
+ eieio();
+ putdcr(Uer, getdcr(Uer) | bit);
+ eieio();
+ iunlock(&veclock);
+}
+
+static void
+irqdisable(int v)
+{
+ putdcr(Uer, getdcr(Uer) & ~IBIT(v));
+}
+
+void
+intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+ Handler *h, **hp;
+
+ v &= IRQmask;
+ if(v < 0 || v >= nelem(halloc.ivec))
+ panic("intrdisable(%d)", v);
+ ilock(&veclock);
+ for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+ if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){
+ *hp = h->next;
+ h->next = halloc.freelist;
+ halloc.freelist = h;
+ break;
+ }
+ if(halloc.ivec[v] == nil)
+ irqdisable(v);
+ iunlock(&veclock);
+}
+
+/*
+ * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock.
+ */
+void
+intr(Ureg *ur)
+{
+ ulong msr, b;
+ int v;
+ Handler *h;
+ long t0;
+ Proc *oup;
+
+ oup = up;
+ up = nil; /* no process at interrupt level */
+ while((msr = getdcr(Umsr)) != 0){
+ for(v=0; msr!=0 && v<32; v++){
+ b = IBIT(v);
+ if((msr & b) == 0)
+ continue;
+ msr &= ~b;
+ ur->cause = (CEI<<8) | v;
+ h = halloc.ivec[v];
+ if(h == nil){
+ iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+ irqdisable(v);
+ continue;
+ }
+
+ /*
+ * call the interrupt handlers
+ */
+ do {
+ if(h->edge)
+ putdcr(Usr, b);
+ h->nintr++;
+ t0 = getpit();
+ (*h->r)(ur, h->arg);
+ t0 -= getpit();
+ h->ticks += t0;
+ if(h->maxtick < t0)
+ h->maxtick = t0;
+ if(!h->edge)
+ putdcr(Usr, b);
+ h = h->next;
+ } while(h != nil);
+ }
+ }
+ up = oup;
+ preemption(0);
+}
+
+int
+intrstats(char *buf, int bsize)
+{
+ Handler *h;
+ int i, n;
+
+ n = 0;
+ for(i=0; i<nelem(halloc.ivec) && n < bsize; i++)
+ if((h = halloc.ivec[i]) != nil && h->nintr)
+ n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick);
+ return n;
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+ int i;
+ char *s;
+ ulong fppc;
+
+ fppc = ur->pc;
+ s = 0;
+ fpscr >>= 3; /* trap enable bits */
+ fpscr &= (fpscr>>22); /* anded with exceptions */
+ for(i=0; i<5; i++)
+ if(fpscr & (1<<i))
+ s = fpcause[i];
+ if(s == 0)
+ return "no floating point exception";
+ sprint(buf, "%s fppc=0x%lux", s, fppc);
+ return buf;
+}
+
+#define KERNPC(x) (KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext)
+
+void
+kernfault(Ureg *ur, int code)
+{
+ Label l;
+
+ print("panic: kfault %s dear=0x%lux esr=0x%8.8lux\n", excname[code], getdear(), getesr());
+ print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n",
+ up, ur->status, ur->pc, ur->sp);
+ dumpregs(ur);
+ l.sp = ur->sp;
+ l.pc = ur->pc;
+ dumpstack();
+ setpri(PriBackground); /* Let the debugger in */
+ for(;;)
+ sched();
+}
+
+void
+dumpstack(void)
+{
+ ulong l, v;
+ int i;
+
+ if(up == 0)
+ return;
+ i = 0;
+ for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
+ v = *(ulong*)l;
+ if(KTZERO < v && v < (ulong)etext){
+ print("%lux=%lux, ", l, v);
+ if(i++ == 4){
+ print("\n");
+ i = 0;
+ }
+ }
+ }
+}
+
+void
+dumpregs(Ureg *ur)
+{
+ int i;
+ ulong *l;
+ if(up) {
+ print("registers for %s %ld\n", up->text, up->pid);
+ if(ur->usp < (ulong)up->kstack ||
+ ur->usp > (ulong)up->kstack+KSTACK)
+ print("invalid stack ptr\n");
+ }
+ else
+ print("registers for kernel\n");
+
+ l = &ur->cause;
+ for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+ print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ (*up->kpfun)(up->arg);
+ pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK;
+
+ p->kpfun = func;
+ p->arg = arg;
+}
+
+void
+setpanic(void)
+{
+ consoleprint = 1;
+iprint("panic\n");
+}
+
+void
+dumplongs(char*, ulong*, int)
+{
+}