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