155110Storek /* 255110Storek * Copyright (c) 1992 The Regents of the University of California. 355110Storek * All rights reserved. 455110Storek * 555110Storek * This software was developed by the Computer Systems Engineering group 655110Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 755110Storek * contributed to Berkeley. 855110Storek * 9*55500Sbostic * All advertising materials mentioning features or use of this software 10*55500Sbostic * must display the following acknowledgement: 11*55500Sbostic * This product includes software developed by the University of 12*55500Sbostic * California, Lawrence Berkeley Laboratories. 13*55500Sbostic * 1455110Storek * %sccs.include.redist.c% 1555110Storek * 16*55500Sbostic * @(#)fpu.c 7.2 (Berkeley) 07/21/92 1755110Storek * 1855110Storek * from: $Header: fpu.c,v 1.2 92/06/17 05:41:27 torek Exp $ 1955110Storek */ 2055110Storek 2155110Storek #include "sys/param.h" 2255110Storek #include "sys/proc.h" 2355110Storek #include "sys/signal.h" 2455110Storek #include "sys/systm.h" 2555110Storek #include "sys/syslog.h" 2655110Storek 2755110Storek #include "machine/instr.h" 2855110Storek #include "machine/reg.h" 2955110Storek 3055110Storek #include "fpu_emu.h" 3155110Storek 3255110Storek /* 3355110Storek * fpu_execute returns the following error numbers (0 = no error): 3455110Storek */ 3555110Storek #define FPE 1 /* take a floating point exception */ 3655110Storek #define NOTFPU 2 /* not an FPU instruction */ 3755110Storek 3855110Storek /* 3955110Storek * Translate current exceptions into `first' exception. The 4055110Storek * bits go the wrong way for ffs() (0x10 is most important, etc). 4155110Storek * There are only 5, so do it the obvious way. 4255110Storek */ 4355110Storek #define X1(x) x 4455110Storek #define X2(x) x,x 4555110Storek #define X4(x) x,x,x,x 4655110Storek #define X8(x) X4(x),X4(x) 4755110Storek #define X16(x) X8(x),X8(x) 4855110Storek 4955110Storek static char cx_to_trapx[] = { 5055110Storek X1(FSR_NX), 5155110Storek X2(FSR_DZ), 5255110Storek X4(FSR_UF), 5355110Storek X8(FSR_OF), 5455110Storek X16(FSR_NV) 5555110Storek }; 5655110Storek static u_char fpu_codes[] = { 5755110Storek X1(FPE_FLTINEX_TRAP), 5855110Storek X2(FPE_FLTDIV_TRAP), 5955110Storek X4(FPE_FLTUND_TRAP), 6055110Storek X8(FPE_FLTOVF_TRAP), 6155110Storek X16(FPE_FLTOPERR_TRAP) 6255110Storek }; 6355110Storek 6455110Storek /* 6555110Storek * The FPU gave us an exception. Clean up the mess. Note that the 6655110Storek * fp queue can only have FPops in it, never load/store FP registers 6755110Storek * nor FBfcc instructions. Experiments with `crashme' prove that 6855110Storek * unknown FPops do enter the queue, however. 6955110Storek */ 7055110Storek fpu_cleanup(p, fs) 7155110Storek register struct proc *p; 7255110Storek register struct fpstate *fs; 7355110Storek { 7455110Storek register int i, fsr = fs->fs_fsr, error; 7555110Storek union instr instr; 7655110Storek struct fpemu fe; 7755110Storek 7855110Storek switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) { 7955110Storek 8055110Storek case FSR_TT_NONE: 8155110Storek panic("fpu_cleanup 1"); /* ??? */ 8255110Storek break; 8355110Storek 8455110Storek case FSR_TT_IEEE: 8555110Storek /* XXX missing trap address! */ 8655110Storek if ((i = fsr & FSR_CX) == 0) 8755110Storek panic("fpu ieee trap, but no exception"); 8855110Storek trapsignal(p, SIGFPE, fpu_codes[i - 1]); 8955110Storek break; /* XXX should return, but queue remains */ 9055110Storek 9155110Storek case FSR_TT_UNFIN: 9255110Storek case FSR_TT_UNIMP: 9355110Storek if (fs->fs_qsize == 0) 9455110Storek panic("fpu_cleanup 2"); 9555110Storek break; 9655110Storek 9755110Storek case FSR_TT_SEQ: 9855110Storek panic("fpu sequence error"); 9955110Storek /* NOTREACHED */ 10055110Storek 10155110Storek case FSR_TT_HWERR: 10255110Storek log(LOG_ERR, "fpu hardware error (%s[%d])\n", 10355110Storek p->p_comm, p->p_pid); 10455110Storek uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid); 10555110Storek trapsignal(p, SIGFPE, -1); /* ??? */ 10655110Storek goto out; 10755110Storek 10855110Storek default: 10955110Storek printf("fsr=%x\n", fsr); 11055110Storek panic("fpu error"); 11155110Storek } 11255110Storek 11355110Storek /* emulate the instructions left in the queue */ 11455110Storek fe.fe_fpstate = fs; 11555110Storek for (i = 0; i < fs->fs_qsize; i++) { 11655110Storek instr.i_int = fs->fs_queue[i].fq_instr; 11755110Storek if (instr.i_any.i_op != IOP_reg || 11855110Storek (instr.i_op3.i_op3 != IOP3_FPop1 && 11955110Storek instr.i_op3.i_op3 != IOP3_FPop2)) 12055110Storek panic("bogus fpu queue"); 12155110Storek error = fpu_execute(&fe, instr); 12255110Storek switch (error) { 12355110Storek 12455110Storek case 0: 12555110Storek continue; 12655110Storek 12755110Storek case FPE: 12855110Storek trapsignal(p, SIGFPE, 12955110Storek fpu_codes[(fs->fs_fsr & FSR_CX) - 1]); 13055110Storek break; 13155110Storek 13255110Storek case NOTFPU: 13355110Storek trapsignal(p, SIGILL, 0); /* ??? code? */ 13455110Storek break; 13555110Storek 13655110Storek default: 13755110Storek panic("fpu_cleanup 3"); 13855110Storek /* NOTREACHED */ 13955110Storek } 14055110Storek /* XXX should stop here, but queue remains */ 14155110Storek } 14255110Storek out: 14355110Storek fs->fs_qsize = 0; 14455110Storek } 14555110Storek 14655110Storek #ifdef notyet 14755110Storek /* 14855110Storek * If we have no FPU at all (are there any machines like this out 14955110Storek * there!?) we have to emulate each instruction, and we need a pointer 15055110Storek * to the trapframe so that we can step over them and do FBfcc's. 15155110Storek * We know the `queue' is empty, though; we just want to emulate 15255110Storek * the instruction at tf->tf_pc. 15355110Storek */ 15455110Storek fpu_emulate(p, tf, fs) 15555110Storek struct proc *p; 15655110Storek register struct trapframe *tf; 15755110Storek register struct fpstate *fs; 15855110Storek { 15955110Storek 16055110Storek do { 16155110Storek fetch instr from pc 16255110Storek decode 16355110Storek if (integer instr) { 16455110Storek /* 16555110Storek * We do this here, rather than earlier, to avoid 16655110Storek * losing even more badly than usual. 16755110Storek */ 16855110Storek if (p->p_addr->u_pcb.pcb_uw) { 16955110Storek write_user_windows(); 17055110Storek if (rwindow_save(p)) 17155110Storek sigexit(p, SIGILL); 17255110Storek } 17355110Storek if (loadstore) { 17455110Storek do_it; 17555110Storek pc = npc, npc += 4 17655110Storek } else if (fbfcc) { 17755110Storek do_annul_stuff; 17855110Storek } else 17955110Storek return; 18055110Storek } else if (fpu instr) { 18155110Storek fe.fe_fsr = fs->fs_fsr &= ~FSR_CX; 18255110Storek error = fpu_execute(&fe, fs, instr); 18355110Storek switch (error) { 18455110Storek etc; 18555110Storek } 18655110Storek } else 18755110Storek return; 18855110Storek if (want to reschedule) 18955110Storek return; 19055110Storek } while (error == 0); 19155110Storek } 19255110Storek #endif 19355110Storek 19455110Storek /* 19555110Storek * Execute an FPU instruction (one that runs entirely in the FPU; not 19655110Storek * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 19755110Storek * modified to reflect the setting the hardware would have left. 19855110Storek * 19955110Storek * Note that we do not catch all illegal opcodes, so you can, for instance, 20055110Storek * multiply two integers this way. 20155110Storek */ 20255110Storek int 20355110Storek fpu_execute(fe, instr) 20455110Storek register struct fpemu *fe; 20555110Storek union instr instr; 20655110Storek { 20755110Storek register struct fpn *fp; 20855110Storek register int opf, rs1, rs2, rd, type, mask, fsr, cx; 20955110Storek register struct fpstate *fs; 21055110Storek u_int space[4]; 21155110Storek 21255110Storek /* 21355110Storek * `Decode' and execute instruction. Start with no exceptions. 21455110Storek * The type of any i_opf opcode is in the bottom two bits, so we 21555110Storek * squish them out here. 21655110Storek */ 21755110Storek opf = instr.i_opf.i_opf; 21855110Storek type = opf & 3; 21955110Storek mask = "\0\0\1\3"[type]; 22055110Storek rs1 = instr.i_opf.i_rs1 & ~mask; 22155110Storek rs2 = instr.i_opf.i_rs2 & ~mask; 22255110Storek rd = instr.i_opf.i_rd & ~mask; 22355110Storek #ifdef notdef 22455110Storek if ((rs1 | rs2 | rd) & mask) 22555110Storek return (BADREG); 22655110Storek #endif 22755110Storek fs = fe->fe_fpstate; 22855110Storek fe->fe_fsr = fs->fs_fsr & ~FSR_CX; 22955110Storek fe->fe_cx = 0; 23055110Storek switch (opf >>= 2) { 23155110Storek 23255110Storek default: 23355110Storek return (NOTFPU); 23455110Storek 23555110Storek case FMOV >> 2: /* these should all be pretty obvious */ 23655110Storek rs1 = fs->fs_regs[rs2]; 23755110Storek goto mov; 23855110Storek 23955110Storek case FNEG >> 2: 24055110Storek rs1 = fs->fs_regs[rs2] ^ (1 << 31); 24155110Storek goto mov; 24255110Storek 24355110Storek case FABS >> 2: 24455110Storek rs1 = fs->fs_regs[rs2] & ~(1 << 31); 24555110Storek mov: 24655110Storek fs->fs_regs[rd] = rs1; 24755110Storek fs->fs_fsr = fe->fe_fsr; 24855110Storek return (0); /* success */ 24955110Storek 25055110Storek case FSQRT >> 2: 25155110Storek fpu_explode(fe, &fe->fe_f1, type, rs2); 25255110Storek fp = fpu_sqrt(fe); 25355110Storek break; 25455110Storek 25555110Storek case FADD >> 2: 25655110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 25755110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 25855110Storek fp = fpu_add(fe); 25955110Storek break; 26055110Storek 26155110Storek case FSUB >> 2: 26255110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 26355110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 26455110Storek fp = fpu_sub(fe); 26555110Storek break; 26655110Storek 26755110Storek case FMUL >> 2: 26855110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 26955110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 27055110Storek fp = fpu_mul(fe); 27155110Storek break; 27255110Storek 27355110Storek case FDIV >> 2: 27455110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 27555110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 27655110Storek fp = fpu_div(fe); 27755110Storek break; 27855110Storek 27955110Storek case FCMP >> 2: 28055110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 28155110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 28255110Storek fpu_compare(fe, 0); 28355110Storek goto cmpdone; 28455110Storek 28555110Storek case FCMPE >> 2: 28655110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 28755110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 28855110Storek fpu_compare(fe, 1); 28955110Storek cmpdone: 29055110Storek /* 29155110Storek * The only possible exception here is NV; catch it 29255110Storek * early and get out, as there is no result register. 29355110Storek */ 29455110Storek cx = fe->fe_cx; 29555110Storek fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); 29655110Storek if (cx != 0) { 29755110Storek if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { 29855110Storek fs->fs_fsr = (fsr & ~FSR_FTT) | 29955110Storek (FSR_TT_IEEE << FSR_FTT_SHIFT); 30055110Storek return (FPE); 30155110Storek } 30255110Storek fsr |= FSR_NV << FSR_AX_SHIFT; 30355110Storek } 30455110Storek fs->fs_fsr = fsr; 30555110Storek return (0); 30655110Storek 30755110Storek case FSMULD >> 2: 30855110Storek case FDMULX >> 2: 30955110Storek if (type == FTYPE_EXT) 31055110Storek return (NOTFPU); 31155110Storek fpu_explode(fe, &fe->fe_f1, type, rs1); 31255110Storek fpu_explode(fe, &fe->fe_f2, type, rs2); 31355110Storek type++; /* single to double, or double to quad */ 31455110Storek fp = fpu_mul(fe); 31555110Storek break; 31655110Storek 31755110Storek case FTOS >> 2: 31855110Storek case FTOD >> 2: 31955110Storek case FTOX >> 2: 32055110Storek case FTOI >> 2: 32155110Storek fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 32255110Storek type = opf & 3; /* sneaky; depends on instruction encoding */ 32355110Storek break; 32455110Storek } 32555110Storek 32655110Storek /* 32755110Storek * ALU operation is complete. Collapse the result and then check 32855110Storek * for exceptions. If we got any, and they are enabled, do not 32955110Storek * alter the destination register, just stop with an exception. 33055110Storek * Otherwise set new current exceptions and accrue. 33155110Storek */ 33255110Storek fpu_implode(fe, fp, type, space); 33355110Storek cx = fe->fe_cx; 33455110Storek fsr = fe->fe_fsr; 33555110Storek if (cx != 0) { 33655110Storek mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK; 33755110Storek if (cx & mask) { 33855110Storek /* not accrued??? */ 33955110Storek fs->fs_fsr = (fsr & ~FSR_FTT) | 34055110Storek (FSR_TT_IEEE << FSR_FTT_SHIFT) | 34155110Storek (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT); 34255110Storek return (FPE); 34355110Storek } 34455110Storek fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT); 34555110Storek } 34655110Storek fs->fs_fsr = fsr; 34755110Storek fs->fs_regs[rd] = space[0]; 34855110Storek if (type >= FTYPE_DBL) { 34955110Storek fs->fs_regs[rd + 1] = space[1]; 35055110Storek if (type > FTYPE_DBL) { 35155110Storek fs->fs_regs[rd + 2] = space[2]; 35255110Storek fs->fs_regs[rd + 3] = space[3]; 35355110Storek } 35455110Storek } 35555110Storek return (0); /* success */ 35655110Storek } 357