1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * MIPS floating point support 5*1da177e4SLinus Torvalds * Copyright (C) 1994-2000 Algorithmics Ltd. 6*1da177e4SLinus Torvalds * http://www.algor.co.uk 7*1da177e4SLinus Torvalds * 8*1da177e4SLinus Torvalds * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com 9*1da177e4SLinus Torvalds * Copyright (C) 2000 MIPS Technologies, Inc. 10*1da177e4SLinus Torvalds * 11*1da177e4SLinus Torvalds * This program is free software; you can distribute it and/or modify it 12*1da177e4SLinus Torvalds * under the terms of the GNU General Public License (Version 2) as 13*1da177e4SLinus Torvalds * published by the Free Software Foundation. 14*1da177e4SLinus Torvalds * 15*1da177e4SLinus Torvalds * This program is distributed in the hope it will be useful, but WITHOUT 16*1da177e4SLinus Torvalds * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17*1da177e4SLinus Torvalds * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18*1da177e4SLinus Torvalds * for more details. 19*1da177e4SLinus Torvalds * 20*1da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License along 21*1da177e4SLinus Torvalds * with this program; if not, write to the Free Software Foundation, Inc., 22*1da177e4SLinus Torvalds * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 23*1da177e4SLinus Torvalds * 24*1da177e4SLinus Torvalds * A complete emulator for MIPS coprocessor 1 instructions. This is 25*1da177e4SLinus Torvalds * required for #float(switch) or #float(trap), where it catches all 26*1da177e4SLinus Torvalds * COP1 instructions via the "CoProcessor Unusable" exception. 27*1da177e4SLinus Torvalds * 28*1da177e4SLinus Torvalds * More surprisingly it is also required for #float(ieee), to help out 29*1da177e4SLinus Torvalds * the hardware fpu at the boundaries of the IEEE-754 representation 30*1da177e4SLinus Torvalds * (denormalised values, infinities, underflow, etc). It is made 31*1da177e4SLinus Torvalds * quite nasty because emulation of some non-COP1 instructions is 32*1da177e4SLinus Torvalds * required, e.g. in branch delay slots. 33*1da177e4SLinus Torvalds * 34*1da177e4SLinus Torvalds * Note if you know that you won't have an fpu, then you'll get much 35*1da177e4SLinus Torvalds * better performance by compiling with -msoft-float! 36*1da177e4SLinus Torvalds */ 37*1da177e4SLinus Torvalds #include <linux/sched.h> 38*1da177e4SLinus Torvalds 39*1da177e4SLinus Torvalds #include <asm/inst.h> 40*1da177e4SLinus Torvalds #include <asm/bootinfo.h> 41*1da177e4SLinus Torvalds #include <asm/cpu.h> 42*1da177e4SLinus Torvalds #include <asm/cpu-features.h> 43*1da177e4SLinus Torvalds #include <asm/processor.h> 44*1da177e4SLinus Torvalds #include <asm/ptrace.h> 45*1da177e4SLinus Torvalds #include <asm/signal.h> 46*1da177e4SLinus Torvalds #include <asm/mipsregs.h> 47*1da177e4SLinus Torvalds #include <asm/fpu_emulator.h> 48*1da177e4SLinus Torvalds #include <asm/uaccess.h> 49*1da177e4SLinus Torvalds #include <asm/branch.h> 50*1da177e4SLinus Torvalds 51*1da177e4SLinus Torvalds #include "ieee754.h" 52*1da177e4SLinus Torvalds #include "dsemul.h" 53*1da177e4SLinus Torvalds 54*1da177e4SLinus Torvalds /* Strap kernel emulator for full MIPS IV emulation */ 55*1da177e4SLinus Torvalds 56*1da177e4SLinus Torvalds #ifdef __mips 57*1da177e4SLinus Torvalds #undef __mips 58*1da177e4SLinus Torvalds #endif 59*1da177e4SLinus Torvalds #define __mips 4 60*1da177e4SLinus Torvalds 61*1da177e4SLinus Torvalds /* Function which emulates a floating point instruction. */ 62*1da177e4SLinus Torvalds 63*1da177e4SLinus Torvalds static int fpu_emu(struct pt_regs *, struct mips_fpu_soft_struct *, 64*1da177e4SLinus Torvalds mips_instruction); 65*1da177e4SLinus Torvalds 66*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 67*1da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *, 68*1da177e4SLinus Torvalds struct mips_fpu_soft_struct *, mips_instruction); 69*1da177e4SLinus Torvalds #endif 70*1da177e4SLinus Torvalds 71*1da177e4SLinus Torvalds /* Further private data for which no space exists in mips_fpu_soft_struct */ 72*1da177e4SLinus Torvalds 73*1da177e4SLinus Torvalds struct mips_fpu_emulator_private fpuemuprivate; 74*1da177e4SLinus Torvalds 75*1da177e4SLinus Torvalds /* Control registers */ 76*1da177e4SLinus Torvalds 77*1da177e4SLinus Torvalds #define FPCREG_RID 0 /* $0 = revision id */ 78*1da177e4SLinus Torvalds #define FPCREG_CSR 31 /* $31 = csr */ 79*1da177e4SLinus Torvalds 80*1da177e4SLinus Torvalds /* Convert Mips rounding mode (0..3) to IEEE library modes. */ 81*1da177e4SLinus Torvalds static const unsigned char ieee_rm[4] = { 82*1da177e4SLinus Torvalds IEEE754_RN, IEEE754_RZ, IEEE754_RU, IEEE754_RD 83*1da177e4SLinus Torvalds }; 84*1da177e4SLinus Torvalds 85*1da177e4SLinus Torvalds #if __mips >= 4 86*1da177e4SLinus Torvalds /* convert condition code register number to csr bit */ 87*1da177e4SLinus Torvalds static const unsigned int fpucondbit[8] = { 88*1da177e4SLinus Torvalds FPU_CSR_COND0, 89*1da177e4SLinus Torvalds FPU_CSR_COND1, 90*1da177e4SLinus Torvalds FPU_CSR_COND2, 91*1da177e4SLinus Torvalds FPU_CSR_COND3, 92*1da177e4SLinus Torvalds FPU_CSR_COND4, 93*1da177e4SLinus Torvalds FPU_CSR_COND5, 94*1da177e4SLinus Torvalds FPU_CSR_COND6, 95*1da177e4SLinus Torvalds FPU_CSR_COND7 96*1da177e4SLinus Torvalds }; 97*1da177e4SLinus Torvalds #endif 98*1da177e4SLinus Torvalds 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds /* 101*1da177e4SLinus Torvalds * Redundant with logic already in kernel/branch.c, 102*1da177e4SLinus Torvalds * embedded in compute_return_epc. At some point, 103*1da177e4SLinus Torvalds * a single subroutine should be used across both 104*1da177e4SLinus Torvalds * modules. 105*1da177e4SLinus Torvalds */ 106*1da177e4SLinus Torvalds static int isBranchInstr(mips_instruction * i) 107*1da177e4SLinus Torvalds { 108*1da177e4SLinus Torvalds switch (MIPSInst_OPCODE(*i)) { 109*1da177e4SLinus Torvalds case spec_op: 110*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(*i)) { 111*1da177e4SLinus Torvalds case jalr_op: 112*1da177e4SLinus Torvalds case jr_op: 113*1da177e4SLinus Torvalds return 1; 114*1da177e4SLinus Torvalds } 115*1da177e4SLinus Torvalds break; 116*1da177e4SLinus Torvalds 117*1da177e4SLinus Torvalds case bcond_op: 118*1da177e4SLinus Torvalds switch (MIPSInst_RT(*i)) { 119*1da177e4SLinus Torvalds case bltz_op: 120*1da177e4SLinus Torvalds case bgez_op: 121*1da177e4SLinus Torvalds case bltzl_op: 122*1da177e4SLinus Torvalds case bgezl_op: 123*1da177e4SLinus Torvalds case bltzal_op: 124*1da177e4SLinus Torvalds case bgezal_op: 125*1da177e4SLinus Torvalds case bltzall_op: 126*1da177e4SLinus Torvalds case bgezall_op: 127*1da177e4SLinus Torvalds return 1; 128*1da177e4SLinus Torvalds } 129*1da177e4SLinus Torvalds break; 130*1da177e4SLinus Torvalds 131*1da177e4SLinus Torvalds case j_op: 132*1da177e4SLinus Torvalds case jal_op: 133*1da177e4SLinus Torvalds case jalx_op: 134*1da177e4SLinus Torvalds case beq_op: 135*1da177e4SLinus Torvalds case bne_op: 136*1da177e4SLinus Torvalds case blez_op: 137*1da177e4SLinus Torvalds case bgtz_op: 138*1da177e4SLinus Torvalds case beql_op: 139*1da177e4SLinus Torvalds case bnel_op: 140*1da177e4SLinus Torvalds case blezl_op: 141*1da177e4SLinus Torvalds case bgtzl_op: 142*1da177e4SLinus Torvalds return 1; 143*1da177e4SLinus Torvalds 144*1da177e4SLinus Torvalds case cop0_op: 145*1da177e4SLinus Torvalds case cop1_op: 146*1da177e4SLinus Torvalds case cop2_op: 147*1da177e4SLinus Torvalds case cop1x_op: 148*1da177e4SLinus Torvalds if (MIPSInst_RS(*i) == bc_op) 149*1da177e4SLinus Torvalds return 1; 150*1da177e4SLinus Torvalds break; 151*1da177e4SLinus Torvalds } 152*1da177e4SLinus Torvalds 153*1da177e4SLinus Torvalds return 0; 154*1da177e4SLinus Torvalds } 155*1da177e4SLinus Torvalds 156*1da177e4SLinus Torvalds /* 157*1da177e4SLinus Torvalds * In the Linux kernel, we support selection of FPR format on the 158*1da177e4SLinus Torvalds * basis of the Status.FR bit. This does imply that, if a full 32 159*1da177e4SLinus Torvalds * FPRs are desired, there needs to be a flip-flop that can be written 160*1da177e4SLinus Torvalds * to one at that bit position. In any case, O32 MIPS ABI uses 161*1da177e4SLinus Torvalds * only the even FPRs (Status.FR = 0). 162*1da177e4SLinus Torvalds */ 163*1da177e4SLinus Torvalds 164*1da177e4SLinus Torvalds #define CP0_STATUS_FR_SUPPORT 165*1da177e4SLinus Torvalds 166*1da177e4SLinus Torvalds #ifdef CP0_STATUS_FR_SUPPORT 167*1da177e4SLinus Torvalds #define FR_BIT ST0_FR 168*1da177e4SLinus Torvalds #else 169*1da177e4SLinus Torvalds #define FR_BIT 0 170*1da177e4SLinus Torvalds #endif 171*1da177e4SLinus Torvalds 172*1da177e4SLinus Torvalds #define SIFROMREG(si,x) ((si) = \ 173*1da177e4SLinus Torvalds (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ 174*1da177e4SLinus Torvalds (int)ctx->fpr[x] : \ 175*1da177e4SLinus Torvalds (int)(ctx->fpr[x & ~1] >> 32 )) 176*1da177e4SLinus Torvalds #define SITOREG(si,x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \ 177*1da177e4SLinus Torvalds (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ 178*1da177e4SLinus Torvalds ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \ 179*1da177e4SLinus Torvalds ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32) 180*1da177e4SLinus Torvalds 181*1da177e4SLinus Torvalds #define DIFROMREG(di,x) ((di) = \ 182*1da177e4SLinus Torvalds ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)]) 183*1da177e4SLinus Torvalds #define DITOREG(di,x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] \ 184*1da177e4SLinus Torvalds = (di)) 185*1da177e4SLinus Torvalds 186*1da177e4SLinus Torvalds #define SPFROMREG(sp,x) SIFROMREG((sp).bits,x) 187*1da177e4SLinus Torvalds #define SPTOREG(sp,x) SITOREG((sp).bits,x) 188*1da177e4SLinus Torvalds #define DPFROMREG(dp,x) DIFROMREG((dp).bits,x) 189*1da177e4SLinus Torvalds #define DPTOREG(dp,x) DITOREG((dp).bits,x) 190*1da177e4SLinus Torvalds 191*1da177e4SLinus Torvalds /* 192*1da177e4SLinus Torvalds * Emulate the single floating point instruction pointed at by EPC. 193*1da177e4SLinus Torvalds * Two instructions if the instruction is in a branch delay slot. 194*1da177e4SLinus Torvalds */ 195*1da177e4SLinus Torvalds 196*1da177e4SLinus Torvalds static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) 197*1da177e4SLinus Torvalds { 198*1da177e4SLinus Torvalds mips_instruction ir; 199*1da177e4SLinus Torvalds vaddr_t emulpc, contpc; 200*1da177e4SLinus Torvalds unsigned int cond; 201*1da177e4SLinus Torvalds 202*1da177e4SLinus Torvalds if (get_user(ir, (mips_instruction *) xcp->cp0_epc)) { 203*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 204*1da177e4SLinus Torvalds return SIGBUS; 205*1da177e4SLinus Torvalds } 206*1da177e4SLinus Torvalds 207*1da177e4SLinus Torvalds /* XXX NEC Vr54xx bug workaround */ 208*1da177e4SLinus Torvalds if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir)) 209*1da177e4SLinus Torvalds xcp->cp0_cause &= ~CAUSEF_BD; 210*1da177e4SLinus Torvalds 211*1da177e4SLinus Torvalds if (xcp->cp0_cause & CAUSEF_BD) { 212*1da177e4SLinus Torvalds /* 213*1da177e4SLinus Torvalds * The instruction to be emulated is in a branch delay slot 214*1da177e4SLinus Torvalds * which means that we have to emulate the branch instruction 215*1da177e4SLinus Torvalds * BEFORE we do the cop1 instruction. 216*1da177e4SLinus Torvalds * 217*1da177e4SLinus Torvalds * This branch could be a COP1 branch, but in that case we 218*1da177e4SLinus Torvalds * would have had a trap for that instruction, and would not 219*1da177e4SLinus Torvalds * come through this route. 220*1da177e4SLinus Torvalds * 221*1da177e4SLinus Torvalds * Linux MIPS branch emulator operates on context, updating the 222*1da177e4SLinus Torvalds * cp0_epc. 223*1da177e4SLinus Torvalds */ 224*1da177e4SLinus Torvalds emulpc = REG_TO_VA(xcp->cp0_epc + 4); /* Snapshot emulation target */ 225*1da177e4SLinus Torvalds 226*1da177e4SLinus Torvalds if (__compute_return_epc(xcp)) { 227*1da177e4SLinus Torvalds #ifdef CP1DBG 228*1da177e4SLinus Torvalds printk("failed to emulate branch at %p\n", 229*1da177e4SLinus Torvalds REG_TO_VA(xcp->cp0_epc)); 230*1da177e4SLinus Torvalds #endif 231*1da177e4SLinus Torvalds return SIGILL; 232*1da177e4SLinus Torvalds } 233*1da177e4SLinus Torvalds if (get_user(ir, (mips_instruction *) emulpc)) { 234*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 235*1da177e4SLinus Torvalds return SIGBUS; 236*1da177e4SLinus Torvalds } 237*1da177e4SLinus Torvalds /* __compute_return_epc() will have updated cp0_epc */ 238*1da177e4SLinus Torvalds contpc = REG_TO_VA xcp->cp0_epc; 239*1da177e4SLinus Torvalds /* In order not to confuse ptrace() et al, tweak context */ 240*1da177e4SLinus Torvalds xcp->cp0_epc = VA_TO_REG emulpc - 4; 241*1da177e4SLinus Torvalds } 242*1da177e4SLinus Torvalds else { 243*1da177e4SLinus Torvalds emulpc = REG_TO_VA xcp->cp0_epc; 244*1da177e4SLinus Torvalds contpc = REG_TO_VA(xcp->cp0_epc + 4); 245*1da177e4SLinus Torvalds } 246*1da177e4SLinus Torvalds 247*1da177e4SLinus Torvalds emul: 248*1da177e4SLinus Torvalds fpuemuprivate.stats.emulated++; 249*1da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 250*1da177e4SLinus Torvalds #ifndef SINGLE_ONLY_FPU 251*1da177e4SLinus Torvalds case ldc1_op:{ 252*1da177e4SLinus Torvalds u64 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + 253*1da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 254*1da177e4SLinus Torvalds u64 val; 255*1da177e4SLinus Torvalds 256*1da177e4SLinus Torvalds fpuemuprivate.stats.loads++; 257*1da177e4SLinus Torvalds if (get_user(val, va)) { 258*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 259*1da177e4SLinus Torvalds return SIGBUS; 260*1da177e4SLinus Torvalds } 261*1da177e4SLinus Torvalds DITOREG(val, MIPSInst_RT(ir)); 262*1da177e4SLinus Torvalds break; 263*1da177e4SLinus Torvalds } 264*1da177e4SLinus Torvalds 265*1da177e4SLinus Torvalds case sdc1_op:{ 266*1da177e4SLinus Torvalds u64 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + 267*1da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 268*1da177e4SLinus Torvalds u64 val; 269*1da177e4SLinus Torvalds 270*1da177e4SLinus Torvalds fpuemuprivate.stats.stores++; 271*1da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_RT(ir)); 272*1da177e4SLinus Torvalds if (put_user(val, va)) { 273*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 274*1da177e4SLinus Torvalds return SIGBUS; 275*1da177e4SLinus Torvalds } 276*1da177e4SLinus Torvalds break; 277*1da177e4SLinus Torvalds } 278*1da177e4SLinus Torvalds #endif 279*1da177e4SLinus Torvalds 280*1da177e4SLinus Torvalds case lwc1_op:{ 281*1da177e4SLinus Torvalds u32 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + 282*1da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 283*1da177e4SLinus Torvalds u32 val; 284*1da177e4SLinus Torvalds 285*1da177e4SLinus Torvalds fpuemuprivate.stats.loads++; 286*1da177e4SLinus Torvalds if (get_user(val, va)) { 287*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 288*1da177e4SLinus Torvalds return SIGBUS; 289*1da177e4SLinus Torvalds } 290*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 291*1da177e4SLinus Torvalds if (MIPSInst_RT(ir) & 1) { 292*1da177e4SLinus Torvalds /* illegal register in single-float mode */ 293*1da177e4SLinus Torvalds return SIGILL; 294*1da177e4SLinus Torvalds } 295*1da177e4SLinus Torvalds #endif 296*1da177e4SLinus Torvalds SITOREG(val, MIPSInst_RT(ir)); 297*1da177e4SLinus Torvalds break; 298*1da177e4SLinus Torvalds } 299*1da177e4SLinus Torvalds 300*1da177e4SLinus Torvalds case swc1_op:{ 301*1da177e4SLinus Torvalds u32 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + 302*1da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 303*1da177e4SLinus Torvalds u32 val; 304*1da177e4SLinus Torvalds 305*1da177e4SLinus Torvalds fpuemuprivate.stats.stores++; 306*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 307*1da177e4SLinus Torvalds if (MIPSInst_RT(ir) & 1) { 308*1da177e4SLinus Torvalds /* illegal register in single-float mode */ 309*1da177e4SLinus Torvalds return SIGILL; 310*1da177e4SLinus Torvalds } 311*1da177e4SLinus Torvalds #endif 312*1da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_RT(ir)); 313*1da177e4SLinus Torvalds if (put_user(val, va)) { 314*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 315*1da177e4SLinus Torvalds return SIGBUS; 316*1da177e4SLinus Torvalds } 317*1da177e4SLinus Torvalds break; 318*1da177e4SLinus Torvalds } 319*1da177e4SLinus Torvalds 320*1da177e4SLinus Torvalds case cop1_op: 321*1da177e4SLinus Torvalds switch (MIPSInst_RS(ir)) { 322*1da177e4SLinus Torvalds 323*1da177e4SLinus Torvalds #if __mips64 && !defined(SINGLE_ONLY_FPU) 324*1da177e4SLinus Torvalds case dmfc_op: 325*1da177e4SLinus Torvalds /* copregister fs -> gpr[rt] */ 326*1da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 327*1da177e4SLinus Torvalds DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 328*1da177e4SLinus Torvalds MIPSInst_RD(ir)); 329*1da177e4SLinus Torvalds } 330*1da177e4SLinus Torvalds break; 331*1da177e4SLinus Torvalds 332*1da177e4SLinus Torvalds case dmtc_op: 333*1da177e4SLinus Torvalds /* copregister fs <- rt */ 334*1da177e4SLinus Torvalds DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 335*1da177e4SLinus Torvalds break; 336*1da177e4SLinus Torvalds #endif 337*1da177e4SLinus Torvalds 338*1da177e4SLinus Torvalds case mfc_op: 339*1da177e4SLinus Torvalds /* copregister rd -> gpr[rt] */ 340*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 341*1da177e4SLinus Torvalds if (MIPSInst_RD(ir) & 1) { 342*1da177e4SLinus Torvalds /* illegal register in single-float mode */ 343*1da177e4SLinus Torvalds return SIGILL; 344*1da177e4SLinus Torvalds } 345*1da177e4SLinus Torvalds #endif 346*1da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 347*1da177e4SLinus Torvalds SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 348*1da177e4SLinus Torvalds MIPSInst_RD(ir)); 349*1da177e4SLinus Torvalds } 350*1da177e4SLinus Torvalds break; 351*1da177e4SLinus Torvalds 352*1da177e4SLinus Torvalds case mtc_op: 353*1da177e4SLinus Torvalds /* copregister rd <- rt */ 354*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 355*1da177e4SLinus Torvalds if (MIPSInst_RD(ir) & 1) { 356*1da177e4SLinus Torvalds /* illegal register in single-float mode */ 357*1da177e4SLinus Torvalds return SIGILL; 358*1da177e4SLinus Torvalds } 359*1da177e4SLinus Torvalds #endif 360*1da177e4SLinus Torvalds SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 361*1da177e4SLinus Torvalds break; 362*1da177e4SLinus Torvalds 363*1da177e4SLinus Torvalds case cfc_op:{ 364*1da177e4SLinus Torvalds /* cop control register rd -> gpr[rt] */ 365*1da177e4SLinus Torvalds u32 value; 366*1da177e4SLinus Torvalds 367*1da177e4SLinus Torvalds if (ir == CP1UNDEF) { 368*1da177e4SLinus Torvalds return do_dsemulret(xcp); 369*1da177e4SLinus Torvalds } 370*1da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 371*1da177e4SLinus Torvalds value = ctx->fcr31; 372*1da177e4SLinus Torvalds #ifdef CSRTRACE 373*1da177e4SLinus Torvalds printk("%p gpr[%d]<-csr=%08x\n", 374*1da177e4SLinus Torvalds REG_TO_VA(xcp->cp0_epc), 375*1da177e4SLinus Torvalds MIPSInst_RT(ir), value); 376*1da177e4SLinus Torvalds #endif 377*1da177e4SLinus Torvalds } 378*1da177e4SLinus Torvalds else if (MIPSInst_RD(ir) == FPCREG_RID) 379*1da177e4SLinus Torvalds value = 0; 380*1da177e4SLinus Torvalds else 381*1da177e4SLinus Torvalds value = 0; 382*1da177e4SLinus Torvalds if (MIPSInst_RT(ir)) 383*1da177e4SLinus Torvalds xcp->regs[MIPSInst_RT(ir)] = value; 384*1da177e4SLinus Torvalds break; 385*1da177e4SLinus Torvalds } 386*1da177e4SLinus Torvalds 387*1da177e4SLinus Torvalds case ctc_op:{ 388*1da177e4SLinus Torvalds /* copregister rd <- rt */ 389*1da177e4SLinus Torvalds u32 value; 390*1da177e4SLinus Torvalds 391*1da177e4SLinus Torvalds if (MIPSInst_RT(ir) == 0) 392*1da177e4SLinus Torvalds value = 0; 393*1da177e4SLinus Torvalds else 394*1da177e4SLinus Torvalds value = xcp->regs[MIPSInst_RT(ir)]; 395*1da177e4SLinus Torvalds 396*1da177e4SLinus Torvalds /* we only have one writable control reg 397*1da177e4SLinus Torvalds */ 398*1da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 399*1da177e4SLinus Torvalds #ifdef CSRTRACE 400*1da177e4SLinus Torvalds printk("%p gpr[%d]->csr=%08x\n", 401*1da177e4SLinus Torvalds REG_TO_VA(xcp->cp0_epc), 402*1da177e4SLinus Torvalds MIPSInst_RT(ir), value); 403*1da177e4SLinus Torvalds #endif 404*1da177e4SLinus Torvalds ctx->fcr31 = value; 405*1da177e4SLinus Torvalds /* copy new rounding mode and 406*1da177e4SLinus Torvalds flush bit to ieee library state! */ 407*1da177e4SLinus Torvalds ieee754_csr.nod = (ctx->fcr31 & 0x1000000) != 0; 408*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[value & 0x3]; 409*1da177e4SLinus Torvalds } 410*1da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 411*1da177e4SLinus Torvalds return SIGFPE; 412*1da177e4SLinus Torvalds } 413*1da177e4SLinus Torvalds break; 414*1da177e4SLinus Torvalds } 415*1da177e4SLinus Torvalds 416*1da177e4SLinus Torvalds case bc_op:{ 417*1da177e4SLinus Torvalds int likely = 0; 418*1da177e4SLinus Torvalds 419*1da177e4SLinus Torvalds if (xcp->cp0_cause & CAUSEF_BD) 420*1da177e4SLinus Torvalds return SIGILL; 421*1da177e4SLinus Torvalds 422*1da177e4SLinus Torvalds #if __mips >= 4 423*1da177e4SLinus Torvalds cond = ctx->fcr31 & fpucondbit[MIPSInst_RT(ir) >> 2]; 424*1da177e4SLinus Torvalds #else 425*1da177e4SLinus Torvalds cond = ctx->fcr31 & FPU_CSR_COND; 426*1da177e4SLinus Torvalds #endif 427*1da177e4SLinus Torvalds switch (MIPSInst_RT(ir) & 3) { 428*1da177e4SLinus Torvalds case bcfl_op: 429*1da177e4SLinus Torvalds likely = 1; 430*1da177e4SLinus Torvalds case bcf_op: 431*1da177e4SLinus Torvalds cond = !cond; 432*1da177e4SLinus Torvalds break; 433*1da177e4SLinus Torvalds case bctl_op: 434*1da177e4SLinus Torvalds likely = 1; 435*1da177e4SLinus Torvalds case bct_op: 436*1da177e4SLinus Torvalds break; 437*1da177e4SLinus Torvalds default: 438*1da177e4SLinus Torvalds /* thats an illegal instruction */ 439*1da177e4SLinus Torvalds return SIGILL; 440*1da177e4SLinus Torvalds } 441*1da177e4SLinus Torvalds 442*1da177e4SLinus Torvalds xcp->cp0_cause |= CAUSEF_BD; 443*1da177e4SLinus Torvalds if (cond) { 444*1da177e4SLinus Torvalds /* branch taken: emulate dslot 445*1da177e4SLinus Torvalds * instruction 446*1da177e4SLinus Torvalds */ 447*1da177e4SLinus Torvalds xcp->cp0_epc += 4; 448*1da177e4SLinus Torvalds contpc = REG_TO_VA 449*1da177e4SLinus Torvalds (xcp->cp0_epc + 450*1da177e4SLinus Torvalds (MIPSInst_SIMM(ir) << 2)); 451*1da177e4SLinus Torvalds 452*1da177e4SLinus Torvalds if (get_user(ir, (mips_instruction *) 453*1da177e4SLinus Torvalds REG_TO_VA xcp->cp0_epc)) { 454*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 455*1da177e4SLinus Torvalds return SIGBUS; 456*1da177e4SLinus Torvalds } 457*1da177e4SLinus Torvalds 458*1da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 459*1da177e4SLinus Torvalds case lwc1_op: 460*1da177e4SLinus Torvalds case swc1_op: 461*1da177e4SLinus Torvalds #if (__mips >= 2 || __mips64) && !defined(SINGLE_ONLY_FPU) 462*1da177e4SLinus Torvalds case ldc1_op: 463*1da177e4SLinus Torvalds case sdc1_op: 464*1da177e4SLinus Torvalds #endif 465*1da177e4SLinus Torvalds case cop1_op: 466*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 467*1da177e4SLinus Torvalds case cop1x_op: 468*1da177e4SLinus Torvalds #endif 469*1da177e4SLinus Torvalds /* its one of ours */ 470*1da177e4SLinus Torvalds goto emul; 471*1da177e4SLinus Torvalds #if __mips >= 4 472*1da177e4SLinus Torvalds case spec_op: 473*1da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) == movc_op) 474*1da177e4SLinus Torvalds goto emul; 475*1da177e4SLinus Torvalds break; 476*1da177e4SLinus Torvalds #endif 477*1da177e4SLinus Torvalds } 478*1da177e4SLinus Torvalds 479*1da177e4SLinus Torvalds /* 480*1da177e4SLinus Torvalds * Single step the non-cp1 481*1da177e4SLinus Torvalds * instruction in the dslot 482*1da177e4SLinus Torvalds */ 483*1da177e4SLinus Torvalds return mips_dsemul(xcp, ir, VA_TO_REG contpc); 484*1da177e4SLinus Torvalds } 485*1da177e4SLinus Torvalds else { 486*1da177e4SLinus Torvalds /* branch not taken */ 487*1da177e4SLinus Torvalds if (likely) { 488*1da177e4SLinus Torvalds /* 489*1da177e4SLinus Torvalds * branch likely nullifies 490*1da177e4SLinus Torvalds * dslot if not taken 491*1da177e4SLinus Torvalds */ 492*1da177e4SLinus Torvalds xcp->cp0_epc += 4; 493*1da177e4SLinus Torvalds contpc += 4; 494*1da177e4SLinus Torvalds /* 495*1da177e4SLinus Torvalds * else continue & execute 496*1da177e4SLinus Torvalds * dslot as normal insn 497*1da177e4SLinus Torvalds */ 498*1da177e4SLinus Torvalds } 499*1da177e4SLinus Torvalds } 500*1da177e4SLinus Torvalds break; 501*1da177e4SLinus Torvalds } 502*1da177e4SLinus Torvalds 503*1da177e4SLinus Torvalds default: 504*1da177e4SLinus Torvalds if (!(MIPSInst_RS(ir) & 0x10)) 505*1da177e4SLinus Torvalds return SIGILL; 506*1da177e4SLinus Torvalds { 507*1da177e4SLinus Torvalds int sig; 508*1da177e4SLinus Torvalds 509*1da177e4SLinus Torvalds /* a real fpu computation instruction */ 510*1da177e4SLinus Torvalds if ((sig = fpu_emu(xcp, ctx, ir))) 511*1da177e4SLinus Torvalds return sig; 512*1da177e4SLinus Torvalds } 513*1da177e4SLinus Torvalds } 514*1da177e4SLinus Torvalds break; 515*1da177e4SLinus Torvalds 516*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 517*1da177e4SLinus Torvalds case cop1x_op:{ 518*1da177e4SLinus Torvalds int sig; 519*1da177e4SLinus Torvalds 520*1da177e4SLinus Torvalds if ((sig = fpux_emu(xcp, ctx, ir))) 521*1da177e4SLinus Torvalds return sig; 522*1da177e4SLinus Torvalds break; 523*1da177e4SLinus Torvalds } 524*1da177e4SLinus Torvalds #endif 525*1da177e4SLinus Torvalds 526*1da177e4SLinus Torvalds #if __mips >= 4 527*1da177e4SLinus Torvalds case spec_op: 528*1da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != movc_op) 529*1da177e4SLinus Torvalds return SIGILL; 530*1da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 531*1da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 532*1da177e4SLinus Torvalds xcp->regs[MIPSInst_RD(ir)] = 533*1da177e4SLinus Torvalds xcp->regs[MIPSInst_RS(ir)]; 534*1da177e4SLinus Torvalds break; 535*1da177e4SLinus Torvalds #endif 536*1da177e4SLinus Torvalds 537*1da177e4SLinus Torvalds default: 538*1da177e4SLinus Torvalds return SIGILL; 539*1da177e4SLinus Torvalds } 540*1da177e4SLinus Torvalds 541*1da177e4SLinus Torvalds /* we did it !! */ 542*1da177e4SLinus Torvalds xcp->cp0_epc = VA_TO_REG(contpc); 543*1da177e4SLinus Torvalds xcp->cp0_cause &= ~CAUSEF_BD; 544*1da177e4SLinus Torvalds return 0; 545*1da177e4SLinus Torvalds } 546*1da177e4SLinus Torvalds 547*1da177e4SLinus Torvalds /* 548*1da177e4SLinus Torvalds * Conversion table from MIPS compare ops 48-63 549*1da177e4SLinus Torvalds * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 550*1da177e4SLinus Torvalds */ 551*1da177e4SLinus Torvalds static const unsigned char cmptab[8] = { 552*1da177e4SLinus Torvalds 0, /* cmp_0 (sig) cmp_sf */ 553*1da177e4SLinus Torvalds IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 554*1da177e4SLinus Torvalds IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 555*1da177e4SLinus Torvalds IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 556*1da177e4SLinus Torvalds IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 557*1da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 558*1da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 559*1da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 560*1da177e4SLinus Torvalds }; 561*1da177e4SLinus Torvalds 562*1da177e4SLinus Torvalds 563*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 564*1da177e4SLinus Torvalds 565*1da177e4SLinus Torvalds /* 566*1da177e4SLinus Torvalds * Additional MIPS4 instructions 567*1da177e4SLinus Torvalds */ 568*1da177e4SLinus Torvalds 569*1da177e4SLinus Torvalds #define DEF3OP(name, p, f1, f2, f3) \ 570*1da177e4SLinus Torvalds static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \ 571*1da177e4SLinus Torvalds ieee754##p t) \ 572*1da177e4SLinus Torvalds { \ 573*1da177e4SLinus Torvalds struct ieee754_csr ieee754_csr_save; \ 574*1da177e4SLinus Torvalds s = f1 (s, t); \ 575*1da177e4SLinus Torvalds ieee754_csr_save = ieee754_csr; \ 576*1da177e4SLinus Torvalds s = f2 (s, r); \ 577*1da177e4SLinus Torvalds ieee754_csr_save.cx |= ieee754_csr.cx; \ 578*1da177e4SLinus Torvalds ieee754_csr_save.sx |= ieee754_csr.sx; \ 579*1da177e4SLinus Torvalds s = f3 (s); \ 580*1da177e4SLinus Torvalds ieee754_csr.cx |= ieee754_csr_save.cx; \ 581*1da177e4SLinus Torvalds ieee754_csr.sx |= ieee754_csr_save.sx; \ 582*1da177e4SLinus Torvalds return s; \ 583*1da177e4SLinus Torvalds } 584*1da177e4SLinus Torvalds 585*1da177e4SLinus Torvalds static ieee754dp fpemu_dp_recip(ieee754dp d) 586*1da177e4SLinus Torvalds { 587*1da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), d); 588*1da177e4SLinus Torvalds } 589*1da177e4SLinus Torvalds 590*1da177e4SLinus Torvalds static ieee754dp fpemu_dp_rsqrt(ieee754dp d) 591*1da177e4SLinus Torvalds { 592*1da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 593*1da177e4SLinus Torvalds } 594*1da177e4SLinus Torvalds 595*1da177e4SLinus Torvalds static ieee754sp fpemu_sp_recip(ieee754sp s) 596*1da177e4SLinus Torvalds { 597*1da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), s); 598*1da177e4SLinus Torvalds } 599*1da177e4SLinus Torvalds 600*1da177e4SLinus Torvalds static ieee754sp fpemu_sp_rsqrt(ieee754sp s) 601*1da177e4SLinus Torvalds { 602*1da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 603*1da177e4SLinus Torvalds } 604*1da177e4SLinus Torvalds 605*1da177e4SLinus Torvalds DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add,); 606*1da177e4SLinus Torvalds DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub,); 607*1da177e4SLinus Torvalds DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 608*1da177e4SLinus Torvalds DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 609*1da177e4SLinus Torvalds DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add,); 610*1da177e4SLinus Torvalds DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub,); 611*1da177e4SLinus Torvalds DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 612*1da177e4SLinus Torvalds DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 613*1da177e4SLinus Torvalds 614*1da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, 615*1da177e4SLinus Torvalds mips_instruction ir) 616*1da177e4SLinus Torvalds { 617*1da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 618*1da177e4SLinus Torvalds 619*1da177e4SLinus Torvalds fpuemuprivate.stats.cp1xops++; 620*1da177e4SLinus Torvalds 621*1da177e4SLinus Torvalds switch (MIPSInst_FMA_FFMT(ir)) { 622*1da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 623*1da177e4SLinus Torvalds 624*1da177e4SLinus Torvalds ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp); 625*1da177e4SLinus Torvalds ieee754sp fd, fr, fs, ft; 626*1da177e4SLinus Torvalds u32 *va; 627*1da177e4SLinus Torvalds u32 val; 628*1da177e4SLinus Torvalds 629*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 630*1da177e4SLinus Torvalds case lwxc1_op: 631*1da177e4SLinus Torvalds va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + 632*1da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 633*1da177e4SLinus Torvalds 634*1da177e4SLinus Torvalds fpuemuprivate.stats.loads++; 635*1da177e4SLinus Torvalds if (get_user(val, va)) { 636*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 637*1da177e4SLinus Torvalds return SIGBUS; 638*1da177e4SLinus Torvalds } 639*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 640*1da177e4SLinus Torvalds if (MIPSInst_FD(ir) & 1) { 641*1da177e4SLinus Torvalds /* illegal register in single-float 642*1da177e4SLinus Torvalds * mode 643*1da177e4SLinus Torvalds */ 644*1da177e4SLinus Torvalds return SIGILL; 645*1da177e4SLinus Torvalds } 646*1da177e4SLinus Torvalds #endif 647*1da177e4SLinus Torvalds SITOREG(val, MIPSInst_FD(ir)); 648*1da177e4SLinus Torvalds break; 649*1da177e4SLinus Torvalds 650*1da177e4SLinus Torvalds case swxc1_op: 651*1da177e4SLinus Torvalds va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + 652*1da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 653*1da177e4SLinus Torvalds 654*1da177e4SLinus Torvalds fpuemuprivate.stats.stores++; 655*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 656*1da177e4SLinus Torvalds if (MIPSInst_FS(ir) & 1) { 657*1da177e4SLinus Torvalds /* illegal register in single-float 658*1da177e4SLinus Torvalds * mode 659*1da177e4SLinus Torvalds */ 660*1da177e4SLinus Torvalds return SIGILL; 661*1da177e4SLinus Torvalds } 662*1da177e4SLinus Torvalds #endif 663*1da177e4SLinus Torvalds 664*1da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_FS(ir)); 665*1da177e4SLinus Torvalds if (put_user(val, va)) { 666*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 667*1da177e4SLinus Torvalds return SIGBUS; 668*1da177e4SLinus Torvalds } 669*1da177e4SLinus Torvalds break; 670*1da177e4SLinus Torvalds 671*1da177e4SLinus Torvalds case madd_s_op: 672*1da177e4SLinus Torvalds handler = fpemu_sp_madd; 673*1da177e4SLinus Torvalds goto scoptop; 674*1da177e4SLinus Torvalds case msub_s_op: 675*1da177e4SLinus Torvalds handler = fpemu_sp_msub; 676*1da177e4SLinus Torvalds goto scoptop; 677*1da177e4SLinus Torvalds case nmadd_s_op: 678*1da177e4SLinus Torvalds handler = fpemu_sp_nmadd; 679*1da177e4SLinus Torvalds goto scoptop; 680*1da177e4SLinus Torvalds case nmsub_s_op: 681*1da177e4SLinus Torvalds handler = fpemu_sp_nmsub; 682*1da177e4SLinus Torvalds goto scoptop; 683*1da177e4SLinus Torvalds 684*1da177e4SLinus Torvalds scoptop: 685*1da177e4SLinus Torvalds SPFROMREG(fr, MIPSInst_FR(ir)); 686*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 687*1da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 688*1da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 689*1da177e4SLinus Torvalds SPTOREG(fd, MIPSInst_FD(ir)); 690*1da177e4SLinus Torvalds 691*1da177e4SLinus Torvalds copcsr: 692*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 693*1da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 694*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 695*1da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 696*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 697*1da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 698*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 699*1da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 700*1da177e4SLinus Torvalds 701*1da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 702*1da177e4SLinus Torvalds if (ieee754_csr.nod) 703*1da177e4SLinus Torvalds ctx->fcr31 |= 0x1000000; 704*1da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 705*1da177e4SLinus Torvalds /*printk ("SIGFPE: fpu csr = %08x\n", 706*1da177e4SLinus Torvalds ctx->fcr31); */ 707*1da177e4SLinus Torvalds return SIGFPE; 708*1da177e4SLinus Torvalds } 709*1da177e4SLinus Torvalds 710*1da177e4SLinus Torvalds break; 711*1da177e4SLinus Torvalds 712*1da177e4SLinus Torvalds default: 713*1da177e4SLinus Torvalds return SIGILL; 714*1da177e4SLinus Torvalds } 715*1da177e4SLinus Torvalds break; 716*1da177e4SLinus Torvalds } 717*1da177e4SLinus Torvalds 718*1da177e4SLinus Torvalds #ifndef SINGLE_ONLY_FPU 719*1da177e4SLinus Torvalds case d_fmt:{ /* 1 */ 720*1da177e4SLinus Torvalds ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp); 721*1da177e4SLinus Torvalds ieee754dp fd, fr, fs, ft; 722*1da177e4SLinus Torvalds u64 *va; 723*1da177e4SLinus Torvalds u64 val; 724*1da177e4SLinus Torvalds 725*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 726*1da177e4SLinus Torvalds case ldxc1_op: 727*1da177e4SLinus Torvalds va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + 728*1da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 729*1da177e4SLinus Torvalds 730*1da177e4SLinus Torvalds fpuemuprivate.stats.loads++; 731*1da177e4SLinus Torvalds if (get_user(val, va)) { 732*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 733*1da177e4SLinus Torvalds return SIGBUS; 734*1da177e4SLinus Torvalds } 735*1da177e4SLinus Torvalds DITOREG(val, MIPSInst_FD(ir)); 736*1da177e4SLinus Torvalds break; 737*1da177e4SLinus Torvalds 738*1da177e4SLinus Torvalds case sdxc1_op: 739*1da177e4SLinus Torvalds va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + 740*1da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 741*1da177e4SLinus Torvalds 742*1da177e4SLinus Torvalds fpuemuprivate.stats.stores++; 743*1da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_FS(ir)); 744*1da177e4SLinus Torvalds if (put_user(val, va)) { 745*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 746*1da177e4SLinus Torvalds return SIGBUS; 747*1da177e4SLinus Torvalds } 748*1da177e4SLinus Torvalds break; 749*1da177e4SLinus Torvalds 750*1da177e4SLinus Torvalds case madd_d_op: 751*1da177e4SLinus Torvalds handler = fpemu_dp_madd; 752*1da177e4SLinus Torvalds goto dcoptop; 753*1da177e4SLinus Torvalds case msub_d_op: 754*1da177e4SLinus Torvalds handler = fpemu_dp_msub; 755*1da177e4SLinus Torvalds goto dcoptop; 756*1da177e4SLinus Torvalds case nmadd_d_op: 757*1da177e4SLinus Torvalds handler = fpemu_dp_nmadd; 758*1da177e4SLinus Torvalds goto dcoptop; 759*1da177e4SLinus Torvalds case nmsub_d_op: 760*1da177e4SLinus Torvalds handler = fpemu_dp_nmsub; 761*1da177e4SLinus Torvalds goto dcoptop; 762*1da177e4SLinus Torvalds 763*1da177e4SLinus Torvalds dcoptop: 764*1da177e4SLinus Torvalds DPFROMREG(fr, MIPSInst_FR(ir)); 765*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 766*1da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 767*1da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 768*1da177e4SLinus Torvalds DPTOREG(fd, MIPSInst_FD(ir)); 769*1da177e4SLinus Torvalds goto copcsr; 770*1da177e4SLinus Torvalds 771*1da177e4SLinus Torvalds default: 772*1da177e4SLinus Torvalds return SIGILL; 773*1da177e4SLinus Torvalds } 774*1da177e4SLinus Torvalds break; 775*1da177e4SLinus Torvalds } 776*1da177e4SLinus Torvalds #endif 777*1da177e4SLinus Torvalds 778*1da177e4SLinus Torvalds case 0x7: /* 7 */ 779*1da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != pfetch_op) { 780*1da177e4SLinus Torvalds return SIGILL; 781*1da177e4SLinus Torvalds } 782*1da177e4SLinus Torvalds /* ignore prefx operation */ 783*1da177e4SLinus Torvalds break; 784*1da177e4SLinus Torvalds 785*1da177e4SLinus Torvalds default: 786*1da177e4SLinus Torvalds return SIGILL; 787*1da177e4SLinus Torvalds } 788*1da177e4SLinus Torvalds 789*1da177e4SLinus Torvalds return 0; 790*1da177e4SLinus Torvalds } 791*1da177e4SLinus Torvalds #endif 792*1da177e4SLinus Torvalds 793*1da177e4SLinus Torvalds 794*1da177e4SLinus Torvalds 795*1da177e4SLinus Torvalds /* 796*1da177e4SLinus Torvalds * Emulate a single COP1 arithmetic instruction. 797*1da177e4SLinus Torvalds */ 798*1da177e4SLinus Torvalds static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, 799*1da177e4SLinus Torvalds mips_instruction ir) 800*1da177e4SLinus Torvalds { 801*1da177e4SLinus Torvalds int rfmt; /* resulting format */ 802*1da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 803*1da177e4SLinus Torvalds unsigned cond; 804*1da177e4SLinus Torvalds union { 805*1da177e4SLinus Torvalds ieee754dp d; 806*1da177e4SLinus Torvalds ieee754sp s; 807*1da177e4SLinus Torvalds int w; 808*1da177e4SLinus Torvalds #if __mips64 809*1da177e4SLinus Torvalds s64 l; 810*1da177e4SLinus Torvalds #endif 811*1da177e4SLinus Torvalds } rv; /* resulting value */ 812*1da177e4SLinus Torvalds 813*1da177e4SLinus Torvalds fpuemuprivate.stats.cp1ops++; 814*1da177e4SLinus Torvalds switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 815*1da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 816*1da177e4SLinus Torvalds union { 817*1da177e4SLinus Torvalds ieee754sp(*b) (ieee754sp, ieee754sp); 818*1da177e4SLinus Torvalds ieee754sp(*u) (ieee754sp); 819*1da177e4SLinus Torvalds } handler; 820*1da177e4SLinus Torvalds 821*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 822*1da177e4SLinus Torvalds /* binary ops */ 823*1da177e4SLinus Torvalds case fadd_op: 824*1da177e4SLinus Torvalds handler.b = ieee754sp_add; 825*1da177e4SLinus Torvalds goto scopbop; 826*1da177e4SLinus Torvalds case fsub_op: 827*1da177e4SLinus Torvalds handler.b = ieee754sp_sub; 828*1da177e4SLinus Torvalds goto scopbop; 829*1da177e4SLinus Torvalds case fmul_op: 830*1da177e4SLinus Torvalds handler.b = ieee754sp_mul; 831*1da177e4SLinus Torvalds goto scopbop; 832*1da177e4SLinus Torvalds case fdiv_op: 833*1da177e4SLinus Torvalds handler.b = ieee754sp_div; 834*1da177e4SLinus Torvalds goto scopbop; 835*1da177e4SLinus Torvalds 836*1da177e4SLinus Torvalds /* unary ops */ 837*1da177e4SLinus Torvalds #if __mips >= 2 || __mips64 838*1da177e4SLinus Torvalds case fsqrt_op: 839*1da177e4SLinus Torvalds handler.u = ieee754sp_sqrt; 840*1da177e4SLinus Torvalds goto scopuop; 841*1da177e4SLinus Torvalds #endif 842*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 843*1da177e4SLinus Torvalds case frsqrt_op: 844*1da177e4SLinus Torvalds handler.u = fpemu_sp_rsqrt; 845*1da177e4SLinus Torvalds goto scopuop; 846*1da177e4SLinus Torvalds case frecip_op: 847*1da177e4SLinus Torvalds handler.u = fpemu_sp_recip; 848*1da177e4SLinus Torvalds goto scopuop; 849*1da177e4SLinus Torvalds #endif 850*1da177e4SLinus Torvalds #if __mips >= 4 851*1da177e4SLinus Torvalds case fmovc_op: 852*1da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 853*1da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 854*1da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 855*1da177e4SLinus Torvalds return 0; 856*1da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 857*1da177e4SLinus Torvalds break; 858*1da177e4SLinus Torvalds case fmovz_op: 859*1da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 860*1da177e4SLinus Torvalds return 0; 861*1da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 862*1da177e4SLinus Torvalds break; 863*1da177e4SLinus Torvalds case fmovn_op: 864*1da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 865*1da177e4SLinus Torvalds return 0; 866*1da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 867*1da177e4SLinus Torvalds break; 868*1da177e4SLinus Torvalds #endif 869*1da177e4SLinus Torvalds case fabs_op: 870*1da177e4SLinus Torvalds handler.u = ieee754sp_abs; 871*1da177e4SLinus Torvalds goto scopuop; 872*1da177e4SLinus Torvalds case fneg_op: 873*1da177e4SLinus Torvalds handler.u = ieee754sp_neg; 874*1da177e4SLinus Torvalds goto scopuop; 875*1da177e4SLinus Torvalds case fmov_op: 876*1da177e4SLinus Torvalds /* an easy one */ 877*1da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 878*1da177e4SLinus Torvalds goto copcsr; 879*1da177e4SLinus Torvalds 880*1da177e4SLinus Torvalds /* binary op on handler */ 881*1da177e4SLinus Torvalds scopbop: 882*1da177e4SLinus Torvalds { 883*1da177e4SLinus Torvalds ieee754sp fs, ft; 884*1da177e4SLinus Torvalds 885*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 886*1da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 887*1da177e4SLinus Torvalds 888*1da177e4SLinus Torvalds rv.s = (*handler.b) (fs, ft); 889*1da177e4SLinus Torvalds goto copcsr; 890*1da177e4SLinus Torvalds } 891*1da177e4SLinus Torvalds scopuop: 892*1da177e4SLinus Torvalds { 893*1da177e4SLinus Torvalds ieee754sp fs; 894*1da177e4SLinus Torvalds 895*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 896*1da177e4SLinus Torvalds rv.s = (*handler.u) (fs); 897*1da177e4SLinus Torvalds goto copcsr; 898*1da177e4SLinus Torvalds } 899*1da177e4SLinus Torvalds copcsr: 900*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 901*1da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 902*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 903*1da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 904*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 905*1da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 906*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) 907*1da177e4SLinus Torvalds rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 908*1da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 909*1da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 910*1da177e4SLinus Torvalds break; 911*1da177e4SLinus Torvalds 912*1da177e4SLinus Torvalds /* unary conv ops */ 913*1da177e4SLinus Torvalds case fcvts_op: 914*1da177e4SLinus Torvalds return SIGILL; /* not defined */ 915*1da177e4SLinus Torvalds case fcvtd_op:{ 916*1da177e4SLinus Torvalds #ifdef SINGLE_ONLY_FPU 917*1da177e4SLinus Torvalds return SIGILL; /* not defined */ 918*1da177e4SLinus Torvalds #else 919*1da177e4SLinus Torvalds ieee754sp fs; 920*1da177e4SLinus Torvalds 921*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 922*1da177e4SLinus Torvalds rv.d = ieee754dp_fsp(fs); 923*1da177e4SLinus Torvalds rfmt = d_fmt; 924*1da177e4SLinus Torvalds goto copcsr; 925*1da177e4SLinus Torvalds } 926*1da177e4SLinus Torvalds #endif 927*1da177e4SLinus Torvalds case fcvtw_op:{ 928*1da177e4SLinus Torvalds ieee754sp fs; 929*1da177e4SLinus Torvalds 930*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 931*1da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 932*1da177e4SLinus Torvalds rfmt = w_fmt; 933*1da177e4SLinus Torvalds goto copcsr; 934*1da177e4SLinus Torvalds } 935*1da177e4SLinus Torvalds 936*1da177e4SLinus Torvalds #if __mips >= 2 || __mips64 937*1da177e4SLinus Torvalds case fround_op: 938*1da177e4SLinus Torvalds case ftrunc_op: 939*1da177e4SLinus Torvalds case fceil_op: 940*1da177e4SLinus Torvalds case ffloor_op:{ 941*1da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 942*1da177e4SLinus Torvalds ieee754sp fs; 943*1da177e4SLinus Torvalds 944*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 945*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3]; 946*1da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 947*1da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 948*1da177e4SLinus Torvalds rfmt = w_fmt; 949*1da177e4SLinus Torvalds goto copcsr; 950*1da177e4SLinus Torvalds } 951*1da177e4SLinus Torvalds #endif /* __mips >= 2 */ 952*1da177e4SLinus Torvalds 953*1da177e4SLinus Torvalds #if __mips64 && !defined(SINGLE_ONLY_FPU) 954*1da177e4SLinus Torvalds case fcvtl_op:{ 955*1da177e4SLinus Torvalds ieee754sp fs; 956*1da177e4SLinus Torvalds 957*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 958*1da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 959*1da177e4SLinus Torvalds rfmt = l_fmt; 960*1da177e4SLinus Torvalds goto copcsr; 961*1da177e4SLinus Torvalds } 962*1da177e4SLinus Torvalds 963*1da177e4SLinus Torvalds case froundl_op: 964*1da177e4SLinus Torvalds case ftruncl_op: 965*1da177e4SLinus Torvalds case fceill_op: 966*1da177e4SLinus Torvalds case ffloorl_op:{ 967*1da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 968*1da177e4SLinus Torvalds ieee754sp fs; 969*1da177e4SLinus Torvalds 970*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 971*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3]; 972*1da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 973*1da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 974*1da177e4SLinus Torvalds rfmt = l_fmt; 975*1da177e4SLinus Torvalds goto copcsr; 976*1da177e4SLinus Torvalds } 977*1da177e4SLinus Torvalds #endif /* __mips64 && !fpu(single) */ 978*1da177e4SLinus Torvalds 979*1da177e4SLinus Torvalds default: 980*1da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 981*1da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 982*1da177e4SLinus Torvalds ieee754sp fs, ft; 983*1da177e4SLinus Torvalds 984*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 985*1da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 986*1da177e4SLinus Torvalds rv.w = ieee754sp_cmp(fs, ft, 987*1da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 988*1da177e4SLinus Torvalds rfmt = -1; 989*1da177e4SLinus Torvalds if ((cmpop & 0x8) && ieee754_cxtest 990*1da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 991*1da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 992*1da177e4SLinus Torvalds else 993*1da177e4SLinus Torvalds goto copcsr; 994*1da177e4SLinus Torvalds 995*1da177e4SLinus Torvalds } 996*1da177e4SLinus Torvalds else { 997*1da177e4SLinus Torvalds return SIGILL; 998*1da177e4SLinus Torvalds } 999*1da177e4SLinus Torvalds break; 1000*1da177e4SLinus Torvalds } 1001*1da177e4SLinus Torvalds break; 1002*1da177e4SLinus Torvalds } 1003*1da177e4SLinus Torvalds 1004*1da177e4SLinus Torvalds #ifndef SINGLE_ONLY_FPU 1005*1da177e4SLinus Torvalds case d_fmt:{ 1006*1da177e4SLinus Torvalds union { 1007*1da177e4SLinus Torvalds ieee754dp(*b) (ieee754dp, ieee754dp); 1008*1da177e4SLinus Torvalds ieee754dp(*u) (ieee754dp); 1009*1da177e4SLinus Torvalds } handler; 1010*1da177e4SLinus Torvalds 1011*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 1012*1da177e4SLinus Torvalds /* binary ops */ 1013*1da177e4SLinus Torvalds case fadd_op: 1014*1da177e4SLinus Torvalds handler.b = ieee754dp_add; 1015*1da177e4SLinus Torvalds goto dcopbop; 1016*1da177e4SLinus Torvalds case fsub_op: 1017*1da177e4SLinus Torvalds handler.b = ieee754dp_sub; 1018*1da177e4SLinus Torvalds goto dcopbop; 1019*1da177e4SLinus Torvalds case fmul_op: 1020*1da177e4SLinus Torvalds handler.b = ieee754dp_mul; 1021*1da177e4SLinus Torvalds goto dcopbop; 1022*1da177e4SLinus Torvalds case fdiv_op: 1023*1da177e4SLinus Torvalds handler.b = ieee754dp_div; 1024*1da177e4SLinus Torvalds goto dcopbop; 1025*1da177e4SLinus Torvalds 1026*1da177e4SLinus Torvalds /* unary ops */ 1027*1da177e4SLinus Torvalds #if __mips >= 2 || __mips64 1028*1da177e4SLinus Torvalds case fsqrt_op: 1029*1da177e4SLinus Torvalds handler.u = ieee754dp_sqrt; 1030*1da177e4SLinus Torvalds goto dcopuop; 1031*1da177e4SLinus Torvalds #endif 1032*1da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 1033*1da177e4SLinus Torvalds case frsqrt_op: 1034*1da177e4SLinus Torvalds handler.u = fpemu_dp_rsqrt; 1035*1da177e4SLinus Torvalds goto dcopuop; 1036*1da177e4SLinus Torvalds case frecip_op: 1037*1da177e4SLinus Torvalds handler.u = fpemu_dp_recip; 1038*1da177e4SLinus Torvalds goto dcopuop; 1039*1da177e4SLinus Torvalds #endif 1040*1da177e4SLinus Torvalds #if __mips >= 4 1041*1da177e4SLinus Torvalds case fmovc_op: 1042*1da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 1043*1da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 1044*1da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 1045*1da177e4SLinus Torvalds return 0; 1046*1da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 1047*1da177e4SLinus Torvalds break; 1048*1da177e4SLinus Torvalds case fmovz_op: 1049*1da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 1050*1da177e4SLinus Torvalds return 0; 1051*1da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 1052*1da177e4SLinus Torvalds break; 1053*1da177e4SLinus Torvalds case fmovn_op: 1054*1da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 1055*1da177e4SLinus Torvalds return 0; 1056*1da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 1057*1da177e4SLinus Torvalds break; 1058*1da177e4SLinus Torvalds #endif 1059*1da177e4SLinus Torvalds case fabs_op: 1060*1da177e4SLinus Torvalds handler.u = ieee754dp_abs; 1061*1da177e4SLinus Torvalds goto dcopuop; 1062*1da177e4SLinus Torvalds 1063*1da177e4SLinus Torvalds case fneg_op: 1064*1da177e4SLinus Torvalds handler.u = ieee754dp_neg; 1065*1da177e4SLinus Torvalds goto dcopuop; 1066*1da177e4SLinus Torvalds 1067*1da177e4SLinus Torvalds case fmov_op: 1068*1da177e4SLinus Torvalds /* an easy one */ 1069*1da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 1070*1da177e4SLinus Torvalds goto copcsr; 1071*1da177e4SLinus Torvalds 1072*1da177e4SLinus Torvalds /* binary op on handler */ 1073*1da177e4SLinus Torvalds dcopbop:{ 1074*1da177e4SLinus Torvalds ieee754dp fs, ft; 1075*1da177e4SLinus Torvalds 1076*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1077*1da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 1078*1da177e4SLinus Torvalds 1079*1da177e4SLinus Torvalds rv.d = (*handler.b) (fs, ft); 1080*1da177e4SLinus Torvalds goto copcsr; 1081*1da177e4SLinus Torvalds } 1082*1da177e4SLinus Torvalds dcopuop:{ 1083*1da177e4SLinus Torvalds ieee754dp fs; 1084*1da177e4SLinus Torvalds 1085*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1086*1da177e4SLinus Torvalds rv.d = (*handler.u) (fs); 1087*1da177e4SLinus Torvalds goto copcsr; 1088*1da177e4SLinus Torvalds } 1089*1da177e4SLinus Torvalds 1090*1da177e4SLinus Torvalds /* unary conv ops */ 1091*1da177e4SLinus Torvalds case fcvts_op:{ 1092*1da177e4SLinus Torvalds ieee754dp fs; 1093*1da177e4SLinus Torvalds 1094*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1095*1da177e4SLinus Torvalds rv.s = ieee754sp_fdp(fs); 1096*1da177e4SLinus Torvalds rfmt = s_fmt; 1097*1da177e4SLinus Torvalds goto copcsr; 1098*1da177e4SLinus Torvalds } 1099*1da177e4SLinus Torvalds case fcvtd_op: 1100*1da177e4SLinus Torvalds return SIGILL; /* not defined */ 1101*1da177e4SLinus Torvalds 1102*1da177e4SLinus Torvalds case fcvtw_op:{ 1103*1da177e4SLinus Torvalds ieee754dp fs; 1104*1da177e4SLinus Torvalds 1105*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1106*1da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); /* wrong */ 1107*1da177e4SLinus Torvalds rfmt = w_fmt; 1108*1da177e4SLinus Torvalds goto copcsr; 1109*1da177e4SLinus Torvalds } 1110*1da177e4SLinus Torvalds 1111*1da177e4SLinus Torvalds #if __mips >= 2 || __mips64 1112*1da177e4SLinus Torvalds case fround_op: 1113*1da177e4SLinus Torvalds case ftrunc_op: 1114*1da177e4SLinus Torvalds case fceil_op: 1115*1da177e4SLinus Torvalds case ffloor_op:{ 1116*1da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 1117*1da177e4SLinus Torvalds ieee754dp fs; 1118*1da177e4SLinus Torvalds 1119*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1120*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3]; 1121*1da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); 1122*1da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 1123*1da177e4SLinus Torvalds rfmt = w_fmt; 1124*1da177e4SLinus Torvalds goto copcsr; 1125*1da177e4SLinus Torvalds } 1126*1da177e4SLinus Torvalds #endif 1127*1da177e4SLinus Torvalds 1128*1da177e4SLinus Torvalds #if __mips64 && !defined(SINGLE_ONLY_FPU) 1129*1da177e4SLinus Torvalds case fcvtl_op:{ 1130*1da177e4SLinus Torvalds ieee754dp fs; 1131*1da177e4SLinus Torvalds 1132*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1133*1da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 1134*1da177e4SLinus Torvalds rfmt = l_fmt; 1135*1da177e4SLinus Torvalds goto copcsr; 1136*1da177e4SLinus Torvalds } 1137*1da177e4SLinus Torvalds 1138*1da177e4SLinus Torvalds case froundl_op: 1139*1da177e4SLinus Torvalds case ftruncl_op: 1140*1da177e4SLinus Torvalds case fceill_op: 1141*1da177e4SLinus Torvalds case ffloorl_op:{ 1142*1da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 1143*1da177e4SLinus Torvalds ieee754dp fs; 1144*1da177e4SLinus Torvalds 1145*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1146*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3]; 1147*1da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 1148*1da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 1149*1da177e4SLinus Torvalds rfmt = l_fmt; 1150*1da177e4SLinus Torvalds goto copcsr; 1151*1da177e4SLinus Torvalds } 1152*1da177e4SLinus Torvalds #endif /* __mips >= 3 && !fpu(single) */ 1153*1da177e4SLinus Torvalds 1154*1da177e4SLinus Torvalds default: 1155*1da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 1156*1da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 1157*1da177e4SLinus Torvalds ieee754dp fs, ft; 1158*1da177e4SLinus Torvalds 1159*1da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 1160*1da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 1161*1da177e4SLinus Torvalds rv.w = ieee754dp_cmp(fs, ft, 1162*1da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 1163*1da177e4SLinus Torvalds rfmt = -1; 1164*1da177e4SLinus Torvalds if ((cmpop & 0x8) 1165*1da177e4SLinus Torvalds && 1166*1da177e4SLinus Torvalds ieee754_cxtest 1167*1da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 1168*1da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 1169*1da177e4SLinus Torvalds else 1170*1da177e4SLinus Torvalds goto copcsr; 1171*1da177e4SLinus Torvalds 1172*1da177e4SLinus Torvalds } 1173*1da177e4SLinus Torvalds else { 1174*1da177e4SLinus Torvalds return SIGILL; 1175*1da177e4SLinus Torvalds } 1176*1da177e4SLinus Torvalds break; 1177*1da177e4SLinus Torvalds } 1178*1da177e4SLinus Torvalds break; 1179*1da177e4SLinus Torvalds } 1180*1da177e4SLinus Torvalds #endif /* ifndef SINGLE_ONLY_FPU */ 1181*1da177e4SLinus Torvalds 1182*1da177e4SLinus Torvalds case w_fmt:{ 1183*1da177e4SLinus Torvalds ieee754sp fs; 1184*1da177e4SLinus Torvalds 1185*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 1186*1da177e4SLinus Torvalds case fcvts_op: 1187*1da177e4SLinus Torvalds /* convert word to single precision real */ 1188*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 1189*1da177e4SLinus Torvalds rv.s = ieee754sp_fint(fs.bits); 1190*1da177e4SLinus Torvalds rfmt = s_fmt; 1191*1da177e4SLinus Torvalds goto copcsr; 1192*1da177e4SLinus Torvalds #ifndef SINGLE_ONLY_FPU 1193*1da177e4SLinus Torvalds case fcvtd_op: 1194*1da177e4SLinus Torvalds /* convert word to double precision real */ 1195*1da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 1196*1da177e4SLinus Torvalds rv.d = ieee754dp_fint(fs.bits); 1197*1da177e4SLinus Torvalds rfmt = d_fmt; 1198*1da177e4SLinus Torvalds goto copcsr; 1199*1da177e4SLinus Torvalds #endif 1200*1da177e4SLinus Torvalds default: 1201*1da177e4SLinus Torvalds return SIGILL; 1202*1da177e4SLinus Torvalds } 1203*1da177e4SLinus Torvalds break; 1204*1da177e4SLinus Torvalds } 1205*1da177e4SLinus Torvalds 1206*1da177e4SLinus Torvalds #if __mips64 && !defined(SINGLE_ONLY_FPU) 1207*1da177e4SLinus Torvalds case l_fmt:{ 1208*1da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 1209*1da177e4SLinus Torvalds case fcvts_op: 1210*1da177e4SLinus Torvalds /* convert long to single precision real */ 1211*1da177e4SLinus Torvalds rv.s = ieee754sp_flong(ctx->fpr[MIPSInst_FS(ir)]); 1212*1da177e4SLinus Torvalds rfmt = s_fmt; 1213*1da177e4SLinus Torvalds goto copcsr; 1214*1da177e4SLinus Torvalds case fcvtd_op: 1215*1da177e4SLinus Torvalds /* convert long to double precision real */ 1216*1da177e4SLinus Torvalds rv.d = ieee754dp_flong(ctx->fpr[MIPSInst_FS(ir)]); 1217*1da177e4SLinus Torvalds rfmt = d_fmt; 1218*1da177e4SLinus Torvalds goto copcsr; 1219*1da177e4SLinus Torvalds default: 1220*1da177e4SLinus Torvalds return SIGILL; 1221*1da177e4SLinus Torvalds } 1222*1da177e4SLinus Torvalds break; 1223*1da177e4SLinus Torvalds } 1224*1da177e4SLinus Torvalds #endif 1225*1da177e4SLinus Torvalds 1226*1da177e4SLinus Torvalds default: 1227*1da177e4SLinus Torvalds return SIGILL; 1228*1da177e4SLinus Torvalds } 1229*1da177e4SLinus Torvalds 1230*1da177e4SLinus Torvalds /* 1231*1da177e4SLinus Torvalds * Update the fpu CSR register for this operation. 1232*1da177e4SLinus Torvalds * If an exception is required, generate a tidy SIGFPE exception, 1233*1da177e4SLinus Torvalds * without updating the result register. 1234*1da177e4SLinus Torvalds * Note: cause exception bits do not accumulate, they are rewritten 1235*1da177e4SLinus Torvalds * for each op; only the flag/sticky bits accumulate. 1236*1da177e4SLinus Torvalds */ 1237*1da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 1238*1da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 1239*1da177e4SLinus Torvalds /*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */ 1240*1da177e4SLinus Torvalds return SIGFPE; 1241*1da177e4SLinus Torvalds } 1242*1da177e4SLinus Torvalds 1243*1da177e4SLinus Torvalds /* 1244*1da177e4SLinus Torvalds * Now we can safely write the result back to the register file. 1245*1da177e4SLinus Torvalds */ 1246*1da177e4SLinus Torvalds switch (rfmt) { 1247*1da177e4SLinus Torvalds case -1:{ 1248*1da177e4SLinus Torvalds #if __mips >= 4 1249*1da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FD(ir) >> 2]; 1250*1da177e4SLinus Torvalds #else 1251*1da177e4SLinus Torvalds cond = FPU_CSR_COND; 1252*1da177e4SLinus Torvalds #endif 1253*1da177e4SLinus Torvalds if (rv.w) 1254*1da177e4SLinus Torvalds ctx->fcr31 |= cond; 1255*1da177e4SLinus Torvalds else 1256*1da177e4SLinus Torvalds ctx->fcr31 &= ~cond; 1257*1da177e4SLinus Torvalds break; 1258*1da177e4SLinus Torvalds } 1259*1da177e4SLinus Torvalds #ifndef SINGLE_ONLY_FPU 1260*1da177e4SLinus Torvalds case d_fmt: 1261*1da177e4SLinus Torvalds DPTOREG(rv.d, MIPSInst_FD(ir)); 1262*1da177e4SLinus Torvalds break; 1263*1da177e4SLinus Torvalds #endif 1264*1da177e4SLinus Torvalds case s_fmt: 1265*1da177e4SLinus Torvalds SPTOREG(rv.s, MIPSInst_FD(ir)); 1266*1da177e4SLinus Torvalds break; 1267*1da177e4SLinus Torvalds case w_fmt: 1268*1da177e4SLinus Torvalds SITOREG(rv.w, MIPSInst_FD(ir)); 1269*1da177e4SLinus Torvalds break; 1270*1da177e4SLinus Torvalds #if __mips64 && !defined(SINGLE_ONLY_FPU) 1271*1da177e4SLinus Torvalds case l_fmt: 1272*1da177e4SLinus Torvalds DITOREG(rv.l, MIPSInst_FD(ir)); 1273*1da177e4SLinus Torvalds break; 1274*1da177e4SLinus Torvalds #endif 1275*1da177e4SLinus Torvalds default: 1276*1da177e4SLinus Torvalds return SIGILL; 1277*1da177e4SLinus Torvalds } 1278*1da177e4SLinus Torvalds 1279*1da177e4SLinus Torvalds return 0; 1280*1da177e4SLinus Torvalds } 1281*1da177e4SLinus Torvalds 1282*1da177e4SLinus Torvalds int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp, 1283*1da177e4SLinus Torvalds struct mips_fpu_soft_struct *ctx) 1284*1da177e4SLinus Torvalds { 1285*1da177e4SLinus Torvalds gpreg_t oldepc, prevepc; 1286*1da177e4SLinus Torvalds mips_instruction insn; 1287*1da177e4SLinus Torvalds int sig = 0; 1288*1da177e4SLinus Torvalds 1289*1da177e4SLinus Torvalds oldepc = xcp->cp0_epc; 1290*1da177e4SLinus Torvalds do { 1291*1da177e4SLinus Torvalds prevepc = xcp->cp0_epc; 1292*1da177e4SLinus Torvalds 1293*1da177e4SLinus Torvalds if (get_user(insn, (mips_instruction *) xcp->cp0_epc)) { 1294*1da177e4SLinus Torvalds fpuemuprivate.stats.errors++; 1295*1da177e4SLinus Torvalds return SIGBUS; 1296*1da177e4SLinus Torvalds } 1297*1da177e4SLinus Torvalds if (insn == 0) 1298*1da177e4SLinus Torvalds xcp->cp0_epc += 4; /* skip nops */ 1299*1da177e4SLinus Torvalds else { 1300*1da177e4SLinus Torvalds /* Update ieee754_csr. Only relevant if we have a 1301*1da177e4SLinus Torvalds h/w FPU */ 1302*1da177e4SLinus Torvalds ieee754_csr.nod = (ctx->fcr31 & 0x1000000) != 0; 1303*1da177e4SLinus Torvalds ieee754_csr.rm = ieee_rm[ctx->fcr31 & 0x3]; 1304*1da177e4SLinus Torvalds ieee754_csr.cx = (ctx->fcr31 >> 12) & 0x1f; 1305*1da177e4SLinus Torvalds sig = cop1Emulate(xcp, ctx); 1306*1da177e4SLinus Torvalds } 1307*1da177e4SLinus Torvalds 1308*1da177e4SLinus Torvalds if (cpu_has_fpu) 1309*1da177e4SLinus Torvalds break; 1310*1da177e4SLinus Torvalds if (sig) 1311*1da177e4SLinus Torvalds break; 1312*1da177e4SLinus Torvalds 1313*1da177e4SLinus Torvalds cond_resched(); 1314*1da177e4SLinus Torvalds } while (xcp->cp0_epc > prevepc); 1315*1da177e4SLinus Torvalds 1316*1da177e4SLinus Torvalds /* SIGILL indicates a non-fpu instruction */ 1317*1da177e4SLinus Torvalds if (sig == SIGILL && xcp->cp0_epc != oldepc) 1318*1da177e4SLinus Torvalds /* but if epc has advanced, then ignore it */ 1319*1da177e4SLinus Torvalds sig = 0; 1320*1da177e4SLinus Torvalds 1321*1da177e4SLinus Torvalds return sig; 1322*1da177e4SLinus Torvalds } 1323