diff options
Diffstat (limited to 'arch/mips/math-emu/cp1emu.c')
-rw-r--r-- | arch/mips/math-emu/cp1emu.c | 298 |
1 files changed, 121 insertions, 177 deletions
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 3a0dfa4..6258291 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1,5 +1,5 @@ /* - * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator + * cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator * * MIPS floating point support * Copyright (C) 1994-2000 Algorithmics Ltd. @@ -18,19 +18,19 @@ * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * A complete emulator for MIPS coprocessor 1 instructions. This is * required for #float(switch) or #float(trap), where it catches all * COP1 instructions via the "CoProcessor Unusable" exception. * * More surprisingly it is also required for #float(ieee), to help out - * the hardware fpu at the boundaries of the IEEE-754 representation + * the hardware FPU at the boundaries of the IEEE-754 representation * (denormalised values, infinities, underflow, etc). It is made * quite nasty because emulation of some non-COP1 instructions is * required, e.g. in branch delay slots. * - * Note if you know that you won't have an fpu, then you'll get much + * Note if you know that you won't have an FPU, then you'll get much * better performance by compiling with -msoft-float! */ #include <linux/sched.h> @@ -72,14 +72,14 @@ static int fpux_emu(struct pt_regs *, #define MM_POOL32A_MINOR_SHIFT 0x6 #define MM_MIPS32_COND_FC 0x30 -/* Convert Mips rounding mode (0..3) to IEEE library modes. */ +/* Convert MIPS rounding mode (0..3) to IEEE library modes. */ static const unsigned char ieee_rm[4] = { [FPU_CSR_RN] = IEEE754_RN, [FPU_CSR_RZ] = IEEE754_RZ, [FPU_CSR_RU] = IEEE754_RU, [FPU_CSR_RD] = IEEE754_RD, }; -/* Convert IEEE library modes to Mips rounding mode (0..3). */ +/* Convert IEEE library modes to MIPS rounding mode (0..3). */ static const unsigned char mips_rm[4] = { [IEEE754_RN] = FPU_CSR_RN, [IEEE754_RZ] = FPU_CSR_RZ, @@ -914,10 +914,16 @@ do { \ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, struct mm_decoded_insn dec_insn, void *__user *fault_addr) { - mips_instruction ir; unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; - unsigned int cond; - int pc_inc; + unsigned int cond, cbit; + mips_instruction ir; + int likely, pc_inc; + u32 __user *wva; + u64 __user *dva; + u32 value; + u32 wval; + u64 dval; + int sig; /* XXX NEC Vr54xx bug workaround */ if (delay_slot(xcp)) { @@ -972,94 +978,81 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return SIGILL; } - emul: +emul: perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); MIPS_FPU_EMU_INC_STATS(emulated); switch (MIPSInst_OPCODE(ir)) { - case ldc1_op:{ - u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + - MIPSInst_SIMM(ir)); - u64 val; - + case ldc1_op: + dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); MIPS_FPU_EMU_INC_STATS(loads); - if (!access_ok(VERIFY_READ, va, sizeof(u64))) { + if (!access_ok(VERIFY_READ, dva, sizeof(u64))) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = dva; return SIGBUS; } - if (__get_user(val, va)) { + if (__get_user(dval, dva)) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = dva; return SIGSEGV; } - DITOREG(val, MIPSInst_RT(ir)); + DITOREG(dval, MIPSInst_RT(ir)); break; - } - - case sdc1_op:{ - u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + - MIPSInst_SIMM(ir)); - u64 val; + case sdc1_op: + dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); MIPS_FPU_EMU_INC_STATS(stores); - DIFROMREG(val, MIPSInst_RT(ir)); - if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) { + DIFROMREG(dval, MIPSInst_RT(ir)); + if (!access_ok(VERIFY_WRITE, dva, sizeof(u64))) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = dva; return SIGBUS; } - if (__put_user(val, va)) { + if (__put_user(dval, dva)) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = dva; return SIGSEGV; } break; - } - - case lwc1_op:{ - u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + - MIPSInst_SIMM(ir)); - u32 val; + case lwc1_op: + wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); MIPS_FPU_EMU_INC_STATS(loads); - if (!access_ok(VERIFY_READ, va, sizeof(u32))) { + if (!access_ok(VERIFY_READ, wva, sizeof(u32))) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = wva; return SIGBUS; } - if (__get_user(val, va)) { + if (__get_user(wval, wva)) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = wva; return SIGSEGV; } - SITOREG(val, MIPSInst_RT(ir)); + SITOREG(wval, MIPSInst_RT(ir)); break; - } - - case swc1_op:{ - u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + - MIPSInst_SIMM(ir)); - u32 val; + case swc1_op: + wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); MIPS_FPU_EMU_INC_STATS(stores); - SIFROMREG(val, MIPSInst_RT(ir)); - if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) { + SIFROMREG(wval, MIPSInst_RT(ir)); + if (!access_ok(VERIFY_WRITE, wva, sizeof(u32))) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = wva; return SIGBUS; } - if (__put_user(val, va)) { + if (__put_user(wval, wva)) { MIPS_FPU_EMU_INC_STATS(errors); - *fault_addr = va; + *fault_addr = wva; return SIGSEGV; } break; - } case cop1_op: switch (MIPSInst_RS(ir)) { - case dmfc_op: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; @@ -1111,10 +1104,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); break; - case cfc_op:{ + case cfc_op: /* cop control register rd -> gpr[rt] */ - u32 value; - if (MIPSInst_RD(ir) == FPCREG_CSR) { value = ctx->fcr31; value = (value & ~FPU_CSR_RM) | @@ -1130,12 +1121,9 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, if (MIPSInst_RT(ir)) xcp->regs[MIPSInst_RT(ir)] = value; break; - } - case ctc_op:{ + case ctc_op: /* copregister rd <- rt */ - u32 value; - if (MIPSInst_RT(ir) == 0) value = 0; else @@ -1160,12 +1148,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return SIGFPE; } break; - } - - case bc_op:{ - unsigned int cbit; - int likely = 0; + case bc_op: if (delay_slot(xcp)) return SIGILL; @@ -1175,6 +1159,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, cbit = FPU_CSR_COND; cond = ctx->fcr31 & cbit; + likely = 0; switch (MIPSInst_RT(ir) & 3) { case bcfl_op: likely = 1; @@ -1192,8 +1177,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, set_delay_slot(xcp); if (cond) { - /* branch taken: emulate dslot - * instruction + /* + * Branch taken: emulate dslot instruction */ xcp->cp0_epc += dec_insn.pc_inc; @@ -1228,8 +1213,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, switch (MIPSInst_OPCODE(ir)) { case lwc1_op: goto emul; + case swc1_op: goto emul; + case ldc1_op: case sdc1_op: if (cpu_has_mips_2_3_4_5 || @@ -1238,14 +1225,17 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return SIGILL; goto emul; + case cop1_op: goto emul; + case cop1x_op: if (cpu_has_mips_4_5 || cpu_has_mips64) /* its one of ours */ goto emul; return SIGILL; + case spec_op: if (!cpu_has_mips_4_5_r) return SIGILL; @@ -1260,10 +1250,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, * instruction in the dslot */ return mips_dsemul(xcp, ir, contpc); - } - else { - /* branch not taken */ - if (likely) { + } else if (likely) { /* branch not taken */ /* * branch likely nullifies * dslot if not taken @@ -1275,26 +1262,19 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, * dslot as normal insn */ } - } break; - } default: if (!(MIPSInst_RS(ir) & 0x10)) return SIGILL; - { - int sig; - /* a real fpu computation instruction */ - if ((sig = fpu_emu(xcp, ctx, ir))) - return sig; - } + /* a real fpu computation instruction */ + if ((sig = fpu_emu(xcp, ctx, ir))) + return sig; } break; - case cop1x_op:{ - int sig; - + case cop1x_op: if (!cpu_has_mips_4_5 && !cpu_has_mips64) return SIGILL; @@ -1302,7 +1282,6 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, if (sig) return sig; break; - } case spec_op: if (!cpu_has_mips_4_5_r) @@ -1477,7 +1456,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { - /*printk ("SIGFPE: fpu csr = %08x\n", + /*printk ("SIGFPE: FPU csr = %08x\n", ctx->fcr31); */ return SIGFPE; } @@ -1584,6 +1563,8 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, { int rfmt; /* resulting format */ unsigned rcsr = 0; /* resulting csr */ + unsigned int oldrm; + unsigned int cbit; unsigned cond; union { union ieee754dp d; @@ -1591,14 +1572,16 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, int w; s64 l; } rv; /* resulting value */ + u64 bits; MIPS_FPU_EMU_INC_STATS(cp1ops); switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { - case s_fmt:{ /* 0 */ + case s_fmt: { /* 0 */ union { union ieee754sp(*b) (union ieee754sp, union ieee754sp); union ieee754sp(*u) (union ieee754sp); } handler; + union ieee754sp fs, ft; switch (MIPSInst_FUNC(ir)) { /* binary ops */ @@ -1622,6 +1605,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, handler.u = ieee754sp_sqrt; goto scopuop; + /* * Note that on some MIPS IV implementations such as the * R5000 and R8000 the FSQRT and FRECIP instructions do not @@ -1633,6 +1617,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, handler.u = fpemu_sp_rsqrt; goto scopuop; + case frecip_op: if (!cpu_has_mips_4_5_r2) return SIGILL; @@ -1650,6 +1635,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return 0; SPFROMREG(rv.s, MIPSInst_FS(ir)); break; + case fmovz_op: if (!cpu_has_mips_4_5_r) return SIGILL; @@ -1658,6 +1644,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return 0; SPFROMREG(rv.s, MIPSInst_FS(ir)); break; + case fmovn_op: if (!cpu_has_mips_4_5_r) return SIGILL; @@ -1666,37 +1653,32 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return 0; SPFROMREG(rv.s, MIPSInst_FS(ir)); break; + case fabs_op: handler.u = ieee754sp_abs; goto scopuop; + case fneg_op: handler.u = ieee754sp_neg; goto scopuop; + case fmov_op: /* an easy one */ SPFROMREG(rv.s, MIPSInst_FS(ir)); goto copcsr; /* binary op on handler */ - scopbop: - { - union ieee754sp fs, ft; - - SPFROMREG(fs, MIPSInst_FS(ir)); - SPFROMREG(ft, MIPSInst_FT(ir)); - - rv.s = (*handler.b) (fs, ft); - goto copcsr; - } - scopuop: - { - union ieee754sp fs; +scopbop: + SPFROMREG(fs, MIPSInst_FS(ir)); + SPFROMREG(ft, MIPSInst_FT(ir)); - SPFROMREG(fs, MIPSInst_FS(ir)); - rv.s = (*handler.u) (fs); - goto copcsr; - } - copcsr: + rv.s = (*handler.b) (fs, ft); + goto copcsr; +scopuop: + SPFROMREG(fs, MIPSInst_FS(ir)); + rv.s = (*handler.u) (fs); + goto copcsr; +copcsr: if (ieee754_cxtest(IEEE754_INEXACT)) rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; if (ieee754_cxtest(IEEE754_UNDERFLOW)) @@ -1712,44 +1694,35 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, /* unary conv ops */ case fcvts_op: return SIGILL; /* not defined */ - case fcvtd_op:{ - union ieee754sp fs; + case fcvtd_op: SPFROMREG(fs, MIPSInst_FS(ir)); rv.d = ieee754dp_fsp(fs); rfmt = d_fmt; goto copcsr; - } - case fcvtw_op:{ - union ieee754sp fs; + case fcvtw_op: SPFROMREG(fs, MIPSInst_FS(ir)); rv.w = ieee754sp_tint(fs); rfmt = w_fmt; goto copcsr; - } case fround_op: case ftrunc_op: case fceil_op: - case ffloor_op:{ - unsigned int oldrm = ieee754_csr.rm; - union ieee754sp fs; - + case ffloor_op: if (!cpu_has_mips_2_3_4_5 && !cpu_has_mips64) return SIGILL; + oldrm = ieee754_csr.rm; SPFROMREG(fs, MIPSInst_FS(ir)); ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; rv.w = ieee754sp_tint(fs); ieee754_csr.rm = oldrm; rfmt = w_fmt; goto copcsr; - } - - case fcvtl_op:{ - union ieee754sp fs; + case fcvtl_op: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; @@ -1757,25 +1730,21 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, rv.l = ieee754sp_tlong(fs); rfmt = l_fmt; goto copcsr; - } case froundl_op: case ftruncl_op: case fceill_op: - case ffloorl_op:{ - unsigned int oldrm = ieee754_csr.rm; - union ieee754sp fs; - + case ffloorl_op: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; + oldrm = ieee754_csr.rm; SPFROMREG(fs, MIPSInst_FS(ir)); ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; rv.l = ieee754sp_tlong(fs); ieee754_csr.rm = oldrm; rfmt = l_fmt; goto copcsr; - } default: if (MIPSInst_FUNC(ir) >= fcmp_op) { @@ -1793,16 +1762,15 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else goto copcsr; - } - else { + } else return SIGILL; - } break; } break; } - case d_fmt:{ + case d_fmt: { + union ieee754dp fs, ft; union { union ieee754dp(*b) (union ieee754dp, union ieee754dp); union ieee754dp(*u) (union ieee754dp); @@ -1887,65 +1855,51 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, goto copcsr; /* binary op on handler */ - dcopbop:{ - union ieee754dp fs, ft; - - DPFROMREG(fs, MIPSInst_FS(ir)); - DPFROMREG(ft, MIPSInst_FT(ir)); - - rv.d = (*handler.b) (fs, ft); - goto copcsr; - } - dcopuop:{ - union ieee754dp fs; - - DPFROMREG(fs, MIPSInst_FS(ir)); - rv.d = (*handler.u) (fs); - goto copcsr; - } +dcopbop: + DPFROMREG(fs, MIPSInst_FS(ir)); + DPFROMREG(ft, MIPSInst_FT(ir)); - /* unary conv ops */ - case fcvts_op:{ - union ieee754dp fs; + rv.d = (*handler.b) (fs, ft); + goto copcsr; +dcopuop: + DPFROMREG(fs, MIPSInst_FS(ir)); + rv.d = (*handler.u) (fs); + goto copcsr; + /* + * unary conv ops + */ + case fcvts_op: DPFROMREG(fs, MIPSInst_FS(ir)); rv.s = ieee754sp_fdp(fs); rfmt = s_fmt; goto copcsr; - } + case fcvtd_op: return SIGILL; /* not defined */ - case fcvtw_op:{ - union ieee754dp fs; - + case fcvtw_op: DPFROMREG(fs, MIPSInst_FS(ir)); rv.w = ieee754dp_tint(fs); /* wrong */ rfmt = w_fmt; goto copcsr; - } case fround_op: case ftrunc_op: case fceil_op: - case ffloor_op:{ - unsigned int oldrm = ieee754_csr.rm; - union ieee754dp fs; - + case ffloor_op: if (!cpu_has_mips_2_3_4_5_r) return SIGILL; + oldrm = ieee754_csr.rm; DPFROMREG(fs, MIPSInst_FS(ir)); ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; rv.w = ieee754dp_tint(fs); ieee754_csr.rm = oldrm; rfmt = w_fmt; goto copcsr; - } - - case fcvtl_op:{ - union ieee754dp fs; + case fcvtl_op: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; @@ -1953,25 +1907,21 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, rv.l = ieee754dp_tlong(fs); rfmt = l_fmt; goto copcsr; - } case froundl_op: case ftruncl_op: case fceill_op: - case ffloorl_op:{ - unsigned int oldrm = ieee754_csr.rm; - union ieee754dp fs; - + case ffloorl_op: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; + oldrm = ieee754_csr.rm; DPFROMREG(fs, MIPSInst_FS(ir)); ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; rv.l = ieee754dp_tlong(fs); ieee754_csr.rm = oldrm; rfmt = l_fmt; goto copcsr; - } default: if (MIPSInst_FUNC(ir) >= fcmp_op) { @@ -1998,11 +1948,8 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, break; } break; - } - - case w_fmt:{ - union ieee754sp fs; + case w_fmt: switch (MIPSInst_FUNC(ir)) { case fcvts_op: /* convert word to single precision real */ @@ -2022,8 +1969,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, break; } - case l_fmt:{ - u64 bits; + case l_fmt: if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) return SIGILL; @@ -2045,7 +1991,6 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, return SIGILL; } break; - } default: return SIGILL; @@ -2060,7 +2005,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, */ ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { - /*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */ + /*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */ return SIGFPE; } @@ -2068,7 +2013,6 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, * Now we can safely write the result back to the register file. */ switch (rfmt) { - unsigned int cbit; case -1: if (cpu_has_mips_4_5_r) @@ -2200,7 +2144,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, /* SIGILL indicates a non-fpu instruction */ if (sig == SIGILL && xcp->cp0_epc != oldepc) - /* but if epc has advanced, then ignore it */ + /* but if EPC has advanced, then ignore it */ sig = 0; return sig; |