1 /* $OpenBSD: fpu.c,v 1.26 2024/03/29 21:14:31 miod Exp $ */ 2 /* $NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp $ */ 3 4 /* 5 * Copyright (c) 2001 Jason L. Wright (jason@thought.net) 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * Copyright (c) 1992, 1993 32 * The Regents of the University of California. All rights reserved. 33 * 34 * This software was developed by the Computer Systems Engineering group 35 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 36 * contributed to Berkeley. 37 * 38 * All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by the University of 41 * California, Lawrence Berkeley Laboratory. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. Neither the name of the University nor the names of its contributors 52 * may be used to endorse or promote products derived from this software 53 * without specific prior written permission. 54 * 55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65 * SUCH DAMAGE. 66 * 67 * @(#)fpu.c 8.1 (Berkeley) 6/11/93 68 */ 69 70 #include <sys/param.h> 71 #include <sys/proc.h> 72 #include <sys/signal.h> 73 #include <sys/systm.h> 74 #include <sys/syslog.h> 75 #include <sys/signalvar.h> 76 77 #include <machine/instr.h> 78 #include <machine/fsr.h> 79 #include <machine/reg.h> 80 81 #include <sparc64/fpu/fpu_emu.h> 82 #include <sparc64/fpu/fpu_extern.h> 83 84 int fpu_regoffset(int, int); 85 int fpu_insn_fmov(struct fpstate *, struct fpemu *, union instr); 86 int fpu_insn_fabs(struct fpstate *, struct fpemu *, union instr); 87 int fpu_insn_fneg(struct fpstate *, struct fpemu *, union instr); 88 int fpu_insn_itof(struct fpemu *, union instr, int, int *, 89 int *, u_int *); 90 int fpu_insn_ftoi(struct fpemu *, union instr, int *, int, u_int *); 91 int fpu_insn_ftof(struct fpemu *, union instr, int *, int *, u_int *); 92 int fpu_insn_fsqrt(struct fpemu *, union instr, int *, int *, u_int *); 93 int fpu_insn_fcmp(struct fpstate *, struct fpemu *, union instr, int); 94 int fpu_insn_fmul(struct fpemu *, union instr, int *, int *, u_int *); 95 int fpu_insn_fmulx(struct fpemu *, union instr, int *, int *, u_int *); 96 int fpu_insn_fdiv(struct fpemu *, union instr, int *, int *, u_int *); 97 int fpu_insn_fadd(struct fpemu *, union instr, int *, int *, u_int *); 98 int fpu_insn_fsub(struct fpemu *, union instr, int *, int *, u_int *); 99 int fpu_insn_fmovcc(struct proc *, struct fpstate *, union instr); 100 int fpu_insn_fmovr(struct proc *, struct fpstate *, union instr); 101 void fpu_fcopy(u_int *, u_int *, int); 102 103 #ifdef DEBUG 104 int fpe_debug = 0; 105 106 /* 107 * Dump a `fpn' structure. 108 */ 109 void 110 fpu_dumpfpn(struct fpn *fp) 111 { 112 static char *class[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" }; 113 114 printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2], 115 fp->fp_sign ? '-' : ' ', fp->fp_mant[0], fp->fp_mant[1], 116 fp->fp_mant[2], fp->fp_mant[3], fp->fp_exp); 117 } 118 void 119 fpu_dumpstate(struct fpstate *fs) 120 { 121 int i; 122 123 for (i = 0; i < 64; i++) 124 printf("%%f%02d: %08x%s", 125 i, fs->fs_regs[i], ((i & 3) == 3) ? "\n" : " "); 126 } 127 #endif 128 129 /* 130 * fpu_execute returns the following error numbers (0 = no error): 131 */ 132 #define FPE 1 /* take a floating point exception */ 133 #define NOTFPU 2 /* not an FPU instruction */ 134 135 /* 136 * Translate current exceptions into `first' exception. The 137 * bits go the wrong way for ffs() (0x10 is most important, etc). 138 * There are only 5, so do it the obvious way. 139 */ 140 #define X1(x) x 141 #define X2(x) x,x 142 #define X4(x) x,x,x,x 143 #define X8(x) X4(x),X4(x) 144 #define X16(x) X8(x),X8(x) 145 146 static const char cx_to_trapx[] = { 147 X1(FSR_NX), 148 X2(FSR_DZ), 149 X4(FSR_UF), 150 X8(FSR_OF), 151 X16(FSR_NV) 152 }; 153 static const u_char fpu_codes[] = { 154 X1(FPE_FLTINEX_TRAP), 155 X2(FPE_FLTDIV_TRAP), 156 X4(FPE_FLTUND_TRAP), 157 X8(FPE_FLTOVF_TRAP), 158 X16(FPE_FLTOPERR_TRAP) 159 }; 160 161 static const int fpu_types[] = { 162 X1(FPE_FLTRES), 163 X2(FPE_FLTDIV), 164 X4(FPE_FLTUND), 165 X8(FPE_FLTOVF), 166 X16(FPE_FLTINV) 167 }; 168 169 void 170 fpu_fcopy(u_int *src, u_int *dst, int type) 171 { 172 *dst++ = *src++; 173 if (type == FTYPE_SNG || type == FTYPE_INT) 174 return; 175 *dst++ = *src++; 176 if (type != FTYPE_EXT) 177 return; 178 *dst++ = *src++; 179 *dst = *src; 180 } 181 182 /* 183 * The FPU gave us an exception. Clean up the mess. 184 */ 185 void 186 fpu_cleanup(struct proc *p, struct fpstate *fs, union instr instr, 187 union sigval sv) 188 { 189 int i, fsr = fs->fs_fsr, error; 190 struct fpemu fe; 191 192 switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) { 193 case FSR_TT_NONE: 194 #ifdef DEBUG 195 printf("fpu_cleanup: invoked although no exception\n"); 196 #endif 197 return; 198 case FSR_TT_IEEE: 199 if ((i = fsr & FSR_CX) == 0) 200 panic("fpu ieee trap, but no exception"); 201 trapsignal(p, SIGFPE, fpu_codes[i - 1], fpu_types[i - 1], sv); 202 return; 203 case FSR_TT_UNFIN: 204 if (instr.i_int == 0) { 205 #ifdef DEBUG 206 printf("fpu_cleanup: unfinished fpop\n"); 207 #endif 208 return; 209 } 210 break; 211 case FSR_TT_UNIMP: 212 if (instr.i_int == 0) 213 panic("fpu_cleanup: unimplemented fpop without insn"); 214 break; 215 case FSR_TT_SEQ: 216 panic("fpu sequence error"); 217 /* NOTREACHED */ 218 case FSR_TT_HWERR: 219 log(LOG_ERR, "fpu hardware error (%s[%d])\n", 220 p->p_p->ps_comm, p->p_p->ps_pid); 221 uprintf("%s[%d]: fpu hardware error\n", 222 p->p_p->ps_comm, p->p_p->ps_pid); 223 trapsignal(p, SIGFPE, -1, FPE_FLTINV, sv); /* ??? */ 224 return; 225 default: 226 printf("fsr=0x%x\n", fsr); 227 panic("fpu error"); 228 } 229 230 /* emulate the instructions left in the queue */ 231 fe.fe_fpstate = fs; 232 if (instr.i_any.i_op != IOP_reg || 233 (instr.i_op3.i_op3 != IOP3_FPop1 && 234 instr.i_op3.i_op3 != IOP3_FPop2)) 235 panic("bogus fpu instruction to emulate"); 236 error = fpu_execute(p, &fe, instr); 237 switch (error) { 238 case 0: 239 break; 240 case FPE: 241 trapsignal(p, SIGFPE, 242 fpu_codes[(fs->fs_fsr & FSR_CX) - 1], 243 fpu_types[(fs->fs_fsr & FSR_CX) - 1], sv); 244 break; 245 case NOTFPU: 246 default: 247 trapsignal(p, SIGILL, 0, ILL_COPROC, sv); 248 break; 249 } 250 } 251 252 /* 253 * Compute offset given a register and type. For 32 bit sparc, bits 1 and 0 254 * must be zero for ext types, and bit 0 must be 0 for double and long types. 255 * For 64bit sparc, bit 1 must be zero for quad types, and bit 0 becomes bit 256 * 5 in the register offset for long, double, and quad types. 257 */ 258 int 259 fpu_regoffset(int rx, int type) 260 { 261 if (type == FTYPE_LNG || type == FTYPE_DBL || type == FTYPE_EXT) { 262 rx |= (rx & 1) << 5; 263 rx &= 0x3e; 264 if ((type == FTYPE_EXT) && (rx & 2)) 265 return (-1); 266 } 267 return (rx); 268 } 269 270 /* 271 * Execute an FPU instruction (one that runs entirely in the FPU; not 272 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 273 * modified to reflect the setting the hardware would have left. 274 */ 275 int 276 fpu_execute(struct proc *p, struct fpemu *fe, union instr instr) 277 { 278 struct fpstate *fs; 279 int opf, rdtype, rd, err, mask, cx, fsr; 280 u_int space[4]; 281 282 DPRINTF(FPE_INSN, ("op3: %x, opf %x\n", instr.i_opf.i_op3, 283 instr.i_opf.i_opf)); 284 DPRINTF(FPE_STATE, ("BEFORE:\n")); 285 DUMPSTATE(FPE_STATE, fe->fe_fpstate); 286 opf = instr.i_opf.i_opf; 287 fs = fe->fe_fpstate; 288 fe->fe_fsr = fs->fs_fsr & ~FSR_CX; 289 fe->fe_cx = 0; 290 291 if ((instr.i_int & 0xc0000000) != 0x80000000) 292 return (NOTFPU); 293 294 if (instr.i_opf.i_op3 == IOP3_FPop2) { 295 switch (opf) { 296 case FCMPS: case FCMPD: case FCMPQ: 297 return (fpu_insn_fcmp(fs, fe, instr, 0)); 298 299 case FCMPES: case FCMPED: case FCMPEQ: 300 return (fpu_insn_fcmp(fs, fe, instr, 1)); 301 302 case FMVFC0S: case FMVFC0D: case FMVFC0Q: 303 case FMVFC1S: case FMVFC1D: case FMVFC1Q: 304 case FMVFC2S: case FMVFC2D: case FMVFC2Q: 305 case FMVFC3S: case FMVFC3D: case FMVFC3Q: 306 case FMVICS: case FMVICD: case FMVICQ: 307 case FMVXCS: case FMVXCD: case FMVXCQ: 308 return (fpu_insn_fmovcc(p, fs, instr)); 309 310 case FMOVZS: case FMOVZD: case FMOVZQ: 311 case FMOVLEZS: case FMOVLEZD: case FMOVLEZQ: 312 case FMOVLZS: case FMOVLZD: case FMOVLZQ: 313 case FMOVNZS: case FMOVNZD: case FMOVNZQ: 314 case FMOVGZS: case FMOVGZD: case FMOVGZQ: 315 case FMOVGEZS: case FMOVGEZD: case FMOVGEZQ: 316 return (fpu_insn_fmovr(p, fs, instr)); 317 } 318 return (NOTFPU); 319 } 320 321 if (instr.i_opf.i_op3 != IOP3_FPop1) 322 return (NOTFPU); 323 324 switch (instr.i_opf.i_opf) { 325 case FSTOX: case FDTOX: case FQTOX: 326 rdtype = FTYPE_LNG; 327 if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0) 328 return (err); 329 break; 330 331 case FSTOI: case FDTOI: case FQTOI: 332 rdtype = FTYPE_INT; 333 if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0) 334 return (err); 335 break; 336 337 case FITOS: case FITOD: case FITOQ: 338 if ((err = fpu_insn_itof(fe, instr, FTYPE_INT, &rd, 339 &rdtype, space)) != 0) 340 return (err); 341 break; 342 343 case FXTOS: case FXTOD: case FXTOQ: 344 if ((err = fpu_insn_itof(fe, instr, FTYPE_LNG, &rd, 345 &rdtype, space)) != 0) 346 return (err); 347 break; 348 349 case FSTOD: case FSTOQ: 350 case FDTOS: case FDTOQ: 351 case FQTOS: case FQTOD: 352 if ((err = fpu_insn_ftof(fe, instr, &rd, &rdtype, space)) != 0) 353 return (err); 354 break; 355 356 case FMOVS: case FMOVD: case FMOVQ: 357 return (fpu_insn_fmov(fs, fe, instr)); 358 359 case FNEGS: case FNEGD: case FNEGQ: 360 return (fpu_insn_fneg(fs, fe, instr)); 361 362 case FABSS: case FABSD: case FABSQ: 363 return (fpu_insn_fabs(fs, fe, instr)); 364 365 case FSQRTS: case FSQRTD: case FSQRTQ: 366 if ((err = fpu_insn_fsqrt(fe, instr, &rd, &rdtype, space)) != 0) 367 return (err); 368 break; 369 370 case FMULS: case FMULD: case FMULQ: 371 if ((err = fpu_insn_fmul(fe, instr, &rd, &rdtype, space)) != 0) 372 return (err); 373 break; 374 375 case FDIVS: case FDIVD: case FDIVQ: 376 if ((err = fpu_insn_fdiv(fe, instr, &rd, &rdtype, space)) != 0) 377 return (err); 378 break; 379 380 case FSMULD: case FDMULQ: 381 if ((err = fpu_insn_fmulx(fe, instr, &rd, &rdtype, space)) != 0) 382 return (err); 383 break; 384 385 case FADDS: case FADDD: case FADDQ: 386 if ((err = fpu_insn_fadd(fe, instr, &rd, &rdtype, space)) != 0) 387 return (err); 388 break; 389 390 case FSUBS: case FSUBD: case FSUBQ: 391 if ((err = fpu_insn_fsub(fe, instr, &rd, &rdtype, space)) != 0) 392 return (err); 393 break; 394 default: 395 return (NOTFPU); 396 } 397 398 cx = fe->fe_cx; 399 fsr = fe->fe_fsr; 400 if (cx != 0) { 401 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK; 402 if (cx & mask) { 403 /* not accrued??? */ 404 fs->fs_fsr = (fsr & ~FSR_FTT) | 405 (FSR_TT_IEEE << FSR_FTT_SHIFT) | 406 (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT); 407 return (FPE); 408 } 409 fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT); 410 } 411 fs->fs_fsr = fsr; 412 fpu_fcopy(space, fs->fs_regs + rd, rdtype); 413 DPRINTF(FPE_STATE, ("AFTER:\n")); 414 DUMPSTATE(FPE_STATE, fs); 415 return (0); 416 } 417 418 /* 419 * Handler for FMOV[SDQ] emulation. 420 */ 421 int 422 fpu_insn_fmov(struct fpstate *fs, struct fpemu *fe, union instr instr) 423 { 424 int opf = instr.i_opf.i_opf, rs, rd, rtype; 425 426 rtype = opf & 3; 427 if (rtype == 0) 428 return (NOTFPU); 429 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 430 return (NOTFPU); 431 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 432 return (NOTFPU); 433 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); 434 fs->fs_fsr = fe->fe_fsr; 435 return (0); 436 } 437 438 /* 439 * Handler for FABS[SDQ] emulation. 440 */ 441 int 442 fpu_insn_fabs(struct fpstate *fs, struct fpemu *fe, union instr instr) 443 { 444 int opf = instr.i_opf.i_opf, rs, rd, rtype; 445 446 rtype = opf & 3; 447 if (rtype == 0) 448 return (NOTFPU); 449 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 450 return (NOTFPU); 451 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 452 return (NOTFPU); 453 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); 454 fs->fs_regs[rd] = fs->fs_regs[rd] & ~(1U << 31); 455 fs->fs_fsr = fe->fe_fsr; 456 return (0); 457 } 458 459 /* 460 * Handler for FNEG[SDQ] emulation. 461 */ 462 int 463 fpu_insn_fneg(struct fpstate *fs, struct fpemu *fe, union instr instr) 464 { 465 int opf = instr.i_opf.i_opf, rs, rd, rtype; 466 467 rtype = opf & 3; 468 if (rtype == 0) 469 return (NOTFPU); 470 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 471 return (NOTFPU); 472 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 473 return (NOTFPU); 474 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); 475 fs->fs_regs[rd] = fs->fs_regs[rd] ^ (1U << 31); 476 fs->fs_fsr = fe->fe_fsr; 477 return (0); 478 } 479 480 /* 481 * Handler for F[XI]TO[SDQ] emulation. 482 */ 483 int 484 fpu_insn_itof(struct fpemu *fe, union instr instr, int rstype, int *rdp, 485 int *rdtypep, u_int *space) 486 { 487 int opf = instr.i_opf.i_opf, rs, rd, rdtype; 488 489 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) 490 return (NOTFPU); 491 492 rdtype = (opf >> 2) & 3; 493 if (rdtype == 0) 494 return (NOTFPU); 495 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) 496 return (NOTFPU); 497 498 DPRINTF(FPE_INSN, ("itof %%f%d(%d, %d) -> %%f%d(%d, %d)\n", 499 rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd)); 500 fpu_explode(fe, &fe->fe_f1, rstype, rs); 501 fpu_implode(fe, &fe->fe_f1, rdtype, space); 502 *rdp = rd; 503 *rdtypep = rdtype; 504 return (0); 505 } 506 507 /* 508 * Handler for F[SDQ]TO[XI] emulation. 509 */ 510 int 511 fpu_insn_ftoi(struct fpemu *fe, union instr instr, int *rdp, int rdtype, 512 u_int *space) 513 { 514 int opf = instr.i_opf.i_opf, rd, rstype, rs; 515 516 rstype = opf & 3; 517 if (rstype == 0) 518 return (NOTFPU); 519 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) 520 return (NOTFPU); 521 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) 522 return (NOTFPU); 523 524 fpu_explode(fe, &fe->fe_f1, rstype, rs); 525 fpu_implode(fe, &fe->fe_f1, rdtype, space); 526 *rdp = rd; 527 return (0); 528 } 529 530 /* 531 * Handler for F[SDQ]TO[SDQ] emulation. 532 */ 533 int 534 fpu_insn_ftof(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 535 u_int *space) 536 { 537 int opf = instr.i_opf.i_opf, rd, rs, rdtype, rstype; 538 539 rstype = opf & 3; 540 rdtype = (opf >> 2) & 3; 541 542 if ((rstype == rdtype) || (rstype == 0) || (rdtype == 0)) 543 return (NOTFPU); 544 545 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) 546 return (NOTFPU); 547 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) 548 return (NOTFPU); 549 550 DPRINTF(FPE_INSN, ("ftof %%f%d(%d, %d) -> %%f%d(%d, %d)\n", 551 rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd)); 552 553 fpu_explode(fe, &fe->fe_f1, rstype, rs); 554 fpu_implode(fe, &fe->fe_f1, rdtype, space); 555 *rdp = rd; 556 *rdtypep = rdtype; 557 return (0); 558 } 559 560 /* 561 * Handler for FQSRT[SDQ] emulation. 562 */ 563 int 564 fpu_insn_fsqrt(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 565 u_int *space) 566 { 567 int opf = instr.i_opf.i_opf, rd, rs, rtype; 568 struct fpn *fp; 569 570 rtype = opf & 3; 571 if (rtype == 0) 572 return (NOTFPU); 573 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 574 return (NOTFPU); 575 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 576 return (NOTFPU); 577 578 fpu_explode(fe, &fe->fe_f1, rtype, rs); 579 fp = fpu_sqrt(fe); 580 fpu_implode(fe, fp, rtype, space); 581 *rdp = rd; 582 *rdtypep = rtype; 583 return (0); 584 } 585 586 /* 587 * Handler for FCMP{E}[SDQ] emulation. 588 */ 589 int 590 fpu_insn_fcmp(struct fpstate *fs, struct fpemu *fe, union instr instr, 591 int cmpe) 592 { 593 int opf = instr.i_opf.i_opf, rs1, rs2, rtype, cx, fsr; 594 595 rtype = opf & 3; 596 if (rtype == 0) 597 return (NOTFPU); 598 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) 599 return (NOTFPU); 600 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 601 return (NOTFPU); 602 603 fpu_explode(fe, &fe->fe_f1, rtype, rs1); 604 fpu_explode(fe, &fe->fe_f2, rtype, rs2); 605 fpu_compare(fe, cmpe); 606 607 /* 608 * The only possible exception here is NV; catch it early 609 * and get out, as there is no result register. 610 */ 611 cx = fe->fe_cx; 612 fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); 613 if (cx != 0) { 614 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { 615 fs->fs_fsr = (fsr & ~FSR_FTT) | 616 (FSR_TT_IEEE << FSR_FTT_SHIFT); 617 return (FPE); 618 } 619 fsr |= FSR_NV << FSR_AX_SHIFT; 620 } 621 fs->fs_fsr = fsr; 622 return (0); 623 } 624 625 /* 626 * Handler for FMUL[SDQ] emulation. 627 */ 628 int 629 fpu_insn_fmul(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 630 u_int *space) 631 { 632 struct fpn *fp; 633 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; 634 635 rtype = opf & 3; 636 if (rtype == 0) 637 return (NOTFPU); 638 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) 639 return (NOTFPU); 640 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 641 return (NOTFPU); 642 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 643 return (NOTFPU); 644 645 fpu_explode(fe, &fe->fe_f1, rtype, rs1); 646 fpu_explode(fe, &fe->fe_f2, rtype, rs2); 647 fp = fpu_mul(fe); 648 fpu_implode(fe, fp, rtype, space); 649 *rdp = rd; 650 *rdtypep = rtype; 651 return (0); 652 } 653 654 /* 655 * Handler for FSMULD, FDMULQ emulation. 656 */ 657 int 658 fpu_insn_fmulx(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 659 u_int *space) 660 { 661 struct fpn *fp; 662 int opf = instr.i_opf.i_opf, rd, rdtype, rstype, rs1, rs2; 663 664 rstype = opf & 3; 665 rdtype = (opf >> 2) & 3; 666 if ((rstype != rdtype + 1) || (rstype == 0) || (rdtype == 0)) 667 return (NOTFPU); 668 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rstype)) < 0) 669 return (NOTFPU); 670 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) 671 return (NOTFPU); 672 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) 673 return (NOTFPU); 674 675 fpu_explode(fe, &fe->fe_f1, rstype, rs1); 676 fpu_explode(fe, &fe->fe_f2, rstype, rs2); 677 fp = fpu_mul(fe); 678 fpu_implode(fe, fp, rdtype, space); 679 *rdp = rd; 680 *rdtypep = rdtype; 681 return (0); 682 } 683 684 /* 685 * Handler for FDIV[SDQ] emulation. 686 */ 687 int 688 fpu_insn_fdiv(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 689 u_int *space) 690 { 691 struct fpn *fp; 692 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; 693 694 rtype = opf & 3; 695 if (rtype == 0) 696 return (NOTFPU); 697 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) 698 return (NOTFPU); 699 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 700 return (NOTFPU); 701 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 702 return (NOTFPU); 703 704 fpu_explode(fe, &fe->fe_f1, rtype, rs1); 705 fpu_explode(fe, &fe->fe_f2, rtype, rs2); 706 fp = fpu_div(fe); 707 fpu_implode(fe, fp, rtype, space); 708 *rdp = rd; 709 *rdtypep = rtype; 710 return (0); 711 } 712 713 /* 714 * Handler for FADD[SDQ] emulation. 715 */ 716 int 717 fpu_insn_fadd(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 718 u_int *space) 719 { 720 struct fpn *fp; 721 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; 722 723 rtype = opf & 3; 724 if (rtype == 0) 725 return (NOTFPU); 726 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) 727 return (NOTFPU); 728 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 729 return (NOTFPU); 730 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 731 return (NOTFPU); 732 733 fpu_explode(fe, &fe->fe_f1, rtype, rs1); 734 fpu_explode(fe, &fe->fe_f2, rtype, rs2); 735 fp = fpu_add(fe); 736 fpu_implode(fe, fp, rtype, space); 737 *rdp = rd; 738 *rdtypep = rtype; 739 return (0); 740 } 741 742 /* 743 * Handler for FSUB[SDQ] emulation. 744 */ 745 int 746 fpu_insn_fsub(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep, 747 u_int *space) 748 { 749 struct fpn *fp; 750 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; 751 752 rtype = opf & 3; 753 if (rtype == 0) 754 return (NOTFPU); 755 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) 756 return (NOTFPU); 757 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) 758 return (NOTFPU); 759 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) 760 return (NOTFPU); 761 762 fpu_explode(fe, &fe->fe_f1, rtype, rs1); 763 fpu_explode(fe, &fe->fe_f2, rtype, rs2); 764 fp = fpu_sub(fe); 765 fpu_implode(fe, fp, rtype, space); 766 *rdp = rd; 767 *rdtypep = rtype; 768 return (0); 769 } 770 771 /* 772 * Handler for FMOV[SDQ][cond] emulation. 773 */ 774 int 775 fpu_insn_fmovcc(struct proc *p, struct fpstate *fs, union instr instr) 776 { 777 int rtype, rd, rs, cond; 778 779 rtype = instr.i_fmovcc.i_opf_low & 3; 780 if ((rtype == 0) || (instr.i_int & 0x00040000)) 781 return (NOTFPU); 782 783 if ((rd = fpu_regoffset(instr.i_fmovcc.i_rd, rtype)) < 0) 784 return (NOTFPU); 785 if ((rs = fpu_regoffset(instr.i_fmovcc.i_rs2, rtype)) < 0) 786 return (NOTFPU); 787 788 switch (instr.i_fmovcc.i_opf_cc) { 789 case 0: 790 cond = (fs->fs_fsr >> FSR_FCC_SHIFT) & FSR_FCC_MASK; 791 break; 792 case 1: 793 cond = (fs->fs_fsr >> FSR_FCC1_SHIFT) & FSR_FCC_MASK; 794 break; 795 case 2: 796 cond = (fs->fs_fsr >> FSR_FCC2_SHIFT) & FSR_FCC_MASK; 797 break; 798 case 3: 799 cond = (fs->fs_fsr >> FSR_FCC3_SHIFT) & FSR_FCC_MASK; 800 break; 801 case 4: 802 cond = (p->p_md.md_tf->tf_tstate >> TSTATE_CCR_SHIFT) & 803 PSR_ICC; 804 break; 805 case 6: 806 cond = (p->p_md.md_tf->tf_tstate >> 807 (TSTATE_CCR_SHIFT + XCC_SHIFT)) & PSR_ICC; 808 break; 809 default: 810 return (NOTFPU); 811 } 812 813 if (instr.i_fmovcc.i_cond != cond) 814 return (0); 815 816 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); 817 return (0); 818 } 819 820 /* 821 * Handler for FMOVR[icond][SDQ] emulation. 822 */ 823 int 824 fpu_insn_fmovr(struct proc *p, struct fpstate *fs, union instr instr) 825 { 826 int rtype, rd, rs2, rs1; 827 828 rtype = instr.i_fmovcc.i_opf_low & 3; 829 if ((rtype == 0) || (instr.i_int & 0x00002000)) 830 return (NOTFPU); 831 832 if ((rd = fpu_regoffset(instr.i_fmovr.i_rd, rtype)) < 0) 833 return (NOTFPU); 834 if ((rs2 = fpu_regoffset(instr.i_fmovr.i_rs2, rtype)) < 0) 835 return (NOTFPU); 836 rs1 = instr.i_fmovr.i_rs1; 837 838 switch (instr.i_fmovr.i_rcond) { 839 case 1: /* Z */ 840 if (rs1 != 0 && 841 (int64_t)p->p_md.md_tf->tf_global[rs1] != 0) 842 return (0); 843 break; 844 case 2: /* LEZ */ 845 if (rs1 != 0 && 846 (int64_t)p->p_md.md_tf->tf_global[rs1] > 0) 847 return (0); 848 break; 849 case 3: /* LZ */ 850 if (rs1 == 0 || 851 (int64_t)p->p_md.md_tf->tf_global[rs1] >= 0) 852 return (0); 853 break; 854 case 5: /* NZ */ 855 if (rs1 == 0 || 856 (int64_t)p->p_md.md_tf->tf_global[rs1] == 0) 857 return (0); 858 break; 859 case 6: /* NGZ */ 860 if (rs1 == 0 || 861 (int64_t)p->p_md.md_tf->tf_global[rs1] <= 0) 862 return (0); 863 break; 864 case 7: /* NGEZ */ 865 if (rs1 != 0 && 866 (int64_t)p->p_md.md_tf->tf_global[rs1] < 0) 867 return (0); 868 break; 869 default: 870 return (NOTFPU); 871 } 872 873 fpu_fcopy(fs->fs_regs + rs2, fs->fs_regs + rd, rtype); 874 return (0); 875 } 876