/* * arm mpcore generic interrupt controller (gic) v1 * traps, exceptions, interrupts, system calls. * * there are two pieces: the interrupt distributor and the cpu interface. * * memset or memmove on any of the distributor registers generates an * exception like this one: * panic: external abort 0x28 pc 0xc048bf68 addr 0x50041800 * * we use l1 and l2 cache ops to force vectors to be visible everywhere. * * apparently irqs 0—15 (SGIs) are always enabled. */ #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" #define ISSGI(irq) ((uint)(irq) < Nsgi) enum { Debug = 0, Nvec = 8, /* # of vectors at start of lexception.s */ Bi2long = BI2BY * sizeof(long), Nirqs = 1024, Nsgi = 16, /* software-generated (inter-processor) intrs */ Nppi = 32, /* sgis + other private peripheral intrs */ }; typedef struct Intrcpuregs Intrcpuregs; typedef struct Intrdistregs Intrdistregs; /* * almost this entire register set is buggered. * the distributor is supposed to be per-system, not per-cpu, * yet some registers are banked per-cpu, as marked. */ struct Intrdistregs { /* distributor */ ulong ctl; ulong ctlrtype; ulong distid; uchar _pad0[0x80 - 0xc]; /* botch: *[0] are banked per-cpu from here */ /* bit maps */ ulong grp[32]; /* in group 1 (non-secure) */ ulong setena[32]; /* forward to cpu interfaces */ ulong clrena[32]; ulong setpend[32]; ulong clrpend[32]; ulong setact[32]; /* active? */ ulong clract[32]; /* botch: *[0] are banked per-cpu until here */ uchar pri[1020]; /* botch: pri[0] — pri[7] are banked per-cpu */ ulong _rsrvd1; /* botch: targ[0] through targ[7] are banked per-cpu and RO */ uchar targ[1020]; /* byte bit maps: cpu targets indexed by intr */ ulong _rsrvd2; /* botch: cfg[1] is banked per-cpu */ ulong cfg[64]; /* bit pairs: edge? 1-N? */ ulong _pad1[64]; ulong nsac[64]; /* bit pairs (v2 only) */ /* software-generated intrs (a.k.a. sgi) */ ulong swgen; /* intr targets */ uchar _pad2[0xf10 - 0xf04]; uchar clrsgipend[16]; /* bit map (v2 only) */ uchar setsgipend[16]; /* bit map (v2 only) */ }; enum { /* ctl bits */ Forw2cpuif = 1, /* ctlrtype bits */ Cpunoshft = 5, Cpunomask = MASK(3), Intrlines = MASK(5), /* cfg bits */ Level = 0<<1, Edge = 1<<1, /* edge-, not level-sensitive */ Toall = 0<<0, To1 = 1<<0, /* vs. to all */ /* swgen bits */ Totargets = 0, Tonotme = 1<<24, Tome = 2<<24, }; /* each cpu sees its own registers at the same base address (soc.intr) */ struct Intrcpuregs { ulong ctl; ulong primask; ulong binpt; /* group pri vs subpri split */ ulong ack; ulong end; ulong runpri; ulong hipripend; /* aliased regs (secure, for group 1) */ ulong alibinpt; ulong aliack; /* (v2 only) */ ulong aliend; /* (v2 only) */ ulong alihipripend; /* (v2 only) */ uchar _pad0[0xd0 - 0x2c]; ulong actpri[4]; /* (v2 only) */ ulong nsactpri[4]; /* (v2 only) */ uchar _pad0[0xfc - 0xf0]; ulong ifid; /* ro */ uchar _pad0[0x1000 - 0x100]; ulong deact; /* wo (v2 only) */ }; enum { /* ctl bits */ Enable = 1, Eoinodeact = 1<<9, /* (v2 only) */ /* (ali) ack/end/hipriend/deact bits */ Intrmask = MASK(10), Cpuidshift = 10, Cpuidmask = MASK(3), /* ifid bits */ Archversshift = 16, Archversmask = MASK(4), }; 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; enum { Ntimevec = 20 /* number of time buckets for each intr */ }; ulong intrtimes[Nirqs][Ntimevec]; uvlong ninterrupt; uvlong ninterruptticks; int irqtooearly = 1; static ulong shadena[32]; /* copy of enable bits, saved by intcmaskall */ static Lock distlock, nintrlock; extern int notify(Ureg*); static void dumpstackwithureg(Ureg *ureg); void printrs(int base, ulong word) { int bit; for (bit = 0; word; bit++, word >>= 1) if (word & 1) iprint(" %d", base + bit); } void dumpintrs(char *what, ulong *bits) { int i, first, some; ulong word; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; first = 1; some = 0; USED(idp); for (i = 0; i < nelem(idp->setpend); i++) { word = bits[i]; if (word) { if (first) { first = 0; iprint("%s", what); } some = 1; printrs(i * Bi2long, word); } } if (!some) iprint("%s none", what); iprint("\n"); } void dumpintrpend(void) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; iprint("\ncpu%d gic regs:\n", m->machno); dumpintrs("group 1", idp->grp); dumpintrs("enabled", idp->setena); dumpintrs("pending", idp->setpend); dumpintrs("active ", idp->setact); } /* * keep histogram of interrupt service times */ void intrtime(Mach*, int vno) { ulong diff; ulong x; 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->cpumhz == 0) return; /* don't divide by zero */ diff /= m->cpumhz*100; /* quantum = 100µsec */ if(diff >= Ntimevec) diff = Ntimevec-1; if ((uint)vno >= Nirqs) vno = Nirqs-1; intrtimes[vno][diff]++; } static ulong intack(Intrcpuregs *icp) { return icp->ack & Intrmask; } static void intdismiss(Intrcpuregs *icp, ulong ack) { icp->end = ack; coherence(); } static int irqinuse(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; return idp->setena[irq / Bi2long] & (1 << (irq % Bi2long)); } void intcunmask(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->setena[irq / Bi2long] = 1 << (irq % Bi2long); iunlock(&distlock); } void intcmask(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->clrena[irq / Bi2long] = 1 << (irq % Bi2long); iunlock(&distlock); } static void intcmaskall(Intrdistregs *idp) /* mask all intrs for all cpus */ { int i; for (i = 0; i < nelem(idp->setena); i++) shadena[i] = idp->setena[i]; for (i = 0; i < nelem(idp->clrena); i++) idp->clrena[i] = ~0; coherence(); } static void intcunmaskall(Intrdistregs *idp) /* unused */ { int i; for (i = 0; i < nelem(idp->setena); i++) idp->setena[i] = shadena[i]; coherence(); } static ulong permintrs(Intrdistregs *idp, int base, int r) { ulong perms; idp->clrena[r] = ~0; /* disable all */ coherence(); perms = idp->clrena[r]; if (perms) { iprint("perm intrs:"); printrs(base, perms); iprint("\n"); } return perms; } static void intrcfg(Intrdistregs *idp) { int i, cpumask; ulong pat; /* set up all interrupts as level-sensitive, to one cpu (0) */ pat = 0; for (i = 0; i < Bi2long; i += 2) pat |= (Level | To1) << i; if (m->machno == 0) { /* system-wide & cpu0 cfg */ for (i = 0; i < nelem(idp->grp); i++) idp->grp[i] = 0; /* secure */ for (i = 0; i < nelem(idp->pri); i++) idp->pri[i] = 0; /* highest priority */ /* set up all interrupts as level-sensitive, to one cpu (0) */ for (i = 0; i < nelem(idp->cfg); i++) idp->cfg[i] = pat; /* first Nppi are read-only for SGIs and PPIs */ cpumask = 1<<0; /* just cpu 0 */ navailcpus = getncpus(); for (i = Nppi; i < sizeof idp->targ; i++) idp->targ[i] = cpumask; coherence(); intcmaskall(idp); for (i = 0; i < nelem(idp->clrena); i++) { // permintrs(idp, i * Bi2long, i); idp->clrpend[i] = idp->clract[i] = idp->clrena[i] = ~0; } } else { /* per-cpu config */ idp->grp[0] = 0; /* secure */ for (i = 0; i < 8; i++) idp->pri[i] = 0; /* highest priority */ /* idp->targ[0 through Nppi-1] are supposed to be read-only */ for (i = 0; i < Nppi; i++) idp->targ[i] = 1<machno; idp->cfg[1] = pat; coherence(); // permintrs(idp, i * Bi2long, i); idp->clrpend[0] = idp->clract[0] = idp->clrena[0] = ~0; /* on cpu1, irq Extpmuirq (118) is always pending here */ } coherence(); } void intrto(int cpu, int irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; /* first Nppi are read-only for SGIs and the like */ ilock(&distlock); idp->targ[irq] = 1 << cpu; iunlock(&distlock); } void intrsto(int cpu) /* unused */ { int i; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; /* first Nppi are read-only for SGIs and the like */ for (i = Nppi; i < sizeof idp->targ; i++) intrto(cpu, i); USED(idp); } void intrcpu(int cpu) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->swgen = Totargets | 1 << (cpu + 16) | m->machno; iunlock(&distlock); } /* * set up for exceptions */ void trapinit(void) { int s; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; Intrcpuregs *icp = (Intrcpuregs *)soc.intr; Vpage0 *vpage0; enum { Vecsize = sizeof vpage0->vectors + sizeof vpage0->vtable, }; /* * set up the exception vectors, high and low. * * we can't use cache ops on HVECTORS address, since they * work on virtual addresses, and only those that have a * physical address == PADDR(virtual). */ if (m->machno == 0) { vpage0 = (Vpage0*)HVECTORS; memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); vpage0 = (Vpage0*)KADDR(0); memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); allcache->wbse(vpage0, Vecsize); cacheiinv(); } /* * set up the stack pointers for the exception modes for this cpu. * they point to small `save areas' in Mach, not actual stacks. */ s = splhi(); /* make these modes ignore intrs too */ setr13(PsrMfiq, m->sfiq); setr13(PsrMirq, m->sirq); setr13(PsrMmon, m->smon); setr13(PsrMabt, m->sabt); setr13(PsrMund, m->sund); setr13(PsrMsys, m->ssys); splx(s); assert((idp->distid & MASK(12)) == 0x43b); /* made by arm */ assert((icp->ifid & MASK(12)) == 0x43b); /* made by arm */ ilock(&distlock); idp->ctl = 0; icp->ctl = 0; coherence(); intrcfg(idp); /* some per-cpu cfg here */ icp->ctl = Enable; icp->primask = (uchar)~0; /* let all priorities through */ coherence(); idp->ctl = Forw2cpuif; iunlock(&distlock); } void intrsoff(void) { ilock(&distlock); intcmaskall((Intrdistregs *)soc.intrdist); iunlock(&distlock); } void intrcpushutdown(void) { Intrcpuregs *icp = (Intrcpuregs *)soc.intr; icp->ctl = 0; icp->primask = 0; /* let no priorities through */ coherence(); } /* called from cpu0 after other cpus are shutdown */ void intrshutdown(void) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; intrsoff(); idp->ctl = 0; intrcpushutdown(); } /* * enable an irq interrupt * note that the same private interrupt may be enabled on multiple cpus */ int irqenable(uint irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl *v; if(irq >= nelem(vctl)) panic("irqenable irq %d", irq); if (irqtooearly) { iprint("irqenable for %d %s called too early\n", irq, name); return -1; } /* * if in use, could be a private interrupt on a secondary cpu, * so don't add anything to the vector chain. irqs should * otherwise be one-to-one with devices. */ if(!ISSGI(irq) && irqinuse(irq)) { lock(&vctllock); if (vctl[irq] == nil) { dumpintrpend(); panic("non-sgi irq %d in use yet no Vctl allocated", irq); } unlock(&vctllock); } /* could be 1st use of this irq or could be an sgi (always in use) */ else if (vctl[irq] == nil) { 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); if (vctl[irq] != nil) { /* allocation race: someone else did it first */ free(v->name); free(v); } else { v->next = vctl[irq]; vctl[irq] = v; } unlock(&vctllock); } intcunmask(irq); return 0; } /* * disable an irq interrupt */ int irqdisable(uint irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl **vp, *v; if(irq >= nelem(vctl)) 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){ if(Debug) print("irqdisable: remove %s\n", name); *vp = v->next; free(v->name); free(v); break; } if(v == nil) print("irqdisable: irq %d, name %s not enabled\n", irq, name); if(vctl[irq] == nil){ if(Debug) 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; if(up == nil) { dumpstackwithureg(ureg); panic("faultarm: cpu%d: nil up, %sing %#p at %#p", m->machno, (read? "read": "writ"), va, ureg->pc); } insyscall = up->insyscall; up->insyscall = 1; n = fault(va, read); /* goes spllo */ splhi(); if(n < 0){ char buf[ERRMAX]; if(!user){ dumpstackwithureg(ureg); panic("fault: cpu%d: kernel %sing %#p at %#p", m->machno, read? "read": "writ", va, ureg->pc); } /* 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, ack; uint irqno, handled, t, ticks; Intrcpuregs *icp = (Intrcpuregs *)soc.intr; Vctl *v; ticks = perfticks(); handled = 0; ack = intack(icp); irqno = ack & Intrmask; if (irqno >= nelem(vctl)) { iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl)); intdismiss(icp, ack); return 0; } if (irqno == Loctmrirq) /* this is a clock intr? */ m->inclockintr++; /* yes, count nesting */ if(m->machno && m->inclockintr > 1) { iprint("cpu%d: nested clock intrs\n", m->machno); m->inclockintr--; intdismiss(icp, ack); return 0; } 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) if (irqno >= 1022) iprint("cpu%d: ignoring spurious interrupt\n", m->machno); else { intcmask(irqno); iprint("cpu%d: unexpected interrupt %d, now masked\n", m->machno, irqno); } t = perfticks(); if (0) { /* left over from another port? */ ilock(&nintrlock); ninterrupt++; if(t < ticks) ninterruptticks += ticks-t; else ninterruptticks += t-ticks; iunlock(&nintrlock); } USED(t, ticks); clockintr = m->inclockintr == 1; if (irqno == Loctmrirq) m->inclockintr--; intdismiss(icp, ack); intrtime(m, irqno); 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; } static void datafault(Ureg *ureg, int user) { int x; ulong inst, fsr; uintptr va; va = farget(); if (m->probing && !user) { if (m->trapped++ > 0) { dumpstackwithureg(ureg); panic("trap: recursive probe %#lux", va); } ureg->pc += 4; /* continue after faulting instr'n */ return; } 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; 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){ char buf[ERRMAX]; snprint(buf, sizeof buf, "sys: alignment: pc %#lux va %#p\n", ureg->pc, va); postnote(up, 1, buf, NDebug); } else { dumpstackwithureg(ureg); 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 0x16: /* imprecise ext. abort, non-xlt'n */ case 0x36: panic("external non-translation abort %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0xc: /* l1 translation, precise ext. abort */ case 0x2c: case 0xe: /* l2 translation, precise ext. abort */ case 0x2e: panic("external translation 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){ char buf[ERRMAX]; 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; } } /* * here on all exceptions other than syscall (SWI) and reset */ void trap(Ureg *ureg) { int clockintr, user, rem; uintptr va, ifar, ifsr; 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 m %#p cpu%d at pc %#lux\n", rem, up, ureg, m, m->machno, ureg->pc); dumpstackwithureg(ureg); panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux", rem, up, ureg, ureg->pc); } m->perf.intrts = perfticks(); 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: m->intr++; clockintr = irq(ureg); if(0 && up && !clockintr) preempted(); /* this causes spurious suicides */ break; case PsrMabt: /* prefetch (instruction) fault */ va = ureg->pc; ifsr = cprdsc(0, CpFSR, 0, CpIFSR); ifsr = (ifsr>>7) & 0x8 | ifsr & 0x7; switch(ifsr){ case 0x02: /* instruction debug event (BKPT) */ if(user) postnote(up, 1, "sys: breakpoint", NDebug); else{ iprint("kernel bkpt: pc %#lux inst %#ux\n", va, *(u32int*)va); panic("kernel bkpt"); } break; default: ifar = cprdsc(0, CpFAR, 0, CpIFAR); if (va != ifar) iprint("trap: cpu%d: i-fault va %#p != ifar %#p\n", m->machno, va, ifar); faultarm(ureg, va, user, 1); break; } break; case PsrMabt+1: /* data fault */ datafault(ureg, user); break; case PsrMund: /* undefined instruction */ if(!user) { if (ureg->pc & 3) { iprint("rounding fault pc %#lux down to word\n", ureg->pc); ureg->pc &= ~3; } if (Debug) iprint("mathemu: cpu%d fpon %d instr %#8.8lux at %#p\n", m->machno, m->fpon, *(ulong *)ureg->pc, ureg->pc); dumpstackwithureg(ureg); panic("cpu%d: undefined instruction: pc %#lux inst %#ux", m->machno, ureg->pc, ((u32int*)ureg->pc)[0]); } else if(seg(up, ureg->pc, 0) != nil && *(u32int*)ureg->pc == 0xD1200070) postnote(up, 1, "sys: breakpoint", NDebug); else if(fpuemu(ureg) == 0){ /* didn't find any FP instrs? */ char buf[ERRMAX]; snprint(buf, sizeof buf, "undefined instruction: pc %#lux instr %#8.8lux\n", ureg->pc, *(ulong *)ureg->pc); postnote(up, 1, buf, NDebug); } break; } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr){ 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; memset(&ureg, 0, sizeof 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; } delay(1000); 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"); delay(3000); } 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(500); } void dumpregs(Ureg* ureg) { dumpgpr(ureg); dumpscr(); } vlong probeaddr(uintptr addr) { vlong v; ilock(&m->probelock); m->trapped = 0; m->probing = 1; coherence(); v = *(ulong *)addr; /* this may cause a fault */ coherence(); m->probing = 0; if (m->trapped) v = -1; iunlock(&m->probelock); return v; }