plan9fox/sys/src/9/sgi/trap.c
cinap_lenrek 8d51e7fa1a kernel: implement portable userinit() and simplify process creation
replace machine specific userinit() by a portable
implemntation that uses kproc() to create the first
process. the initcode text is mapped using kmap(),
so there is no need for machine specific tmpmap()
functions.

initcode stack preparation should be done in init0()
where the stack is mapped and can be accessed directly.

replacing the machine specific userinit() allows some
big simplifications as sysrfork() and kproc() are now
the only callers of newproc() and we can avoid initializing
fields that we know are being initialized by these
callers.

rename autogenerated init.h and reboot.h headers.
the initcode[] and rebootcode[] blobs are now in *.i
files and hex generation was moved to portmkfile. the
machine specific mkfile only needs to specify how to
build rebootcode.out and initcode.out.
2020-01-26 19:01:36 +01:00

882 lines
18 KiB
C

/*
* traps, exceptions, faults and interrupts on ar7161
*/
#include "u.h"
#include "tos.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "ureg.h"
#include "io.h"
#include "../port/error.h"
typedef struct Handler Handler;
struct Handler {
void (*handler)(Ureg*, void *);
void *arg;
Handler *next; /* at this interrupt level */
};
int intr(Ureg*);
void kernfault(Ureg*, int);
void noted(Ureg*, ulong);
void rfnote(Ureg**);
char *excname[] =
{
"trap: external interrupt",
"trap: TLB modification (store to unwritable)",
"trap: TLB miss (load or fetch)",
"trap: TLB miss (store)",
"trap: address error (load or fetch)",
"trap: address error (store)",
"trap: bus error (fetch)",
"trap: bus error (data load or store)",
"trap: system call",
"breakpoint",
"trap: reserved instruction",
"trap: coprocessor unusable",
"trap: arithmetic overflow",
"trap: TRAP exception",
"trap: VCE (instruction)",
"trap: floating-point exception",
"trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */
"trap: corextend unusable",
"trap: precise coprocessor 2 exception",
"trap: TLB read-inhibit",
"trap: TLB execute-inhibit",
"trap: undefined 21",
"trap: undefined 22",
"trap: WATCH exception",
"trap: machine checkcore",
"trap: undefined 25",
"trap: undefined 26",
"trap: undefined 27",
"trap: undefined 28",
"trap: undefined 29",
"trap: cache error",
"trap: VCE (data)",
};
char *fpcause[] =
{
"inexact operation",
"underflow",
"overflow",
"division by zero",
"invalid operation",
};
char *fpexcname(Ureg*, ulong, char*, uint);
#define FPEXPMASK (0x3f<<12) /* Floating exception bits in fcr31 */
struct {
char *name;
uint off;
} regname[] = {
"STATUS", offsetof(Ureg, status),
"PC", offsetof(Ureg, pc),
"SP", offsetof(Ureg, sp),
"CAUSE",offsetof(Ureg, cause),
"BADADDR", offsetof(Ureg, badvaddr),
"TLBVIRT", offsetof(Ureg, tlbvirt),
"HI", offsetof(Ureg, hi),
"LO", offsetof(Ureg, lo),
"R31", offsetof(Ureg, r31),
"R30", offsetof(Ureg, r30),
"R28", offsetof(Ureg, r28),
"R27", offsetof(Ureg, r27),
"R26", offsetof(Ureg, r26),
"R25", offsetof(Ureg, r25),
"R24", offsetof(Ureg, r24),
"R23", offsetof(Ureg, r23),
"R22", offsetof(Ureg, r22),
"R21", offsetof(Ureg, r21),
"R20", offsetof(Ureg, r20),
"R19", offsetof(Ureg, r19),
"R18", offsetof(Ureg, r18),
"R17", offsetof(Ureg, r17),
"R16", offsetof(Ureg, r16),
"R15", offsetof(Ureg, r15),
"R14", offsetof(Ureg, r14),
"R13", offsetof(Ureg, r13),
"R12", offsetof(Ureg, r12),
"R11", offsetof(Ureg, r11),
"R10", offsetof(Ureg, r10),
"R9", offsetof(Ureg, r9),
"R8", offsetof(Ureg, r8),
"R7", offsetof(Ureg, r7),
"R6", offsetof(Ureg, r6),
"R5", offsetof(Ureg, r5),
"R4", offsetof(Ureg, r4),
"R3", offsetof(Ureg, r3),
"R2", offsetof(Ureg, r2),
"R1", offsetof(Ureg, r1),
};
static Handler handlers[8];
void
kvce(Ureg *ur, int ecode)
{
char c;
Pte **p;
Page **pg;
Segment *s;
ulong addr, soff;
c = 'D';
if(ecode == CVCEI)
c = 'I';
print("Trap: VCE%c: addr=%#lux\n", c, ur->badvaddr);
if(up && !(ur->badvaddr & KSEGM)) {
addr = ur->badvaddr;
s = seg(up, addr, 0);
if(s == nil){
print("kvce: no seg for %#lux\n", addr);
for(;;);
}
addr &= ~(BY2PG-1);
soff = addr - s->base;
p = &s->map[soff/PTEMAPMEM];
if(*p){
pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG];
if(*pg)
print("kvce: pa=%#lux, va=%#lux\n",
(*pg)->pa, (*pg)->va);
else
print("kvce: no *pg\n");
}else
print("kvce: no *p\n");
}
}
/* prepare to go to user space */
void
kexit(Ureg *ur)
{
Tos *tos;
/* replicate fpstate to ureg status */
if(up->fpstate != FPactive)
ur->status &= ~CU1;
/* precise time accounting, kernel exit */
tos = (Tos*)(USTKTOP-sizeof(Tos));
tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry;
tos->pcycles = up->pcycles;
tos->pid = up->pid;
}
void
trap(Ureg *ur)
{
int ecode, clockintr, user, cop, x, fpchk;
ulong fpfcr31;
char buf[2*ERRMAX], buf1[ERRMAX], *fpexcep;
static int dumps;
if (up && (char *)(ur) - up->kstack < 1024 && dumps++ == 0) {
iprint("trap: proc %ld kernel stack getting full\n", up->pid);
dumpregs(ur);
dumpstack();
for(;;);
}
if (up == nil &&
(char *)(ur) - (char *)m->stack < 1024 && dumps++ == 0) {
iprint("trap: cpu%d kernel stack getting full\n", m->machno);
dumpregs(ur);
dumpstack();
for(;;);
}
ecode = (ur->cause>>2)&EXCMASK;
user = userureg(ur);
if (ur->cause & TS)
panic("trap: tlb shutdown");
fpchk = 0;
if(user){
up->dbgreg = ur;
cycles(&up->kentry);
}
clockintr = 0;
switch(ecode){
case CINT:
clockintr = intr(ur);
break;
case CFPE:
if(!user)
goto Default;
if(up->fpstate == FPactive){
savefpregs(up->fpsave);
up->fpstate = FPinactive;
}
clrfpintr();
fptrap(ur);
fpchk = 1;
break;
case CTLBM:
case CTLBL:
case CTLBS:
if(up == nil || !user && (ur->badvaddr & KSEGM) == KSEG3) {
kfault(ur);
break;
}
x = up->insyscall;
up->insyscall = 1;
spllo();
faultmips(ur, user, ecode);
up->insyscall = x;
break;
case CVCEI:
case CVCED:
kvce(ur, ecode);
goto Default;
case CWATCH:
if(!user)
panic("watchpoint trap from kernel mode pc=%#p",
ur->pc);
// fpwatch(ur);
break;
case CCPU:
cop = (ur->cause>>28)&3;
if(user && up && cop == 1) {
if(up->fpstate & FPillegal) {
/* someone used floating point in a note handler */
postnote(up, 1,
"sys: floating point in note handler",
NDebug);
break;
}
if(up->fpstate == FPinit || up->fpstate == FPinactive){
restfpregs(up->fpsave, up->fpsave->fpstatus&~FPEXPMASK);
up->fpstate = FPactive;
ur->status |= CU1;
break;
}
fpchk = 1;
break;
}
/* Fallthrough */
Default:
default:
if(user) {
spllo();
snprint(buf, sizeof buf, "sys: %s", excname[ecode]);
postnote(up, 1, buf, NDebug);
break;
}
if (ecode == CADREL || ecode == CADRES)
iprint("kernel addr exception for va %#p pid %#ld %s\n",
ur->badvaddr, (up? up->pid: 0),
(up? up->text: ""));
print("cpu%d: kernel %s pc=%#lux\n",
m->machno, excname[ecode], ur->pc);
dumpregs(ur);
dumpstack();
if(m->machno == 0)
spllo();
exit(1);
}
if(fpchk) {
fpfcr31 = up->fpsave->fpstatus;
if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) {
spllo();
fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1);
snprint(buf, sizeof buf, "sys: fp: %s", fpexcep);
postnote(up, 1, buf, NDebug);
}
}
splhi();
/* delaysched set because we held a lock or because our quantum ended */
if(up && up->delaysched && clockintr){
sched();
splhi();
}
if(user){
notify(ur);
kexit(ur);
}
}
/* map HPC3 irq to INTR2 */
int
hpc3irqlevel(int irq)
{
*IO(uchar, LIO_0_MASK) |= 1 << (irq & 7);
return 2 + irq/8;
}
/*
* set handlers
*/
void
intrenable(int level, void (*h)(Ureg*, void *), void *arg)
{
Handler *hp;
hp = &handlers[level];
if (hp->handler != nil) { /* occupied? */
/* add a new one at the end of the chain */
for (; hp->next != nil; hp = hp->next)
;
if((hp->next = xalloc(sizeof *hp)) == nil)
panic("intrenable: out of memory");
hp = hp->next;
}
hp->arg = arg;
hp->handler = h;
intron(INTR0 << level);
}
int
intr(Ureg *ur)
{
ulong cause, mask;
int clockintr;
Handler *hh, *hp;
m->intr++;
clockintr = 0;
/*
* ignore interrupts that we have disabled, even if their cause bits
* are set.
*/
cause = ur->cause & ur->status & INTMASK;
cause &= ~(INTR1|INTR0); /* ignore sw interrupts */
if(cause & INTR7){
clock(ur);
cause &= ~INTR7;
clockintr = 1;
}
hh = &handlers[2];
for(mask = INTR2; cause != 0 && mask < INTR7; mask <<= 1){
if(cause & mask){
for(hp = hh; hp != nil; hp = hp->next){
if(hp->handler != nil){
(*hp->handler)(ur, hp->arg);
cause &= ~mask;
}
}
}
hh++;
}
if(cause != 0)
iprint("unhandled interrupts %lux\n", cause);
/* preemptive scheduling */
if(up != nil && !clockintr)
preempted();
/* if it was a clockintr, sched will be called at end of trap() */
return clockintr;
}
char*
fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size)
{
int i;
char *s;
ulong fppc;
fppc = ur->pc;
if(ur->cause & BD) /* branch delay */
fppc += 4;
s = 0;
if(fcr31 & (1<<17))
s = "unimplemented operation";
else{
fcr31 >>= 7; /* trap enable bits */
fcr31 &= (fcr31>>5); /* anded with exceptions */
for(i=0; i<5; i++)
if(fcr31 & (1<<i))
s = fpcause[i];
}
if(s == 0)
return "no floating point exception";
snprint(buf, size, "%s fppc=%#lux", s, fppc);
return buf;
}
static void
getpcsp(ulong *pc, ulong *sp)
{
*pc = getcallerpc(&pc);
*sp = (ulong)&pc-4;
}
void
callwithureg(void (*fn)(Ureg*))
{
Ureg ureg;
memset(&ureg, 0, sizeof ureg);
getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
ureg.r31 = getcallerpc(&fn);
fn(&ureg);
}
static void
_dumpstack(Ureg *ureg)
{
ulong l, v, top, i;
extern ulong etext;
print("ktrace /kernel/path %.8lux %.8lux %.8lux\n",
ureg->pc, ureg->sp, ureg->r31);
if(up == nil)
top = (ulong)MACHADDR + MACHSIZE;
else
top = (ulong)up->kstack + KSTACK;
i = 0;
for(l=ureg->sp; l < top; l += BY2WD) {
v = *(ulong*)l;
if(KTZERO < v && v < (ulong)&etext) {
print("%.8lux=%.8lux ", l, v);
if((++i%4) == 0){
print("\n");
delay(200);
}
}
}
print("\n");
}
void
dumpstack(void)
{
callwithureg(_dumpstack);
}
static ulong
R(Ureg *ur, int i)
{
uchar *s;
s = (uchar*)ur;
return *(ulong*)(s + regname[i].off);
}
void
dumpregs(Ureg *ur)
{
int i;
if(up)
print("registers for %s %lud\n", up->text, up->pid);
else
print("registers for kernel\n");
for(i = 0; i < nelem(regname); i += 2)
print("%s\t%#.8lux\t%s\t%#.8lux\n",
regname[i].name, R(ur, i),
regname[i+1].name, R(ur, i+1));
}
int
notify(Ureg *ur)
{
int l, s;
ulong sp;
Note *n;
if(up->procctl)
procctl();
if(up->nnote == 0)
return 0;
if(up->fpstate == FPactive){
savefpregs(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;
seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#lux", 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);
splx(s);
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)); /* push user regs */
*(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); /* push err string */
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;
/*
* arrange to resume at user's handler as if handler(ureg, errstr)
* were being called.
*/
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(); called from user's handler.
*/
void
noted(Ureg *kur, ulong arg0)
{
Ureg *nur;
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;
nur = up->ureg;
oureg = (ulong)nur;
if((oureg & (BY2WD-1)) || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
pprint("bad up->ureg in noted or call to noted() when not notified\n");
qunlock(&up->debug);
pexit("Suicide", 0);
}
setregisters(kur, (char*)kur, (char*)up->ureg, sizeof(Ureg));
switch(arg0) {
case NCONT:
case NRSTR: /* only used by APE */
if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
pprint("suicide: trap in noted\n");
qunlock(&up->debug);
pexit("Suicide", 0);
}
up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
qunlock(&up->debug);
splhi();
break;
case NSAVE: /* only used by APE */
if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
pprint("suicide: trap in noted\n");
qunlock(&up->debug);
pexit("Suicide", 0);
}
qunlock(&up->debug);
sp = oureg-4*BY2WD-ERRMAX;
splhi();
kur->sp = sp;
kur->r1 = oureg; /* arg 1 is ureg* */
((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */
((ulong*)sp)[0] = 0; /* arg 0 is pc */
break;
default:
pprint("unknown noted arg %#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);
}
}
#include "../port/systab.h"
static void
sctracesetup(ulong scallnr, ulong sp, uintptr pc, vlong *startnsp)
{
if(up->procctl == Proc_tracesyscall){
/*
* Redundant validaddr. Do we care?
* Tracing syscalls is not exactly a fast path...
* Beware, validaddr currently does a pexit rather
* than an error if there's a problem; that might
* change in the future.
*/
if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
validaddr(sp, sizeof(Sargs)+BY2WD, 0);
syscallfmt(scallnr, pc, (va_list)(sp+BY2WD));
up->procctl = Proc_stopme;
procctl();
if(up->syscalltrace)
free(up->syscalltrace);
up->syscalltrace = nil;
*startnsp = todget(nil);
}
}
static void
sctracefinish(ulong scallnr, ulong sp, int ret, vlong startns)
{
int s;
if(up->procctl == Proc_tracesyscall){
up->procctl = Proc_stopme;
sysretfmt(scallnr, (va_list)(sp+BY2WD), ret,
startns, todget(nil));
s = splhi();
procctl();
splx(s);
if(up->syscalltrace)
free(up->syscalltrace);
up->syscalltrace = nil;
}
}
/*
* called directly from assembler, not via trap()
*/
void
syscall(Ureg *ur)
{
int i;
volatile long ret;
ulong sp, scallnr;
vlong startns;
char *e;
cycles(&up->kentry);
m->syscall++;
up->insyscall = 1;
up->pc = ur->pc;
up->dbgreg = ur;
ur->cause = 16<<2; /* for debugging: system call is undef 16 */
scallnr = ur->r1;
up->scallnr = ur->r1;
sp = ur->sp;
sctracesetup(scallnr, sp, ur->pc, &startns);
/* no fpu, so no fp state to save */
spllo();
up->nerrlab = 0;
ret = -1;
if(!waserror()) {
if(scallnr >= nsyscall || systab[scallnr] == 0){
pprint("bad sys call number %ld pc %#lux\n",
scallnr, ur->pc);
postnote(up, 1, "sys: bad sys call", NDebug);
error(Ebadarg);
}
if(sp & (BY2WD-1)){
pprint("odd sp in sys call pc %#lux sp %#lux\n",
ur->pc, ur->sp);
postnote(up, 1, "sys: odd stack", 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]((va_list)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("[%lud %s] syscall %lud: %s\n",
up->pid, up->text, scallnr, up->errstr);
}
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");
}
sctracefinish(scallnr, sp, ret, startns);
ur->pc += 4;
ur->r1 = ret;
up->psstate = 0;
up->insyscall = 0;
if(scallnr == NOTED) /* ugly hack */
noted(ur, *(ulong*)(sp+BY2WD)); /* may return */
splhi();
if(scallnr!=RFORK && (up->procctl || up->nnote))
notify(ur);
/* if we delayed sched because we held a lock, sched now */
if(up->delaysched)
sched();
kexit(ur);
}
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->status &= ~CU1; /* FPU off when returning */
cur->r1 = 0;
cur->pc += 4;
}
static
void
linkproc(void)
{
spllo();
up->kpfun(up->kparg);
pexit("kproc exiting", 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;
}
/* set up user registers before return from exec() */
uintptr
execregs(ulong entry, ulong ssize, ulong nargs)
{
Ureg *ur;
ulong *sp;
sp = (ulong*)(USTKTOP - ssize);
*--sp = nargs;
ur = (Ureg*)up->dbgreg;
ur->usp = (ulong)sp;
ur->pc = entry - 4; /* syscall advances it */
return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */
}
ulong
userpc(void)
{
Ureg *ur;
ur = (Ureg*)up->dbgreg;
return ur->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, r27;
r27 = xp->r27; /* return PC for GEVector() */
status = xp->status;
memmove(pureg, uva, n);
xp->r27 = r27;
xp->status = status;
}
/*
* Give enough context in the ureg to produce a kernel stack for
* a sleeping process
*/
void
setkernur(Ureg *xp, Proc *p)
{
xp->pc = p->sched.pc;
xp->sp = p->sched.sp;
xp->r24 = (ulong)p; /* up */
xp->r31 = (ulong)sched;
}
ulong
dbgpc(Proc *p)
{
Ureg *ur;
ur = p->dbgreg;
if(ur == 0)
return 0;
return ur->pc;
}