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