123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- /*
- * sheevaplug traps, exceptions, interrupts, system calls.
- */
- #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"
- #include "arm.h"
- enum {
- Ntimevec = 20, /* # of time buckets for each intr */
- Nvecs = 256,
- };
- extern int notify(Ureg*);
- extern int ldrexvalid;
- 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[32];
- uvlong ninterrupt;
- uvlong ninterruptticks;
- ulong intrtimes[Nvecs][Ntimevec];
- typedef struct Handler Handler;
- struct Handler {
- void (*r)(Ureg*, void*);
- void *a;
- char name[KNAMELEN];
- };
- static Handler irqlo[32];
- static Handler irqhi[32];
- static Handler irqbridge[32];
- static Lock irqlock;
- static int probing, trapped;
- typedef struct Irq Irq;
- struct Irq {
- ulong *irq;
- ulong *irqmask;
- Handler *irqvec;
- int nirqvec;
- char *name;
- };
- /* irq and irqmask are filled in by trapinit */
- static Irq irqs[] = {
- [Irqlo] {nil, nil, irqlo, nelem(irqlo), "lo"},
- [Irqhi] {nil, nil, irqhi, nelem(irqhi), "hi"},
- [Irqbridge] {nil, nil, irqbridge, nelem(irqbridge), "bridge"},
- };
- /*
- * keep histogram of interrupt service times
- */
- void
- intrtime(Mach*, int vno)
- {
- ulong diff, x;
- if (m == nil)
- return;
- x = perfticks();
- diff = x - m->perf.intrts;
- m->perf.intrts = x;
- m->perf.inintr += diff;
- if(up == nil && m->perf.inidle > diff)
- m->perf.inidle -= diff;
- if (m->cpuhz == 0) /* not set yet? */
- return;
- diff /= (m->cpuhz/1000000)*100; /* quantum = 100µsec */
- if(diff >= Ntimevec)
- diff = Ntimevec-1;
- assert(vno >= 0 && vno < Nvecs);
- intrtimes[vno][diff]++;
- }
- void
- intrfmtcounts(char *s, char *se)
- {
- USED(s, se);
- }
- static void
- dumpcounts(void)
- {
- }
- void
- intrclear(int sort, int v)
- {
- *irqs[sort].irq = ~(1 << v);
- }
- void
- intrmask(int sort, int v)
- {
- *irqs[sort].irqmask &= ~(1 << v);
- }
- void
- intrunmask(int sort, int v)
- {
- *irqs[sort].irqmask |= 1 << v;
- }
- static void
- maskallints(void)
- {
- CpucsReg *cpu = (CpucsReg *)soc.cpu;
- IntrReg *intr;
- /* no fiq or ep in use */
- intr = (IntrReg *)soc.intr;
- intr->lo.irqmask = 0;
- intr->hi.irqmask = 0;
- cpu->irqmask = 0;
- coherence();
- }
- void
- intrset(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
- {
- if(h->r != nil) {
- // iprint("duplicate irq: %s (%#p)\n", h->name, h->r);
- return;
- }
- h->r = f;
- h->a = a;
- strncpy(h->name, name, KNAMELEN-1);
- h->name[KNAMELEN-1] = 0;
- }
- void
- intrunset(Handler *h)
- {
- h->r = nil;
- h->a = nil;
- h->name[0] = 0;
- }
- void
- intrdel(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
- {
- if(h->r != f || h->a != a || strcmp(h->name, name) != 0)
- return;
- intrunset(h);
- }
- void
- intrenable(int sort, int v, void (*f)(Ureg*, void*), void *a, char *name)
- {
- //iprint("enabling intr %d vec %d for %s\n", sort, v, name);
- ilock(&irqlock);
- intrset(&irqs[sort].irqvec[v], f, a, name);
- intrunmask(sort, v);
- iunlock(&irqlock);
- }
- void
- intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
- {
- ilock(&irqlock);
- intrdel(&irqs[sort].irqvec[v], f, a, name);
- intrmask(sort, v);
- iunlock(&irqlock);
- }
- /*
- * called by trap to handle interrupts
- */
- static void
- intrs(Ureg *ur, int sort)
- {
- int i, s;
- ulong ibits;
- Handler *h;
- Irq irq;
- assert(sort >= 0 && sort < nelem(irqs));
- irq = irqs[sort];
- ibits = *irq.irq;
- ibits &= *irq.irqmask;
- for(i = 0; i < irq.nirqvec && ibits; i++)
- if(ibits & (1<<i)){
- h = &irq.irqvec[i];
- if(h->r != nil){
- h->r(ur, h->a);
- splhi();
- intrtime(m, sort*32 + i);
- if (sort == Irqbridge && i == IRQcputimer0)
- m->inclockintr = 1;
- ibits &= ~(1<<i);
- }
- }
- if(ibits != 0) {
- iprint("spurious irq%s interrupt: %8.8lux\n", irq.name, ibits);
- s = splfhi();
- *irq.irq &= ibits;
- splx(s);
- }
- }
- void
- intrhi(Ureg *ureg, void*)
- {
- intrs(ureg, Irqhi);
- }
- void
- intrbridge(Ureg *ureg, void*)
- {
- intrs(ureg, Irqbridge);
- intrclear(Irqlo, IRQ0bridge);
- }
- void
- trapinit(void)
- {
- int i;
- CpucsReg *cpu;
- IntrReg *intr;
- Vectorpage *page0 = (Vectorpage*)HVECTORS;
- intr = (IntrReg *)soc.intr;
- cpu = (CpucsReg *)soc.cpu;
- irqs[Irqlo].irq = &intr->lo.irq;
- irqs[Irqlo].irqmask = &intr->lo.irqmask;
- irqs[Irqhi].irq = &intr->hi.irq;
- irqs[Irqhi].irqmask = &intr->hi.irqmask;
- irqs[Irqbridge].irq = &cpu->irq;
- irqs[Irqbridge].irqmask = &cpu->irqmask;
- coherence();
- 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));
- memmove(page0->vectors, vectors, sizeof page0->vectors);
- memmove(page0->vtable, vtable, sizeof page0->vtable);
- cacheuwbinv();
- l2cacheuwbinv();
- cpu->cpucfg &= ~Cfgvecinithi;
- for(i = 0; i < nelem(irqlo); i++)
- intrunset(&irqlo[i]);
- for(i = 0; i < nelem(irqhi); i++)
- intrunset(&irqhi[i]);
- for(i = 0; i < nelem(irqbridge); i++)
- intrunset(&irqbridge[i]);
- /* disable all interrupts */
- intr->lo.fiqmask = intr->hi.fiqmask = 0;
- intr->lo.irqmask = intr->hi.irqmask = 0;
- intr->lo.epmask = intr->hi.epmask = 0;
- cpu->irqmask = 0;
- coherence();
- /* clear interrupts */
- intr->lo.irq = intr->hi.irq = ~0;
- cpu->irq = ~0;
- coherence();
- intrenable(Irqlo, IRQ0hisum, intrhi, nil, "hi");
- intrenable(Irqlo, IRQ0bridge, intrbridge, nil, "bridge");
- /* enable watchdog & access-error interrupts */
- cpu->irqmask |= 1 << IRQcputimerwd | 1 << IRQaccesserr;
- coherence();
- }
- static char *trapnames[PsrMask+1] = {
- [ PsrMusr ] "user mode",
- [ PsrMfiq ] "fiq interrupt",
- [ PsrMirq ] "irq 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 = "unknown trap number in psr";
- return s;
- }
- /*
- * called by trap to handle access faults
- */
- static void
- faultarm(Ureg *ureg, uintptr va, int user, int read)
- {
- int n, insyscall;
- char buf[ERRMAX];
- static int cnt, lastpid;
- static ulong lastva;
- if(up == nil) {
- dumpregs(ureg);
- panic("fault: nil up in faultarm, accessing %#p", va);
- }
- insyscall = up->insyscall;
- up->insyscall = 1;
- /* this is quite helpful during mmu and cache debugging */
- if(va == lastva && up->pid == lastpid) {
- ++cnt;
- if (cnt >= 2)
- /* fault() isn't fixing the underlying cause */
- panic("fault: %d consecutive faults for va %#lux",
- cnt+1, va);
- } else {
- cnt = 0;
- lastva = va;
- lastpid = up->pid;
- }
- 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;
- }
- /*
- * 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
- trap(Ureg *ureg)
- {
- int user, x, rv, rem;
- ulong inst;
- u32int fsr;
- uintptr va;
- char buf[ERRMAX];
- if(up != nil)
- rem = (char*)ureg - up->kstack;
- else
- rem = (char*)ureg - ((char*)m + sizeof(Mach));
- if(rem < 256) {
- dumpstack();
- panic("trap %d bytes remaining, 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);
- }
- if(ureg->type == PsrMabt+1)
- ureg->pc -= 8;
- else
- ureg->pc -= 4;
- m->inclockintr = 0;
- switch(ureg->type) {
- default:
- panic("unknown trap %ld", ureg->type);
- break;
- case PsrMirq:
- ldrexvalid = 0;
- // splflo(); /* allow fast interrupts */
- intrs(ureg, Irqlo);
- 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);
- fsr = fsrget() & 0xf;
- if (probing && !user) {
- if (trapped++ > 0)
- panic("trap: recursive probe %#lux", va);
- ureg->pc += 4; /* continue at next instruction */
- break;
- }
- switch(fsr){
- case 0x0:
- panic("vector exception at %#lux", ureg->pc);
- break;
- case 0x1:
- case 0x3:
- 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:
- case 0x6:
- case 0x8:
- case 0xa:
- case 0xc:
- case 0xe:
- panic("external abort %#ux pc %#lux addr %#px",
- 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",
- ureg->pc);
- postnote(up, 1, buf, NDebug);
- }
- }else{
- 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 && m->inclockintr){
- ldrexvalid = 0;
- sched();
- splhi();
- }
- if(user){
- if(up->procctl || up->nnote)
- notify(ureg);
- kexit(ureg);
- }
- }
- int
- isvalidaddr(void *v)
- {
- return (uintptr)v >= KZERO;
- }
- 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(isvalidaddr(v)){
- iprint(" %.8lux", *v++);
- l++;
- }else{
- iprint(" invalid");
- break;
- }
- }
- iprint("\n");
- }
- static void
- dumpstackwithureg(Ureg *ureg)
- {
- uintptr l, i, v, estack;
- u32int *p;
- iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
- ureg->pc, ureg->sp, ureg->r14);
- delay(2000);
- i = 0;
- if(up != nil && (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{
- if(up != nil)
- iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
- else
- iprint("&m %#p &l %#p\n", m, &l);
- return;
- }
- for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
- v = *(uintptr*)l;
- if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
- v -= sizeof(u32int); /* back up an instr */
- p = (u32int*)v;
- if((*p & 0x0f000000) == 0x0b000000){ /* BL instr? */
- iprint("%#8.8lux=%#8.8lux ", l, v);
- i++;
- }
- }
- if(i == 4){
- i = 0;
- iprint("\n");
- }
- }
- if(i)
- iprint("\n");
- }
- /*
- * 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);
- }
- void
- dumpstack(void)
- {
- callwithureg(dumpstackwithureg);
- }
- void
- dumpregs(Ureg* ureg)
- {
- int s;
- if (ureg == nil) {
- iprint("trap: no user process\n");
- return;
- }
- s = splhi();
- iprint("trap: %s", trapname(ureg->type));
- if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
- iprint(" in %s", trapname(ureg->psr));
- iprint("\n");
- iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
- ureg->psr, ureg->type, ureg->pc, ureg->link);
- iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
- ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
- iprint("R9 %8.8lux R8 %8.8lux R7 %8.8lux R6 %8.8lux R5 %8.8lux\n",
- ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
- iprint("R4 %8.8lux R3 %8.8lux R2 %8.8lux R1 %8.8lux R0 %8.8lux\n",
- ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
- iprint("stack is at %#p\n", ureg);
- iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
- if(up)
- iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
- else
- iprint("kernel stack: %8.8lux-%8.8lux\n",
- (ulong)(m+1), (ulong)m+BY2PG-4);
- dumplongs("stack", (ulong *)(ureg + 1), 16);
- delay(2000);
- dumpstack();
- splx(s);
- }
- void
- idlehands(void)
- {
- extern void _idlehands(void);
- _idlehands();
- }
- 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;
- }
|