summaryrefslogtreecommitdiff
path: root/sys/src/9/mtx/trap.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/mtx/trap.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/mtx/trap.c')
-rwxr-xr-xsys/src/9/mtx/trap.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/sys/src/9/mtx/trap.c b/sys/src/9/mtx/trap.c
new file mode 100755
index 000000000..767e96c3b
--- /dev/null
+++ b/sys/src/9/mtx/trap.c
@@ -0,0 +1,836 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "io.h"
+#include "tos.h"
+#include "../port/error.h"
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+void
+hwintrinit(void)
+{
+ i8259init();
+ mpicenable(0, nil); /* 8259 interrupts are routed through MPIC intr 0 */
+}
+
+static int
+hwintrenable(Vctl *v)
+{
+ int vec, irq;
+
+ irq = v->irq;
+ if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */
+ if(irq > 15) {
+ print("intrenable: pci irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq;
+ mpicenable(vec, v);
+ }
+ else {
+ if(irq > MaxIrqPIC) {
+ print("intrenable: irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq+VectorPIC;
+ if(i8259enable(v) == -1)
+ return -1;
+ }
+ return vec;
+}
+
+static int
+hwintrdisable(Vctl *v)
+{
+ int vec, irq;
+
+ irq = v->irq;
+ if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */
+ if(irq > 15) {
+ print("intrdisable: pci irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq;
+ mpicdisable(vec);
+ }
+ else {
+ if(irq > MaxIrqPIC) {
+ print("intrdisable: irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq+VectorPIC;
+ if(i8259disable(irq) == -1)
+ return -1;
+ }
+ return vec;
+}
+
+static int
+hwvecno(int irq, int tbdf)
+{
+ if(BUSTYPE(tbdf) == BusPCI) /* MPIC? */
+ return irq;
+ else
+ return irq+VectorPIC;
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int vno;
+ Vctl *v;
+
+ if(f == nil){
+ print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, name);
+ return;
+ }
+
+ v = xalloc(sizeof(Vctl));
+ v->isintr = 1;
+ v->irq = irq;
+ v->tbdf = tbdf;
+ v->f = f;
+ v->a = a;
+ strncpy(v->name, name, KNAMELEN-1);
+ v->name[KNAMELEN-1] = 0;
+
+ ilock(&vctllock);
+ vno = hwintrenable(v);
+ if(vno == -1){
+ iunlock(&vctllock);
+ print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, v->name);
+ xfree(v);
+ return;
+ }
+ if(vctl[vno]){
+ if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
+ panic("intrenable: handler: %s %s %#p %#p %#p %#p",
+ vctl[vno]->name, v->name,
+ vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
+ v->next = vctl[vno];
+ }
+ vctl[vno] = v;
+ iunlock(&vctllock);
+}
+
+void
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
+{
+ Vctl **pv, *v;
+ int vno;
+
+ vno = hwvecno(irq, tbdf);
+ ilock(&vctllock);
+ pv = &vctl[vno];
+ while (*pv &&
+ ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
+ strcmp((*pv)->name, name)))
+ pv = &((*pv)->next);
+ assert(*pv);
+
+ v = *pv;
+ *pv = (*pv)->next; /* Link out the entry */
+
+ if(vctl[vno] == nil)
+ hwintrdisable(v);
+ iunlock(&vctllock);
+ xfree(v);
+}
+
+void syscall(Ureg*);
+void noted(Ureg*, ulong);
+static void _dumpstack(Ureg*);
+
+char *excname[] =
+{
+ "reserved 0",
+ "system reset",
+ "machine check",
+ "data access",
+ "instruction access",
+ "external interrupt",
+ "alignment",
+ "program exception",
+ "floating-point unavailable",
+ "decrementer",
+ "reserved A",
+ "reserved B",
+ "system call",
+ "trace trap",
+ "floating point assist",
+ "reserved F",
+ "reserved 10",
+ "reserved 11",
+ "reserved 12",
+ "instruction address breakpoint",
+ "system management interrupt",
+};
+
+char *fpcause[] =
+{
+ "inexact operation",
+ "division by zero",
+ "underflow",
+ "overflow",
+ "invalid operation",
+};
+char *fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */
+
+
+char *regname[]={
+ "CAUSE", "SRR1",
+ "PC", "GOK",
+ "LR", "CR",
+ "XER", "CTR",
+ "R0", "R1",
+ "R2", "R3",
+ "R4", "R5",
+ "R6", "R7",
+ "R8", "R9",
+ "R10", "R11",
+ "R12", "R13",
+ "R14", "R15",
+ "R16", "R17",
+ "R18", "R19",
+ "R20", "R21",
+ "R22", "R23",
+ "R24", "R25",
+ "R26", "R27",
+ "R28", "R29",
+ "R30", "R31",
+};
+
+void
+trap(Ureg *ureg)
+{
+ ulong dsisr;
+ int ecode, user;
+ char buf[ERRMAX], *s;
+
+ ecode = (ureg->cause >> 8) & 0xff;
+ user = (ureg->srr1 & MSR_PR) != 0;
+ if(user)
+ up->dbgreg = ureg;
+
+ if(ureg->status & MSR_RI == 0)
+ print("double fault?: ecode = %d\n", ecode);
+
+ switch(ecode) {
+ case CEI:
+ intr(ureg);
+ break;
+ case CDEC:
+ clockintr(ureg);
+ break;
+ case CSYSCALL:
+ if(!user)
+ panic("syscall in kernel: srr1 0x%4.4luX", ureg->srr1);
+ syscall(ureg);
+ return; /* syscall() calls notify itself, don't do it again */
+ case CFPU:
+ if(!user || up == nil) {
+ dumpregs(ureg);
+ panic("floating point in kernel");
+ }
+ switch(up->fpstate){
+ case FPinit:
+ fprestore(&initfp);
+ up->fpstate = FPactive;
+ break;
+ case FPinactive:
+ fprestore(&up->fpsave);
+ up->fpstate = FPactive;
+ break;
+ default:
+ panic("fpstate");
+ }
+ ureg->srr1 |= MSR_FP;
+ break;
+ case CISI:
+ faultpower(ureg, ureg->pc, 1);
+ break;
+ case CDSI:
+ dsisr = getdsisr();
+ if(dsisr & BIT(6))
+ faultpower(ureg, getdar(), 0);
+ else
+ faultpower(ureg, getdar(), 1);
+ break;
+ case CPROG:
+ if(ureg->status & (1<<19))
+ s = "floating point exception";
+ else if(ureg->status & (1<<18))
+ s = "illegal instruction";
+ else if(ureg->status & (1<<17))
+ s = "privileged instruction";
+ else
+ s = "undefined program exception";
+ if(user){
+ spllo();
+ sprint(buf, "sys: trap: %s", s);
+ postnote(up, 1, buf, NDebug);
+ break;
+ }
+ dumpregs(ureg);
+ panic(s);
+ break;
+ default:
+ if(ecode < nelem(excname) && user){
+ spllo();
+ sprint(buf, "sys: trap: %s", excname[ecode]);
+ postnote(up, 1, buf, NDebug);
+ break;
+ }
+ dumpregs(ureg);
+ if(ecode < nelem(excname))
+ panic("%s", excname[ecode]);
+ panic("unknown trap/intr: %d", ecode);
+ }
+
+ /* restoreureg must execute at high IPL */
+ splhi();
+
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up && up->delaysched && ecode == CDEC){
+ sched();
+ splhi();
+ }
+
+ if(user) {
+ notify(ureg);
+ if(up->fpstate == FPinactive)
+ ureg->srr1 &= ~MSR_FP;
+ kexit(ureg);
+ }
+}
+
+void
+faultpower(Ureg *ureg, ulong addr, int read)
+{
+ int user, insyscall, n;
+ char buf[ERRMAX];
+
+ user = (ureg->srr1 & MSR_PR) != 0;
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+ n = fault(addr, read);
+ if(n < 0){
+ if(!user){
+ dumpregs(ureg);
+ panic("fault: 0x%lux", addr);
+ }
+ sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr);
+ postnote(up, 1, buf, NDebug);
+ }
+ up->insyscall = insyscall;
+}
+
+void
+sethvec(int v, void (*r)(void))
+{
+ ulong *vp, pa, o;
+
+ vp = KADDR(v);
+ vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */
+ vp[1] = 0x7c0802a6; /* MOVW LR, R0 */
+ vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */
+ pa = PADDR(r);
+ o = pa >> 25;
+ if(o != 0 && o != 0x7F){
+ /* a branch too far */
+ vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */
+ vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */
+ vp[5] = 0x7c0803a6; /* MOVW R0, LR */
+ vp[6] = 0x4e800021; /* BL (LR) */
+ }else
+ vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */
+ dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+trapinit(void)
+{
+ int i;
+
+ /*
+ * set all exceptions to trap
+ */
+ for(i = 0; i < 0x2000; i += 0x100)
+ sethvec(i, trapvec);
+
+ putmsr(getmsr() & ~MSR_IP);
+}
+
+void
+intr(Ureg *ureg)
+{
+ int vno;
+ Vctl *ctl, *v;
+
+ vno = mpicintack();
+ if(vno == 0) { /* 8259, wired through MPIC vec 0 */
+ vno = i8259intack();
+ mpiceoi(0);
+ }
+
+ if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) {
+ panic("spurious intr %d", vno);
+ return;
+ }
+
+ if(ctl->isr)
+ ctl->isr(vno);
+ for(v = ctl; v != nil; v = v->next){
+ if(v->f)
+ v->f(ureg, v->a);
+ }
+ if(ctl->eoi)
+ ctl->eoi(vno);
+
+ if(up)
+ preempted();
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+ int i;
+ char *s;
+ ulong fppc;
+
+ fppc = ur->pc;
+ s = 0;
+ fpscr >>= 3; /* trap enable bits */
+ fpscr &= (fpscr>>22); /* anded with exceptions */
+ for(i=0; i<5; i++)
+ if(fpscr & (1<<i))
+ s = fpcause[i];
+ if(s == 0)
+ return "no floating point exception";
+ sprint(buf, "%s fppc=0x%lux", s, fppc);
+ return buf;
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+
+static void
+getpcsp(ulong *pc, ulong *sp)
+{
+ *pc = getcallerpc(&pc);
+ *sp = (ulong)&pc-4;
+}
+
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+
+ getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
+ ureg.lr = getcallerpc(&fn);
+ fn(&ureg);
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong l, sl, el, v;
+ int i;
+
+ l = (ulong)&l;
+ if(up == 0){
+ el = (ulong)m+BY2PG;
+ sl = el-KSTACK;
+ }
+ else{
+ sl = (ulong)up->kstack;
+ el = sl + KSTACK;
+ }
+ if(l > el || l < sl){
+ el = (ulong)m+BY2PG;
+ sl = el-KSTACK;
+ }
+ if(l > el || l < sl)
+ return;
+ print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr);
+ i = 0;
+ for(; l < el; l += 4){
+ v = *(ulong*)l;
+ if(KTZERO < v && v < (ulong)etext){
+ print("%.8lux=%.8lux ", l, v);
+ if(i++ == 4){
+ print("\n");
+ i = 0;
+ }
+ }
+ }
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(_dumpstack);
+}
+
+void
+dumpregs(Ureg *ur)
+{
+ int i;
+ ulong *l;
+
+ if(up) {
+ print("registers for %s %ld\n", up->text, up->pid);
+ if(ur->srr1 & MSR_PR == 0)
+ if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK)
+ print("invalid stack ptr\n");
+ }
+ else
+ print("registers for kernel\n");
+
+ print("dsisr\t%.8lux\tdar\t%.8lux\n", getdsisr(), getdar());
+ l = &ur->cause;
+ for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+ print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ (*up->kpfun)(up->kparg);
+ pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK;
+
+ p->kpfun = func;
+ p->kparg = arg;
+}
+
+/*
+ * called in sysfile.c
+ */
+void
+evenaddr(ulong addr)
+{
+ if(addr & 3){
+ postnote(up, 1, "sys: odd address", NDebug);
+ error(Ebadarg);
+ }
+}
+
+long
+execregs(ulong entry, ulong ssize, ulong nargs)
+{
+ ulong *sp;
+ Ureg *ureg;
+
+ sp = (ulong*)(USTKTOP - ssize);
+ *--sp = nargs;
+
+ ureg = up->dbgreg;
+ ureg->usp = (ulong)sp;
+ ureg->pc = entry;
+ ureg->srr1 &= ~MSR_FP;
+ return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */
+}
+
+void
+forkchild(Proc *p, Ureg *ur)
+{
+ Ureg *cur;
+
+ p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE;
+ p->sched.pc = (ulong)forkret;
+
+ cur = (Ureg*)(p->sched.sp+2*BY2WD);
+ memmove(cur, ur, sizeof(Ureg));
+ cur->r3 = 0;
+
+ /* Things from bottom of syscall we never got to execute */
+ p->psstate = 0;
+ p->insyscall = 0;
+}
+
+ulong
+userpc(void)
+{
+ Ureg *ureg;
+
+ ureg = (Ureg*)up->dbgreg;
+ return ureg->pc;
+}
+
+
+/* This routine must save the values of registers the user is not
+ * permitted to write from devproc and then restore the saved values
+ * before returning
+ */
+void
+setregisters(Ureg *xp, char *pureg, char *uva, int n)
+{
+ ulong status;
+
+ status = xp->status;
+ memmove(pureg, uva, n);
+ xp->status = status;
+}
+
+/* Give enough context in the ureg to produce a kernel stack for
+ * a sleeping process
+ */
+void
+setkernur(Ureg* ureg, Proc* p)
+{
+ ureg->pc = p->sched.pc;
+ ureg->sp = p->sched.sp+4;
+}
+
+ulong
+dbgpc(Proc *p)
+{
+ Ureg *ureg;
+
+ ureg = p->dbgreg;
+ if(ureg == 0)
+ return 0;
+
+ return ureg->pc;
+}
+
+/*
+ * system calls
+ */
+#include "../port/systab.h"
+
+/* TODO: make this trap a separate asm entry point, like on other RISC architectures */
+void
+syscall(Ureg* ureg)
+{
+ int i;
+ char *e;
+ long ret;
+ ulong sp, scallnr;
+
+ m->syscall++;
+ up->insyscall = 1;
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+
+ scallnr = ureg->r3;
+ up->scallnr = ureg->r3;
+ spllo();
+
+ sp = ureg->usp;
+ up->nerrlab = 0;
+ ret = -1;
+ if(!waserror()){
+ if(scallnr >= nsyscall || systab[scallnr] == nil){
+ pprint("bad sys call number %d pc %lux\n", 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);
+
+ up->s = *((Sargs*)(sp+BY2WD));
+ up->psstate = sysctab[scallnr];
+
+ ret = systab[scallnr](up->s.args);
+ poperror();
+ }else{
+ /* failure: save the error buffer for errstr */
+ e = up->syserrstr;
+ up->syserrstr = up->errstr;
+ up->errstr = e;
+ }
+ if(up->nerrlab){
+ print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab);
+ print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr);
+ for(i = 0; i < NERR; i++)
+ print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
+ panic("error stack");
+ }
+
+ up->insyscall = 0;
+ up->psstate = 0;
+
+ /*
+ * Put return value in frame. On the x86 the syscall is
+ * just another trap and the return value from syscall is
+ * ignored. On other machines the return value is put into
+ * the results register by caller of syscall.
+ */
+ ureg->r3 = ret;
+
+ if(scallnr == NOTED)
+ noted(ureg, *(ulong*)(sp+BY2WD));
+
+ /* restoreureg must execute at high IPL */
+ splhi();
+ if(scallnr!=RFORK)
+ notify(ureg);
+ if(up->fpstate == FPinactive)
+ ureg->srr1 &= ~MSR_FP;
+}
+
+/*
+ * Call user, if necessary, with note.
+ * Pass user the Ureg struct and the note on his stack.
+ */
+int
+notify(Ureg* ur)
+{
+ int l;
+ ulong s, sp;
+ Note *n;
+
+ if(up->procctl)
+ procctl(up);
+ if(up->nnote == 0)
+ return 0;
+
+ s = spllo();
+ qlock(&up->debug);
+ up->notepending = 0;
+ n = &up->note[0];
+ if(strncmp(n->msg, "sys:", 4) == 0){
+ l = strlen(n->msg);
+ if(l > ERRMAX-15) /* " pc=0x12345678\0" */
+ l = ERRMAX-15;
+ sprint(n->msg+l, " pc=0x%.8lux", ur->pc);
+ }
+
+ if(n->flag!=NUser && (up->notified || up->notify==0)){
+ if(n->flag == NDebug)
+ pprint("suicide: %s\n", n->msg);
+ qunlock(&up->debug);
+ pexit(n->msg, n->flag!=NDebug);
+ }
+
+ if(up->notified) {
+ qunlock(&up->debug);
+ splhi();
+ return 0;
+ }
+
+ if(!up->notify) {
+ qunlock(&up->debug);
+ pexit(n->msg, n->flag!=NDebug);
+ }
+ sp = ur->usp & ~(BY2V-1);
+ sp -= sizeof(Ureg);
+
+ if(!okaddr((ulong)up->notify, BY2WD, 0) ||
+ !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) {
+ pprint("suicide: bad address or sp in notify\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+
+ memmove((Ureg*)sp, ur, sizeof(Ureg));
+ *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
+ up->ureg = (void*)sp;
+ sp -= BY2WD+ERRMAX;
+ memmove((char*)sp, up->note[0].msg, ERRMAX);
+ sp -= 3*BY2WD;
+ *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */
+ ur->r1 = (long)up->ureg; /* arg 1 is ureg* */
+ ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */
+ ((ulong*)sp)[0] = 0; /* arg 0 is pc */
+ ur->usp = sp;
+ ur->pc = (ulong)up->notify;
+ up->notified = 1;
+ up->nnote--;
+ memmove(&up->lastnote, &up->note[0], sizeof(Note));
+ memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
+
+ qunlock(&up->debug);
+ splx(s);
+ return 1;
+}
+
+
+/*
+ * Return user to state before notify()
+ */
+void
+noted(Ureg* ureg, ulong arg0)
+{
+ Ureg *nureg;
+ ulong oureg, sp;
+
+ qlock(&up->debug);
+ if(arg0!=NRSTR && !up->notified) {
+ qunlock(&up->debug);
+ pprint("call to noted() when not notified\n");
+ pexit("Suicide", 0);
+ }
+ up->notified = 0;
+
+ nureg = up->ureg; /* pointer to user returned Ureg struct */
+
+ /* sanity clause */
+ oureg = (ulong)nureg;
+ if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
+ pprint("bad ureg in noted or call to noted when not notified\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+
+ memmove(ureg, nureg, sizeof(Ureg));
+
+ switch(arg0){
+ case NCONT:
+ case NRSTR:
+ if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){
+ pprint("suicide: trap in noted\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+ up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
+ qunlock(&up->debug);
+ break;
+
+ case NSAVE:
+ if(!okaddr(nureg->pc, BY2WD, 0)
+ || !okaddr(nureg->usp, BY2WD, 0)){
+ pprint("suicide: trap in noted\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+ qunlock(&up->debug);
+ sp = oureg-4*BY2WD-ERRMAX;
+ splhi();
+ ureg->sp = sp;
+ ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */
+ ((ulong*)sp)[0] = 0; /* arg 0 is pc */
+ break;
+
+ default:
+ pprint("unknown noted arg 0x%lux\n", arg0);
+ up->lastnote.flag = NDebug;
+ /* fall through */
+
+ case NDFLT:
+ if(up->lastnote.flag == NDebug)
+ pprint("suicide: %s\n", up->lastnote.msg);
+ qunlock(&up->debug);
+ pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
+ }
+}