1 /* 2 * Copyright (c) 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)fpu.c 7.1 (Berkeley) 07/13/92 12 * 13 * from: $Header: fpu.c,v 1.2 92/06/17 05:41:27 torek Exp $ 14 */ 15 16 #include "sys/param.h" 17 #include "sys/proc.h" 18 #include "sys/signal.h" 19 #include "sys/systm.h" 20 #include "sys/syslog.h" 21 22 #include "machine/instr.h" 23 #include "machine/reg.h" 24 25 #include "fpu_emu.h" 26 27 /* 28 * fpu_execute returns the following error numbers (0 = no error): 29 */ 30 #define FPE 1 /* take a floating point exception */ 31 #define NOTFPU 2 /* not an FPU instruction */ 32 33 /* 34 * Translate current exceptions into `first' exception. The 35 * bits go the wrong way for ffs() (0x10 is most important, etc). 36 * There are only 5, so do it the obvious way. 37 */ 38 #define X1(x) x 39 #define X2(x) x,x 40 #define X4(x) x,x,x,x 41 #define X8(x) X4(x),X4(x) 42 #define X16(x) X8(x),X8(x) 43 44 static char cx_to_trapx[] = { 45 X1(FSR_NX), 46 X2(FSR_DZ), 47 X4(FSR_UF), 48 X8(FSR_OF), 49 X16(FSR_NV) 50 }; 51 static u_char fpu_codes[] = { 52 X1(FPE_FLTINEX_TRAP), 53 X2(FPE_FLTDIV_TRAP), 54 X4(FPE_FLTUND_TRAP), 55 X8(FPE_FLTOVF_TRAP), 56 X16(FPE_FLTOPERR_TRAP) 57 }; 58 59 /* 60 * The FPU gave us an exception. Clean up the mess. Note that the 61 * fp queue can only have FPops in it, never load/store FP registers 62 * nor FBfcc instructions. Experiments with `crashme' prove that 63 * unknown FPops do enter the queue, however. 64 */ 65 fpu_cleanup(p, fs) 66 register struct proc *p; 67 register struct fpstate *fs; 68 { 69 register int i, fsr = fs->fs_fsr, error; 70 union instr instr; 71 struct fpemu fe; 72 73 switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) { 74 75 case FSR_TT_NONE: 76 panic("fpu_cleanup 1"); /* ??? */ 77 break; 78 79 case FSR_TT_IEEE: 80 /* XXX missing trap address! */ 81 if ((i = fsr & FSR_CX) == 0) 82 panic("fpu ieee trap, but no exception"); 83 trapsignal(p, SIGFPE, fpu_codes[i - 1]); 84 break; /* XXX should return, but queue remains */ 85 86 case FSR_TT_UNFIN: 87 case FSR_TT_UNIMP: 88 if (fs->fs_qsize == 0) 89 panic("fpu_cleanup 2"); 90 break; 91 92 case FSR_TT_SEQ: 93 panic("fpu sequence error"); 94 /* NOTREACHED */ 95 96 case FSR_TT_HWERR: 97 log(LOG_ERR, "fpu hardware error (%s[%d])\n", 98 p->p_comm, p->p_pid); 99 uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid); 100 trapsignal(p, SIGFPE, -1); /* ??? */ 101 goto out; 102 103 default: 104 printf("fsr=%x\n", fsr); 105 panic("fpu error"); 106 } 107 108 /* emulate the instructions left in the queue */ 109 fe.fe_fpstate = fs; 110 for (i = 0; i < fs->fs_qsize; i++) { 111 instr.i_int = fs->fs_queue[i].fq_instr; 112 if (instr.i_any.i_op != IOP_reg || 113 (instr.i_op3.i_op3 != IOP3_FPop1 && 114 instr.i_op3.i_op3 != IOP3_FPop2)) 115 panic("bogus fpu queue"); 116 error = fpu_execute(&fe, instr); 117 switch (error) { 118 119 case 0: 120 continue; 121 122 case FPE: 123 trapsignal(p, SIGFPE, 124 fpu_codes[(fs->fs_fsr & FSR_CX) - 1]); 125 break; 126 127 case NOTFPU: 128 trapsignal(p, SIGILL, 0); /* ??? code? */ 129 break; 130 131 default: 132 panic("fpu_cleanup 3"); 133 /* NOTREACHED */ 134 } 135 /* XXX should stop here, but queue remains */ 136 } 137 out: 138 fs->fs_qsize = 0; 139 } 140 141 #ifdef notyet 142 /* 143 * If we have no FPU at all (are there any machines like this out 144 * there!?) we have to emulate each instruction, and we need a pointer 145 * to the trapframe so that we can step over them and do FBfcc's. 146 * We know the `queue' is empty, though; we just want to emulate 147 * the instruction at tf->tf_pc. 148 */ 149 fpu_emulate(p, tf, fs) 150 struct proc *p; 151 register struct trapframe *tf; 152 register struct fpstate *fs; 153 { 154 155 do { 156 fetch instr from pc 157 decode 158 if (integer instr) { 159 /* 160 * We do this here, rather than earlier, to avoid 161 * losing even more badly than usual. 162 */ 163 if (p->p_addr->u_pcb.pcb_uw) { 164 write_user_windows(); 165 if (rwindow_save(p)) 166 sigexit(p, SIGILL); 167 } 168 if (loadstore) { 169 do_it; 170 pc = npc, npc += 4 171 } else if (fbfcc) { 172 do_annul_stuff; 173 } else 174 return; 175 } else if (fpu instr) { 176 fe.fe_fsr = fs->fs_fsr &= ~FSR_CX; 177 error = fpu_execute(&fe, fs, instr); 178 switch (error) { 179 etc; 180 } 181 } else 182 return; 183 if (want to reschedule) 184 return; 185 } while (error == 0); 186 } 187 #endif 188 189 /* 190 * Execute an FPU instruction (one that runs entirely in the FPU; not 191 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 192 * modified to reflect the setting the hardware would have left. 193 * 194 * Note that we do not catch all illegal opcodes, so you can, for instance, 195 * multiply two integers this way. 196 */ 197 int 198 fpu_execute(fe, instr) 199 register struct fpemu *fe; 200 union instr instr; 201 { 202 register struct fpn *fp; 203 register int opf, rs1, rs2, rd, type, mask, fsr, cx; 204 register struct fpstate *fs; 205 u_int space[4]; 206 207 /* 208 * `Decode' and execute instruction. Start with no exceptions. 209 * The type of any i_opf opcode is in the bottom two bits, so we 210 * squish them out here. 211 */ 212 opf = instr.i_opf.i_opf; 213 type = opf & 3; 214 mask = "\0\0\1\3"[type]; 215 rs1 = instr.i_opf.i_rs1 & ~mask; 216 rs2 = instr.i_opf.i_rs2 & ~mask; 217 rd = instr.i_opf.i_rd & ~mask; 218 #ifdef notdef 219 if ((rs1 | rs2 | rd) & mask) 220 return (BADREG); 221 #endif 222 fs = fe->fe_fpstate; 223 fe->fe_fsr = fs->fs_fsr & ~FSR_CX; 224 fe->fe_cx = 0; 225 switch (opf >>= 2) { 226 227 default: 228 return (NOTFPU); 229 230 case FMOV >> 2: /* these should all be pretty obvious */ 231 rs1 = fs->fs_regs[rs2]; 232 goto mov; 233 234 case FNEG >> 2: 235 rs1 = fs->fs_regs[rs2] ^ (1 << 31); 236 goto mov; 237 238 case FABS >> 2: 239 rs1 = fs->fs_regs[rs2] & ~(1 << 31); 240 mov: 241 fs->fs_regs[rd] = rs1; 242 fs->fs_fsr = fe->fe_fsr; 243 return (0); /* success */ 244 245 case FSQRT >> 2: 246 fpu_explode(fe, &fe->fe_f1, type, rs2); 247 fp = fpu_sqrt(fe); 248 break; 249 250 case FADD >> 2: 251 fpu_explode(fe, &fe->fe_f1, type, rs1); 252 fpu_explode(fe, &fe->fe_f2, type, rs2); 253 fp = fpu_add(fe); 254 break; 255 256 case FSUB >> 2: 257 fpu_explode(fe, &fe->fe_f1, type, rs1); 258 fpu_explode(fe, &fe->fe_f2, type, rs2); 259 fp = fpu_sub(fe); 260 break; 261 262 case FMUL >> 2: 263 fpu_explode(fe, &fe->fe_f1, type, rs1); 264 fpu_explode(fe, &fe->fe_f2, type, rs2); 265 fp = fpu_mul(fe); 266 break; 267 268 case FDIV >> 2: 269 fpu_explode(fe, &fe->fe_f1, type, rs1); 270 fpu_explode(fe, &fe->fe_f2, type, rs2); 271 fp = fpu_div(fe); 272 break; 273 274 case FCMP >> 2: 275 fpu_explode(fe, &fe->fe_f1, type, rs1); 276 fpu_explode(fe, &fe->fe_f2, type, rs2); 277 fpu_compare(fe, 0); 278 goto cmpdone; 279 280 case FCMPE >> 2: 281 fpu_explode(fe, &fe->fe_f1, type, rs1); 282 fpu_explode(fe, &fe->fe_f2, type, rs2); 283 fpu_compare(fe, 1); 284 cmpdone: 285 /* 286 * The only possible exception here is NV; catch it 287 * early and get out, as there is no result register. 288 */ 289 cx = fe->fe_cx; 290 fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); 291 if (cx != 0) { 292 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { 293 fs->fs_fsr = (fsr & ~FSR_FTT) | 294 (FSR_TT_IEEE << FSR_FTT_SHIFT); 295 return (FPE); 296 } 297 fsr |= FSR_NV << FSR_AX_SHIFT; 298 } 299 fs->fs_fsr = fsr; 300 return (0); 301 302 case FSMULD >> 2: 303 case FDMULX >> 2: 304 if (type == FTYPE_EXT) 305 return (NOTFPU); 306 fpu_explode(fe, &fe->fe_f1, type, rs1); 307 fpu_explode(fe, &fe->fe_f2, type, rs2); 308 type++; /* single to double, or double to quad */ 309 fp = fpu_mul(fe); 310 break; 311 312 case FTOS >> 2: 313 case FTOD >> 2: 314 case FTOX >> 2: 315 case FTOI >> 2: 316 fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 317 type = opf & 3; /* sneaky; depends on instruction encoding */ 318 break; 319 } 320 321 /* 322 * ALU operation is complete. Collapse the result and then check 323 * for exceptions. If we got any, and they are enabled, do not 324 * alter the destination register, just stop with an exception. 325 * Otherwise set new current exceptions and accrue. 326 */ 327 fpu_implode(fe, fp, type, space); 328 cx = fe->fe_cx; 329 fsr = fe->fe_fsr; 330 if (cx != 0) { 331 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK; 332 if (cx & mask) { 333 /* not accrued??? */ 334 fs->fs_fsr = (fsr & ~FSR_FTT) | 335 (FSR_TT_IEEE << FSR_FTT_SHIFT) | 336 (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT); 337 return (FPE); 338 } 339 fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT); 340 } 341 fs->fs_fsr = fsr; 342 fs->fs_regs[rd] = space[0]; 343 if (type >= FTYPE_DBL) { 344 fs->fs_regs[rd + 1] = space[1]; 345 if (type > FTYPE_DBL) { 346 fs->fs_regs[rd + 2] = space[2]; 347 fs->fs_regs[rd + 3] = space[3]; 348 } 349 } 350 return (0); /* success */ 351 } 352