123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- /*-
- * Copyright (C) 1996 Wolfgang Solfrank.
- * Copyright (C) 1996 TooLs GmbH.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by TooLs GmbH.
- * 4. The name of TooLs GmbH may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $
- */
- #include <sys/cdefs.h>
- __FBSDID("$FreeBSD$");
- #include <sys/param.h>
- #include <sys/proc.h>
- #include <sys/systm.h>
- #include <sys/limits.h>
- #include <machine/altivec.h>
- #include <machine/fpu.h>
- #include <machine/ieeefp.h>
- #include <machine/pcb.h>
- #include <machine/psl.h>
- #include <powerpc/fpu/fpu_arith.h>
- #include <powerpc/fpu/fpu_emu.h>
- #include <powerpc/fpu/fpu_extern.h>
- void spe_handle_fpdata(struct trapframe *);
- void spe_handle_fpround(struct trapframe *);
- static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *);
- static void
- save_vec_int(struct thread *td)
- {
- int msr;
- struct pcb *pcb;
- pcb = td->td_pcb;
- /*
- * Temporarily re-enable the vector unit during the save
- */
- msr = mfmsr();
- mtmsr(msr | PSL_VEC);
- /*
- * Save the vector registers and SPEFSCR to the PCB
- */
- #define EVSTDW(n) __asm ("evstdw %1,0(%0)" \
- :: "b"(pcb->pcb_vec.vr[n]), "n"(n));
- EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
- EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
- EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
- EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
- EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
- EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
- EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
- EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
- #undef EVSTDW
- __asm ( "evxor 0,0,0\n"
- "evmwumiaa 0,0,0\n"
- "evstdd 0,0(%0)" :: "b"(&pcb->pcb_vec.spare[0]));
- pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
- /*
- * Disable vector unit again
- */
- isync();
- mtmsr(msr);
- }
- void
- enable_vec(struct thread *td)
- {
- int msr;
- struct pcb *pcb;
- struct trapframe *tf;
- pcb = td->td_pcb;
- tf = trapframe(td);
- /*
- * Save the thread's SPE CPU number, and set the CPU's current
- * vector thread
- */
- td->td_pcb->pcb_veccpu = PCPU_GET(cpuid);
- PCPU_SET(vecthread, td);
- /*
- * Enable the vector unit for when the thread returns from the
- * exception. If this is the first time the unit has been used by
- * the thread, initialise the vector registers and VSCR to 0, and
- * set the flag to indicate that the vector unit is in use.
- */
- tf->srr1 |= PSL_VEC;
- if (!(pcb->pcb_flags & PCB_VEC)) {
- memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec);
- pcb->pcb_flags |= PCB_VEC;
- pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
- }
- /*
- * Temporarily enable the vector unit so the registers
- * can be restored.
- */
- msr = mfmsr();
- mtmsr(msr | PSL_VEC);
- /* Restore SPEFSCR and ACC. Use %r0 as the scratch for ACC. */
- mtspr(SPR_SPEFSCR, pcb->pcb_vec.vscr);
- __asm __volatile("isync;evldd 0, 0(%0); evmra 0,0\n"
- :: "b"(&pcb->pcb_vec.spare[0]));
- /*
- * The lower half of each register will be restored on trap return. Use
- * %r0 as a scratch register, and restore it last.
- */
- #define EVLDW(n) __asm __volatile("evldw 0, 0(%0); evmergehilo "#n",0,"#n \
- :: "b"(&pcb->pcb_vec.vr[n]));
- EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
- EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
- EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
- EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
- EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
- EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
- EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
- EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
- #undef EVLDW
- isync();
- mtmsr(msr);
- }
- void
- save_vec(struct thread *td)
- {
- struct pcb *pcb;
- pcb = td->td_pcb;
- save_vec_int(td);
- /*
- * Clear the current vec thread and pcb's CPU id
- * XXX should this be left clear to allow lazy save/restore ?
- */
- pcb->pcb_veccpu = INT_MAX;
- PCPU_SET(vecthread, NULL);
- }
- /*
- * Save SPE state without dropping ownership. This will only save state if
- * the current vector-thread is `td'. This is used for taking core dumps, so
- * don't leak kernel information; overwrite the low words of each vector with
- * their real value, taken from the thread's trap frame, unconditionally.
- */
- void
- save_vec_nodrop(struct thread *td)
- {
- struct pcb *pcb;
- int i;
- if (td == PCPU_GET(vecthread))
- save_vec_int(td);
- pcb = td->td_pcb;
- for (i = 0; i < 32; i++) {
- pcb->pcb_vec.vr[i][1] =
- td->td_frame ? td->td_frame->fixreg[i] : 0;
- }
- }
- #define SPE_INST_MASK 0x31f
- #define EADD 0x200
- #define ESUB 0x201
- #define EABS 0x204
- #define ENABS 0x205
- #define ENEG 0x206
- #define EMUL 0x208
- #define EDIV 0x209
- #define ECMPGT 0x20c
- #define ECMPLT 0x20d
- #define ECMPEQ 0x20e
- #define ECFUI 0x210
- #define ECFSI 0x211
- #define ECTUI 0x214
- #define ECTSI 0x215
- #define ECTUF 0x216
- #define ECTSF 0x217
- #define ECTUIZ 0x218
- #define ECTSIZ 0x21a
- #define SPE 0x4
- #define SPFP 0x6
- #define DPFP 0x7
- #define SPE_OPC 4
- #define OPC_SHIFT 26
- #define EVFSADD 0x280
- #define EVFSSUB 0x281
- #define EVFSABS 0x284
- #define EVFSNABS 0x285
- #define EVFSNEG 0x286
- #define EVFSMUL 0x288
- #define EVFSDIV 0x289
- #define EVFSCMPGT 0x28c
- #define EVFSCMPLT 0x28d
- #define EVFSCMPEQ 0x28e
- #define EVFSCFUI 0x290
- #define EVFSCFSI 0x291
- #define EVFSCTUI 0x294
- #define EVFSCTSI 0x295
- #define EVFSCTUF 0x296
- #define EVFSCTSF 0x297
- #define EVFSCTUIZ 0x298
- #define EVFSCTSIZ 0x29a
- #define EFSADD 0x2c0
- #define EFSSUB 0x2c1
- #define EFSABS 0x2c4
- #define EFSNABS 0x2c5
- #define EFSNEG 0x2c6
- #define EFSMUL 0x2c8
- #define EFSDIV 0x2c9
- #define EFSCMPGT 0x2cc
- #define EFSCMPLT 0x2cd
- #define EFSCMPEQ 0x2ce
- #define EFSCFD 0x2cf
- #define EFSCFUI 0x2d0
- #define EFSCFSI 0x2d1
- #define EFSCTUI 0x2d4
- #define EFSCTSI 0x2d5
- #define EFSCTUF 0x2d6
- #define EFSCTSF 0x2d7
- #define EFSCTUIZ 0x2d8
- #define EFSCTSIZ 0x2da
- #define EFDADD 0x2e0
- #define EFDSUB 0x2e1
- #define EFDABS 0x2e4
- #define EFDNABS 0x2e5
- #define EFDNEG 0x2e6
- #define EFDMUL 0x2e8
- #define EFDDIV 0x2e9
- #define EFDCMPGT 0x2ec
- #define EFDCMPLT 0x2ed
- #define EFDCMPEQ 0x2ee
- #define EFDCFS 0x2ef
- #define EFDCFUI 0x2f0
- #define EFDCFSI 0x2f1
- #define EFDCTUI 0x2f4
- #define EFDCTSI 0x2f5
- #define EFDCTUF 0x2f6
- #define EFDCTSF 0x2f7
- #define EFDCTUIZ 0x2f8
- #define EFDCTSIZ 0x2fa
- enum {
- NONE,
- SINGLE,
- DOUBLE,
- VECTOR,
- };
- static uint32_t fpscr_to_spefscr(uint32_t fpscr)
- {
- uint32_t spefscr;
- spefscr = 0;
- if (fpscr & FPSCR_VX)
- spefscr |= SPEFSCR_FINV;
- if (fpscr & FPSCR_OX)
- spefscr |= SPEFSCR_FOVF;
- if (fpscr & FPSCR_UX)
- spefscr |= SPEFSCR_FUNF;
- if (fpscr & FPSCR_ZX)
- spefscr |= SPEFSCR_FDBZ;
- if (fpscr & FPSCR_XX)
- spefscr |= SPEFSCR_FX;
- return (spefscr);
- }
- /* Sign is 0 for unsigned, 1 for signed. */
- static int
- spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign)
- {
- uint32_t res[2];
- res[0] = fpu_ftox(fpemu, fpn, res);
- if (res[0] != UINT_MAX && res[0] != 0)
- fpemu->fe_cx |= FPSCR_OX;
- else if (sign == 0 && res[0] != 0)
- fpemu->fe_cx |= FPSCR_UX;
- else
- *val = res[1];
- return (0);
- }
- /* Masked instruction */
- /*
- * For compare instructions, returns 1 if success, 0 if not. For all others,
- * returns -1, or -2 if no result needs recorded.
- */
- static int
- spe_emu_instr(uint32_t instr, struct fpemu *fpemu,
- struct fpn **result, uint32_t *iresult)
- {
- switch (instr & SPE_INST_MASK) {
- case EABS:
- case ENABS:
- case ENEG:
- /* Taken care of elsewhere. */
- break;
- case ECTUIZ:
- fpemu->fe_cx &= ~FPSCR_RN;
- fpemu->fe_cx |= FP_RZ;
- case ECTUI:
- spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0);
- return (-1);
- case ECTSIZ:
- fpemu->fe_cx &= ~FPSCR_RN;
- fpemu->fe_cx |= FP_RZ;
- case ECTSI:
- spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1);
- return (-1);
- case EADD:
- *result = fpu_add(fpemu);
- break;
- case ESUB:
- *result = fpu_sub(fpemu);
- break;
- case EMUL:
- *result = fpu_mul(fpemu);
- break;
- case EDIV:
- *result = fpu_div(fpemu);
- break;
- case ECMPGT:
- fpu_compare(fpemu, 0);
- if (fpemu->fe_cx & FPSCR_FG)
- return (1);
- return (0);
- case ECMPLT:
- fpu_compare(fpemu, 0);
- if (fpemu->fe_cx & FPSCR_FL)
- return (1);
- return (0);
- case ECMPEQ:
- fpu_compare(fpemu, 0);
- if (fpemu->fe_cx & FPSCR_FE)
- return (1);
- return (0);
- default:
- printf("Unknown instruction %x\n", instr);
- }
- return (-1);
- }
- static int
- spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type,
- uint32_t hi, uint32_t lo)
- {
- uint32_t s;
- fp->fp_sign = hi >> 31;
- fp->fp_sticky = 0;
- switch (type) {
- case SINGLE:
- s = fpu_stof(fp, hi);
- break;
- case DOUBLE:
- s = fpu_dtof(fp, hi, lo);
- break;
- }
- if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {
- /*
- * Input is a signalling NaN. All operations that return
- * an input NaN operand put it through a ``NaN conversion'',
- * which basically just means ``turn on the quiet bit''.
- * We do this here so that all NaNs internally look quiet
- * (we can tell signalling ones by their class).
- */
- fp->fp_mant[0] |= FP_QUIETBIT;
- fe->fe_cx = FPSCR_VXSNAN; /* assert invalid operand */
- s = FPC_SNAN;
- }
- fp->fp_class = s;
- return (0);
- }
- /*
- * Save the high word of a 64-bit GPR for manipulation in the exception handler.
- */
- static uint32_t
- spe_save_reg_high(int reg)
- {
- uint32_t vec[2];
- #define EVSTDW(n) case n: __asm __volatile ("evstdw %1,0(%0)" \
- :: "b"(vec), "n"(n) : "memory"); break;
- switch (reg) {
- EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
- EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
- EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
- EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
- EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
- EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
- EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
- EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
- }
- #undef EVSTDW
- return (vec[0]);
- }
- /*
- * Load the given value into the high word of the requested register.
- */
- static void
- spe_load_reg_high(int reg, uint32_t val)
- {
- #define EVLDW(n) case n: __asm __volatile("evmergelo "#n",%0,"#n \
- :: "r"(val)); break;
- switch (reg) {
- EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
- EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
- EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
- EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
- EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
- EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
- EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
- EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
- }
- #undef EVLDW
- }
- void
- spe_handle_fpdata(struct trapframe *frame)
- {
- struct fpemu fpemu;
- struct fpn *result;
- uint32_t instr, instr_sec_op;
- uint32_t cr_shift, ra, rb, rd, src;
- uint32_t high, low, res, tmp; /* For vector operations. */
- uint32_t spefscr = 0;
- uint32_t ftod_res[2];
- int width; /* Single, Double, Vector, Integer */
- int err;
- uint32_t msr;
- err = fueword32((void *)frame->srr0, &instr);
- if (err != 0)
- return;
- /* Fault. */;
- if ((instr >> OPC_SHIFT) != SPE_OPC)
- return;
- msr = mfmsr();
- /*
- * 'cr' field is the upper 3 bits of rd. Magically, since a) rd is 5
- * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is
- * modified for most compare operations, the full value of rd can be
- * used as a shift value.
- */
- rd = (instr >> 21) & 0x1f;
- ra = (instr >> 16) & 0x1f;
- rb = (instr >> 11) & 0x1f;
- src = (instr >> 5) & 0x7;
- cr_shift = 28 - (rd & 0x1f);
- instr_sec_op = (instr & 0x7ff);
- memset(&fpemu, 0, sizeof(fpemu));
- width = NONE;
- switch (src) {
- case SPE:
- mtmsr(msr | PSL_VEC);
- switch (instr_sec_op) {
- case EVFSABS:
- high = spe_save_reg_high(ra) & ~(1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
- spe_load_reg_high(rd, high);
- break;
- case EVFSNABS:
- high = spe_save_reg_high(ra) | (1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
- spe_load_reg_high(rd, high);
- break;
- case EVFSNEG:
- high = spe_save_reg_high(ra) ^ (1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
- spe_load_reg_high(rd, high);
- break;
- default:
- /* High word */
- spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
- spe_save_reg_high(ra), 0);
- spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
- spe_save_reg_high(rb), 0);
- high = spe_emu_instr(instr_sec_op, &fpemu, &result,
- &tmp);
- if (high < 0)
- spe_load_reg_high(rd, tmp);
- spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16;
- /* Clear the fpemu to start over on the lower bits. */
- memset(&fpemu, 0, sizeof(fpemu));
- /* Now low word */
- spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
- frame->fixreg[ra], 0);
- spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
- frame->fixreg[rb], 0);
- spefscr |= fpscr_to_spefscr(fpemu.fe_cx);
- low = spe_emu_instr(instr_sec_op, &fpemu, &result,
- &frame->fixreg[rd]);
- if (instr_sec_op == EVFSCMPEQ ||
- instr_sec_op == EVFSCMPGT ||
- instr_sec_op == EVFSCMPLT) {
- res = (high << 3) | (low << 2) |
- ((high | low) << 1) | (high & low);
- width = NONE;
- } else
- width = VECTOR;
- break;
- }
- goto end;
- case SPFP:
- switch (instr_sec_op) {
- case EFSABS:
- frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
- break;
- case EFSNABS:
- frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
- break;
- case EFSNEG:
- frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
- break;
- case EFSCFD:
- mtmsr(msr | PSL_VEC);
- spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE,
- spe_save_reg_high(rb), frame->fixreg[rb]);
- result = &fpemu.fe_f3;
- width = SINGLE;
- break;
- default:
- spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
- frame->fixreg[ra], 0);
- spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
- frame->fixreg[rb], 0);
- width = SINGLE;
- }
- break;
- case DPFP:
- mtmsr(msr | PSL_VEC);
- switch (instr_sec_op) {
- case EFDABS:
- high = spe_save_reg_high(ra) & ~(1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra];
- spe_load_reg_high(rd, high);
- break;
- case EFDNABS:
- high = spe_save_reg_high(ra) | (1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra];
- spe_load_reg_high(rd, high);
- break;
- case EFDNEG:
- high = spe_save_reg_high(ra) ^ (1U << 31);
- frame->fixreg[rd] = frame->fixreg[ra];
- spe_load_reg_high(rd, high);
- break;
- case EFDCFS:
- spe_explode(&fpemu, &fpemu.fe_f3, SINGLE,
- frame->fixreg[rb], 0);
- result = &fpemu.fe_f3;
- width = DOUBLE;
- break;
- default:
- spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE,
- spe_save_reg_high(ra), frame->fixreg[ra]);
- spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE,
- spe_save_reg_high(rb), frame->fixreg[rb]);
- width = DOUBLE;
- }
- break;
- }
- switch (instr_sec_op) {
- case EFDCFS:
- case EFSCFD:
- /* Already handled. */
- break;
- default:
- res = spe_emu_instr(instr_sec_op, &fpemu, &result,
- &frame->fixreg[rd]);
- if (res != -1)
- res <<= 2;
- break;
- }
- switch (instr_sec_op & SPE_INST_MASK) {
- case ECMPEQ:
- case ECMPGT:
- case ECMPLT:
- frame->cr &= ~(0xf << cr_shift);
- frame->cr |= (res << cr_shift);
- break;
- case ECTUI:
- case ECTUIZ:
- case ECTSI:
- case ECTSIZ:
- break;
- default:
- switch (width) {
- case NONE:
- case VECTOR:
- break;
- case SINGLE:
- frame->fixreg[rd] = fpu_ftos(&fpemu, result);
- break;
- case DOUBLE:
- spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res));
- frame->fixreg[rd] = ftod_res[1];
- break;
- default:
- panic("Unknown storage width %d", width);
- break;
- }
- }
- end:
- spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS);
- mtspr(SPR_SPEFSCR, spefscr);
- frame->srr0 += 4;
- mtmsr(msr);
- return;
- }
- void
- spe_handle_fpround(struct trapframe *frame)
- {
- /*
- * Punt fpround exceptions for now. This leaves the truncated result in
- * the register. We'll deal with overflow/underflow later.
- */
- return;
- }
|