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