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