/*
* 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));
break;
}
break;
case PsrMund: /* undefined instruction */
if(user){
if(seg(up, ureg->pc, 0) != nil &&
(*(u32int*)ureg->pc & ~(0xF<<28)) == 0x01200070)
postnote(up, 1, "sys: breakpoint", NDebug);
else{
/* 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();
}
/* assumes that addr is already mapped suitable (e.g., by mmuidmap) */
vlong
probeaddr(uintptr addr)
{
vlong v;
static Lock fltlck;
ilock(&fltlck);
trapped = 0;
probing = 1;
coherence();
v = *(ulong *)addr; /* this may cause a fault (okay under ilock) */
USED(probing);
coherence();
probing = 0;
coherence();
if (trapped)
v = -1;
iunlock(&fltlck);
return v;
}
|