[PATCH 3/7] MIPS: math-emu: Make microMIPS branch delay slot emulation work

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
Ralf,

 Please apply and backport to stable branches.  This is the program I used 
to validate this change:

#include <stdio.h>

static int li(double x, double y, int i)
{
	asm(	
	"	.set	push		\n"
	"	.set	noreorder	\n"
	"	c.eq.d	%1, %2		\n"
	"	bc1t	0f		\n"
	"	 li	%0, %3		\n"
	"0:	.set	pop		"
		: "=r" (i)
		: "f" (x), "f" (y), "i" (i));

	return i;
}

int main(int argc, char **argv)
{
	printf("ne 16: %d\n", li(1.0, 2.0, 16));
	printf("eq 16: %d\n", li(0.0, 0.0, 16));

	return 0;
}

With the current state of our tree, where FPU emulation is involved this 
program, when compiled to microMIPS code, produces the following output 
(on a DSP-enabled processor):

ne 16: 16
Segmentation fault

whereas with the change applied it prints:

ne 16: 16
eq 16: 16

instead, as expected.

  Maciej

linux-umips-dsemul-break.diff
Index: linux-sfr-sead/arch/mips/include/asm/fpu_emulator.h
===================================================================
--- linux-sfr-sead.orig/arch/mips/include/asm/fpu_emulator.h	2016-01-22 01:56:44.281483000 +0000
+++ linux-sfr-sead/arch/mips/include/asm/fpu_emulator.h	2016-01-22 01:57:50.686025000 +0000
@@ -79,7 +79,7 @@ int mm_isBranchInstr(struct pt_regs *reg
 /*
  * 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: linux-sfr-sead/arch/mips/include/asm/mips-r2-to-r6-emul.h
===================================================================
--- linux-sfr-sead.orig/arch/mips/include/asm/mips-r2-to-r6-emul.h	2016-01-22 01:56:44.283483000 +0000
+++ linux-sfr-sead/arch/mips/include/asm/mips-r2-to-r6-emul.h	2016-01-22 01:57:50.711027000 +0000
@@ -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: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c	2016-01-22 01:57:12.640605000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c	2016-01-22 01:57:50.714027000 +0000
@@ -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, mi
 	 * 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, mi
 				 (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;
 	}




[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux