ref: 497fd3205393e98d75f6b0555d2cbaccf842b4b9
dir: /trap.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "../port/systab.h"
#include "tos.h"
#include "ureg.h"
void
callwithureg(void (*f) (Ureg *))
{
Ureg u;
u.pc = getcallerpc(&f);
u.sp = (uintptr) &f - 4;
f(&u);
}
void
dumpstackureg(Ureg *ureg)
{
uintptr l, v, i, estack;
int x;
x = 0;
x += iprint("ktrace /arm/9n900 %#.8lux %#.8lux %#.8lux <<EOF\n",
ureg->pc, ureg->sp, ureg->r14);
i = 0;
if(up)
estack = (uintptr)up;
else
estack = (uintptr)m+MACHSZ;
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=%.8p ", l, v);
i++;
}
if(i == 4) {
i = 0;
x += iprint("\n");
}
}
if(i)
iprint("\n");
iprint("EOF\n");
}
void
dumpstack(void)
{
callwithureg(dumpstackureg);
}
void
dumpureg(Ureg *ureg)
{
if(up)
iprint("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid);
else
iprint("cpu%d: registers for kernel\n", m->machno);
iprint("r0 %#.8lux\tr1 %#.8lux\tr2 %#.8lux\tr3 %#.8lux\n", ureg->r0, ureg->r1, ureg->r2, ureg->r3);
iprint("r4 %#.8lux\tr5 %#.8lux\tr6 %#.8lux\tr7 %#.8lux\n", ureg->r4, ureg->r5, ureg->r6, ureg->r7);
iprint("r8 %#.8lux\tr9 %#.8lux\tr10 %#.8lux\tr11 %#.8lux\n", ureg->r8, ureg->r9, ureg->r10, ureg->r11);
iprint("r12 %#.8lux\tr13 %#.8lux\tr14 %#.8lux\tr15 %#.8lux\n", ureg->r12, ureg->r13, ureg->r14, ureg->pc);
}
uintptr
userpc(void)
{
return ((Ureg*)up->dbgreg)->pc;
}
uintptr
dbgpc(Proc *)
{
if(up->dbgreg)
return userpc();
return 0;
}
void
procsetup(Proc *p)
{
p->fpstate = FPinit;
fpoff();
}
void
procsave(Proc *p)
{
if(p->fpstate == FPactive) {
if(p->state == Moribund)
fpclear();
else
fpsave(p->fpsave);
p->fpstate = FPinactive;
}
}
void
procrestore(Proc *)
{
}
void
procfork(Proc *p)
{
ulong s;
s = splhi();
switch(up->fpstate & ~FPillegal) {
case FPactive:
fpsave(up->fpsave);
up->fpstate = FPinactive;
case FPinactive:
memmove(p->fpsave, up->fpsave, sizeof(FPsave));
p->fpstate = FPinactive;
}
splx(s);
}
void
kprocchild(Proc *p, void (*entry)(void))
{
p->sched.pc = (uintptr) entry;
p->sched.sp = (uintptr) p;
}
void
forkchild(Proc *p, Ureg *ureg)
{
Ureg *cureg;
p->sched.sp = (uintptr) p - sizeof(Ureg);
p->sched.pc = (uintptr) forkret;
cureg = (Ureg*) p->sched.sp;
memmove(cureg, ureg, sizeof(Ureg));
cureg->r0 = 0;
}
uintptr
execregs(uintptr entry, ulong ssize, ulong nargs)
{
ulong *sp;
Ureg *ureg;
sp = (ulong*)(USTKTOP - ssize); *--sp = nargs;
ureg = up->dbgreg;
ureg->sp = (uintptr) sp;
ureg->pc = entry;
ureg->r14 = 0;
return USTKTOP-sizeof(Tos);
}
void
setkernur(Ureg *ureg, Proc *p)
{
ureg->pc = p->sched.pc;
ureg->sp = p->sched.sp+4;
ureg->r14 = (uintptr) sched;
}
void
setregisters(Ureg *ureg, char *pureg, char *uva, int n)
{
uvlong v;
v = ureg->psr;
memmove(pureg, uva, n);
ureg->psr &= ~(PsrMask|PsrDfiq|PsrDirq);
ureg->psr |= v & (PsrMask|PsrDfiq|PsrDirq);
}
void
evenaddr(uintptr addr)
{
if(addr & 3) {
postnote(up, 1, "sys: odd address", NDebug);
error(Ebadarg);
}
}
int
notify(Ureg *ureg)
{
ulong s, sp;
char *msg;
if(up->procctl)
procctl();
if(up->nnote == 0)
return 0;
s = spllo();
qlock(&up->debug);
msg = popnote(ureg);
if(msg == nil) {
qunlock(&up->debug);
splhi();
return 0;
}
sp = ureg->sp;
sp -= 256;
sp -= sizeof(Ureg);
if(!okaddr((uintptr)up->notify, 1, 0)
|| !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
|| ((uintptr) up->notify & 3) != 0
|| (sp & 3) != 0) {
qunlock(&up->debug);
pprint("suicide: bad address in notify\n");
pexit("Suicide", 0);
}
memmove((Ureg*)sp, ureg, sizeof(Ureg));
*(Ureg**)(sp-BY2WD) = up->ureg;
up->ureg = (void*)sp;
sp -= BY2WD+ERRMAX;
memmove((char*)sp, msg, ERRMAX);
sp -= 3*BY2WD;
*(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD;
*(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg;
ureg->r0 = (uintptr) up->ureg;
ureg->sp = sp;
ureg->pc = (uintptr) up->notify;
ureg->r14 = 0;
qunlock(&up->debug);
splx(s);
return 1;
}
void
noted(Ureg *ureg, ulong arg0)
{
Ureg *nureg;
ulong oureg, sp;
qlock(&up->debug);
if(arg0 != NRSTR && !up->notified) {
qunlock(&up->debug);
iprint("called to noted when not notified\n");
pexit("Suicide", 0);
}
up->notified = 0;
nureg = up->ureg;
oureg = (ulong) nureg;
if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 3) != 0) {
qunlock(&up->debug);
pprint("bad ureg in noted or call to noted when not notifed\n");
pexit("Suicide", 0);
}
nureg->psr &= PsrMask|PsrDfiq|PsrDirq;
nureg->psr |= (ureg->psr & ~(PsrMask|PsrDfiq|PsrDirq));
memmove(ureg, nureg, sizeof(Ureg));
switch(arg0) {
case NCONT:
case NRSTR:
if(!okaddr(nureg->pc, BY2WD, 0) || (nureg->pc & 3) != 0
|| !okaddr(nureg->sp, BY2WD, 0) || (nureg->sp & 3) != 0) {
qunlock(&up->debug);
pprint("suicide: trap in noted\n");
pexit("Suicide", 0);
}
up->ureg = (Ureg *) (*(ulong*) (oureg - BY2WD));
qunlock(&up->debug);
break;
case NSAVE:
if(!okaddr(nureg->pc, BY2WD, 0) || (nureg->pc & 3) != 0
|| !okaddr(nureg->sp, BY2WD, 0) || (nureg->sp & 3) != 0) {
qunlock(&up->debug);
pprint("suicide: trap in noted\n");
pexit("Suicide", 0);
}
qunlock(&up->debug);
sp = oureg - 4 * BY2WD - ERRMAX;
splhi();
ureg->sp = sp;
ureg->r0 = (uintptr) oureg;
((ulong*) sp)[1] = oureg;
((ulong*) sp)[0] = 0;
break;
default:
up->lastnote->flag = NDebug;
/* wet floor */
case NDFLT:
qunlock(&up->debug);
if(up->lastnote->flag == NDebug)
pprint("suicide: %s\n", up->lastnote->msg);
pexit(up->lastnote->msg, up->lastnote->flag != NDebug);
break;
}
}
void
trapinit(void)
{
extern ulong vectors[];
/* install stack pointer for other exception modes */
setR13(PsrMfiq, m->save);
setR13(PsrMirq, m->save);
setR13(PsrMiabt, m->save);
setR13(PsrMund, m->save);
setR13(PsrMsys, m->save);
/* install vectors and vtable to MACHVEC because vectors must be
* aligned on a 128 byte boundary */
memmove((ulong*)MACHVEC(m->machno), vectors, 64 * 4);
/* set vectors base address */
setvectors(MACHVEC(m->machno));
}
static void
trapfpu(void)
{
int s;
if((up->fpstate & FPillegal) != 0) {
postnote(up, 1, "sys: floating point in note handler", NDebug);
return;
}
switch(up->fpstate) {
case FPinit:
s = splhi();
fpinit(); up->fpstate = FPactive;
splx(s);
break;
case FPinactive:
s = splhi();
fprestore(up->fpsave); up->fpstate = FPactive;
splx(s);
break;
case FPactive:
postnote(up, 1, "sys: floating point error", NDebug);
break;
}
}
static void
traparm(Ureg *ureg, ulong fsr, uintptr far)
{
int user;
int read;
int syscall;
static char buf[ERRMAX];
read = (fsr & (1<<11)) == 0;
user = userureg(ureg);
if(!user) {
if(far >= USTKTOP)
panic("kernel fault: bad address pc=%#.8lux far=%#.8lux fsr=%#.8lux",
ureg->pc, far, fsr);
if(up == nil)
panic("kernel fault: no user process pc=%#.8lux far=%#.8lux fsr=%#.8lux",
ureg->pc, far, fsr);
}
if(up == nil) {
panic("user fault: up=nil pc=%#.8lux far=%#.8lux fsr=%#.8lux",
ureg->pc, far, fsr);
}
syscall = up->insyscall; up->insyscall = 1;
switch(fsr & 0x1f) {
case 0x03: /* l1 access flag fault */
case 0x05: /* l1 translation fault */
case 0x06: /* l2 access flag fault */
case 0x07: /* l2 translation fault */
case 0x09: /* l1 domain fault */
case 0x0b: /* l2 domain fault */
case 0x0d: /* l1 permission fault */
case 0x0f: /* l2 permission fault */
if(fault(far, ureg->pc, read) == 0)
break;
default:
if(!user)
panic("kernel fault: pc=%#.8lux far=%#.8lux fsr=%#.8lux",
ureg->pc, far, fsr);
dumpureg(ureg);
dumpstackureg(ureg);
snprint(buf, sizeof(buf), "sys: trap: fault %s far=%#.8lux fsr=%#.8lux",
read ? "read" : "write", far, fsr);
postnote(up, 1, buf, NDebug);
}
up->insyscall = syscall;
}
void
trap(Ureg *ureg)
{
int user;
u32int op, cp;
user = kenter(ureg);
switch(ureg->type) {
case PsrMfiq:
case PsrMirq:
ureg->pc -= 4;
intr(ureg);
break;
case PsrMiabt:
ureg->pc -= 4;
traparm(ureg, getifsr(), getifar());
break;
case PsrMdabt:
ureg->pc -= 8;
traparm(ureg, getdfsr(), getdfar());
break;
case PsrMund:
ureg->pc -= 4;
if(user) {
spllo();
if(okaddr(ureg->pc, 4, 0)) {
op = *(u32int*)ureg->pc;
if((op & 0x0f000000) == 0x0e000000 || (op & 0x0e000000) == 0x0c000000) {
cp = op >> 8 & 15;
if(cp == 10 || cp == 11) {
trapfpu();
break;
}
}
}
postnote(up, 1, "sys: trap: invalid opcode", NDebug);
break;
}
panic("invalid opcode at pc=%#.8lux lr=%#.8lux", ureg->pc, ureg->r14);
break;
default:
panic("unknown trap at pc=%#.8lux lr=%#.8lux", ureg->pc, ureg->r14);
break;
}
splhi();
if(user) {
if(up->procctl || up->nnote)
notify(ureg);
kexit(ureg);
}
}
void
syscall(Ureg *ureg)
{
char *e;
uintptr sp;
long ret;
int i, s;
ulong scallnr;
vlong startns, stopns;
if(!kenter(ureg))
panic("syscall: from kernel: pc=%#.8lux", ureg->pc);
m->syscall++;
up->insyscall = 1;
up->pc = ureg->pc;
scallnr = up->scallnr = ureg->r0;
sp = ureg->sp;
spllo();
up->nerrlab = 0;
ret = -1;
if(!waserror()) {
if(scallnr >= nsyscall) {
pprint("bad sys call number %lux pc %#lux", scallnr, ureg->pc);
postnote(up, 1, "sys: bad sys call", NDebug);
error(Ebadarg);
}
if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) {
validaddr(sp, sizeof(Sargs)+BY2WD, 0);
evenaddr(sp);
}
up->s = *((Sargs*)(sp + BY2WD));
up->psstate = sysctab[scallnr];
if (up->procctl == Proc_tracesyscall) {
syscallfmt(scallnr, ureg->pc, (va_list)up->s.args);
s = splhi();
up->procctl = Proc_stopme;
procctl();
splx(s);
startns = todget(nil);
}
ret = systab[scallnr]((va_list)up->s.args);
poperror();
} else {
e = up->syserrstr;
up->syserrstr = up->errstr;
up->errstr = e;
}
if(up->nerrlab) {
print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
for (i = 0; i < NERR; i++)
print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
panic("error stack");
}
ureg->r0 = ret;
if(up->procctl == Proc_tracesyscall) {
stopns = todget(nil);
sysretfmt(scallnr, (va_list)up->s.args, ret, startns, stopns);
s = splhi();
up->procctl = Proc_stopme;
procctl();
splx(s);
}
up->insyscall = 0;
up->psstate = 0;
if(scallnr == NOTED)
noted(ureg, *((ulong *)up->s.args));
if(scallnr != RFORK && (up->procctl || up->nnote)) {
splhi();
notify(ureg);
}
if(up->delaysched)
sched();
kexit(ureg);
splhi();
}