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