From 5a95dc0d9ca47d56ea38f79aa304a50c353be930 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Sun, 2 Aug 1992 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1992-08-02 --- port/sysproc.c | 7 +- power/fns.h | 1 + power/fptrap.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++ power/trap.c | 20 ++-- ss/dat.h | 2 + ss/fns.h | 1 + ss/fptrap.c | 103 ++++++++++------ ss/l.s | 1 + ss/main.c | 1 + ss/trap.c | 72 +++++------ 10 files changed, 432 insertions(+), 91 deletions(-) create mode 100644 power/fptrap.c diff --git a/port/sysproc.c b/port/sysproc.c index fd61b59b5d797c1d29bf0ab0cba8d4a30823aa69..7be02418e3daf70573d813758e42f6064cbba649 100644 --- a/port/sysproc.c +++ b/port/sysproc.c @@ -308,6 +308,12 @@ sysexec(ulong *arg) nargs++; } ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1)); + /* + * 8-byte align SP for those (e.g. sparc) that need it. + * execregs() will subtract another 4 bytes for argc. + */ + if((ssize+4) & 7) + ssize += 4; spage = (ssize+(BY2PG-1)) >> PGSHIFT; /* * Build the stack segment, putting it in kernel virtual for the moment @@ -315,7 +321,6 @@ sysexec(ulong *arg) if(spage > TSTKSIZ) error(Enovmem); - p->seg[ESEG] = newseg(SG_STACK, TSTKTOP-USTKSIZE, USTKSIZE/BY2PG); /* diff --git a/power/fns.h b/power/fns.h index ff0f6a4b256b0fc1fb8b2fcd4df6c8b795f7679d..bc7a1f36350698ed1ae2bd3c8dfcf222c7647aa2 100644 --- a/power/fns.h +++ b/power/fns.h @@ -23,6 +23,7 @@ ulong fcr31(void); void firmware(int); void flushmmu(void); #define flushpage(s) icflush((void*)(s), BY2PG) +int fptrap(Ureg*, ulong); void gettlb(int, ulong*); ulong gettlbvirt(int); void gotopc(ulong); diff --git a/power/fptrap.c b/power/fptrap.c new file mode 100644 index 0000000000000000000000000000000000000000..747d129e9e54bb5c115bceac2fa99b27675af713 --- /dev/null +++ b/power/fptrap.c @@ -0,0 +1,315 @@ +#include "u.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 FPinstr FPinstr; +struct FPinstr +{ + ulong op; + ulong load; + ulong store; + ulong branch; + ulong fmt; + ulong fs; + ulong fd; + ulong ft; + ulong cft; +}; + +enum /* op */ +{ + ABS = 5, + ADD = 0, + CVTD = 65, + CVTS = 64, + CVTW = 68, + DIV = 3, + MOV = 6, + MUL = 2, + NEG = 7, + SUB = 1, +}; + +static int fpunimp(Ureg*, ulong, ulong); +static ulong branch(Ureg*, ulong); + +int +fptrap(Ureg *ur, ulong fcr31) +{ + ulong iw, x, npc; + int i, ret; + + savefpregs(&u->fpsave); + if(ur->cause & (1<<31)) + iw = *(ulong*)(ur->pc+4); + else + iw = *(ulong*)ur->pc; + ret = 0; + x = fcr31>>12; + fcr31 &= ~(0x3F<<12); + for(i=0; i<6; i++,x>>=1) + if(x & 1) + switch(i){ + case 0: /* inexact */ +pprint("inexact\n"); +return 0; + case 1: /* underflow */ +pprint("underflow\n"); +return 0; + case 2: /* overflow */ +pprint("overflow\n"); +return 0; + case 3: /* division by zero */ + return 0; + case 4: /* invalid operation */ +pprint("invalid op\n"); +return 0; + case 5: /* unimplemented operation */ + ret = fpunimp(ur, fcr31, iw); + } + if(ret){ + if(ur->cause & (1<<31)){ + npc = branch(ur, fcr31); + if(npc) + ur->pc = npc; + else + return 0; + }else + ur->pc += 4; + restfpregs(&u->fpsave, fcr31); + } + return ret; +} + +static int +fpdas(ulong iw, FPinstr *fp) +{ + memset(fp, ~0, sizeof(*fp)); + if((iw>>25) == 0x23){ + fp->op = iw & ((1<<5)-1); + fp->fmt = (iw>>21) & ((1<<4)-1); + fp->ft = (iw>>16) & ((1<<5)-1); + fp->fs = (iw>>11) & ((1<<5)-1); + fp->fd = (iw>>6) & ((1<<5)-1); + fp->cft = (iw>>21) & ((1<<5)-1); + return 1; + } + return 0; +} + +static void +unpack(FPsave *f, int fmt, int reg, int *sign, int *exp) +{ + *sign = 1; + if(f->fpreg[reg] & 0x80000000) + *sign = -1; + switch(fmt){ + case 0: + *exp = ((f->fpreg[reg]>>23)&0xFF) - ((1<<7)-2); + break; + case 1: + if(reg & 1){ + pprint("unaligned double fp register\n"); + reg &= ~1; + } +pprint("%lux %lux\n", f->fpreg[reg], f->fpreg[reg+1]); + *exp = ((f->fpreg[reg]>>20)&0x7FF) - ((1<<10)-2); + break; + } +} + +static void +zeroreg(FPsave *f, int fmt, int reg, int sign) +{ + int size; + + size = 0; + switch(fmt){ + case 0: + size = 4; + break; + case 1: + if(reg & 1) + reg &= ~1; + size = 8; + break; + } + memset(&f->fpreg[reg], 0, size); + if(sign < 0) + f->fpreg[reg] |= 0x80000000; +} + +static int +fpunimp(Ureg *ur, ulong fcr31, ulong iw) +{ + FPinstr instr; + int ss, st, sd; + int es, et, ed; + int maxe, maxm; + +pprint("fpunimp %lux %lux\n", iw, iw>>25); + if(!fpdas(iw, &instr)) + return 0; +pprint("%d\n", instr.op); + if(instr.op == ~0) + return 0; + unpack(&u->fpsave, instr.fmt, instr.fs, &ss, &es); + unpack(&u->fpsave, instr.fmt, instr.ft, &st, &et); + ed = 0; + maxe = 0; + maxm = 0; + switch(instr.fmt){ + case 0: + maxe = 1<<7; + maxm = 24; + break; + case 1: + maxe = 1<<10; + maxm = 53; + break; + } + switch(instr.op){ + case SUB: + st = -st; + case ADD: + if(es<-(maxe-maxm) && et<-(maxe-maxm)) + ed = -maxe; + if(es > et) + sd = es; + else + sd = et; + break; + + case DIV: + et = -et; + case MUL: + sd = 1; + if(ss != st) + sd = -1; + ed = es + et; + break; + default: + pprint("unknown unimplemented fp op\n"); + return 0; + } + if(ed <= -(maxe-4)){ /* guess: underflow */ +pprint("guess underflow\n"); + zeroreg(&u->fpsave, instr.fmt, instr.fd, sd); + return 1; + } + return 0; +} + +static ulong* +reg(Ureg *ur, int regno) +{ + /* regs go from R31 down in ureg, R29 is missing */ + if(regno == 31) + return &ur->r31; + if(regno == 30) + return &ur->r30; + if(regno == 29) + return &ur->sp; + return (&ur->r28) + (28-regno); +} + +static ulong +branch(Ureg *ur, ulong fcr31) +{ + ulong iw, npc, rs, rt, rd, offset; + + iw = *(ulong*)ur->pc; + rs = (iw>>21) & 0x1F; + if(rs) + rs = *reg(ur, rs); + rt = (iw>>16) & 0x1F; + if(rt) + rt = *reg(ur, rt); + offset = iw & ((1<<16)-1); + if(offset & (1<<15)) /* sign extend */ + offset |= ~((1<<16)-1); + offset <<= 2; + /* + * Integer unit jumps first + */ + switch(iw>>26){ + case 0: /* SPECIAL: JR or JALR */ + switch(iw&0x3F){ + case 0x09: /* JALR */ + rd = (iw>>11) & 0x1F; + if(rd) + *reg(ur, rd) = ur->pc+8; + /* fall through */ + case 0x08: /* JR */ + return rs; + default: + return 0; + } + case 1: /* BCOND */ + switch((iw>>16) & 0x1F){ + case 0x10: /* BLTZAL */ + ur->r31 = ur->pc + 8; + /* fall through */ + case 0x00: /* BLTZ */ + if((long)rs < 0) + return ur->pc+4 + offset; + return ur->pc + 8; + case 0x11: /* BGEZAL */ + ur->r31 = ur->pc + 8; + /* fall through */ + case 0x01: /* BGEZ */ + if((long)rs >= 0) + return ur->pc+4 + offset; + return ur->pc + 8; + default: + return 0; + } + case 3: /* JAL */ + ur->r31 = ur->pc+8; + /* fall through */ + case 2: /* JMP */ + npc = iw & ((1<<26)-1); + npc <<= 2; + return npc | (ur->pc&0xF0000000); + case 4: /* BEQ */ + if(rs == rt) + return ur->pc+4 + offset; + return ur->pc + 8; + case 5: /* BNE */ + if(rs != rt) + return ur->pc+4 + offset; + return ur->pc + 8; + case 6: /* BLEZ */ + if((long)rs <= 0) + return ur->pc+4 + offset; + return ur->pc + 8; + case 7: /* BGTZ */ + if((long)rs > 0) + return ur->pc+4 + offset; + return ur->pc + 8; + } + /* + * Floating point unit jumps + */ + if((iw>>26) == 0x11) /* COP1 */ + switch((iw>>16) & 0x3C1){ + case 0x101: /* BCT */ + case 0x181: /* BCT */ + if(fcr31 & (1<<23)) + return ur->pc+4 + offset; + return ur->pc + 8; + case 0x100: /* BCF */ + case 0x180: /* BCF */ + if(!(fcr31 & (1<<23))) + return ur->pc+4 + offset; + return ur->pc + 8; + } + pprint("fptrap: can't do jump %lux\n", iw); + return 0; + +} diff --git a/power/trap.c b/power/trap.c index 4aba77823bb1ea5949fa5cceb9f208ce7479d4ba..a9c7fa860d13bf0d4a3ca79e7ba041aed9defb68 100644 --- a/power/trap.c +++ b/power/trap.c @@ -75,11 +75,11 @@ void trap(Ureg *ur) { int ecode; - int user; - ulong x; + int user, x; + ulong fcr31; char buf[ERRLEN]; - SET(x); + SET(fcr31); ecode = EXCCODE(ur->cause); LEDON(ecode); user = ur->status&KUP; @@ -92,14 +92,15 @@ trap(Ureg *ur) if(u && u->p->state==Running){ if(u->p->fpstate == FPactive) { if(ur->cause & INTR3){ /* FP trap */ - x = clrfpintr(); + fcr31 = clrfpintr(); + if(user && fptrap(ur, fcr31)) + goto Return; ecode = FPEXC; + goto Default; } savefpregs(&u->fpsave); u->p->fpstate = FPinactive; ur->status &= ~CU1; - if(ecode == FPEXC) - goto Default; } } intr(ur); @@ -151,7 +152,7 @@ trap(Ureg *ur) if(user){ spllo(); if(ecode == FPEXC) - sprint(buf, "sys: fp: %s FCR31 %lux", fpexcname(x), x); + sprint(buf, "sys: fp: %s FCR31 %lux", fpexcname(fcr31), fcr31); else sprint(buf, "sys: %s", excname[ecode]); @@ -159,7 +160,7 @@ trap(Ureg *ur) }else{ print("%s %s pc=%lux\n", user? "user": "kernel", excname[ecode], ur->pc); if(ecode == FPEXC) - print("fp: %s FCR31 %lux\n", fpexcname(x), x); + print("fp: %s FCR31 %lux\n", fpexcname(fcr31), fcr31); dumpregs(ur); if(m->machno == 0) spllo(); @@ -168,6 +169,7 @@ trap(Ureg *ur) } splhi(); + Return: if(user) { notify(ur); if(u->p->fpstate == FPinactive) { @@ -600,7 +602,7 @@ setvmevec(int v, void (*f)(int)) } /* This routine must save the values of registers the user is not permitted to write - * from devproc and the restore the saved values before returning + * from devproc and then restore the saved values before returning */ void setregisters(Ureg *xp, char *pureg, char *uva, int n) diff --git a/ss/dat.h b/ss/dat.h index 023e9ff6fa4b7b1d8b420231fd59df20de8ca043..da145dad68d95a890040b8cdc62483deac10b9bc 100644 --- a/ss/dat.h +++ b/ss/dat.h @@ -124,6 +124,8 @@ struct Mach int cs; int syscall; int load; + int fpunsafe; /* FP unsaved */ + int fptrap; /* FP trap occurred while unsafe */ int intr; int stack[1]; diff --git a/ss/fns.h b/ss/fns.h index 66bbc36198437789a6127b86066d2c2da5fd35ef..a673e8ae8cf34ac975457c4e0e1cef3d71ff9a52 100644 --- a/ss/fns.h +++ b/ss/fns.h @@ -13,6 +13,7 @@ void faultsparc(Ureg*); void flushcpucache(void); #define flushpage(x) int fpcr(int); +int fpquiet(void); void fpregrestore(char*); void fpregsave(char*); void fprestore(FPsave*); diff --git a/ss/fptrap.c b/ss/fptrap.c index 92378cb9166d1453b86ba2e0afdc23119260b00e..42c3a9a7c746d2385da2b1a0f2bb205280705e9b 100644 --- a/ss/fptrap.c +++ b/ss/fptrap.c @@ -8,7 +8,7 @@ #include "../port/error.h" static int unfinished(FPsave*); -static int fixq(FPsave*, int); +static void fixq(FPsave*, int); extern void* bbmalloc(int); int @@ -35,9 +35,7 @@ fptrap(void) clearftt(fsr & ~0x1F); /* clear ftt and cexc */ restfpregs(&u->fpsave); enabfp(); /* restfpregs disables it */ - n = fixq(&u->fpsave, n); - if(n > 0) - goto again; + fixq(&u->fpsave, n); } return ret; } @@ -172,52 +170,83 @@ unfinished(FPsave *f) return 0; } -static int +static void fixq(FPsave *f, int n) { ulong instr, fsr; ulong *ip; while(n > 1){ - memmove(&f->q[0], &f->q[1], (n-1)*sizeof f->q[0]); + --n; + memmove(&f->q[0], &f->q[1], n*sizeof f->q[0]); + memset(&f->q[n], 0, (NFPQ-n)*sizeof f->q[0]); instr = f->q[0].i; + /* + * Sparc says you have to emulate. To hell with that. + * Let's compile! + */ ip = bbmalloc(3*sizeof(ulong)); ip[0] = instr; ip[1] = 0x81c3e008; /* JMPL #8(R15), R0 [RETURN] */ ip[2] = 0x01000000; /* SETHI #0, R0 [NOP] */ - (*(void(*)(void))ip)(); /* - * WARNING: This code is wrong (and I don't know how - * to fix it without emulating the entire FPU in - * software--please let me knw) if the queued - * instruction gets an exception: the getfsr() generates - * a trap and the staggeringly inept SPARC design - * translates that to a reset, as you can't have - * nested exceptions. So here's the fairly solid but - * not fail-safe solution: jump out NOW if there's - * nothing else pending. If this last instruction - * causes an exception, it will fire when the *user* - * executes the next FP instruction. The system - * avoids executing any FP on the way out. The - * user will trap and we'll be back but will have - * made progress. The FQ will point to kernel space - * but the trap will happen in user space, and that's - * what matters. - * - * This same botch may cause the system to reset - * if a trap is pending when we call savefpregs() in - * trap.c. I don't know; the documentation is unclear. + * You can always pump one instruction without immediate trap. + * Therefore run one and wait for it to complete or trap. + * If it traps, we'll recurse but we won't overwrite + * our own data structures because there will be only + * one instruction uncompleted in the FPU. + * If that instruction generates an unfixable trap, + * stop the processing, which means a buglet: if the user + * process is attempting to resolve FP errors, it must + * find and extract the uncompleted instructions behind + * the trap by examining the non-zero members of the FPQ. + * Tough. */ - if(n == 1) - return 0; - for(;;){ - fsr = getfsr(); - if((fsr & (1<<13)) == 0) /* qne */ - break; - if(fsr & 0x1F) /* cexc */ - return n; + (*(void(*)(void))ip)(); + if(!fpquiet()) + break; + } +} + +/* + * Must only be called splhi() when it is safe to spllo(). Because the FP unit + * traps if you touch it when an exception is pending, and because if you + * trap with ET==0 you halt, this routine sets some global flags to enable + * the rest of the system to handle the trap that might occur here without + * upsetting the kernel. + */ +int +fpquiet(void) +{ + int i, notrap; + ulong fsr; + char buf[128]; + + i = 0; + notrap = 1; + m->fpunsafe = 1; + u->p->fpstate = FPinactive; + for(;;){ + m->fptrap = 0; + spllo(); + fsr = getfsr(); + splhi(); + if(m->fptrap){ + /* trap occurred and u->fpsave contains state */ + spllo(); + sprint(buf, "sys: %s", excname(8)); + postnote(u->p, 1, buf, NDebug); + notrap = 0; + break; + } + if((fsr&(1<<13)) == 0) + break; + if(++i > 1000){ + print("fp not quiescent\n"); + break; } - --n; } - return 0; + m->fpunsafe = 0; + u->p->fpstate = FPactive; + return notrap; } diff --git a/ss/l.s b/ss/l.s index 38df7d1130be433af0db3f8e260120a0093846da..463a0ec727dfc9c83e53891734b8c258a4757500 100644 --- a/ss/l.s +++ b/ss/l.s @@ -598,6 +598,7 @@ getfpq1: MOVW (R9), R10 ANDCC $(1<<13), R10 /* queue not empty? */ BE getfpq2 + MOVW (R8), R0 /* SS2 bug fix */ MOVD FQ, (R8) ADD $1, R7 ADD $8, R8 diff --git a/ss/main.c b/ss/main.c index c52f37c14ba94c3afa21fbc10d1477f3dd104f90..9a18c2541092bb75c47bd1ecb5a58fcf50926096 100644 --- a/ss/main.c +++ b/ss/main.c @@ -149,6 +149,7 @@ userinit(void) */ p->sched.pc = (((ulong)init0) - 8); /* 8 because of RETURN in gotolabel */ p->sched.sp = USERADDR+BY2PG-(1+MAXSYSARG)*BY2WD; + p->sched.sp &= ~7; /* SP must be 8-byte aligned */ p->upage = newpage(1, 0, USERADDR|(p->pid&0xFFFF)); /* diff --git a/ss/trap.c b/ss/trap.c index 6810ee49b679806f82989bbf6f91f48eb5483480..6a4264708e1d066b64cf26b39095373db9b41516 100644 --- a/ss/trap.c +++ b/ss/trap.c @@ -52,11 +52,11 @@ excname(ulong tbr) switch(tbr){ case 8: - if(u == 0){ - fsr = getfsr(); - sprint(excbuf, "fp: %s FSR %lux", - fptrapname[(fsr>>14)&7], fsr); - }else{ + if(u == 0) + panic("fptrap in kernel\n"); + else{ + if(m->fpunsafe==0 && u->p->fpstate!=FPactive) + panic("fptrap not active\n"); fsr = u->fpsave.fsr; sprint(excbuf, "fp: %s FQpc=0x%lux", fptrapname[(fsr>>14)&7], @@ -95,33 +95,10 @@ excname(ulong tbr) return excbuf; } -/* - * This routine is frightening. See comment in fptrap.c - */ -void -fpquiet(void) -{ - int i; - ulong fsr; - - i = 0; - fsr = getfsr(); - while(fsr & (1<<13)){ - if(fsr & 0x1F){ - print("trap in fpquiet\n"); - break; - } - if(++i > 1000){ - print("fp not quiescent\n"); - break; - } - } -} - void trap(Ureg *ur) { - int user, x; + int user, x, fpunsafe; char buf[64]; ulong tbr, iw; @@ -130,6 +107,7 @@ trap(Ureg *ur) u->dbgreg = ur; } + fpunsafe = 0; user = !(ur->psr&PSRPSUPER); tbr = (ur->tbr&0xFFF)>>4; /* SS2 bug: flush cache line holding trap entry in table */ @@ -137,11 +115,11 @@ trap(Ureg *ur) putw2(CACHETAGS+((TRAPS+16*tbr)&(VACSIZE-1)), 0); if(tbr > 16){ /* interrupt */ if(u && u->p->state==Running){ + /* if active, FPop at head of Q is probably an excep */ if(u->p->fpstate == FPactive){ - fpquiet(); - savefpregs(&u->fpsave); + fpunsafe = 1; + m->fpunsafe = 1; u->p->fpstate = FPinactive; - ur->psr &= ~PSREF; } } switch(tbr-16){ @@ -165,10 +143,9 @@ trap(Ureg *ur) case 1: /* instr. access */ case 9: /* data access */ if(u && u->p->fpstate==FPactive) { - fpquiet(); - savefpregs(&u->fpsave); + fpunsafe = 1; + m->fpunsafe = 1; u->p->fpstate = FPinactive; - ur->psr &= ~PSREF; } if(u){ x = u->p->insyscall; @@ -200,10 +177,13 @@ trap(Ureg *ur) } break; case 8: /* floating point exception */ - if(fptrap()){ - /* do NOT talk to the FPU: see fptrap.c */ + /* if unsafe, trap happened shutting down FPU; just return */ + if(m->fpunsafe){ + m->fptrap = (fptrap()==0); return; } + if(fptrap()) + goto Return; /* handled the problem */ break; default: break; @@ -213,14 +193,16 @@ trap(Ureg *ur) spllo(); sprint(buf, "sys: %s", excname(tbr)); postnote(u->p, 1, buf, NDebug); - }else{ - print("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc); - dumpregs(ur); - for(;;); - } + }else + panic("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc); } Return: - if(user) { + /* + * Handled the interrupt; now it's safe to look at the FPQ + */ + if(fpunsafe) + fpquiet(); + if(user){ notify(ur); if(u->p->fpstate == FPinactive) { restfpregs(&u->fpsave); @@ -443,7 +425,9 @@ notify(Ureg *ur) sent = 1; u->svpsr = ur->psr; sp = ur->usp; - sp -= sizeof(Ureg); + sp -= sizeof(Ureg)+ERRLEN+3*BY2WD; + sp &= ~7; /* SP must be 8-byte aligned */ + sp += ERRLEN+3*BY2WD; if(!okaddr((ulong)u->notify, 1, 0) || !okaddr(sp-ERRLEN-3*BY2WD, sizeof(Ureg)+ERRLEN-3*BY2WD, 0)){ pprint("suicide: bad address in notify\n");