M port/sysproc.c => port/sysproc.c +6 -1
@@ 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);
/*
M power/fns.h => power/fns.h +1 -0
@@ 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);
A power/fptrap.c => power/fptrap.c +315 -0
@@ 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;
+
+}
M power/trap.c => power/trap.c +11 -9
@@ 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)
M ss/dat.h => ss/dat.h +2 -0
@@ 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];
M ss/fns.h => ss/fns.h +1 -0
@@ 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*);
M ss/fptrap.c => ss/fptrap.c +66 -37
@@ 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;
}
M ss/l.s => ss/l.s +1 -0
@@ 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
M ss/main.c => ss/main.c +1 -0
@@ 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));
/*
M ss/trap.c => ss/trap.c +28 -44
@@ 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");