M gnot/mem.h => gnot/mem.h +0 -1
@@ 15,7 15,6 @@
#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1))
#define ICACHESIZE 0
#define MB4 (4*1024*1024) /* Lots of things are 4Mb in size */
-#define MB (1024*1024)
#define MAXMACH 1 /* max # cpus system can run */
M pc/mem.h => pc/mem.h +0 -1
@@ 12,7 12,6 @@
#define WD2PG (BY2PG/BY2WD) /* words per page */
#define PGSHIFT 12 /* log(BY2PG) */
#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1))
-#define MB (1024*1024)
#define MAXMACH 1 /* max # cpus system can run */
M port/alloc.c => port/alloc.c +1 -1
@@ 113,7 113,7 @@ xinit(void)
palloc.np0 = np0;
palloc.np1 = np1;
- /* Save the bounds of kernel alloc memory for kernel mmu mapping (NeXT) */
+ /* Save the bounds of kernel alloc memory for kernel mmu mapping */
conf.base0 = (ulong)KADDR(conf.base0);
conf.base1 = (ulong)KADDR(conf.base1);
conf.npage0 = (ulong)KADDR(conf.npage0);
M port/portdat.h => port/portdat.h +1 -0
@@ 676,6 676,7 @@ struct Network
#define PRINTSIZE 256
#define NUMSIZE 12 /* size of formatted number */
+#define MB (1024*1024)
extern Conf conf;
extern char* conffile;
M power/mem.h => power/mem.h +0 -1
@@ 14,7 14,6 @@
#define PGSHIFT 12 /* log(BY2PG) */
#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1))
#define ICACHESIZE (64*1024) /* Power series */
-#define MB (1024*1024)
#define MAXMACH 4 /* max # cpus system can run */
M ss/dat.h => ss/dat.h +6 -1
@@ 48,11 48,16 @@ enum
FPinactive,
};
+#define NFPQ 4 /* just a guess */
+
struct FPsave
{
long fsr;
long fpreg[32];
- long pad; /* so fsr can be guaranteed at 4 mod 8 */
+ struct{
+ ulong a; /* address */
+ ulong i; /* instruction */
+ }q[NFPQ];
};
struct Conf
M ss/fns.h => ss/fns.h +6 -1
@@ 1,9 1,11 @@
#include "../port/portfns.h"
void cacheinit(void);
-void clearfpintr(void);
+void clearftt(ulong);
#define clearmmucache()
void clockinit(void);
+void disabfp(void);
+void enabfp(void);
void evenaddr(ulong);
char* excname(ulong);
void faultasync(Ureg*);
@@ 15,7 17,10 @@ void fpregrestore(char*);
void fpregsave(char*);
void fprestore(FPsave*);
void fpsave(FPsave*);
+int fptrap(void);
int getb2(ulong);
+int getfpq(ulong*);
+ulong getfsr(void);
int getw2(ulong);
void intrinit(void);
void ioinit(void);
A ss/fptrap.c => ss/fptrap.c +223 -0
@@ 0,0 1,223 @@
+#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"
+
+static int unfinished(FPsave*);
+static int fixq(FPsave*, int);
+extern void* bbmalloc(int);
+
+int
+fptrap(void)
+{
+ int n, ret;
+ ulong fsr;
+
+ n = getfpq(&u->fpsave.q[0].a);
+ if(n > NFPQ)
+ panic("FPQ %d\n", n);
+ again:
+ fsr = getfsr();
+ savefpregs(&u->fpsave);
+ u->fpsave.fsr = fsr;
+ ret = 0;
+ switch((fsr>>14) & 7){
+ case 2:
+ ret = unfinished(&u->fpsave);
+ break;
+ }
+ if(ret){
+ enabfp(); /* savefpregs disables it */
+ clearftt(fsr & ~0x1F); /* clear ftt and cexc */
+ restfpregs(&u->fpsave);
+ enabfp(); /* restfpregs disables it */
+ n = fixq(&u->fpsave, n);
+ if(n > 0)
+ goto again;
+ }
+ return ret;
+}
+
+static void
+unpack(FPsave *f, int size, int reg, int *sign, int *exp)
+{
+ *sign = 1;
+ if(f->fpreg[reg] & 0x80000000)
+ *sign = -1;
+ switch(size){
+ case 1:
+ *exp = ((f->fpreg[reg]>>23)&0xFF) - ((1<<7)-2);
+ break;
+ case 2:
+ if(reg & 1){
+ pprint("unaligned double fp register\n");
+ reg &= ~1;
+ }
+ *exp = ((f->fpreg[reg]>>20)&0x7FF) - ((1<<10)-2);
+ break;
+ case 3:
+ if(reg & 3){
+ pprint("unaligned quad fp register\n");
+ reg &= ~3;
+ }
+ *exp = ((f->fpreg[reg]>>16)&0x7FFF) - ((1<<14)-2);
+ break;
+ }
+}
+
+static void
+zeroreg(FPsave *f, int size, int reg, int sign)
+{
+ switch(size){
+ case 1:
+ size = 4;
+ break;
+ case 2:
+ if(reg & 1)
+ reg &= ~1;
+ size = 8;
+ break;
+ case 3:
+ if(reg & 3)
+ reg &= ~3;
+ size = 16;
+ break;
+ }
+ memset(&f->fpreg[reg], 0, size);
+ if(sign < 0)
+ f->fpreg[reg] |= 0x80000000;
+}
+
+static int
+unfinished(FPsave *f)
+{
+ ulong instr;
+ int size, maxe, maxm, op, rd, rs1, rs2;
+ int sd, ss1, ss2;
+ int ed, es1, es2;
+
+ instr = f->q[0].i;
+ if((instr&0xC1F80000) != 0x81A00000){
+ bad:
+ pprint("unknown unfinished instruction %lux\n", instr);
+ return 0;
+ }
+ size = (instr>>5) & 0x3;
+ if(size == 0)
+ goto bad;
+ maxe = 0;
+ maxm = 0;
+ switch(size){
+ case 1:
+ maxe = 1<<7;
+ maxm = 24;
+ break;
+ case 2:
+ maxe = 1<<10;
+ maxm = 53;
+ break;
+ case 3:
+ maxe = 1<<14;
+ maxm = 113;
+ break;
+ }
+ rd = (instr>>25) & 0x1F;
+ rs1 = (instr>>14) & 0x1F;
+ rs2 = (instr>>0) & 0x1F;
+ unpack(f, size, rs1, &ss1, &es1);
+ unpack(f, size, rs2, &ss2, &es2);
+ op = (instr>>7) & 0x7F;
+ ed = 0;
+ switch(op){
+ case 0x11: /* FSUB */
+ ss2 = -ss2;
+ case 0x10: /* FADD */
+ if(es1<-(maxe-maxm) && es2<-(maxe-maxm))
+ ed = -maxe;
+ if(es1 > es2)
+ sd = es1;
+ else
+ sd = es2;
+ break;
+
+ case 0x13: /* FDIV */
+ es2 = -es2;
+ case 0x12: /* FMUL */
+ sd = 1;
+ if(ss1 != ss2)
+ sd = -1;
+ ed = es1 + es2;
+ break;
+
+ case 0x31: /* F?TOS */
+ case 0x32: /* F?TOD */
+ case 0x33: /* F?TOQ */
+ if(es2 == maxe) /* NaN or Inf */
+ return 0;
+ sd = ss2;
+ ed = es2; /* if underflow, this will do the trick */
+ break;
+
+ default:
+ goto bad;
+ }
+ if(ed <= -(maxe-4)){ /* guess: underflow */
+ zeroreg(f, size, rd, sd);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+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]);
+ instr = f->q[0].i;
+ 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.
+ */
+ if(n == 1)
+ return 0;
+ for(;;){
+ fsr = getfsr();
+ if((fsr & (1<<13)) == 0) /* qne */
+ break;
+ if(fsr & 0x1F) /* cexc */
+ return n;
+ }
+ --n;
+ }
+ return 0;
+}
M ss/l.s => ss/l.s +33 -9
@@ 500,6 500,20 @@ TEXT savefpregs(SB), $0
MOVW R8, PSR
RETURN
+TEXT enabfp(SB), $0
+
+ MOVW PSR, R8
+ OR $PSREF, R8
+ MOVW R8, PSR
+ RETURN
+
+TEXT disabfp(SB), $0
+
+ MOVW PSR, R8
+ ANDN $PSREF, R8
+ MOVW R8, PSR
+ RETURN
+
TEXT restfpregs(SB), $0
MOVW PSR, R8
@@ 533,17 547,21 @@ TEXT restfpregs(SB), $0
MOVW R8, PSR
RETURN
-TEXT clearfpintr(SB), $0
+TEXT getfpq(SB), $0
- MOVW $fpq+BY2WD(SB), R7
- ANDN $0x7, R7 /* must be D aligned */
+ MOVW R7, R8 /* must be D aligned */
MOVW $fsr+0(SB), R9
-clrq:
- MOVD FQ, (R7)
+ MOVW $0, R7
+getfpq1:
MOVW FSR, (R9)
- MOVW (R9), R8
- AND $(1<<13), R8 /* queue not empty? */
- BNE clrq
+ MOVW (R9), R10
+ ANDCC $(1<<13), R10 /* queue not empty? */
+ BE getfpq2
+ MOVD FQ, (R8)
+ ADD $1, R7
+ ADD $8, R8
+ BA getfpq1
+getfpq2:
RETURN
TEXT getfsr(SB), $0
@@ 552,6 570,12 @@ TEXT getfsr(SB), $0
MOVW (R7), R7
RETURN
+TEXT clearftt(SB), $0
+ MOVW R7, fsr+0(SB)
+ MOVW $fsr+0(SB), R7
+ MOVW (R7), FSR
+ FMOVF F0, F0
+ RETURN
+
GLOBL mach0+0(SB), $MACHSIZE
-GLOBL fpq+0(SB), $(3*BY2WD)
GLOBL fsr+0(SB), $BY2WD
M ss/trap.c => ss/trap.c +33 -8
@@ 14,7 14,8 @@ int domuldiv(ulong, Ureg*);
extern void traplink(void);
extern void syslink(void);
-long ticks;
+ long ticks;
+static char excbuf[64]; /* BUG: not reentrant! */
char *trapname[]={
"reset",
@@ 31,14 32,37 @@ char *trapname[]={
"watchpoint detected",
};
+char *fptrapname[]={
+ "none",
+ "IEEE 754 exception",
+ "unfinished FP op",
+ "unimplemented FP op",
+ "sequence error",
+ "hardware error",
+ "invalid FP register",
+ "reserved",
+};
+
char*
excname(ulong tbr)
{
- static char buf[64]; /* BUG: not reentrant! */
char xx[64];
char *t;
+ ulong fsr;
switch(tbr){
+ case 8:
+ if(u == 0){
+ fsr = getfsr();
+ sprint(excbuf, "fp: %s FSR %lux",
+ fptrapname[(fsr>>14)&7], fsr);
+ }else{
+ fsr = u->fpsave.fsr;
+ sprint(excbuf, "fp: %s FQpc=0x%lux",
+ fptrapname[(fsr>>14)&7],
+ u->fpsave.q[0].a, fsr);
+ }
+ return excbuf;
case 36:
return "trap: cp disabled";
case 37:
@@ 65,10 89,10 @@ excname(ulong tbr)
t = xx;
}
if(strncmp(t, "fp: ", 4) == 0)
- strcpy(buf, t);
+ strcpy(excbuf, t);
else
- sprint(buf, "trap: %s", t);
- return buf;
+ sprint(excbuf, "trap: %s", t);
+ return excbuf;
}
void
@@ 148,7 172,10 @@ trap(Ureg *ur)
}
break;
case 8: /* floating point exception */
- clearfpintr();
+ if(fptrap()){
+ /* do NOT talk to the FPU: see fptrap.c */
+ return;
+ }
break;
default:
break;
@@ 157,8 184,6 @@ trap(Ureg *ur)
if(user){
spllo();
sprint(buf, "sys: %s", excname(tbr));
- if(tbr == 8)
- sprint(buf+strlen(buf), " FSR %lux", u->fpsave.fsr);
postnote(u->p, 1, buf, NDebug);
}else{
print("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc);