summaryrefslogtreecommitdiff
path: root/sys/src/9/omap/trap.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/omap/trap.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/omap/trap.c')
-rwxr-xr-xsys/src/9/omap/trap.c769
1 files changed, 769 insertions, 0 deletions
diff --git a/sys/src/9/omap/trap.c b/sys/src/9/omap/trap.c
new file mode 100755
index 000000000..6b0911e1b
--- /dev/null
+++ b/sys/src/9/omap/trap.c
@@ -0,0 +1,769 @@
+/*
+ * omap3530 traps, exceptions, interrupts, system calls.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ureg.h"
+#include "arm.h"
+
+enum {
+ Nirqs = 96,
+ Nvec = 8, /* # of vectors at start of lexception.s */
+ Bi2long = BI2BY * sizeof(long),
+};
+
+extern int notify(Ureg*);
+
+extern int ldrexvalid;
+
+/* omap35x intc (aka mpu_intc) */
+typedef struct Intrregs Intrregs;
+struct Intrregs {
+ /*
+ * the manual inserts "INTCPS_" before each register name;
+ * we'll just assume the prefix.
+ */
+ uchar _pad0[4*4];
+ ulong sysconfig;
+ ulong sysstatus; /* ro */
+ uchar _pad1[0x40 - 0x18];
+ ulong sir_irq; /* ro */
+ ulong sir_fiq; /* ro */
+ ulong control;
+ ulong protection;
+ ulong idle;
+ uchar _pad2[0x60 - 0x54];
+ ulong irq_priority;
+ ulong fiq_priority;
+ ulong threshold;
+ uchar _pad3[0x80 - 0x6c];
+ struct Bits { /* bitmaps */
+ ulong itr; /* ro: pending intrs (no mask) */
+ ulong mir; /* interrupt mask: 1 means masked */
+ ulong mir_clear; /* wo: 1 sets the bit */
+ ulong mir_set; /* wo: 1 clears the bit */
+ ulong isr_set; /* software interrupts */
+ ulong isr_clear; /* wo */
+ ulong pending_irq; /* ro */
+ ulong pending_fiq; /* ro */
+ } bits[3]; /* 3*32 = 96 (Nirqs) */
+ ulong ilr[Nirqs];
+};
+
+enum {
+ /* sysconfig bits */
+ Softreset = 1<<1,
+
+ /* sysstatus bits */
+ Resetdone = 1<<0,
+
+ /* sir_irq/fiq bits */
+ Activeirq = MASK(7),
+
+ /* control bits */
+ Newirqagr = 1<<0,
+
+ /* protection bits */
+ Protection = 1<<0,
+
+ /* irq/fiq_priority bits */
+ Irqpriority = MASK(6),
+
+ /* threshold bits */
+ Prioritythreshold = MASK(8),
+
+ /* ilr bits */
+ Priority = MASK(8) - MASK(2),
+};
+
+typedef struct Vctl Vctl;
+typedef struct Vctl {
+ Vctl* next; /* handlers on this vector */
+ char *name; /* of driver, xallocated */
+ void (*f)(Ureg*, void*); /* handler to call */
+ void* a; /* argument to call it with */
+} Vctl;
+
+static Lock vctllock;
+static Vctl* vctl[Nirqs];
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vpage0 {
+ void (*vectors[Nvec])(void);
+ u32int vtable[Nvec];
+} Vpage0;
+static Vpage0 *vpage0;
+
+uvlong ninterrupt;
+uvlong ninterruptticks;
+int irqtooearly = 1;
+
+static volatile int probing, trapped;
+
+static int
+irqinuse(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ /*
+ * mir registers are odd: a 0 bit means intr unmasked (i.e.,
+ * we've unmasked it because it's in use).
+ */
+ return (ip->bits[irq / Bi2long].mir & (1 << (irq % Bi2long))) == 0;
+}
+
+static void
+intcmask(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ ip->bits[irq / Bi2long].mir_set = 1 << (irq % Bi2long);
+ coherence();
+}
+
+static void
+intcunmask(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ ip->bits[irq / Bi2long].mir_clear = 1 << (irq % Bi2long);
+ coherence();
+}
+
+static void
+intcmaskall(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < 3; i++)
+ ip->bits[i].mir_set = ~0;
+ coherence();
+}
+
+static void
+intcunmaskall(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < 3; i++)
+ ip->bits[i].mir_clear = ~0;
+ coherence();
+}
+
+static void
+intcinvertall(void)
+{
+ int i, s;
+ ulong bits;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ s = splhi();
+ for (i = 0; i < 3; i++) {
+ bits = ip->bits[i].mir;
+ ip->bits[i].mir_set = ~0; /* mask all */
+ coherence();
+ /* clearing enables only those intrs. that were disabled */
+ ip->bits[i].mir_clear = bits;
+ }
+ coherence();
+ splx(s);
+}
+
+static void
+intrsave(ulong buf[3])
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < nelem(buf); i++)
+ buf[i] = ip->bits[i].mir;
+ coherence();
+}
+
+static void
+intrrestore(ulong buf[3])
+{
+ int i, s;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ s = splhi();
+ for (i = 0; i < nelem(buf); i++) {
+ ip->bits[i].mir_clear = ~0; /* unmask all */
+ coherence();
+ ip->bits[i].mir_set = buf[i]; /* mask previously disabled */
+ }
+ coherence();
+ splx(s);
+}
+
+/*
+ * set up for exceptions
+ */
+void
+trapinit(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ /* set up the exception vectors */
+ vpage0 = (Vpage0*)HVECTORS;
+ memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
+ memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ /* set up the stacks for the interrupt modes */
+ setr13(PsrMfiq, m->sfiq);
+ setr13(PsrMirq, m->sirq);
+ setr13(PsrMabt, m->sabt);
+ setr13(PsrMund, m->sund);
+#ifdef HIGH_SECURITY
+ setr13(PsrMmon, m->smon);
+#endif
+ setr13(PsrMsys, m->ssys);
+
+ intcmaskall();
+ ip->control = 0;
+ ip->threshold = Prioritythreshold; /* disable threshold */
+ for (i = 0; i < Nirqs; i++)
+ ip->ilr[i] = 0<<2 | 0; /* all intrs pri 0 & to irq, not fiq */
+ irqtooearly = 0;
+ coherence();
+}
+
+void
+intrsoff(void)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ intcmaskall();
+ ip->control = Newirqagr; /* dismiss interrupt */
+ coherence();
+}
+
+/*
+ * enable an irq interrupt
+ */
+int
+irqenable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ Vctl *v;
+
+ if(irq >= nelem(vctl) || irq < 0)
+ panic("irqenable irq %d", irq);
+
+ if (irqtooearly) {
+ iprint("irqenable for %d %s called too early\n", irq, name);
+ return -1;
+ }
+ if(irqinuse(irq))
+ print("irqenable: %s: irq %d already in use, chaining\n",
+ name, irq);
+ v = malloc(sizeof(Vctl));
+ if (v == nil)
+ panic("irqenable: malloc Vctl");
+ v->f = f;
+ v->a = a;
+ v->name = malloc(strlen(name)+1);
+ if (v->name == nil)
+ panic("irqenable: malloc name");
+ strcpy(v->name, name);
+
+ lock(&vctllock);
+ v->next = vctl[irq];
+ vctl[irq] = v;
+
+ intcunmask(irq);
+ unlock(&vctllock);
+ return 0;
+}
+
+/*
+ * disable an irq interrupt
+ */
+int
+irqdisable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ Vctl **vp, *v;
+
+ if(irq >= nelem(vctl) || irq < 0)
+ panic("irqdisable irq %d", irq);
+
+ lock(&vctllock);
+ for(vp = &vctl[irq]; v = *vp; vp = &v->next)
+ if (v->f == f && v->a == a && strcmp(v->name, name) == 0){
+ print("irqdisable: remove %s\n", name);
+ *vp = v->next;
+ free(v);
+ break;
+ }
+
+ if(v == nil)
+ print("irqdisable: irq %d, name %s not enabled\n", irq, name);
+ if(vctl[irq] == nil){
+ print("irqdisable: clear icmr bit %d\n", irq);
+ intcmask(irq);
+ }
+ unlock(&vctllock);
+
+ return 0;
+}
+
+/*
+ * called by trap to handle access faults
+ */
+static void
+faultarm(Ureg *ureg, uintptr va, int user, int read)
+{
+ int n, insyscall;
+ char buf[ERRMAX];
+
+ if(up == nil) {
+ dumpregs(ureg);
+ panic("fault: nil up in faultarm, accessing %#p", va);
+ }
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+ n = fault(va, read);
+ if(n < 0){
+ if(!user){
+ dumpregs(ureg);
+ panic("fault: kernel accessing %#p", va);
+ }
+ /* don't dump registers; programs suicide all the time */
+ snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
+ read? "read": "write", va);
+ postnote(up, 1, buf, NDebug);
+ }
+ up->insyscall = insyscall;
+}
+
+/*
+ * called by trap to handle interrupts.
+ * returns true iff a clock interrupt, thus maybe reschedule.
+ */
+static int
+irq(Ureg* ureg)
+{
+ int clockintr;
+ uint irqno, handled, t, ticks = perfticks();
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+ Vctl *v;
+ static int nesting, lastirq = -1;
+
+ handled = 0;
+ irqno = ip->sir_irq & Activeirq;
+
+ if (irqno >= 37 && irqno <= 47) /* this is a clock intr? */
+ m->inclockintr++; /* yes, count nesting */
+ lastirq = irqno;
+
+ if (irqno >= nelem(vctl)) {
+ iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl));
+ ip->control = Newirqagr; /* dismiss interrupt */
+ return 0;
+ }
+
+ ++nesting;
+ for(v = vctl[irqno]; v != nil; v = v->next)
+ if (v->f) {
+ if (islo())
+ panic("trap: pl0 before trap handler for %s",
+ v->name);
+ v->f(ureg, v->a);
+ if (islo())
+ panic("trap: %s lowered pl", v->name);
+// splhi(); /* in case v->f lowered pl */
+ handled++;
+ }
+ if(!handled) {
+ iprint("unexpected interrupt: irq %d", irqno);
+ switch (irqno) {
+ case 56:
+ case 57:
+ iprint(" (I⁲C)");
+ break;
+ case 83:
+ case 86:
+ case 94:
+ iprint(" (MMC)");
+ break;
+ }
+
+ if(irqno < nelem(vctl)) {
+ intcmask(irqno);
+ iprint(", now masked");
+ }
+ iprint("\n");
+ }
+ t = perfticks();
+ ninterrupt++;
+ if(t < ticks)
+ ninterruptticks += ticks-t;
+ else
+ ninterruptticks += t-ticks;
+ ip->control = Newirqagr; /* dismiss interrupt */
+ coherence();
+
+ --nesting;
+ clockintr = m->inclockintr == 1;
+ if (irqno >= 37 && irqno <= 47)
+ m->inclockintr--;
+ return clockintr;
+}
+
+/*
+ * returns 1 if the instruction writes memory, 0 otherwise
+ */
+int
+writetomem(ulong inst)
+{
+ /* swap always write memory */
+ if((inst & 0x0FC00000) == 0x01000000)
+ return 1;
+
+ /* loads and stores are distinguished by bit 20 */
+ if(inst & (1<<20))
+ return 0;
+
+ return 1;
+}
+
+void prgpmcerrs(void);
+
+/*
+ * here on all exceptions other than syscall (SWI)
+ */
+void
+trap(Ureg *ureg)
+{
+ int clockintr, user, x, rv, rem;
+ ulong inst, fsr;
+ uintptr va;
+ char buf[ERRMAX];
+
+ splhi(); /* paranoia */
+ if(up != nil)
+ rem = ((char*)ureg)-up->kstack;
+ else
+ rem = ((char*)ureg)-((char*)m+sizeof(Mach));
+ if(rem < 1024) {
+ iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
+ rem, up, ureg, ureg->pc);
+ delay(1000);
+ dumpstack();
+ panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
+ rem, up, ureg, ureg->pc);
+ }
+
+ user = (ureg->psr & PsrMask) == PsrMusr;
+ if(user){
+ up->dbgreg = ureg;
+ cycles(&up->kentry);
+ }
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ if(ureg->type == (PsrMabt+1))
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+
+ clockintr = 0; /* if set, may call sched() before return */
+ switch(ureg->type){
+ default:
+ panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
+ ureg->psr & PsrMask);
+ break;
+ case PsrMirq:
+ ldrexvalid = 0;
+ clockintr = irq(ureg);
+ m->intr++;
+ break;
+ case PsrMabt: /* prefetch fault */
+ ldrexvalid = 0;
+ faultarm(ureg, ureg->pc, user, 1);
+ break;
+ case PsrMabt+1: /* data fault */
+ ldrexvalid = 0;
+ va = farget();
+ inst = *(ulong*)(ureg->pc);
+ /* bits 12 and 10 have to be concatenated with status */
+ x = fsrget();
+ fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
+ if (probing && !user) {
+ if (trapped++ > 0)
+ panic("trap: recursive probe %#lux", va);
+ ureg->pc += 4; /* continue at next instruction */
+ break;
+ }
+ switch(fsr){
+ default:
+ case 0xa: /* ? was under external abort */
+ panic("unknown data fault, 6b fsr %#lux", fsr);
+ break;
+ case 0x0:
+ panic("vector exception at %#lux", ureg->pc);
+ break;
+ case 0x1: /* alignment fault */
+ case 0x3: /* access flag fault (section) */
+ if(user){
+ snprint(buf, sizeof buf,
+ "sys: alignment: pc %#lux va %#p\n",
+ ureg->pc, va);
+ postnote(up, 1, buf, NDebug);
+ } else
+ panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
+ break;
+ case 0x2:
+ panic("terminal exception at %#lux", ureg->pc);
+ break;
+ case 0x4: /* icache maint fault */
+ case 0x6: /* access flag fault (page) */
+ case 0x8: /* precise external abort, non-xlat'n */
+ case 0x28:
+ case 0xc: /* l1 translation, precise ext. abort */
+ case 0x2c:
+ case 0xe: /* l2 translation, precise ext. abort */
+ case 0x2e:
+ case 0x16: /* imprecise ext. abort, non-xlt'n */
+ case 0x36:
+ panic("external abort %#lux pc %#lux addr %#p",
+ fsr, ureg->pc, va);
+ break;
+ case 0x1c: /* l1 translation, precise parity err */
+ case 0x1e: /* l2 translation, precise parity err */
+ case 0x18: /* imprecise parity or ecc err */
+ panic("translation parity error %#lux pc %#lux addr %#p",
+ fsr, ureg->pc, va);
+ break;
+ case 0x5: /* translation fault, no section entry */
+ case 0x7: /* translation fault, no page entry */
+ faultarm(ureg, va, user, !writetomem(inst));
+ break;
+ case 0x9:
+ case 0xb:
+ /* domain fault, accessing something we shouldn't */
+ if(user){
+ snprint(buf, sizeof buf,
+ "sys: access violation: pc %#lux va %#p\n",
+ ureg->pc, va);
+ postnote(up, 1, buf, NDebug);
+ } else
+ panic("kernel access violation: pc %#lux va %#p",
+ ureg->pc, va);
+ break;
+ case 0xd:
+ case 0xf:
+ /* permission error, copy on write or real permission error */
+ faultarm(ureg, va, user, !writetomem(inst));
+ break;
+ }
+ break;
+ case PsrMund: /* undefined instruction */
+ if(user){
+ /* look for floating point instructions to interpret */
+ x = spllo();
+ rv = fpiarm(ureg);
+ splx(x);
+ if(rv == 0){
+ ldrexvalid = 0;
+ snprint(buf, sizeof buf,
+ "undefined instruction: pc %#lux\n",
+ ureg->pc);
+ postnote(up, 1, buf, NDebug);
+ }
+ }else{
+ if (ureg->pc & 3) {
+ iprint("rounding fault pc %#lux down to word\n",
+ ureg->pc);
+ ureg->pc &= ~3;
+ }
+ iprint("undefined instruction: pc %#lux inst %#ux\n",
+ ureg->pc, ((u32int*)ureg->pc)[-2]);
+ panic("undefined instruction");
+ }
+ break;
+ }
+ splhi();
+
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up && up->delaysched && clockintr){
+ ldrexvalid = 0;
+ sched(); /* can cause more traps */
+ splhi();
+ }
+
+ if(user){
+ if(up->procctl || up->nnote)
+ notify(ureg);
+ kexit(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 = PTR2UINT(&fn);
+ fn(&ureg);
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+ int x;
+ uintptr l, v, i, estack;
+ char *s;
+
+ dumpregs(ureg);
+ if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+ iprint("dumpstack disabled\n");
+ return;
+ }
+ iprint("dumpstack\n");
+
+ x = 0;
+ x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
+ ureg->pc, ureg->sp, ureg->r14);
+ delay(20);
+ i = 0;
+ if(up
+ && (uintptr)&l >= (uintptr)up->kstack
+ && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
+ estack = (uintptr)up->kstack+KSTACK;
+ else if((uintptr)&l >= (uintptr)m->stack
+ && (uintptr)&l <= (uintptr)m+MACHSIZE)
+ estack = (uintptr)m+MACHSIZE;
+ else
+ return;
+ x += iprint("estackx %p\n", estack);
+
+ for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+ v = *(uintptr*)l;
+ if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){
+ x += iprint("%.8p ", v);
+ delay(20);
+ i++;
+ }
+ if(i == 8){
+ i = 0;
+ x += iprint("\n");
+ delay(20);
+ }
+ }
+ if(i)
+ iprint("\n");
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(dumpstackwithureg);
+}
+
+/*
+ * dump system control coprocessor registers
+ */
+static void
+dumpscr(void)
+{
+ iprint("0:\t%#8.8ux id\n", cpidget());
+ iprint("\t%8.8#ux ct\n", cpctget());
+ iprint("1:\t%#8.8ux control\n", controlget());
+ iprint("2:\t%#8.8ux ttb\n", ttbget());
+ iprint("3:\t%#8.8ux dac\n", dacget());
+ iprint("4:\t(reserved)\n");
+ iprint("5:\t%#8.8ux fsr\n", fsrget());
+ iprint("6:\t%#8.8ux far\n", farget());
+ iprint("7:\twrite-only cache\n");
+ iprint("8:\twrite-only tlb\n");
+ iprint("13:\t%#8.8ux pid\n", pidget());
+ delay(10);
+}
+
+/*
+ * dump general registers
+ */
+static void
+dumpgpr(Ureg* ureg)
+{
+ if(up != nil)
+ iprint("cpu%d: registers for %s %lud\n",
+ m->machno, up->text, up->pid);
+ else
+ iprint("cpu%d: registers for kernel\n", m->machno);
+
+ delay(20);
+ iprint("%#8.8lux\tr0\n", ureg->r0);
+ iprint("%#8.8lux\tr1\n", ureg->r1);
+ iprint("%#8.8lux\tr2\n", ureg->r2);
+ delay(20);
+ iprint("%#8.8lux\tr3\n", ureg->r3);
+ iprint("%#8.8lux\tr4\n", ureg->r4);
+ iprint("%#8.8lux\tr5\n", ureg->r5);
+ delay(20);
+ iprint("%#8.8lux\tr6\n", ureg->r6);
+ iprint("%#8.8lux\tr7\n", ureg->r7);
+ iprint("%#8.8lux\tr8\n", ureg->r8);
+ delay(20);
+ iprint("%#8.8lux\tr9 (up)\n", ureg->r9);
+ iprint("%#8.8lux\tr10 (m)\n", ureg->r10);
+ iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11);
+ iprint("%#8.8lux\tr12 (SB)\n", ureg->r12);
+ delay(20);
+ iprint("%#8.8lux\tr13 (sp)\n", ureg->r13);
+ iprint("%#8.8lux\tr14 (link)\n", ureg->r14);
+ iprint("%#8.8lux\tr15 (pc)\n", ureg->pc);
+ delay(20);
+ iprint("%10.10lud\ttype\n", ureg->type);
+ iprint("%#8.8lux\tpsr\n", ureg->psr);
+ delay(20);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ dumpgpr(ureg);
+ dumpscr();
+}
+
+vlong
+probeaddr(uintptr addr)
+{
+ vlong v;
+ static Lock fltlck;
+
+ ilock(&fltlck);
+ trapped = 0;
+ probing = 1;
+ coherence();
+
+ v = *(ulong *)addr; /* this may cause a fault */
+ USED(probing);
+ coherence();
+
+ probing = 0;
+ coherence();
+ if (trapped)
+ v = -1;
+ iunlock(&fltlck);
+ return v;
+}