diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /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-x | sys/src/9/omap/trap.c | 769 |
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(" (IC)"); + 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; +} |