/* * Heavily modified copy of /sys/src/9/pc/trap.c * A shell of its former self. */ #define WANT_M #include "u.h" #include "libvx32/vx32.h" #include "tos.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "error.h" #include "trace.h" #include "systab.h" int tracesyscalls; static void noted(Ureg*, ulong); static void mathemu(Ureg*, void*); static void syscall(Ureg*); /* going to user space */ void kexit(Ureg *ureg) { uvlong t; Tos *tos; /* precise time accounting, kernel exit */ tos = (Tos*)(up->pmmu.uzero+USTKTOP-sizeof(Tos)); cycles(&t); tos->kcycles += t - up->kentry; tos->pcycles = up->pcycles; tos->pid = up->pid; } static char* excname[32] = { "divide error", "debug exception", "nonmaskable interrupt", "breakpoint", "overflow", "bounds check", "invalid opcode", "coprocessor not available", "double fault", "coprocessor segment overrun", "invalid TSS", "segment not present", "stack exception", "general protection violation", "page fault", "15 (reserved)", "coprocessor error", "alignment check", "machine check", "19 (reserved)", "20 (reserved)", "21 (reserved)", "22 (reserved)", "23 (reserved)", "24 (reserved)", "25 (reserved)", "26 (reserved)", "27 (reserved)", "28 (reserved)", "29 (reserved)", "30 (reserved)", "31 (reserved)", }; /* * Handle a trap. */ void trap(Ureg *ureg) { char buf[ERRMAX]; int vno; vno = ureg->trap; switch(vno){ case VXTRAP_FPOFF: mathemu(ureg, nil); return; case VXTRAP_SOFT+0x40: /* int $0x40 - system call */ if(tracesyscalls){ uint32 *sp = (uint32*)(up->pmmu.uzero + ureg->usp); iprint("%d [%s] %s %#ux %08ux %08ux %08ux %08ux\n", up->pid, up->text, sysctab[ureg->ax], sp[0], sp[1], sp[2], sp[3]); } syscall(ureg); if(tracesyscalls){ if(ureg->ax == -1) iprint("%d [%s] -> %s\n", up->pid, up->text, up->syserrstr); else iprint("%d [%s] -> %#ux\n", up->pid, up->text, ureg->ax); } return; case VXTRAP_IRQ+VXIRQ_TIMER: sched(); break; case 3: // breakpoint /* restore pc to instruction that caused the trap */ ureg->pc--; sprint(buf, "sys: breakpoint"); postnote(up, 1, buf, NDebug); break; default: if(vno < nelem(excname)){ spllo(); sprint(buf, "sys: trap: %s", excname[vno]); postnote(up, 1, buf, NDebug); }else{ dumpregs(ureg); if(vno < nelem(excname)) panic("%s", excname[vno]); panic("unknown trap/intr: %#x", vno); } break; } if(up->procctl || up->nnote) notify(ureg); spllo(); /* no actual effect, just silences prints */ kexit(ureg); } void dumpregs2(Ureg* ureg) { if(up) print("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid); else print("cpu%d: registers for kernel\n", m->machno); print("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX", ureg->flags, ureg->trap, ureg->ecode, ureg->pc); print(" USP=%luX\n", ureg->usp); print(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", ureg->ax, ureg->bx, ureg->cx, ureg->dx); print(" SI %8.8luX DI %8.8luX BP %8.8luX\n", ureg->si, ureg->di, ureg->bp); } void dumpregs(Ureg* ureg) { dumpregs2(ureg); /* * Processor control registers. * If machine check exception, time stamp counter, page size extensions * or enhanced virtual 8086 mode extensions are supported, there is a * CR4. If there is a CR4 and machine check extensions, read the machine * check address and machine check type registers if RDMSR supported. print(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux", getcr0(), getcr2(), getcr3()); if(m->cpuiddx & 0x9A){ vlong mca, mct; iprint(" CR4 %8.8lux", getcr4()); if((m->cpuiddx & 0xA0) == 0xA0){ rdmsr(0x00, &mca); rdmsr(0x01, &mct); iprint("\n MCA %8.8llux MCT %8.8llux", mca, mct); } } print("\n ur %lux up %lux\n", ureg, up); */ } static void fmtrwdata(Fmt *f, ulong s, int n, char *suffix) { char *t, *src; int i; if (! s) { fmtprint(f, "0x0", suffix); return; } src = uvalidaddr(s, 1, 0); t = smalloc(n+1); for(i = 0; i < n; i++) if (isgraph(src[i])) t[i] = src[i]; else t[i] = '.'; fmtprint(f, "%08ux/\"%s\"%s", s, t, suffix); free(t); } static void fmtuserstring(Fmt *f, ulong s, char *suffix) { char *es, *t, *src; int n; if (! s){ fmtprint(f, "0/\"\"%s", suffix); return; } src = uvalidaddr(s, 1, 0); es = vmemchr(src, 0, 1<<16); n = es - src; t = smalloc(n+1); memmove(t, src, n); t[n] = 0; fmtprint(f, "%08ux/\"%s\"%s", s, t, suffix); free(t); } static void syscallprint(Ureg *ureg) { uint32 *sp; int syscallno; vlong offset; Fmt fmt; int len; uint32 argp, a; sp = (uint32*)(up->pmmu.uzero + ureg->usp); syscallno = ureg->ax; offset = 0; fmtstrinit(&fmt); fmtprint(&fmt, "%d %s ", up->pid, up->text); /* accomodate process-private system calls */ if(syscallno > nelem(sysctab)) fmtprint(&fmt, " %d %#x ", syscallno, sp[0]); else fmtprint(&fmt, "%s %#ux ", sysctab[syscallno], sp[0]); if(up->syscalltrace) free(up->syscalltrace); switch(syscallno) { case SYSR1: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case _ERRSTR: fmtuserstring(&fmt, sp[1], ""); break; case BIND: fmtuserstring(&fmt, sp[1], " "); fmtuserstring(&fmt, sp[2], " "); fmtprint(&fmt, "%#ux", sp[3]); break; case CHDIR: fmtuserstring(&fmt, sp[1], ""); break; case CLOSE: fmtprint(&fmt, "%d", sp[1]); break; case DUP: fmtprint(&fmt, "%08ux %08ux", sp[1], sp[2]); break; case ALARM: fmtprint(&fmt, "%08ux ", sp[1]); break; case EXEC: fmtuserstring(&fmt, sp[1], ""); argp = sp[2]; for(;;argp += BY2WD) { a = *(uint32*)uvalidaddr(argp, BY2WD, 0); if(a == 0) break; fmtprint(&fmt, " "); fmtuserstring(&fmt, a, ""); } break; case EXITS: fmtuserstring(&fmt, sp[1], ""); break; case _FSESSION: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case FAUTH: fmtprint(&fmt, "%08ux", sp[1]); fmtuserstring(&fmt, sp[2], ""); break; case _FSTAT: fmtprint(&fmt, "%08ux %#ux %08ux", sp[1], sp[2], sp[3]); break; case SEGBRK: fmtprint(&fmt, "%#ux %#ux", sp[1], sp[2]); break; case _MOUNT: fmtprint(&fmt, "%d %d ", sp[1], sp[2]); fmtuserstring(&fmt, sp[3], " "); fmtprint(&fmt, "%08ux ", sp[4]); fmtuserstring(&fmt, sp[5], ""); break; case OPEN: fmtuserstring(&fmt, sp[1], " "); fmtprint(&fmt, "%08ux", sp[2]); break; case OSEEK: fmtprint(&fmt, "%08ux %08ux", sp[1], sp[2]); break; case SLEEP: fmtprint(&fmt, "%d", sp[1]); break; case _STAT: fmtuserstring(&fmt, sp[1], " "); fmtprint(&fmt, "%#ux %d", sp[2], sp[3]); break; case RFORK: fmtprint(&fmt, "%08ux", sp[1] ); break; case PIPE: break; case CREATE: fmtuserstring(&fmt, sp[1], " "); fmtprint(&fmt, "%08ux %08ux", sp[2], sp[3]); break; case FD2PATH: fmtprint(&fmt, "%d ", sp[1]); break; case BRK_: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case REMOVE: fmtuserstring(&fmt, sp[1], " "); break; /* deprecated */ case _WSTAT: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case _FWSTAT: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case NOTIFY: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case NOTED: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case SEGATTACH: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case SEGDETACH: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case SEGFREE: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case SEGFLUSH: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case RENDEZVOUS: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case UNMOUNT: fmtuserstring(&fmt, sp[1], " "); break; case _WAIT: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case SEMACQUIRE: fmtprint(&fmt, "%08ux %#ux %d", sp[1], sp[2], sp[3]); break; case SEMRELEASE: fmtprint(&fmt, "%08ux %#ux %d", sp[1], sp[2], sp[3]); break; case SEEK: fmtprint(&fmt, "%08ux %016ux %08ux", sp[1], *(vlong *)&sp[2], sp[4]); break; case FVERSION: fmtprint(&fmt, "%08ux %08ux ", sp[1], sp[2]); fmtuserstring(&fmt, sp[5], ""); break; case ERRSTR: fmtprint(&fmt, "%#ux/", sp[1]); break; case WSTAT: case STAT: fmtprint(&fmt, "%08ux ", sp[1]); fmtuserstring(&fmt, sp[2], " "); fmtprint(&fmt, "%08ux", sp[3]); break; case FSTAT: case FWSTAT: fmtprint(&fmt, "%08ux %08ux %08ux", sp[1], sp[2], sp[3]); break; case MOUNT: fmtprint(&fmt, "%d %d ", sp[1], sp[3]); fmtuserstring(&fmt, sp[3], " "); fmtprint(&fmt, "%08ux", sp[4]); fmtuserstring(&fmt, sp[5], ""); break; case AWAIT: break; case _READ: case PREAD: fmtprint(&fmt, "%d ", sp[1]); break; case _WRITE: offset = -1; case PWRITE: fmtprint(&fmt, "%d ", sp[1]); if (sp[3] < 64) len = sp[3]; else len = 64; fmtrwdata(&fmt, sp[2], len, " "); if(! offset) offset = *(vlong *)&sp[4]; fmtprint(&fmt, "%d %#llx", sp[3], offset); break; } up->syscalltrace = fmtstrflush(&fmt); } static void retprint(Ureg *ureg, int syscallno, uvlong start, uvlong stop) { int errstrlen, len; vlong offset; char *errstr; Fmt fmt; fmtstrinit(&fmt); len = 0; errstrlen = 0; offset = 0; if (ureg->ax != -1) errstr = ""; else errstr = up->syserrstr; if(up->syscalltrace) free(up->syscalltrace); switch(syscallno) { case SYSR1: case BIND: case CHDIR: case CLOSE: case DUP: case ALARM: case EXEC: case EXITS: case _FSESSION: case FAUTH: case _FSTAT: case SEGBRK: case _MOUNT: case OPEN: case OSEEK: case SLEEP: case _STAT: case _WRITE: case PIPE: case CREATE: case BRK_: case REMOVE: case _WSTAT: case _FWSTAT: case NOTIFY: case NOTED: case SEGATTACH: case SEGDETACH: case SEGFREE: case SEGFLUSH: case RENDEZVOUS: case UNMOUNT: case _WAIT: case SEMACQUIRE: case SEMRELEASE: case SEEK: case FVERSION: case STAT: case FSTAT: case WSTAT: case FWSTAT: case MOUNT: case PWRITE: case RFORK: default: break; case AWAIT: if(ureg->ax > 0){ fmtuserstring(&fmt, up->s.args[0], " "); fmtprint(&fmt, "%d", up->s.args[1]); } else { fmtprint(&fmt, "%#ux/\"\" %d", up->s.args[0], up->s.args[1]); } break; case _ERRSTR: errstrlen = 64; case ERRSTR: if(! errstrlen) errstrlen = up->s.args[1]; if(ureg->ax > 0){ fmtuserstring(&fmt, up->s.args[0], " "); fmtprint(&fmt, "%d", errstrlen); } else { fmtprint(&fmt, "\"\" %d", errstrlen); } break; case FD2PATH: if(ureg->ax == -1) fmtprint(&fmt, "\"\" %d", up->s.args[2]); else { fmtuserstring(&fmt, up->s.args[1], " "); fmtprint(&fmt, "%d", errstrlen); } break; case _READ: offset = -1; case PREAD: if(ureg->ax == -1) fmtprint(&fmt, "/\"\" %d 0x%ullx", up->s.args[2], *(vlong *)&up->s.args[3]); else { if (ureg->ax > 64) len = 64; else len = ureg->ax; fmtrwdata(&fmt, up->s.args[1], len, " "); if(! offset) offset = *(vlong *)&up->s.args[3]; fmtprint(&fmt, "%d %#llx", up->s.args[2], offset); } break; } if (syscallno == EXEC) fmtprint(&fmt, " = %p \"%s\" %#ullx %#ullx\n", ureg->ax, errstr, start, stop); else fmtprint(&fmt, " = %d \"%s\" %#ullx %#ullx\n", ureg->ax, errstr, start, stop); up->syscalltrace = fmtstrflush(&fmt); } /* * Handle a system call. */ static void syscall(Ureg *ureg) { char *e; ulong sp; uchar *usp; long ret; int s; ulong scallnr; vlong startnsec, stopnsec; USED(startnsec); cycles(&up->kentry); m->syscall++; up->insyscall = 1; up->pc = ureg->pc; up->dbgreg = ureg; if(up->procctl == Proc_tracesyscall){ up->procctl = Proc_stopme; syscallprint(ureg); procctl(up); if(up->syscalltrace) free(up->syscalltrace); up->syscalltrace = nil; startnsec = todget(nil); } scallnr = ureg->ax; up->scallnr = scallnr; if(scallnr == RFORK && up->fpstate == FPactive){ fpsave(&up->fpsave); up->fpstate = FPinactive; } spllo(); sp = ureg->usp; up->nerrlab = 0; ret = -1; if(!waserror()){ if(scallnr >= nsyscall || systab[scallnr] == 0){ pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc); postnote(up, 1, "sys: bad sys call", NDebug); error(Ebadarg); } usp = uvalidaddr(sp, sizeof(Sargs)+BY2WD, 0); up->s = *((Sargs*)(usp+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(0 && up->pid == 1) print("syscall %lud error %s\n", scallnr, up->syserrstr); } 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", //XXX up->errlab[i].sp, up->errlab[i].pc); panic("error stack"); } // if(ret < 0) // print("%d [%s] %s\n", up->pid, up->psstate, up->syserrstr); // else // print("%d [%s] %d\n", up->pid, up->psstate, ret); // printmap(); /* * 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->ax = ret; if(up->procctl == Proc_tracesyscall){ stopnsec = todget(nil); up->procctl = Proc_stopme; retprint(ureg, scallnr, startnsec, stopnsec); s = splhi(); procctl(up); splx(s); if(up->syscalltrace) free(up->syscalltrace); up->syscalltrace = nil; } up->insyscall = 0; up->psstate = 0; if(scallnr == NOTED) noted(ureg, *(uint32*)(up->pmmu.uzero + sp+BY2WD)); if(scallnr!=RFORK && (up->procctl || up->nnote)){ splhi(); notify(ureg); } /* if we delayed sched because we held a lock, sched now */ if(up->delaysched) sched(); kexit(ureg); } /* * Call user, if necessary, with note. * Pass user the Ureg struct and the note on his stack. */ int notify(Ureg* ureg) { int l; ulong s, sp; Note *n; Ureg *upureg; if(tracesyscalls) iprint("notify\n"); if(up->procctl) procctl(up); if(up->nnote == 0) return 0; if(up->fpstate == FPactive){ fpsave(&up->fpsave); up->fpstate = FPinactive; } up->fpstate |= FPillegal; 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", ureg->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 = ureg->usp; sp -= sizeof(Ureg); if(!okaddr(up->notify, 1, 0) || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)){ pprint("suicide: bad address in notify\n"); qunlock(&up->debug); pexit("Suicide", 0); } uchar *uzero; uzero = up->pmmu.uzero; upureg = (void*)(uzero + sp); memmove(upureg, ureg, sizeof(Ureg)); *(uint32*)(uzero + sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ up->ureg = sp; sp -= BY2WD+ERRMAX; memmove((char*)(uzero + sp), up->note[0].msg, ERRMAX); sp -= 3*BY2WD; *(uint32*)(uzero + sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ *(uint32*)(uzero + sp+1*BY2WD) = up->ureg; /* arg 1 is ureg* */ *(uint32*)(uzero + sp+0*BY2WD) = 0; /* arg 0 is pc */ ureg->usp = sp; ureg->pc = 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() */ static 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; up->fpstate &= ~FPillegal; /* sanity clause */ if(!okaddr(up->ureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ pprint("bad ureg in noted or call to noted when not notified\n"); qunlock(&up->debug); pexit("Suicide", 0); } uchar *uzero; uzero = up->pmmu.uzero; oureg = up->ureg; nureg = (Ureg*)(uzero + up->ureg); /* don't let user change system flags */ nureg->flags = (ureg->flags & ~0xCD5) | (nureg->flags & 0xCD5); memmove(ureg, nureg, sizeof(Ureg)); switch(arg0){ case NCONT: case NRSTR: if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ qunlock(&up->debug); pprint("suicide: trap in noted\n"); pexit("Suicide", 0); } up->ureg = *(uint32*)(uzero+oureg-BY2WD); qunlock(&up->debug); break; case NSAVE: if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->usp, BY2WD, 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; ((uint32*)(uzero+sp))[1] = oureg; /* arg 1 0(FP) is ureg* */ ((uint32*)(uzero+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){ qunlock(&up->debug); pprint("suicide: %s\n", up->lastnote.msg); } else qunlock(&up->debug); pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); } } long execregs(ulong entry, ulong ssize, ulong nargs) { uint32 *sp; Ureg *ureg; up->fpstate = FPinit; fpoff(); sp = (uint32*)(up->pmmu.uzero + USTKTOP - ssize); *--sp = nargs; ureg = up->dbgreg; ureg->usp = (uchar*)sp - up->pmmu.uzero; //showexec(ureg->usp); ureg->pc = entry; return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ } /* * return the userpc the last exception happened at */ 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* ureg, char* pureg, char* uva, int n) { ulong flags; flags = ureg->flags; memmove(pureg, uva, n); ureg->flags = (ureg->flags & 0x00FF) | (flags & 0xFF00); } static void linkproc(void) { spllo(); up->kpfun(up->kparg); pexit("kproc dying", 0); } void kprocchild(Proc* p, void (*func)(void*), void* arg) { /* * gotolabel() needs a word on the stack in * which to place the return PC used to jump * to linkproc(). */ labelinit(&p->sched, (ulong)linkproc, (ulong)p->kstack+KSTACK-BY2WD); p->kpfun = func; p->kparg = arg; } void forkchild(Proc *p, Ureg *ureg) { Ureg *cureg; void *sp; /* * Add 2*BY2WD to the stack to account for * - the return PC * - trap's argument (ur) */ sp = (char*)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2WD); labelinit(&p->sched, (ulong)forkret, (ulong)sp); cureg = (Ureg*)((char*)sp+2*BY2WD); memmove(cureg, ureg, sizeof(Ureg)); /* return value of syscall in child */ cureg->ax = 0; /* Things from bottom of syscall which were never executed */ p->psstate = 0; p->insyscall = 0; } /* * Give enough context in the ureg to produce a kernel stack for * a sleeping process. Or not. */ void setkernur(Ureg* ureg, Proc* p) { memset(ureg, 0, sizeof *ureg); } ulong dbgpc(Proc *p) { Ureg *ureg; ureg = p->dbgreg; if(ureg == 0) return 0; return ureg->pc; } /* * floating point and all its glory. */ static char* mathmsg[] = { nil, /* handled below */ "denormalized operand", "division by zero", "numeric overflow", "numeric underflow", "precision loss", }; static void mathnote(void) { int i; ulong status; char *msg, note[ERRMAX]; status = up->fpsave.status; /* * Some attention should probably be paid here to the * exception masks and error summary. */ msg = "unknown exception"; for(i = 1; i <= 5; i++){ if(!((1<fpsave.pc, status); postnote(up, 1, note, NDebug); } /* * math coprocessor emulation fault */ static void mathemu(Ureg *ureg, void *v) { if(up->fpstate & FPillegal){ /* someone did floating point in a note handler */ postnote(up, 1, "sys: floating point in note handler", NDebug); return; } switch(up->fpstate){ case FPinit: fpinit(); up->fpstate = FPactive; break; case FPinactive: /* * Before restoring the state, check for any pending * exceptions, there's no way to restore the state without * generating an unmasked exception. * More attention should probably be paid here to the * exception masks and error summary. */ if((up->fpsave.status & ~up->fpsave.control) & 0x07F){ mathnote(); break; } fprestore(&up->fpsave); up->fpstate = FPactive; break; case FPactive: panic("math emu pid %ld %s pc 0x%lux", up->pid, up->text, ureg->pc); break; } } /* * math coprocessor segment overrun * TODO: When to call this? */ void mathover(Ureg *u, void *v) { pexit("math overrun", 0); } /* * math coprocessor error * TODO: When to call this? */ void matherror(Ureg *ur, void *v) { /* * a write cycle to port 0xF0 clears the interrupt latch attached * to the error# line from the 387 */ // if(!(m->cpuiddx & 0x01)) // outb(0xF0, 0xFF); /* * save floating point state to check out error */ fpenv(&up->fpsave); mathnote(); if(!okaddr(ur->pc, 1, 0)) panic("fp: status %ux fppc=0x%lux pc=0x%lux", up->fpsave.status, up->fpsave.pc, ur->pc); } /* * set up floating point for a new process */ void procsetup(Proc*p) { p->fpstate = FPinit; fpoff(); } void procrestore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ /* * Fpsave() stores without handling pending * unmasked exeptions. Postnote() can't be called * here as sleep() already has up->rlock, so * the handling of pending exceptions is delayed * until the process runs again and generates an * emulation fault to activate the FPU. */ fpsave(&p->fpsave); } p->fpstate = FPinactive; } }