/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 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 { Debug = 0, 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 */voidintrtime(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]++;} voidintrfmtcounts(char *s, char *se){ USED(s, se);} static voiddumpcounts(void){} voidintrclear(int sort, int v){ *irqs[sort].irq = ~(1 << v);} voidintrmask(int sort, int v){ *irqs[sort].irqmask &= ~(1 << v);} voidintrunmask(int sort, int v){ *irqs[sort].irqmask |= 1 << v;} static voidmaskallints(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();} voidintrset(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;} voidintrunset(Handler *h){ h->r = nil; h->a = nil; h->name[0] = 0;} voidintrdel(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);} voidintrenable(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);} voidintrdisable(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 voidintrs(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; *irq.irqmask &= ~ibits; splx(s); }} voidintrhi(Ureg *ureg, void*){ intrs(ureg, Irqhi);} voidintrbridge(Ureg *ureg, void*){ intrs(ureg, Irqbridge); intrclear(Irqlo, IRQ0bridge);} voidtrapinit(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;} /* this is quite helpful during mmu and cache debugging */static voidckfaultstuck(uintptr va){ static int cnt, lastpid; static uintptr lastva; if (va == lastva && up->pid == lastpid) { ++cnt; if (cnt >= 2) /* fault() isn't fixing the underlying cause */ panic("fault: %d consecutive faults for va %#p", cnt+1, va); } else { cnt = 0; lastva = va; lastpid = up->pid; }} /* * called by trap to handle access faults */static voidfaultarm(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; if (Debug) ckfaultstuck(va); 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 */intwritetomem(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;} voidtrap(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); if(up->nnote == 0 && (*(u32int*)ureg->pc & ~(0xF<<28)) == 0x01200070) postnote(up, 1, "sys: breakpoint", NDebug); 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));Lines 1–500 of 701. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 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 { Debug = 0, 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; *irq.irqmask &= ~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; } /* this is quite helpful during mmu and cache debugging */ static void ckfaultstuck(uintptr va) { static int cnt, lastpid; static uintptr lastva; if (va == lastva && up->pid == lastpid) { ++cnt; if (cnt >= 2) /* fault() isn't fixing the underlying cause */ panic("fault: %d consecutive faults for va %#p", cnt+1, va); } else { cnt = 0; lastva = va; lastpid = up->pid; } } /* * 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; if (Debug) ckfaultstuck(va); 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); if(up->nnote == 0 && (*(u32int*)ureg->pc & ~(0xF<<28)) == 0x01200070) postnote(up, 1, "sys: breakpoint", NDebug); 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));