MIPS: math-emu: Make microMIPS branch delay slot emulation work
authorMaciej W. Rozycki <macro@imgtec.com>
Fri, 22 Jan 2016 05:20:46 +0000 (05:20 +0000)
committerRalf Baechle <ralf@linux-mips.org>
Sun, 24 Jan 2016 00:35:46 +0000 (01:35 +0100)
Complement commit 102cedc32a6e ("MIPS: microMIPS: Floating point
support.") which introduced microMIPS FPU emulation, but did not adjust
the encoding of the BREAK instruction used to terminate the branch delay
slot emulation frame.  Consequently the execution of any such frame is
indeterminate and, depending on CPU configuration, will result in random
code execution or an offending program being terminated with SIGILL.

This is because the regular MIPS BREAK instruction is encoded with the 0
major and the 0xd minor opcode, however in the microMIPS instruction set
this major/minor opcode pair denotes an encoding reserved for the DSP
ASE.  Instead the microMIPS BREAK instruction is encoded with the 0
major and the 0x7 minor opcode.

Use the correct BREAK encoding for microMIPS FPU emulation then.

Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/12174/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/fpu_emulator.h
arch/mips/include/asm/mips-r2-to-r6-emul.h
arch/mips/math-emu/dsemul.c

index 2f021cdfba4f8fc60a5348f7822f0014d02a3eff..3225c3c0724b3642392bad7e9c395c9451afe555 100644 (file)
@@ -79,7 +79,7 @@ int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
 /*
  * Break instruction with special math emu break code set
  */
-#define BREAK_MATH (0x0000000d | (BRK_MEMU << 16))
+#define BREAK_MATH(micromips) (((micromips) ? 0x7 : 0xd) | (BRK_MEMU << 16))
 
 #define SIGNALLING_NAN 0x7ff800007ff80000LL
 
index 4b89f28047f7118588acf2152ba43e44a5d353e2..1f6ea8352ca90408772f0a7f72f7e213bece28ba 100644 (file)
@@ -52,7 +52,7 @@ do {                                                                  \
        __this_cpu_inc(mipsr2emustats.M);                               \
        err = __get_user(nir, (u32 __user *)regs->cp0_epc);             \
        if (!err) {                                                     \
-               if (nir == BREAK_MATH)                                  \
+               if (nir == BREAK_MATH(0))                               \
                        __this_cpu_inc(mipsr2bdemustats.M);             \
        }                                                               \
        preempt_enable();                                               \
index 4e30bfc3cdd5506d29c464d326a629cc628f1fae..32ec5d7e17281d2efc9307471da16bdde50f0bbc 100644 (file)
@@ -38,6 +38,7 @@ struct emuframe {
  */
 int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
 {
+       mips_instruction break_math;
        struct emuframe __user *fr;
        int err;
 
@@ -65,6 +66,7 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
         * branches, but gives us a cleaner interface to the exception
         * handler (single entry point).
         */
+       break_math = BREAK_MATH(get_isa16_mode(regs->cp0_epc));
 
        /* Ensure that the two instructions are in the same cache line */
        fr = (struct emuframe __user *)
@@ -79,13 +81,13 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
                                 (u16 __user *)(&fr->emul));
                err |= __put_user(ir & 0xffff,
                                  (u16 __user *)((long)(&fr->emul) + 2));
-               err |= __put_user(BREAK_MATH >> 16,
+               err |= __put_user(break_math >> 16,
                                  (u16 __user *)(&fr->badinst));
-               err |= __put_user(BREAK_MATH & 0xffff,
+               err |= __put_user(break_math & 0xffff,
                                  (u16 __user *)((long)(&fr->badinst) + 2));
        } else {
                err = __put_user(ir, &fr->emul);
-               err |= __put_user((mips_instruction)BREAK_MATH, &fr->badinst);
+               err |= __put_user(break_math, &fr->badinst);
        }
 
        err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie);
@@ -139,7 +141,8 @@ int do_dsemulret(struct pt_regs *xcp)
        }
        err |= __get_user(cookie, &fr->cookie);
 
-       if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE))) {
+       if (unlikely(err || insn != BREAK_MATH(get_isa16_mode(xcp->cp0_epc)) ||
+                    cookie != BD_COOKIE)) {
                MIPS_FPU_EMU_INC_STATS(errors);
                return 0;
        }