1 /*- 2 * Copyright (C) 1996 Wolfgang Solfrank. 3 * Copyright (C) 1996 TooLs GmbH. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by TooLs GmbH. 17 * 4. The name of TooLs GmbH may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $ 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/proc.h> 39 #include <sys/systm.h> 40 #include <sys/limits.h> 41 42 #include <machine/altivec.h> 43 #include <machine/fpu.h> 44 #include <machine/ieeefp.h> 45 #include <machine/pcb.h> 46 #include <machine/psl.h> 47 48 #include <powerpc/fpu/fpu_arith.h> 49 #include <powerpc/fpu/fpu_emu.h> 50 #include <powerpc/fpu/fpu_extern.h> 51 52 void spe_handle_fpdata(struct trapframe *); 53 void spe_handle_fpround(struct trapframe *); 54 static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *); 55 56 static void 57 save_vec_int(struct thread *td) 58 { 59 int msr; 60 struct pcb *pcb; 61 62 pcb = td->td_pcb; 63 64 /* 65 * Temporarily re-enable the vector unit during the save 66 */ 67 msr = mfmsr(); 68 mtmsr(msr | PSL_VEC); 69 70 /* 71 * Save the vector registers and SPEFSCR to the PCB 72 */ 73 #define EVSTDW(n) __asm ("evstdw %1,0(%0)" \ 74 :: "b"(pcb->pcb_vec.vr[n]), "n"(n)); 75 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3); 76 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7); 77 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11); 78 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15); 79 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19); 80 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23); 81 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27); 82 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31); 83 #undef EVSTDW 84 85 __asm ( "evxor 0,0,0\n" 86 "evmwumiaa 0,0,0\n" 87 "evstdd 0,0(%0)" :: "b"(&pcb->pcb_vec.spare[0])); 88 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR); 89 90 /* 91 * Disable vector unit again 92 */ 93 isync(); 94 mtmsr(msr); 95 96 } 97 98 void 99 enable_vec(struct thread *td) 100 { 101 int msr; 102 struct pcb *pcb; 103 struct trapframe *tf; 104 105 pcb = td->td_pcb; 106 tf = trapframe(td); 107 108 /* 109 * Save the thread's SPE CPU number, and set the CPU's current 110 * vector thread 111 */ 112 td->td_pcb->pcb_veccpu = PCPU_GET(cpuid); 113 PCPU_SET(vecthread, td); 114 115 /* 116 * Enable the vector unit for when the thread returns from the 117 * exception. If this is the first time the unit has been used by 118 * the thread, initialise the vector registers and VSCR to 0, and 119 * set the flag to indicate that the vector unit is in use. 120 */ 121 tf->srr1 |= PSL_VEC; 122 if (!(pcb->pcb_flags & PCB_VEC)) { 123 memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec); 124 pcb->pcb_flags |= PCB_VEC; 125 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR); 126 } 127 128 /* 129 * Temporarily enable the vector unit so the registers 130 * can be restored. 131 */ 132 msr = mfmsr(); 133 mtmsr(msr | PSL_VEC); 134 135 /* Restore SPEFSCR and ACC. Use %r0 as the scratch for ACC. */ 136 mtspr(SPR_SPEFSCR, pcb->pcb_vec.vscr); 137 __asm __volatile("isync;evldd 0, 0(%0); evmra 0,0\n" 138 :: "b"(&pcb->pcb_vec.spare[0])); 139 140 /* 141 * The lower half of each register will be restored on trap return. Use 142 * %r0 as a scratch register, and restore it last. 143 */ 144 #define EVLDW(n) __asm __volatile("evldw 0, 0(%0); evmergehilo "#n",0,"#n \ 145 :: "b"(&pcb->pcb_vec.vr[n])); 146 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4); 147 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8); 148 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12); 149 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16); 150 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20); 151 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24); 152 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28); 153 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0); 154 #undef EVLDW 155 156 isync(); 157 mtmsr(msr); 158 } 159 160 void 161 save_vec(struct thread *td) 162 { 163 struct pcb *pcb; 164 165 pcb = td->td_pcb; 166 167 save_vec_int(td); 168 169 /* 170 * Clear the current vec thread and pcb's CPU id 171 * XXX should this be left clear to allow lazy save/restore ? 172 */ 173 pcb->pcb_veccpu = INT_MAX; 174 PCPU_SET(vecthread, NULL); 175 } 176 177 /* 178 * Save SPE state without dropping ownership. This will only save state if 179 * the current vector-thread is `td'. This is used for taking core dumps, so 180 * don't leak kernel information; overwrite the low words of each vector with 181 * their real value, taken from the thread's trap frame, unconditionally. 182 */ 183 void 184 save_vec_nodrop(struct thread *td) 185 { 186 struct thread *vtd; 187 struct pcb *pcb; 188 int i; 189 190 vtd = PCPU_GET(vecthread); 191 if (td == vtd) { 192 save_vec_int(td); 193 } 194 195 pcb = td->td_pcb; 196 197 for (i = 0; i < 32; i++) { 198 pcb->pcb_vec.vr[i][1] = 199 td->td_frame ? td->td_frame->fixreg[i] : 0; 200 } 201 } 202 203 204 #define SPE_INST_MASK 0x31f 205 #define EADD 0x200 206 #define ESUB 0x201 207 #define EABS 0x204 208 #define ENABS 0x205 209 #define ENEG 0x206 210 #define EMUL 0x208 211 #define EDIV 0x209 212 #define ECMPGT 0x20c 213 #define ECMPLT 0x20d 214 #define ECMPEQ 0x20e 215 #define ECFUI 0x210 216 #define ECFSI 0x211 217 #define ECTUI 0x214 218 #define ECTSI 0x215 219 #define ECTUF 0x216 220 #define ECTSF 0x217 221 #define ECTUIZ 0x218 222 #define ECTSIZ 0x21a 223 224 #define SPE 0x4 225 #define SPFP 0x6 226 #define DPFP 0x7 227 228 #define SPE_OPC 4 229 #define OPC_SHIFT 26 230 231 #define EVFSADD 0x280 232 #define EVFSSUB 0x281 233 #define EVFSABS 0x284 234 #define EVFSNABS 0x285 235 #define EVFSNEG 0x286 236 #define EVFSMUL 0x288 237 #define EVFSDIV 0x289 238 #define EVFSCMPGT 0x28c 239 #define EVFSCMPLT 0x28d 240 #define EVFSCMPEQ 0x28e 241 #define EVFSCFUI 0x290 242 #define EVFSCFSI 0x291 243 #define EVFSCTUI 0x294 244 #define EVFSCTSI 0x295 245 #define EVFSCTUF 0x296 246 #define EVFSCTSF 0x297 247 #define EVFSCTUIZ 0x298 248 #define EVFSCTSIZ 0x29a 249 250 #define EFSADD 0x2c0 251 #define EFSSUB 0x2c1 252 #define EFSABS 0x2c4 253 #define EFSNABS 0x2c5 254 #define EFSNEG 0x2c6 255 #define EFSMUL 0x2c8 256 #define EFSDIV 0x2c9 257 #define EFSCMPGT 0x2cc 258 #define EFSCMPLT 0x2cd 259 #define EFSCMPEQ 0x2ce 260 #define EFSCFD 0x2cf 261 #define EFSCFUI 0x2d0 262 #define EFSCFSI 0x2d1 263 #define EFSCTUI 0x2d4 264 #define EFSCTSI 0x2d5 265 #define EFSCTUF 0x2d6 266 #define EFSCTSF 0x2d7 267 #define EFSCTUIZ 0x2d8 268 #define EFSCTSIZ 0x2da 269 270 #define EFDADD 0x2e0 271 #define EFDSUB 0x2e1 272 #define EFDABS 0x2e4 273 #define EFDNABS 0x2e5 274 #define EFDNEG 0x2e6 275 #define EFDMUL 0x2e8 276 #define EFDDIV 0x2e9 277 #define EFDCMPGT 0x2ec 278 #define EFDCMPLT 0x2ed 279 #define EFDCMPEQ 0x2ee 280 #define EFDCFS 0x2ef 281 #define EFDCFUI 0x2f0 282 #define EFDCFSI 0x2f1 283 #define EFDCTUI 0x2f4 284 #define EFDCTSI 0x2f5 285 #define EFDCTUF 0x2f6 286 #define EFDCTSF 0x2f7 287 #define EFDCTUIZ 0x2f8 288 #define EFDCTSIZ 0x2fa 289 290 enum { 291 NONE, 292 SINGLE, 293 DOUBLE, 294 VECTOR, 295 }; 296 297 static uint32_t fpscr_to_spefscr(uint32_t fpscr) 298 { 299 uint32_t spefscr; 300 301 spefscr = 0; 302 303 if (fpscr & FPSCR_VX) 304 spefscr |= SPEFSCR_FINV; 305 if (fpscr & FPSCR_OX) 306 spefscr |= SPEFSCR_FOVF; 307 if (fpscr & FPSCR_UX) 308 spefscr |= SPEFSCR_FUNF; 309 if (fpscr & FPSCR_ZX) 310 spefscr |= SPEFSCR_FDBZ; 311 if (fpscr & FPSCR_XX) 312 spefscr |= SPEFSCR_FX; 313 314 return (spefscr); 315 } 316 317 /* Sign is 0 for unsigned, 1 for signed. */ 318 static int 319 spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign) 320 { 321 uint32_t res[2]; 322 323 res[0] = fpu_ftox(fpemu, fpn, res); 324 if (res[0] != UINT_MAX && res[0] != 0) 325 fpemu->fe_cx |= FPSCR_OX; 326 else if (sign == 0 && res[0] != 0) 327 fpemu->fe_cx |= FPSCR_UX; 328 else 329 *val = res[1]; 330 331 return (0); 332 } 333 334 /* Masked instruction */ 335 /* 336 * For compare instructions, returns 1 if success, 0 if not. For all others, 337 * returns -1, or -2 if no result needs recorded. 338 */ 339 static int 340 spe_emu_instr(uint32_t instr, struct fpemu *fpemu, 341 struct fpn **result, uint32_t *iresult) 342 { 343 switch (instr & SPE_INST_MASK) { 344 case EABS: 345 case ENABS: 346 case ENEG: 347 /* Taken care of elsewhere. */ 348 break; 349 case ECTUIZ: 350 fpemu->fe_cx &= ~FPSCR_RN; 351 fpemu->fe_cx |= FP_RZ; 352 case ECTUI: 353 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0); 354 return (-1); 355 case ECTSIZ: 356 fpemu->fe_cx &= ~FPSCR_RN; 357 fpemu->fe_cx |= FP_RZ; 358 case ECTSI: 359 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1); 360 return (-1); 361 case EADD: 362 *result = fpu_add(fpemu); 363 break; 364 case ESUB: 365 *result = fpu_sub(fpemu); 366 break; 367 case EMUL: 368 *result = fpu_mul(fpemu); 369 break; 370 case EDIV: 371 *result = fpu_div(fpemu); 372 break; 373 case ECMPGT: 374 fpu_compare(fpemu, 0); 375 if (fpemu->fe_cx & FPSCR_FG) 376 return (1); 377 return (0); 378 case ECMPLT: 379 fpu_compare(fpemu, 0); 380 if (fpemu->fe_cx & FPSCR_FL) 381 return (1); 382 return (0); 383 case ECMPEQ: 384 fpu_compare(fpemu, 0); 385 if (fpemu->fe_cx & FPSCR_FE) 386 return (1); 387 return (0); 388 default: 389 printf("Unknown instruction %x\n", instr); 390 } 391 392 return (-1); 393 } 394 395 static int 396 spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type, 397 uint32_t hi, uint32_t lo) 398 { 399 uint32_t s; 400 401 fp->fp_sign = hi >> 31; 402 fp->fp_sticky = 0; 403 switch (type) { 404 case SINGLE: 405 s = fpu_stof(fp, hi); 406 break; 407 408 case DOUBLE: 409 s = fpu_dtof(fp, hi, lo); 410 break; 411 } 412 413 if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) { 414 /* 415 * Input is a signalling NaN. All operations that return 416 * an input NaN operand put it through a ``NaN conversion'', 417 * which basically just means ``turn on the quiet bit''. 418 * We do this here so that all NaNs internally look quiet 419 * (we can tell signalling ones by their class). 420 */ 421 fp->fp_mant[0] |= FP_QUIETBIT; 422 fe->fe_cx = FPSCR_VXSNAN; /* assert invalid operand */ 423 s = FPC_SNAN; 424 } 425 fp->fp_class = s; 426 427 return (0); 428 } 429 430 /* 431 * Save the high word of a 64-bit GPR for manipulation in the exception handler. 432 */ 433 static uint32_t 434 spe_save_reg_high(int reg) 435 { 436 uint32_t vec[2]; 437 #define EVSTDW(n) case n: __asm __volatile ("evstdw %1,0(%0)" \ 438 :: "b"(vec), "n"(n) : "memory"); break; 439 switch (reg) { 440 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3); 441 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7); 442 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11); 443 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15); 444 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19); 445 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23); 446 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27); 447 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31); 448 } 449 #undef EVSTDW 450 451 return (vec[0]); 452 } 453 454 /* 455 * Load the given value into the high word of the requested register. 456 */ 457 static void 458 spe_load_reg_high(int reg, uint32_t val) 459 { 460 #define EVLDW(n) case n: __asm __volatile("evmergelo "#n",%0,"#n \ 461 :: "r"(val)); break; 462 switch (reg) { 463 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4); 464 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8); 465 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12); 466 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16); 467 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20); 468 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24); 469 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28); 470 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0); 471 } 472 #undef EVLDW 473 474 } 475 476 void 477 spe_handle_fpdata(struct trapframe *frame) 478 { 479 struct fpemu fpemu; 480 struct fpn *result; 481 uint32_t instr, instr_sec_op; 482 uint32_t cr_shift, ra, rb, rd, src; 483 uint32_t high, low, res, tmp; /* For vector operations. */ 484 uint32_t spefscr = 0; 485 uint32_t ftod_res[2]; 486 int width; /* Single, Double, Vector, Integer */ 487 int err; 488 uint32_t msr; 489 490 err = fueword32((void *)frame->srr0, &instr); 491 492 if (err != 0) 493 return; 494 /* Fault. */; 495 496 if ((instr >> OPC_SHIFT) != SPE_OPC) 497 return; 498 499 msr = mfmsr(); 500 /* 501 * 'cr' field is the upper 3 bits of rd. Magically, since a) rd is 5 502 * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is 503 * modified for most compare operations, the full value of rd can be 504 * used as a shift value. 505 */ 506 rd = (instr >> 21) & 0x1f; 507 ra = (instr >> 16) & 0x1f; 508 rb = (instr >> 11) & 0x1f; 509 src = (instr >> 5) & 0x7; 510 cr_shift = 28 - (rd & 0x1f); 511 512 instr_sec_op = (instr & 0x7ff); 513 514 memset(&fpemu, 0, sizeof(fpemu)); 515 516 width = NONE; 517 switch (src) { 518 case SPE: 519 mtmsr(msr | PSL_VEC); 520 switch (instr_sec_op) { 521 case EVFSABS: 522 high = spe_save_reg_high(ra) & ~(1U << 31); 523 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31); 524 spe_load_reg_high(rd, high); 525 break; 526 case EVFSNABS: 527 high = spe_save_reg_high(ra) | (1U << 31); 528 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31); 529 spe_load_reg_high(rd, high); 530 break; 531 case EVFSNEG: 532 high = spe_save_reg_high(ra) ^ (1U << 31); 533 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31); 534 spe_load_reg_high(rd, high); 535 break; 536 default: 537 /* High word */ 538 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 539 spe_save_reg_high(ra), 0); 540 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 541 spe_save_reg_high(rb), 0); 542 high = spe_emu_instr(instr_sec_op, &fpemu, &result, 543 &tmp); 544 545 if (high < 0) 546 spe_load_reg_high(rd, tmp); 547 548 spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16; 549 /* Clear the fpemu to start over on the lower bits. */ 550 memset(&fpemu, 0, sizeof(fpemu)); 551 552 /* Now low word */ 553 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 554 frame->fixreg[ra], 0); 555 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 556 frame->fixreg[rb], 0); 557 spefscr |= fpscr_to_spefscr(fpemu.fe_cx); 558 low = spe_emu_instr(instr_sec_op, &fpemu, &result, 559 &frame->fixreg[rd]); 560 if (instr_sec_op == EVFSCMPEQ || 561 instr_sec_op == EVFSCMPGT || 562 instr_sec_op == EVFSCMPLT) { 563 res = (high << 3) | (low << 2) | 564 ((high | low) << 1) | (high & low); 565 width = NONE; 566 } else 567 width = VECTOR; 568 break; 569 } 570 goto end; 571 572 case SPFP: 573 switch (instr_sec_op) { 574 case EFSABS: 575 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31); 576 break; 577 case EFSNABS: 578 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31); 579 break; 580 case EFSNEG: 581 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31); 582 break; 583 case EFSCFD: 584 mtmsr(msr | PSL_VEC); 585 spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE, 586 spe_save_reg_high(rb), frame->fixreg[rb]); 587 result = &fpemu.fe_f3; 588 width = SINGLE; 589 break; 590 default: 591 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 592 frame->fixreg[ra], 0); 593 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 594 frame->fixreg[rb], 0); 595 width = SINGLE; 596 } 597 break; 598 case DPFP: 599 mtmsr(msr | PSL_VEC); 600 switch (instr_sec_op) { 601 case EFDABS: 602 high = spe_save_reg_high(ra) & ~(1U << 31); 603 frame->fixreg[rd] = frame->fixreg[ra]; 604 spe_load_reg_high(rd, high); 605 break; 606 case EFDNABS: 607 high = spe_save_reg_high(ra) | (1U << 31); 608 frame->fixreg[rd] = frame->fixreg[ra]; 609 spe_load_reg_high(rd, high); 610 break; 611 case EFDNEG: 612 high = spe_save_reg_high(ra) ^ (1U << 31); 613 frame->fixreg[rd] = frame->fixreg[ra]; 614 spe_load_reg_high(rd, high); 615 break; 616 case EFDCFS: 617 spe_explode(&fpemu, &fpemu.fe_f3, SINGLE, 618 frame->fixreg[rb], 0); 619 result = &fpemu.fe_f3; 620 width = DOUBLE; 621 break; 622 default: 623 spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE, 624 spe_save_reg_high(ra), frame->fixreg[ra]); 625 spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE, 626 spe_save_reg_high(rb), frame->fixreg[rb]); 627 width = DOUBLE; 628 } 629 break; 630 } 631 switch (instr_sec_op) { 632 case EFDCFS: 633 case EFSCFD: 634 /* Already handled. */ 635 break; 636 default: 637 res = spe_emu_instr(instr_sec_op, &fpemu, &result, 638 &frame->fixreg[rd]); 639 if (res != -1) 640 res <<= 2; 641 break; 642 } 643 644 switch (instr_sec_op & SPE_INST_MASK) { 645 case ECMPEQ: 646 case ECMPGT: 647 case ECMPLT: 648 frame->cr &= ~(0xf << cr_shift); 649 frame->cr |= (res << cr_shift); 650 break; 651 case ECTUI: 652 case ECTUIZ: 653 case ECTSI: 654 case ECTSIZ: 655 break; 656 default: 657 switch (width) { 658 case NONE: 659 case VECTOR: 660 break; 661 case SINGLE: 662 frame->fixreg[rd] = fpu_ftos(&fpemu, result); 663 break; 664 case DOUBLE: 665 spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res)); 666 frame->fixreg[rd] = ftod_res[1]; 667 break; 668 default: 669 panic("Unknown storage width %d", width); 670 break; 671 } 672 } 673 674 end: 675 spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS); 676 mtspr(SPR_SPEFSCR, spefscr); 677 frame->srr0 += 4; 678 mtmsr(msr); 679 680 return; 681 } 682 683 void 684 spe_handle_fpround(struct trapframe *frame) 685 { 686 687 /* 688 * Punt fpround exceptions for now. This leaves the truncated result in 689 * the register. We'll deal with overflow/underflow later. 690 */ 691 return; 692 } 693