MIPS: math-emu: Correct delay-slot exception propagation
[firefly-linux-kernel-4.4.55.git] / arch / mips / math-emu / cp1emu.c
index 0d8407b51470c1ee92d069d0d744cd520b9e0c5e..acfef06b83112b88a43d913eced9ac5b1d94659a 100644 (file)
@@ -48,6 +48,7 @@
 #include <asm/processor.h>
 #include <asm/fpu_emulator.h>
 #include <asm/fpu.h>
+#include <asm/mips-r2-to-r6-emul.h>
 
 #include "ieee754.h"
 
@@ -64,11 +65,8 @@ static int fpux_emu(struct pt_regs *,
 #define FPCREG_RID     0       /* $0  = revision id */
 #define FPCREG_CSR     31      /* $31 = csr */
 
-/* Determine rounding mode from the RM bits of the FCSR */
-#define modeindex(v) ((v) & FPU_CSR_RM)
-
 /* convert condition code register number to csr bit */
-static const unsigned int fpucondbit[8] = {
+const unsigned int fpucondbit[8] = {
        FPU_CSR_COND0,
        FPU_CSR_COND1,
        FPU_CSR_COND2,
@@ -670,6 +668,30 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                *contpc = regs->cp0_epc + dec_insn.pc_inc +
                        dec_insn.next_pc_inc;
 
+               return 1;
+       case balc6_op:
+               if (!cpu_has_mips_r6)
+                       break;
+               regs->regs[31] = regs->cp0_epc + 4;
+               *contpc = regs->cp0_epc + dec_insn.pc_inc +
+                       dec_insn.next_pc_inc;
+
+               return 1;
+       case beqzcjic_op:
+               if (!cpu_has_mips_r6)
+                       break;
+               *contpc = regs->cp0_epc + dec_insn.pc_inc +
+                       dec_insn.next_pc_inc;
+
+               return 1;
+       case bnezcjialc_op:
+               if (!cpu_has_mips_r6)
+                       break;
+               if (!insn.i_format.rs)
+                       regs->regs[31] = regs->cp0_epc + 4;
+               *contpc = regs->cp0_epc + dec_insn.pc_inc +
+                       dec_insn.next_pc_inc;
+
                return 1;
 #endif
        case cop0_op:
@@ -817,6 +839,52 @@ do {                                                                       \
 #define DPFROMREG(dp, x)       DIFROMREG((dp).bits, x)
 #define DPTOREG(dp, x) DITOREG((dp).bits, x)
 
+/*
+ * Emulate a CFC1 instruction.
+ */
+static inline void cop1_cfc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+                           mips_instruction ir)
+{
+       u32 value;
+
+       if (MIPSInst_RD(ir) == FPCREG_CSR) {
+               value = ctx->fcr31;
+               pr_debug("%p gpr[%d]<-csr=%08x\n",
+                        (void *)xcp->cp0_epc,
+                        MIPSInst_RT(ir), value);
+       } else if (MIPSInst_RD(ir) == FPCREG_RID)
+               value = 0;
+       else
+               value = 0;
+       if (MIPSInst_RT(ir))
+               xcp->regs[MIPSInst_RT(ir)] = value;
+}
+
+/*
+ * Emulate a CTC1 instruction.
+ */
+static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+                           mips_instruction ir)
+{
+       u32 value;
+
+       if (MIPSInst_RT(ir) == 0)
+               value = 0;
+       else
+               value = xcp->regs[MIPSInst_RT(ir)];
+
+       /* we only have one writable control reg
+        */
+       if (MIPSInst_RD(ir) == FPCREG_CSR) {
+               pr_debug("%p gpr[%d]->csr=%08x\n",
+                        (void *)xcp->cp0_epc,
+                        MIPSInst_RT(ir), value);
+
+               /* Don't write reserved bits.  */
+               ctx->fcr31 = value & ~FPU_CSR_RSVD;
+       }
+}
+
 /*
  * Emulate the single floating point instruction pointed at by EPC.
  * Two instructions if the instruction is in a branch delay slot.
@@ -831,7 +899,6 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
        int likely, pc_inc;
        u32 __user *wva;
        u64 __user *dva;
-       u32 value;
        u32 wval;
        u64 dval;
        int sig;
@@ -1024,42 +1091,12 @@ emul:
 
                case cfc_op:
                        /* cop control register rd -> gpr[rt] */
-                       if (MIPSInst_RD(ir) == FPCREG_CSR) {
-                               value = ctx->fcr31;
-                               value = (value & ~FPU_CSR_RM) | modeindex(value);
-                               pr_debug("%p gpr[%d]<-csr=%08x\n",
-                                        (void *) (xcp->cp0_epc),
-                                        MIPSInst_RT(ir), value);
-                       }
-                       else if (MIPSInst_RD(ir) == FPCREG_RID)
-                               value = 0;
-                       else
-                               value = 0;
-                       if (MIPSInst_RT(ir))
-                               xcp->regs[MIPSInst_RT(ir)] = value;
+                       cop1_cfc(xcp, ctx, ir);
                        break;
 
                case ctc_op:
                        /* copregister rd <- rt */
-                       if (MIPSInst_RT(ir) == 0)
-                               value = 0;
-                       else
-                               value = xcp->regs[MIPSInst_RT(ir)];
-
-                       /* we only have one writable control reg
-                        */
-                       if (MIPSInst_RD(ir) == FPCREG_CSR) {
-                               pr_debug("%p gpr[%d]->csr=%08x\n",
-                                        (void *) (xcp->cp0_epc),
-                                        MIPSInst_RT(ir), value);
-
-                               /*
-                                * Don't write reserved bits,
-                                * and convert to ieee library modes
-                                */
-                               ctx->fcr31 = (value & ~(FPU_CSR_RSVD | FPU_CSR_RM)) |
-                                            modeindex(value);
-                       }
+                       cop1_ctc(xcp, ctx, ir);
                        if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
                                return SIGFPE;
                        }
@@ -1078,17 +1115,18 @@ emul:
                        likely = 0;
                        switch (MIPSInst_RT(ir) & 3) {
                        case bcfl_op:
-                               likely = 1;
+                               if (cpu_has_mips_2_3_4_5_r)
+                                       likely = 1;
+                               /* Fall through */
                        case bcf_op:
                                cond = !cond;
                                break;
                        case bctl_op:
-                               likely = 1;
+                               if (cpu_has_mips_2_3_4_5_r)
+                                       likely = 1;
+                               /* Fall through */
                        case bct_op:
                                break;
-                       default:
-                               /* thats an illegal instruction */
-                               return SIGILL;
                        }
 
                        set_delay_slot(xcp);
@@ -1096,6 +1134,14 @@ emul:
                                /*
                                 * Branch taken: emulate dslot instruction
                                 */
+                               unsigned long bcpc;
+
+                               /*
+                                * Remember EPC at the branch to point back
+                                * at so that any delay-slot instruction
+                                * signal is not silently ignored.
+                                */
+                               bcpc = xcp->cp0_epc;
                                xcp->cp0_epc += dec_insn.pc_inc;
 
                                contpc = MIPSInst_SIMM(ir);
@@ -1121,63 +1167,77 @@ emul:
                                                 * Single step the non-CP1
                                                 * instruction in the dslot.
                                                 */
-                                               return mips_dsemul(xcp, ir, contpc);
+                                               sig = mips_dsemul(xcp, ir,
+                                                                 contpc);
+                                               if (sig)
+                                                       xcp->cp0_epc = bcpc;
+                                               /*
+                                                * SIGILL forces out of
+                                                * the emulation loop.
+                                                */
+                                               return sig ? sig : SIGILL;
                                        }
                                } else
                                        contpc = (xcp->cp0_epc + (contpc << 2));
 
                                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 ||
-                                           cpu_has_mips64)
+                                       if (cpu_has_mips_2_3_4_5_r)
                                                goto emul;
 
-                                       return SIGILL;
-                                       goto emul;
+                                       goto bc_sigill;
 
                                case cop1_op:
                                        goto emul;
 
                                case cop1x_op:
-                                       if (cpu_has_mips_4_5 || cpu_has_mips64 || cpu_has_mips32r2)
+                                       if (cpu_has_mips_4_5_64_r2_r6)
                                                /* its one of ours */
                                                goto emul;
 
-                                       return SIGILL;
+                                       goto bc_sigill;
 
                                case spec_op:
-                                       if (!cpu_has_mips_4_5_r)
-                                               return SIGILL;
+                                       switch (MIPSInst_FUNC(ir)) {
+                                       case movc_op:
+                                               if (cpu_has_mips_4_5_r)
+                                                       goto emul;
 
-                                       if (MIPSInst_FUNC(ir) == movc_op)
-                                               goto emul;
+                                               goto bc_sigill;
+                                       }
                                        break;
+
+                               bc_sigill:
+                                       xcp->cp0_epc = bcpc;
+                                       return SIGILL;
                                }
 
                                /*
                                 * Single step the non-cp1
                                 * instruction in the dslot
                                 */
-                               return mips_dsemul(xcp, ir, contpc);
+                               sig = mips_dsemul(xcp, ir, contpc);
+                               if (sig)
+                                       xcp->cp0_epc = bcpc;
+                               /* SIGILL forces out of the emulation loop.  */
+                               return sig ? sig : SIGILL;
                        } else if (likely) {    /* branch not taken */
-                                       /*
-                                        * branch likely nullifies
-                                        * dslot if not taken
-                                        */
-                                       xcp->cp0_epc += dec_insn.pc_inc;
-                                       contpc += dec_insn.pc_inc;
-                                       /*
-                                        * else continue & execute
-                                        * dslot as normal insn
-                                        */
-                               }
+                               /*
+                                * branch likely nullifies
+                                * dslot if not taken
+                                */
+                               xcp->cp0_epc += dec_insn.pc_inc;
+                               contpc += dec_insn.pc_inc;
+                               /*
+                                * else continue & execute
+                                * dslot as normal insn
+                                */
+                       }
                        break;
 
                default:
@@ -1191,7 +1251,7 @@ emul:
                break;
 
        case cop1x_op:
-               if (!cpu_has_mips_4_5 && !cpu_has_mips64 && !cpu_has_mips32r2)
+               if (!cpu_has_mips_4_5_64_r2_r6)
                        return SIGILL;
 
                sig = fpux_emu(xcp, ctx, ir, fault_addr);
@@ -1524,7 +1584,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 
                        /* unary  ops */
                case fsqrt_op:
-                       if (!cpu_has_mips_4_5_r)
+                       if (!cpu_has_mips_2_3_4_5_r)
                                return SIGILL;
 
                        handler.u = ieee754sp_sqrt;
@@ -1536,14 +1596,14 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                 * achieve full IEEE-754 accuracy - however this emulator does.
                 */
                case frsqrt_op:
-                       if (!cpu_has_mips_4_5_r2)
+                       if (!cpu_has_mips_4_5_64_r2_r6)
                                return SIGILL;
 
                        handler.u = fpemu_sp_rsqrt;
                        goto scopuop;
 
                case frecip_op:
-                       if (!cpu_has_mips_4_5_r2)
+                       if (!cpu_has_mips_4_5_64_r2_r6)
                                return SIGILL;
 
                        handler.u = fpemu_sp_recip;
@@ -1645,19 +1705,19 @@ copcsr:
                case ftrunc_op:
                case fceil_op:
                case ffloor_op:
-                       if (!cpu_has_mips_2_3_4_5 && !cpu_has_mips64)
+                       if (!cpu_has_mips_2_3_4_5_r)
                                return SIGILL;
 
                        oldrm = ieee754_csr.rm;
                        SPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir));
+                       ieee754_csr.rm = MIPSInst_FUNC(ir);
                        rv.w = ieee754sp_tint(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = w_fmt;
                        goto copcsr;
 
                case fcvtl_op:
-                       if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+                       if (!cpu_has_mips_3_4_5_64_r2_r6)
                                return SIGILL;
 
                        SPFROMREG(fs, MIPSInst_FS(ir));
@@ -1669,12 +1729,12 @@ copcsr:
                case ftruncl_op:
                case fceill_op:
                case ffloorl_op:
-                       if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+                       if (!cpu_has_mips_3_4_5_64_r2_r6)
                                return SIGILL;
 
                        oldrm = ieee754_csr.rm;
                        SPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir));
+                       ieee754_csr.rm = MIPSInst_FUNC(ir);
                        rv.l = ieee754sp_tlong(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = l_fmt;
@@ -1738,13 +1798,13 @@ copcsr:
                 * achieve full IEEE-754 accuracy - however this emulator does.
                 */
                case frsqrt_op:
-                       if (!cpu_has_mips_4_5_r2)
+                       if (!cpu_has_mips_4_5_64_r2_r6)
                                return SIGILL;
 
                        handler.u = fpemu_dp_rsqrt;
                        goto dcopuop;
                case frecip_op:
-                       if (!cpu_has_mips_4_5_r2)
+                       if (!cpu_has_mips_4_5_64_r2_r6)
                                return SIGILL;
 
                        handler.u = fpemu_dp_recip;
@@ -1827,14 +1887,14 @@ dcopuop:
 
                        oldrm = ieee754_csr.rm;
                        DPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir));
+                       ieee754_csr.rm = MIPSInst_FUNC(ir);
                        rv.w = ieee754dp_tint(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = w_fmt;
                        goto copcsr;
 
                case fcvtl_op:
-                       if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+                       if (!cpu_has_mips_3_4_5_64_r2_r6)
                                return SIGILL;
 
                        DPFROMREG(fs, MIPSInst_FS(ir));
@@ -1846,12 +1906,12 @@ dcopuop:
                case ftruncl_op:
                case fceill_op:
                case ffloorl_op:
-                       if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+                       if (!cpu_has_mips_3_4_5_64_r2_r6)
                                return SIGILL;
 
                        oldrm = ieee754_csr.rm;
                        DPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir));
+                       ieee754_csr.rm = MIPSInst_FUNC(ir);
                        rv.l = ieee754dp_tlong(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = l_fmt;
@@ -1905,7 +1965,7 @@ dcopuop:
 
        case l_fmt:
 
-               if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+               if (!cpu_has_mips_3_4_5_64_r2_r6)
                        return SIGILL;
 
                DIFROMREG(bits, MIPSInst_FS(ir));
@@ -1969,7 +2029,7 @@ dcopuop:
                SITOREG(rv.w, MIPSInst_FD(ir));
                break;
        case l_fmt:
-               if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+               if (!cpu_has_mips_3_4_5_64_r2_r6)
                        return SIGILL;
 
                DITOREG(rv.l, MIPSInst_FD(ir));
@@ -2056,10 +2116,8 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        xcp->cp0_epc += dec_insn.pc_inc;        /* Skip NOPs */
                else {
                        /*
-                        * The 'ieee754_csr' is an alias of
-                        * ctx->fcr31.  No need to copy ctx->fcr31 to
-                        * ieee754_csr.  But ieee754_csr.rm is ieee
-                        * library modes. (not mips rounding mode)
+                        * The 'ieee754_csr' is an alias of ctx->fcr31.
+                        * No need to copy ctx->fcr31 to ieee754_csr.
                         */
                        sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr);
                }