fpu.c revision 92055
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/*- 43 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved. 44 * 45 * Redistribution and use in source and binary forms, with or without 46 * modification, are permitted provided that the following conditions 47 * are met: 48 * 1. Redistributions of source code must retain the above copyright 49 * notice, this list of conditions and the following disclaimer. 50 * 2. Redistributions in binary form must reproduce the above copyright 51 * notice, this list of conditions and the following disclaimer in the 52 * documentation and/or other materials provided with the distribution. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 57 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 58 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 59 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 60 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 61 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 62 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 63 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 * 65 * from: @(#)fpu.c 8.1 (Berkeley) 6/11/93 66 * from: NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp 67 * 68 * $FreeBSD: head/lib/libc/sparc64/fpu/fpu.c 92055 2002-03-11 03:18:17Z tmm $ 69 */ 70 71#include <sys/param.h> 72 73#include "namespace.h" 74#include <errno.h> 75#include <unistd.h> 76#include <signal.h> 77#include <stdlib.h> 78#include "un-namespace.h" 79#include "libc_private.h" 80 81#include <machine/emul.h> 82#include <machine/fp.h> 83#include <machine/frame.h> 84#include <machine/fsr.h> 85#include <machine/instr.h> 86#include <machine/pcb.h> 87#include <machine/tstate.h> 88 89#include "../sys/__sparc_utrap_private.h" 90#include "fpu_emu.h" 91#include "fpu_extern.h" 92 93/* 94 * Translate current exceptions into `first' exception. The 95 * bits go the wrong way for ffs() (0x10 is most important, etc). 96 * There are only 5, so do it the obvious way. 97 */ 98#define X1(x) x 99#define X2(x) x,x 100#define X4(x) x,x,x,x 101#define X8(x) X4(x),X4(x) 102#define X16(x) X8(x),X8(x) 103 104static char cx_to_trapx[] = { 105 X1(FSR_NX), 106 X2(FSR_DZ), 107 X4(FSR_UF), 108 X8(FSR_OF), 109 X16(FSR_NV) 110}; 111 112#ifdef FPU_DEBUG 113#ifdef FPU_DEBUG_MASK 114int __fpe_debug = FPU_DEBUG_MASK; 115#else 116int __fpe_debug = 0; 117#endif 118#endif /* FPU_DEBUG */ 119 120static int __fpu_execute(struct utrapframe *, struct fpemu *, u_int32_t, u_long); 121static void utrap_write(char *); 122static void utrap_kill_self(int); 123 124/* 125 * System call wrappers usable in an utrap environment. 126 */ 127static void 128utrap_write(char *str) 129{ 130 int berrno; 131 132 berrno = errno; 133 __sys_write(STDERR_FILENO, str, strlen(str)); 134 errno = berrno; 135} 136 137static void 138utrap_kill_self(sig) 139{ 140 int berrno; 141 142 berrno = errno; 143 __sys_kill(__sys_getpid(), sig); 144 errno = berrno; 145} 146 147void 148__fpu_panic(char *msg) 149{ 150 151 utrap_write(msg); 152 utrap_write("\n"); 153 utrap_kill_self(SIGKILL); 154} 155 156/* 157 * Need to use an fpstate on the stack; we could switch, so we cannot safely 158 * modify the pcb one, it might get overwritten. 159 */ 160void 161__fpu_exception(struct utrapframe *uf) 162{ 163 struct fpemu fe; 164 u_long fsr, tstate; 165 u_int insn; 166 int rv; 167 168 fsr = uf->uf_fsr; 169 170 switch (FSR_GET_FTT(fsr)) { 171 case FSR_FTT_NONE: 172 utrap_write("lost FPU trap type\n"); 173 return; 174 case FSR_FTT_IEEE: 175 goto fatal; 176 case FSR_FTT_SEQERR: 177 utrap_write("FPU sequence error\n"); 178 goto fatal; 179 case FSR_FTT_HWERR: 180 utrap_write("FPU hardware error\n"); 181 goto fatal; 182 case FSR_FTT_UNFIN: 183 case FSR_FTT_UNIMP: 184 break; 185 default: 186 utrap_write("unknown FPU error\n"); 187 goto fatal; 188 } 189 190 fe.fe_fsr = fsr & ~FSR_FTT_MASK; 191 insn = *(u_int32_t *)uf->uf_pc; 192 if (IF_OP(insn) != IOP_MISC || (IF_F3_OP3(insn) != INS2_FPop1 && 193 IF_F3_OP3(insn) != INS2_FPop2)) 194 __fpu_panic("bogus FP fault"); 195 tstate = uf->uf_state; 196 rv = __fpu_execute(uf, &fe, insn, tstate); 197 if (rv != 0) 198 utrap_kill_self(rv); 199 __asm __volatile("ldx %0, %%fsr" : : "m" (fe.fe_fsr)); 200 return; 201fatal: 202 utrap_kill_self(SIGFPE); 203 return; 204} 205 206#ifdef FPU_DEBUG 207/* 208 * Dump a `fpn' structure. 209 */ 210void 211__fpu_dumpfpn(struct fpn *fp) 212{ 213 static char *class[] = { 214 "SNAN", "QNAN", "ZERO", "NUM", "INF" 215 }; 216 217 printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2], 218 fp->fp_sign ? '-' : ' ', 219 fp->fp_mant[0], fp->fp_mant[1], 220 fp->fp_mant[2], fp->fp_mant[3], 221 fp->fp_exp); 222} 223#endif 224 225static u_long 226fetch_reg(struct utrapframe *uf, int reg) 227{ 228 u_long offs; 229 struct frame *frm; 230 231 if (reg == IREG_G0) 232 return (0); 233 else if (reg < IREG_O0) /* global */ 234 return (uf->uf_global[reg]); 235 else if (reg < IREG_L0) /* out */ 236 return (uf->uf_out[reg - IREG_O0]); 237 else { /* local, in */ 238 /* 239 * The in registers are immediately after the locals in 240 * the frame. 241 */ 242 frm = (struct frame *)(uf->uf_out[6] + SPOFF); 243 return (frm->f_local[reg - IREG_L0]); 244 } 245 __fpu_panic("fetch_reg: bogus register"); 246} 247 248static void 249__fpu_mov(struct fpemu *fe, int type, int rd, int rs1, int rs2) 250{ 251 int i; 252 253 i = 1 << type; 254 __fpu_setreg(rd++, rs1); 255 while (--i) 256 __fpu_setreg(rd++, __fpu_getreg(++rs2)); 257} 258 259static __inline void 260__fpu_ccmov(struct fpemu *fe, int type, int rd, int rs1, int rs2, 261 u_int32_t insn, int fcc) 262{ 263 264 if (IF_F4_COND(insn) == fcc) 265 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 266} 267 268static int 269__fpu_cmpck(struct fpemu *fe) 270{ 271 u_long fsr; 272 int cx; 273 274 /* 275 * The only possible exception here is NV; catch it 276 * early and get out, as there is no result register. 277 */ 278 cx = fe->fe_cx; 279 fsr = fe->fe_fsr | (cx << FSR_CEXC_SHIFT); 280 if (cx != 0) { 281 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { 282 fe->fe_fsr = (fsr & ~FSR_FTT_MASK) | 283 FSR_FTT(FSR_FTT_IEEE); 284 return (SIGFPE); 285 } 286 fsr |= FSR_NV << FSR_AEXC_SHIFT; 287 } 288 fe->fe_fsr = fsr; 289 return (0); 290} 291 292static int opmask[] = {0, 0, 1, 3}; 293 294/* 295 * Helper for forming the below case statements. Build only the op3 and opf 296 * field of the instruction, these are the only that need to match. 297 */ 298#define FOP(op3, opf) \ 299 ((op3) << IF_F3_OP3_SHIFT | (opf) << IF_F3_OPF_SHIFT) 300 301/* 302 * Execute an FPU instruction (one that runs entirely in the FPU; not 303 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 304 * modified to reflect the setting the hardware would have left. 305 * 306 * Note that we do not catch all illegal opcodes, so you can, for instance, 307 * multiply two integers this way. 308 */ 309static int 310__fpu_execute(struct utrapframe *uf, struct fpemu *fe, u_int32_t insn, u_long tstate) 311{ 312 struct fpn *fp; 313 int opf, rs1, rs2, rd, type, mask, cx, cond; 314 u_long reg, fsr; 315 u_int space[4]; 316 317 /* 318 * `Decode' and execute instruction. Start with no exceptions. 319 * The type of any opf opcode is in the bottom two bits, so we 320 * squish them out here. 321 */ 322 opf = insn & (IF_MASK(IF_F3_OP3_SHIFT, IF_F3_OP3_BITS) | 323 IF_MASK(IF_F3_OPF_SHIFT + 2, IF_F3_OPF_BITS - 2)); 324 type = IF_F3_OPF(insn) & 3; 325 mask = opmask[type]; 326 rs1 = IF_F3_RS1(insn) & ~mask; 327 rs2 = IF_F3_RS2(insn) & ~mask; 328 rd = IF_F3_RD(insn) & ~mask; 329 cond = 0; 330#ifdef notdef 331 if ((rs1 | rs2 | rd) & mask) 332 return (SIGILL); 333#endif 334 fsr = fe->fe_fsr; 335 fe->fe_fsr &= ~FSR_CEXC_MASK; 336 fe->fe_cx = 0; 337 switch (opf) { 338 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(0))): 339 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 340 FSR_GET_FCC0(fsr)); 341 return (0); 342 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(1))): 343 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 344 FSR_GET_FCC1(fsr)); 345 return (0); 346 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(2))): 347 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 348 FSR_GET_FCC2(fsr)); 349 return (0); 350 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_FCC(3))): 351 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 352 FSR_GET_FCC3(fsr)); 353 return (0); 354 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_ICC)): 355 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 356 (tstate & TSTATE_ICC_MASK) >> TSTATE_ICC_SHIFT); 357 return (0); 358 case FOP(INS2_FPop2, INSFP2_FMOV_CC(IFCC_XCC)): 359 __fpu_ccmov(fe, type, rd, __fpu_getreg(rs2), rs2, insn, 360 (tstate & TSTATE_XCC_MASK) >> (TSTATE_XCC_SHIFT)); 361 return (0); 362 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_Z)): 363 reg = fetch_reg(uf, IF_F4_RS1(insn)); 364 if (reg == 0) 365 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 366 return (0); 367 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LEZ)): 368 reg = fetch_reg(uf, IF_F4_RS1(insn)); 369 if (reg <= 0) 370 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 371 return (0); 372 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LZ)): 373 reg = fetch_reg(uf, IF_F4_RS1(insn)); 374 if (reg < 0) 375 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 376 return (0); 377 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_NZ)): 378 reg = fetch_reg(uf, IF_F4_RS1(insn)); 379 if (reg != 0) 380 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 381 return (0); 382 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GZ)): 383 reg = fetch_reg(uf, IF_F4_RS1(insn)); 384 if (reg > 0) 385 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 386 return (0); 387 case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GEZ)): 388 reg = fetch_reg(uf, IF_F4_RS1(insn)); 389 if (reg >= 0) 390 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 391 return (0); 392 case FOP(INS2_FPop2, INSFP2_FCMP): 393 __fpu_explode(fe, &fe->fe_f1, type, rs1); 394 __fpu_explode(fe, &fe->fe_f2, type, rs2); 395 __fpu_compare(fe, 0, IF_F3_CC(insn)); 396 return (__fpu_cmpck(fe)); 397 case FOP(INS2_FPop2, INSFP2_FCMPE): 398 __fpu_explode(fe, &fe->fe_f1, type, rs1); 399 __fpu_explode(fe, &fe->fe_f2, type, rs2); 400 __fpu_compare(fe, 1, IF_F3_CC(insn)); 401 return (__fpu_cmpck(fe)); 402 case FOP(INS2_FPop1, INSFP1_FMOV): /* these should all be pretty obvious */ 403 __fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2); 404 return (0); 405 case FOP(INS2_FPop1, INSFP1_FNEG): 406 __fpu_mov(fe, type, rd, __fpu_getreg(rs2) ^ (1 << 31), rs2); 407 return (0); 408 case FOP(INS2_FPop1, INSFP1_FABS): 409 __fpu_mov(fe, type, rd, __fpu_getreg(rs2) & ~(1 << 31), rs2); 410 return (0); 411 case FOP(INS2_FPop1, INSFP1_FSQRT): 412 __fpu_explode(fe, &fe->fe_f1, type, rs2); 413 fp = __fpu_sqrt(fe); 414 break; 415 case FOP(INS2_FPop1, INSFP1_FADD): 416 __fpu_explode(fe, &fe->fe_f1, type, rs1); 417 __fpu_explode(fe, &fe->fe_f2, type, rs2); 418 fp = __fpu_add(fe); 419 break; 420 case FOP(INS2_FPop1, INSFP1_FSUB): 421 __fpu_explode(fe, &fe->fe_f1, type, rs1); 422 __fpu_explode(fe, &fe->fe_f2, type, rs2); 423 fp = __fpu_sub(fe); 424 break; 425 case FOP(INS2_FPop1, INSFP1_FMUL): 426 __fpu_explode(fe, &fe->fe_f1, type, rs1); 427 __fpu_explode(fe, &fe->fe_f2, type, rs2); 428 fp = __fpu_mul(fe); 429 break; 430 case FOP(INS2_FPop1, INSFP1_FDIV): 431 __fpu_explode(fe, &fe->fe_f1, type, rs1); 432 __fpu_explode(fe, &fe->fe_f2, type, rs2); 433 fp = __fpu_div(fe); 434 break; 435 case FOP(INS2_FPop1, INSFP1_FsMULd): 436 case FOP(INS2_FPop1, INSFP1_FdMULq): 437 if (type == FTYPE_EXT) 438 return (SIGILL); 439 __fpu_explode(fe, &fe->fe_f1, type, rs1); 440 __fpu_explode(fe, &fe->fe_f2, type, rs2); 441 type++; /* single to double, or double to quad */ 442 /* 443 * Recalculate rd (the old type applied for the source regs 444 * only, the target one has a different size). 445 */ 446 mask = opmask[type]; 447 rd = IF_F3_RD(insn) & ~mask; 448 fp = __fpu_mul(fe); 449 break; 450 case FOP(INS2_FPop1, INSFP1_FxTOs): 451 case FOP(INS2_FPop1, INSFP1_FxTOd): 452 case FOP(INS2_FPop1, INSFP1_FxTOq): 453 type = FTYPE_LNG; 454 __fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 455 /* sneaky; depends on instruction encoding */ 456 type = (IF_F3_OPF(insn) >> 2) & 3; 457 mask = opmask[type]; 458 rd = IF_F3_RD(insn) & ~mask; 459 break; 460 case FOP(INS2_FPop1, INSFP1_FTOx): 461 __fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 462 type = FTYPE_LNG; 463 mask = 1; /* needs 2 registers */ 464 rd = IF_F3_RD(insn) & ~mask; 465 break; 466 case FOP(INS2_FPop1, INSFP1_FTOs): 467 case FOP(INS2_FPop1, INSFP1_FTOd): 468 case FOP(INS2_FPop1, INSFP1_FTOq): 469 case FOP(INS2_FPop1, INSFP1_FTOi): 470 __fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 471 /* sneaky; depends on instruction encoding */ 472 type = (IF_F3_OPF(insn) >> 2) & 3; 473 mask = opmask[type]; 474 rd = IF_F3_RD(insn) & ~mask; 475 break; 476 default: 477 return (SIGILL); 478 } 479 480 /* 481 * ALU operation is complete. Collapse the result and then check 482 * for exceptions. If we got any, and they are enabled, do not 483 * alter the destination register, just stop with an exception. 484 * Otherwise set new current exceptions and accrue. 485 */ 486 __fpu_implode(fe, fp, type, space); 487 cx = fe->fe_cx; 488 if (cx != 0) { 489 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK; 490 if (cx & mask) { 491 /* not accrued??? */ 492 fsr = (fsr & ~FSR_FTT_MASK) | 493 FSR_FTT(FSR_FTT_IEEE) | 494 FSR_CEXC(cx_to_trapx[(cx & mask) - 1]); 495 return (SIGFPE); 496 } 497 fsr |= (cx << FSR_CEXC_SHIFT) | (cx << FSR_AEXC_SHIFT); 498 } 499 fe->fe_fsr = fsr; 500 __fpu_setreg(rd, space[0]); 501 if (type >= FTYPE_DBL || type == FTYPE_LNG) { 502 __fpu_setreg(rd + 1, space[1]); 503 if (type > FTYPE_DBL) { 504 __fpu_setreg(rd + 2, space[2]); 505 __fpu_setreg(rd + 3, space[3]); 506 } 507 } 508 return (0); /* success */ 509} 510