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