summaryrefslogtreecommitdiff
path: root/os/sa1110/trap.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/sa1110/trap.c')
-rw-r--r--os/sa1110/trap.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/os/sa1110/trap.c b/os/sa1110/trap.c
new file mode 100644
index 00000000..a625535d
--- /dev/null
+++ b/os/sa1110/trap.c
@@ -0,0 +1,601 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+typedef struct Handler Handler;
+
+struct Handler {
+ void (*r)(Ureg*, void*);
+ void *a;
+ int v;
+ char name[KNAMELEN];
+ Handler *next;
+};
+
+enum {
+ MinGpioIRQbit = 11,
+ NumGpioIRQbits = MaxGPIObit-MinGpioIRQbit+1,
+ GpioIRQmask = ((1<<NumGpioIRQbits)-1)<<MinGpioIRQbit,
+};
+
+static Handler irqvec[MaxIRQbit+1];
+static Handler gpiovec[NumGpioIRQbits];
+static Lock veclock;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+void (*idle)(void);
+void (*suspendcode)(void);
+
+extern void (*serwrite)(char *, int);
+
+/*
+ * Interrupt sources not masked by splhi(): special
+ * interrupt handlers (eg, profiler or watchdog), not allowed
+ * to share regular kernel data structures. All interrupts are
+ * masked by splfhi(), which should only be used sparingly.
+ * splflo enables FIQ but no others.
+ */
+enum {
+ IRQ_NONMASK = (1 << OSTimerbit(3)) | (1 << OSTimerbit(2)),
+};
+
+void
+intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int x;
+ GpioReg *g;
+ Handler *ie;
+
+ ilock(&veclock);
+ switch(tbdf) {
+ case BusGPIOfalling:
+ case BusGPIOrising:
+ case BusGPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrenable: gpio source %d out of range", v);
+ g = GPIOREG;
+ switch(tbdf){
+ case BusGPIOfalling:
+ g->gfer |= 1<<v;
+ g->grer &= ~(1<<v);
+ break;
+ case BusGPIOrising:
+ g->grer |= 1<<v;
+ g->gfer &= ~(1<<v);
+ break;
+ case BusGPIOboth:
+ g->grer |= 1<<v;
+ g->gfer |= 1<<v;
+ break;
+ }
+ g->gpdr &= ~(1<<v);
+ if(v >= MinGpioIRQbit) {
+ ie = &gpiovec[v-MinGpioIRQbit];
+ if(ie->r != nil)
+ iprint("duplicate gpio irq: %d (%s)\n", v, ie->name);
+ ie->r = f;
+ ie->a = a;
+ strncpy(ie->name, name, KNAMELEN-1);
+ ie->name[KNAMELEN-1] = 0;
+ iunlock(&veclock);
+ return;
+ }
+ /*FALLTHROUGH for GPIO sources 0-10 */
+ case BUSUNKNOWN:
+ case BusCPU:
+ if(v < 0 || v > MaxIRQbit)
+ panic("intrenable: irq source %d out of range", v);
+ ie = &irqvec[v];
+ if(ie->r != nil)
+ iprint("duplicate irq: %d (%s)\n", v, ie->name);
+ ie->r = f;
+ ie->a = a;
+ strncpy(ie->name, name, KNAMELEN-1);
+ ie->name[KNAMELEN-1] = 0;
+
+ x = splfhi();
+ /* Enable the interrupt by setting the mask bit */
+ INTRREG->icmr |= 1 << v;
+ splx(x);
+ break;
+ default:
+ panic("intrenable: unknown irq bus %d", tbdf);
+ }
+ iunlock(&veclock);
+}
+
+void
+intrdisable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int x;
+ GpioReg *g;
+ Handler *ie;
+
+ ilock(&veclock);
+ switch(tbdf) {
+ case BusGPIOfalling:
+ case BusGPIOrising:
+ case BusGPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrdisable: gpio source %d out of range", v);
+ if(v >= MinGpioIRQbit)
+ ie = &gpiovec[v-MinGpioIRQbit];
+ else
+ ie = &irqvec[v];
+ if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+ break;
+ ie->r = nil;
+ if(v < MinGpioIRQbit){
+ x = splfhi();
+ INTRREG->icmr &= ~(1<<v);
+ splx(x);
+ }
+ g = GPIOREG;
+ switch(tbdf){
+ case BusGPIOfalling:
+ g->gfer &= ~(1<<v);
+ break;
+ case BusGPIOrising:
+ g->grer &= ~(1<<v);
+ break;
+ case BusGPIOboth:
+ g->grer &= ~(1<<v);
+ g->gfer &= ~(1<<v);
+ break;
+ }
+ break;
+ case BUSUNKNOWN:
+ case BusCPU:
+ if(v < 0 || v > MaxIRQbit)
+ panic("intrdisable: irq source %d out of range", v);
+ ie = &irqvec[v];
+ if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+ break;
+ ie->r = nil;
+ x = splfhi();
+ INTRREG->icmr &= ~(1<<v);
+ splx(x);
+ break;
+ default:
+ panic("intrdisable: unknown irq bus %d", tbdf);
+ }
+ iunlock(&veclock);
+}
+
+static void
+gpiointr(Ureg *ur, void*)
+{
+ Handler *cur;
+ ulong e;
+ int i;
+
+ e = GPIOREG->gedr & GpioIRQmask;
+ GPIOREG->gedr = e;
+ for(i = MinGpioIRQbit; i <= MaxGPIObit && e != 0; i++){
+ if(e & (1<<i)){
+ cur = &gpiovec[i-MinGpioIRQbit];
+ if(cur->r != nil){
+ cur->r(ur, cur->a);
+ e &= ~(1<<i);
+ }
+ }
+ }
+ if(e != 0){
+ GPIOREG->gfer &= ~e;
+ GPIOREG->grer &= ~e;
+ iprint("spurious GPIO interrupt: %8.8lux\n", e);
+ }
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+ Handler *cur;
+ int i, s;
+
+ for(i=0; i<nelem(irqvec) && ibits; i++)
+ if(ibits & (1<<i)){
+ cur = &irqvec[i];
+ if(cur->r != nil){
+ cur->r(ur, cur->a);
+ ibits &= ~(1<<i);
+ }
+ }
+ if(ibits != 0){
+ iprint("spurious irq interrupt: %8.8lux\n", ibits);
+ s = splfhi();
+ INTRREG->icmr &= ~ibits;
+ splx(s);
+ }
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+ setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+ setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+ setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+ setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+ int v;
+ IntrReg *intr = INTRREG;
+
+ intr->icmr = 0;
+ intr->iclr = IRQ_NONMASK;
+
+ trapstacks();
+
+ for(v = 0; v < nelem(irqvec); v++) {
+ irqvec[v].r = nil;
+ irqvec[v].a = nil;
+ irqvec[v].v = v;
+ }
+ for(v = 0; v < nelem(gpiovec); v++) {
+ gpiovec[v].r = nil;
+ gpiovec[v].a = nil;
+ gpiovec[v].v = v+MinGpioIRQbit;
+ }
+
+ memmove(page0->vectors, vectors, sizeof(page0->vectors));
+ memmove(page0->vtable, vtable, sizeof(page0->vtable));
+ dcflush(page0, sizeof(*page0));
+
+ idle = xspanalloc(13*sizeof(ulong), CACHELINESZ, 0);
+ memmove(idle, _idlemode, 13*sizeof(ulong));
+ dcflush(idle, 13*sizeof(ulong));
+
+ suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0);
+ memmove(suspendcode, _suspendcode, 16*sizeof(ulong));
+ dcflush(suspendcode, 8*sizeof(ulong));
+
+ icflushall();
+
+ intrenable(MinGpioIRQbit, gpiointr, nil, BusCPU, "gpio");
+}
+
+static char *trapnames[PsrMask+1] = {
+ [ PsrMfiq ] "Fiq interrupt",
+ [ PsrMirq ] "Mirq interrupt",
+ [ PsrMsvc ] "SVC/SWI Exception",
+ [ PsrMabt ] "Prefetch Abort/Data Abort",
+ [ PsrMabt+1 ] "Data Abort",
+ [ PsrMund ] "Undefined instruction",
+ [ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+ char *s;
+
+ s = trapnames[psr & PsrMask];
+ if(s == nil)
+ s = "Undefined trap";
+ return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+ char errbuf[ERRMAX];
+ sprint(errbuf, "sys: trap: %s\n", trapname(type));
+ error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+ char buf[ERRMAX];
+
+ sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+ if(0){
+ iprint("%s\n", buf);
+ dumpregs(ureg);
+ }
+ if(far == ~0)
+ disfault(ureg, "dereference of nil");
+ disfault(ureg, buf);
+}
+
+/*
+ * All traps come here. It might be slightly slower to have all traps call trap
+ * rather than directly vectoring the handler.
+ * However, this avoids a lot of code duplication and possible bugs.
+ * trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+ ulong far, fsr;
+ int rem, t, itype;
+ Proc *oup;
+
+ if(up != nil)
+ rem = ((char*)ureg)-up->kstack;
+ else
+ rem = ((char*)ureg)-(char*)m->stack;
+ if(ureg->type != PsrMfiq && rem < 256)
+ panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux",
+ rem, up?up->text:"", up, ureg, ureg->pc);
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ itype = ureg->type;
+ if(itype == PsrMabt+1)
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+ ureg->sp = (ulong)(ureg+1);
+ if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */
+ oup = up;
+ up = nil;
+ intrs(ureg, INTRREG->icfp);
+ up = oup;
+ return;
+ }
+
+ /* All other traps */
+
+ if(ureg->psr & PsrDfiq)
+ panic("FIQ disabled");
+
+ if(up){
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+ }
+ switch(itype) {
+ case PsrMirq:
+ t = m->ticks; /* CPU time per proc */
+ up = nil; /* no process at interrupt level */
+ splflo(); /* allow fast interrupts */
+ intrs(ureg, INTRREG->icip);
+ up = m->proc;
+ preemption(m->ticks - t);
+ break;
+
+ case PsrMund: /* Undefined instruction */
+ if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+ int s;
+ Proc *p;
+
+ p = up;
+ /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+ p = 0; */
+ s = breakhandler(ureg, p);
+ if(s == BrkSched) {
+ p->preempted = 0;
+ sched();
+ } else if(s == BrkNoSched) {
+ p->preempted = 1; /* stop it being preempted until next instruction */
+ if(up)
+ up->dbgreg = 0;
+ return;
+ }
+ break;
+ }
+ if(up == nil)
+ goto faultpanic;
+ spllo();
+ if(waserror()) {
+ if(waslo(ureg->psr) && up->type == Interp)
+ disfault(ureg, up->env->errstr);
+ setpanic();
+ dumpregs(ureg);
+ panic("%s", up->env->errstr);
+ }
+ if(!fpiarm(ureg)) {
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ poperror();
+ break;
+
+ case PsrMsvc: /* Jump through 0 or SWI */
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ setpanic();
+ dumpregs(ureg);
+ panic("SVC/SWI exception");
+ break;
+
+ case PsrMabt: /* Prefetch abort */
+ if(catchdbg && catchdbg(ureg, 0))
+ break;
+ /* FALL THROUGH */
+ case PsrMabt+1: /* Data abort */
+ fsr = mmugetfsr();
+ far = mmugetfar();
+ if(fsr & (1<<9)) {
+ mmuputfsr(fsr & ~(1<<9));
+ if(catchdbg && catchdbg(ureg, fsr))
+ break;
+ print("Debug/");
+ }
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ faultarm(ureg, far);
+ }
+ print("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far);
+ /* FALL THROUGH */
+
+ default: /* ??? */
+faultpanic:
+ setpanic();
+ dumpregs(ureg);
+ panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+ break;
+ }
+
+ splhi();
+ if(up)
+ up->dbgreg = 0; /* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+ if(breakhandler != 0) /* don't mess up debugger */
+ return;
+ INTRREG->icmr = 0;
+ spllo();
+ consoleprint = 1;
+ serwrite = uartputs;
+}
+
+int
+isvalid_wa(void *v)
+{
+ return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3);
+}
+
+int
+isvalid_va(void *v)
+{
+ return (ulong)v >= KZERO && (ulong)v < conf.topofmem;
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = 0;
+ iprint("%s at %.8p: ", msg, v);
+ for(i=0; i<n; i++){
+ if(l >= 4){
+ iprint("\n %.8p: ", v);
+ l = 0;
+ }
+ if(isvalid_va(v)){
+ iprint(" %.8lux", *v++);
+ l++;
+ }else{
+ iprint(" invalid");
+ break;
+ }
+ }
+ iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong *v, *l;
+ ulong inst;
+ ulong *estack;
+ int i;
+
+ l = (ulong*)(ureg+1);
+ if(!isvalid_wa(l)){
+ iprint("invalid ureg/stack: %.8p\n", l);
+ return;
+ }
+ print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+ if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+ estack = (ulong*)(up->kstack+KSTACK);
+ else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+ estack = (ulong*)((ulong)m+BY2PG-4);
+ else{
+ iprint("unknown stack\n");
+ return;
+ }
+ i = 0;
+ for(; l<estack; l++) {
+ if(!isvalid_wa(l)) {
+ iprint("invalid(%8.8p)", l);
+ break;
+ }
+ v = (ulong*)*l;
+ if(isvalid_wa(v)) {
+ inst = v[-1];
+ if((inst & 0x0ff0f000) == 0x0280f000 &&
+ (*(v-2) & 0x0ffff000) == 0x028fe000 ||
+ (inst & 0x0f000000) == 0x0b000000) {
+ iprint("%8.8p=%8.8lux ", l, v);
+ i++;
+ }
+ }
+ if(i == 4){
+ iprint("\n");
+ i = 0;
+ }
+ }
+ if(i)
+ print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ print("TRAP: %s", trapname(ureg->type));
+ if((ureg->psr & PsrMask) != PsrMsvc)
+ print(" in %s", trapname(ureg->psr));
+ print("\n");
+ print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ print("Stack is at: %8.8luX\n", ureg);
+ print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+ if(up)
+ print("Process stack: %8.8lux-%8.8lux\n",
+ up->kstack, up->kstack+KSTACK-4);
+ else
+ print("System stack: %8.8lux-%8.8lux\n",
+ (ulong)(m+1), (ulong)m+BY2PG-4);
+ dumplongs("stack", (ulong *)(ureg + 1), 16);
+ _dumpstack(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+ ureg.pc = getcallerpc(&fn);
+ ureg.sp = (ulong)&fn;
+ ureg.r14 = 0;
+ fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+ catchdbg = f;
+}