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 "evaddumiaaw 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("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'. 180 */ 181 void 182 save_vec_nodrop(struct thread *td) 183 { 184 struct thread *vtd; 185 186 vtd = PCPU_GET(vecthread); 187 if (td != vtd) { 188 return; 189 } 190 191 save_vec_int(td); 192 } 193 194 195 #define SPE_INST_MASK 0x31f 196 #define EADD 0x200 197 #define ESUB 0x201 198 #define EABS 0x204 199 #define ENABS 0x205 200 #define ENEG 0x206 201 #define EMUL 0x208 202 #define EDIV 0x209 203 #define ECMPGT 0x20c 204 #define ECMPLT 0x20d 205 #define ECMPEQ 0x20e 206 #define ECFUI 0x210 207 #define ECFSI 0x211 208 #define ECTUI 0x214 209 #define ECTSI 0x215 210 #define ECTUF 0x216 211 #define ECTSF 0x217 212 #define ECTUIZ 0x218 213 #define ECTSIZ 0x21a 214 215 #define SPE 0x4 216 #define SPFP 0x6 217 #define DPFP 0x7 218 219 #define SPE_OPC 4 220 #define OPC_SHIFT 26 221 222 #define EVFSADD 0x280 223 #define EVFSSUB 0x281 224 #define EVFSABS 0x284 225 #define EVFSNABS 0x285 226 #define EVFSNEG 0x286 227 #define EVFSMUL 0x288 228 #define EVFSDIV 0x289 229 #define EVFSCMPGT 0x28c 230 #define EVFSCMPLT 0x28d 231 #define EVFSCMPEQ 0x28e 232 #define EVFSCFUI 0x290 233 #define EVFSCFSI 0x291 234 #define EVFSCTUI 0x294 235 #define EVFSCTSI 0x295 236 #define EVFSCTUF 0x296 237 #define EVFSCTSF 0x297 238 #define EVFSCTUIZ 0x298 239 #define EVFSCTSIZ 0x29a 240 241 #define EFSADD 0x2c0 242 #define EFSSUB 0x2c1 243 #define EFSABS 0x2c4 244 #define EFSNABS 0x2c5 245 #define EFSNEG 0x2c6 246 #define EFSMUL 0x2c8 247 #define EFSDIV 0x2c9 248 #define EFSCMPGT 0x2cc 249 #define EFSCMPLT 0x2cd 250 #define EFSCMPEQ 0x2ce 251 #define EFSCFD 0x2cf 252 #define EFSCFUI 0x2d0 253 #define EFSCFSI 0x2d1 254 #define EFSCTUI 0x2d4 255 #define EFSCTSI 0x2d5 256 #define EFSCTUF 0x2d6 257 #define EFSCTSF 0x2d7 258 #define EFSCTUIZ 0x2d8 259 #define EFSCTSIZ 0x2da 260 261 #define EFDADD 0x2e0 262 #define EFDSUB 0x2e1 263 #define EFDABS 0x2e4 264 #define EFDNABS 0x2e5 265 #define EFDNEG 0x2e6 266 #define EFDMUL 0x2e8 267 #define EFDDIV 0x2e9 268 #define EFDCMPGT 0x2ec 269 #define EFDCMPLT 0x2ed 270 #define EFDCMPEQ 0x2ee 271 #define EFDCFS 0x2ef 272 #define EFDCFUI 0x2f0 273 #define EFDCFSI 0x2f1 274 #define EFDCTUI 0x2f4 275 #define EFDCTSI 0x2f5 276 #define EFDCTUF 0x2f6 277 #define EFDCTSF 0x2f7 278 #define EFDCTUIZ 0x2f8 279 #define EFDCTSIZ 0x2fa 280 281 enum { 282 NONE, 283 SINGLE, 284 DOUBLE, 285 VECTOR, 286 }; 287 288 static uint32_t fpscr_to_spefscr(uint32_t fpscr) 289 { 290 uint32_t spefscr; 291 292 spefscr = 0; 293 294 if (fpscr & FPSCR_VX) 295 spefscr |= SPEFSCR_FINV; 296 if (fpscr & FPSCR_OX) 297 spefscr |= SPEFSCR_FOVF; 298 if (fpscr & FPSCR_UX) 299 spefscr |= SPEFSCR_FUNF; 300 if (fpscr & FPSCR_ZX) 301 spefscr |= SPEFSCR_FDBZ; 302 if (fpscr & FPSCR_XX) 303 spefscr |= SPEFSCR_FX; 304 305 return (spefscr); 306 } 307 308 /* Sign is 0 for unsigned, 1 for signed. */ 309 static int 310 spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign) 311 { 312 uint32_t res[2]; 313 314 res[0] = fpu_ftox(fpemu, fpn, res); 315 if (res[0] != UINT_MAX && res[0] != 0) 316 fpemu->fe_cx |= FPSCR_OX; 317 else if (sign == 0 && res[0] != 0) 318 fpemu->fe_cx |= FPSCR_UX; 319 else 320 *val = res[1]; 321 322 return (0); 323 } 324 325 /* Masked instruction */ 326 /* 327 * For compare instructions, returns 1 if success, 0 if not. For all others, 328 * returns -1, or -2 if no result needs recorded. 329 */ 330 static int 331 spe_emu_instr(uint32_t instr, struct fpemu *fpemu, 332 struct fpn **result, uint32_t *iresult) 333 { 334 switch (instr & SPE_INST_MASK) { 335 case EABS: 336 case ENABS: 337 case ENEG: 338 /* Taken care of elsewhere. */ 339 break; 340 case ECTUIZ: 341 fpemu->fe_cx &= ~FPSCR_RN; 342 fpemu->fe_cx |= FP_RZ; 343 case ECTUI: 344 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0); 345 return (-1); 346 case ECTSIZ: 347 fpemu->fe_cx &= ~FPSCR_RN; 348 fpemu->fe_cx |= FP_RZ; 349 case ECTSI: 350 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1); 351 return (-1); 352 case EADD: 353 *result = fpu_add(fpemu); 354 break; 355 case ESUB: 356 *result = fpu_sub(fpemu); 357 break; 358 case EMUL: 359 *result = fpu_mul(fpemu); 360 break; 361 case EDIV: 362 *result = fpu_div(fpemu); 363 break; 364 case ECMPGT: 365 fpu_compare(fpemu, 0); 366 if (fpemu->fe_cx & FPSCR_FG) 367 return (1); 368 return (0); 369 case ECMPLT: 370 fpu_compare(fpemu, 0); 371 if (fpemu->fe_cx & FPSCR_FL) 372 return (1); 373 return (0); 374 case ECMPEQ: 375 fpu_compare(fpemu, 0); 376 if (fpemu->fe_cx & FPSCR_FE) 377 return (1); 378 return (0); 379 default: 380 printf("Unknown instruction %x\n", instr); 381 } 382 383 return (-1); 384 } 385 386 static int 387 spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type, 388 uint32_t hi, uint32_t lo) 389 { 390 uint32_t s; 391 392 fp->fp_sign = hi >> 31; 393 fp->fp_sticky = 0; 394 switch (type) { 395 case SINGLE: 396 s = fpu_stof(fp, hi); 397 break; 398 399 case DOUBLE: 400 s = fpu_dtof(fp, hi, lo); 401 break; 402 } 403 404 if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) { 405 /* 406 * Input is a signalling NaN. All operations that return 407 * an input NaN operand put it through a ``NaN conversion'', 408 * which basically just means ``turn on the quiet bit''. 409 * We do this here so that all NaNs internally look quiet 410 * (we can tell signalling ones by their class). 411 */ 412 fp->fp_mant[0] |= FP_QUIETBIT; 413 fe->fe_cx = FPSCR_VXSNAN; /* assert invalid operand */ 414 s = FPC_SNAN; 415 } 416 fp->fp_class = s; 417 418 return (0); 419 } 420 421 /* 422 * Save the high word of a 64-bit GPR for manipulation in the exception handler. 423 */ 424 static uint32_t 425 spe_save_reg_high(int reg) 426 { 427 uint32_t vec[2]; 428 #define EVSTDW(n) case n: __asm __volatile ("evstdw %1,0(%0)" \ 429 :: "b"(vec), "n"(n)); break; 430 switch (reg) { 431 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3); 432 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7); 433 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11); 434 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15); 435 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19); 436 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23); 437 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27); 438 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31); 439 } 440 #undef EVSTDW 441 442 return (vec[0]); 443 } 444 445 /* 446 * Load the given value into the high word of the requested register. 447 */ 448 static void 449 spe_load_reg_high(int reg, uint32_t val) 450 { 451 #define EVLDW(n) case n: __asm __volatile("evmergelo "#n",%0,"#n \ 452 :: "r"(val)); break; 453 switch (reg) { 454 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4); 455 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8); 456 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12); 457 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16); 458 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20); 459 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24); 460 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28); 461 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0); 462 } 463 #undef EVLDW 464 465 } 466 467 void 468 spe_handle_fpdata(struct trapframe *frame) 469 { 470 struct fpemu fpemu; 471 struct fpn *result; 472 uint32_t instr, instr_sec_op; 473 uint32_t cr_shift, ra, rb, rd, src; 474 uint32_t high, low, res, tmp; /* For vector operations. */ 475 uint32_t spefscr = 0; 476 uint32_t ftod_res[2]; 477 int width; /* Single, Double, Vector, Integer */ 478 int err; 479 uint32_t msr; 480 481 err = fueword32((void *)frame->srr0, &instr); 482 483 if (err != 0) 484 return; 485 /* Fault. */; 486 487 if ((instr >> OPC_SHIFT) != SPE_OPC) 488 return; 489 490 msr = mfmsr(); 491 /* 492 * 'cr' field is the upper 3 bits of rd. Magically, since a) rd is 5 493 * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is 494 * modified for most compare operations, the full value of rd can be 495 * used as a shift value. 496 */ 497 rd = (instr >> 21) & 0x1f; 498 ra = (instr >> 16) & 0x1f; 499 rb = (instr >> 11) & 0x1f; 500 src = (instr >> 5) & 0x7; 501 cr_shift = 28 - (rd & 0x1f); 502 503 instr_sec_op = (instr & 0x7ff); 504 505 memset(&fpemu, 0, sizeof(fpemu)); 506 507 width = NONE; 508 switch (src) { 509 case SPE: 510 mtmsr(msr | PSL_VEC); 511 switch (instr_sec_op) { 512 case EVFSABS: 513 high = spe_save_reg_high(ra) & ~(1U << 31); 514 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31); 515 spe_load_reg_high(rd, high); 516 break; 517 case EVFSNABS: 518 high = spe_save_reg_high(ra) | (1U << 31); 519 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31); 520 spe_load_reg_high(rd, high); 521 break; 522 case EVFSNEG: 523 high = spe_save_reg_high(ra) ^ (1U << 31); 524 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31); 525 spe_load_reg_high(rd, high); 526 break; 527 default: 528 /* High word */ 529 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 530 spe_save_reg_high(ra), 0); 531 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 532 spe_save_reg_high(rb), 0); 533 high = spe_emu_instr(instr_sec_op, &fpemu, &result, 534 &tmp); 535 536 if (high < 0) 537 spe_load_reg_high(rd, tmp); 538 539 spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16; 540 /* Clear the fpemu to start over on the lower bits. */ 541 memset(&fpemu, 0, sizeof(fpemu)); 542 543 /* Now low word */ 544 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 545 frame->fixreg[ra], 0); 546 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 547 frame->fixreg[rb], 0); 548 spefscr |= fpscr_to_spefscr(fpemu.fe_cx); 549 low = spe_emu_instr(instr_sec_op, &fpemu, &result, 550 &frame->fixreg[rd]); 551 if (instr_sec_op == EVFSCMPEQ || 552 instr_sec_op == EVFSCMPGT || 553 instr_sec_op == EVFSCMPLT) { 554 res = (high << 3) | (low << 2) | 555 ((high | low) << 1) | (high & low); 556 width = NONE; 557 } else 558 width = VECTOR; 559 break; 560 } 561 goto end; 562 563 case SPFP: 564 switch (instr_sec_op) { 565 case EFSABS: 566 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31); 567 break; 568 case EFSNABS: 569 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31); 570 break; 571 case EFSNEG: 572 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31); 573 break; 574 case EFSCFD: 575 spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE, 576 spe_save_reg_high(rb), frame->fixreg[rb]); 577 result = &fpemu.fe_f3; 578 width = SINGLE; 579 break; 580 default: 581 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE, 582 frame->fixreg[ra], 0); 583 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE, 584 frame->fixreg[rb], 0); 585 width = SINGLE; 586 } 587 break; 588 case DPFP: 589 mtmsr(msr | PSL_VEC); 590 switch (instr_sec_op) { 591 case EFDABS: 592 high = spe_save_reg_high(ra) & ~(1U << 31); 593 frame->fixreg[rd] = frame->fixreg[ra]; 594 spe_load_reg_high(rd, high); 595 break; 596 case EFDNABS: 597 high = spe_save_reg_high(ra) | (1U << 31); 598 frame->fixreg[rd] = frame->fixreg[ra]; 599 spe_load_reg_high(rd, high); 600 break; 601 case EFDNEG: 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 EFDCFS: 607 spe_explode(&fpemu, &fpemu.fe_f3, SINGLE, 608 frame->fixreg[rb], 0); 609 result = &fpemu.fe_f3; 610 width = DOUBLE; 611 break; 612 default: 613 spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE, 614 spe_save_reg_high(ra), frame->fixreg[ra]); 615 spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE, 616 spe_save_reg_high(rb), frame->fixreg[rb]); 617 width = DOUBLE; 618 } 619 break; 620 } 621 switch (instr_sec_op) { 622 case EFDCFS: 623 case EFSCFD: 624 /* Already handled. */ 625 break; 626 default: 627 res = spe_emu_instr(instr_sec_op, &fpemu, &result, 628 &frame->fixreg[rd]); 629 if (res != -1) 630 res <<= 2; 631 break; 632 } 633 634 switch (instr_sec_op & SPE_INST_MASK) { 635 case ECMPEQ: 636 case ECMPGT: 637 case ECMPLT: 638 frame->cr &= ~(0xf << cr_shift); 639 frame->cr |= (res << cr_shift); 640 break; 641 case ECTUI: 642 case ECTUIZ: 643 case ECTSI: 644 case ECTSIZ: 645 break; 646 default: 647 switch (width) { 648 case NONE: 649 case VECTOR: 650 break; 651 case SINGLE: 652 frame->fixreg[rd] = fpu_ftos(&fpemu, result); 653 break; 654 case DOUBLE: 655 spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res)); 656 frame->fixreg[rd] = ftod_res[1]; 657 break; 658 default: 659 panic("Unknown storage width %d", width); 660 break; 661 } 662 } 663 664 end: 665 spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS); 666 mtspr(SPR_SPEFSCR, spefscr); 667 frame->srr0 += 4; 668 mtmsr(msr); 669 670 return; 671 } 672 673 void 674 spe_handle_fpround(struct trapframe *frame) 675 { 676 677 /* 678 * Punt fpround exceptions for now. This leaves the truncated result in 679 * the register. We'll deal with overflow/underflow later. 680 */ 681 return; 682 } 683