[PATCH 1/7] MIPS: math-emu: Correctly handle NOP emulation

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

 



Fix an issue introduced with commit 9ab4471c9f1b ("MIPS: math-emu: 
Correct delay-slot exception propagation") where the emulation of a NOP 
instruction signals the need to terminate the emulation loop.  This in 
turn, if the PC has not changed from the entry to the loop, will cause 
the kernel to terminate the program with SIGILL.

Consider this program:

static double div(double d)
{
	do
		d /= 2.0;
	while (d > .5);
	return d;
}

int main(int argc, char **argv)
{
	return div(argc);
}

which gets compiled to the following binary code:

00400490 <main>:
  400490:	44840000 	mtc1	a0,$f0
  400494:	3c020040 	lui	v0,0x40
  400498:	d44207f8 	ldc1	$f2,2040(v0)
  40049c:	46800021 	cvt.d.w	$f0,$f0
  4004a0:	46220002 	mul.d	$f0,$f0,$f2
  4004a4:	4620103c 	c.lt.d	$f2,$f0
  4004a8:	4501fffd 	bc1t	4004a0 <main+0x10>
  4004ac:	00000000 	nop
  4004b0:	4620000d 	trunc.w.d	$f0,$f0
  4004b4:	03e00008 	jr	ra
  4004b8:	44020000 	mfc1	v0,$f0
  4004bc:	00000000 	nop

Where the FPU emulator is used, depending on the number of command-line 
arguments this code will either run to completion or terminate with 
SIGILL.

If no arguments are specified, then BC1T will not be taken, NOP will not 
be emulated and code will complete successfully.

If one argument is specified, then BC1T will be taken once and NOP will 
be emulated.  At this point the entry PC value will be 0x400498 and the 
new PC value, set by `mips_dsemul' will be 0x4004a0, the target of BC1T. 
The emulation loop will terminate, but SIGILL will not be issued, 
because the PC has changed.  The FPU emulator will be entered again and 
on the second execution BC1T will not be taken, NOP will not be emulated 
and code will complete successfully.

If two or more arguments are specified, then the first execution of BC1T 
will proceed as above.  Upon reentering the FPU emulator the emulation 
loop will continue to BC1T, at which point the branch will be taken and
NOP emulated again.  At this point however the entry PC value will be 
0x4004a0, the same as the target of BC1T.  This will make the emulator
conclude that execution has not advanced and therefore an unsupported 
FPU instruction has been encountered, and SIGILL will be sent to the 
process.

Fix the problem by extending the internal API of `mips_dsemul', making 
it return -1 if no delay slot emulation frame has been made, the 
instruction has been handled and execution of the emulation loop needs 
to continue as if nothing happened.  Remove code from `mips_dsemul' to 
reproduce steps made by the emulation loop at the conclusion of each 
iteration, as those will be reached normally now.  Adjust call sites 
accordingly.  Document the API.

Signed-off-by: Maciej W. Rozycki <macro@xxxxxxxxxx>
---
Ralf,

 Please apply.  This has to be backported to stable branches.  NB I have 
deliberately left extraneous parentheses in the condition modified intact 
in `mips_dsemul', so as not to mix a semantic change with a syntactic one.  
They will go away altogether as the condition is refactored with a later
change in this series.

  Maciej

linux-mips-dsemul-nop.diff
Index: linux-sfr-test/arch/mips/math-emu/cp1emu.c
===================================================================
--- linux-sfr-test.orig/arch/mips/math-emu/cp1emu.c	2015-09-04 19:19:07.000000000 +0100
+++ linux-sfr-test/arch/mips/math-emu/cp1emu.c	2016-01-20 21:23:44.313505000 +0000
@@ -1266,6 +1266,8 @@ static int cop1Emulate(struct pt_regs *x
 						 */
 						sig = mips_dsemul(xcp, ir,
 								  contpc);
+						if (sig < 0)
+							break;
 						if (sig)
 							xcp->cp0_epc = bcpc;
 						/*
@@ -1319,6 +1321,8 @@ static int cop1Emulate(struct pt_regs *x
 				 * instruction in the dslot
 				 */
 				sig = mips_dsemul(xcp, ir, contpc);
+				if (sig < 0)
+					break;
 				if (sig)
 					xcp->cp0_epc = bcpc;
 				/* SIGILL forces out of the emulation loop.  */
Index: linux-sfr-test/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-test.orig/arch/mips/math-emu/dsemul.c	2015-09-04 19:19:07.000000000 +0100
+++ linux-sfr-test/arch/mips/math-emu/dsemul.c	2016-01-21 00:19:50.241325000 +0000
@@ -31,18 +31,20 @@ struct emuframe {
 	unsigned long		epc;
 };
 
+/*
+ * Set up an emulation frame for instruction IR, from a delay slot of
+ * a branch jumping to CPC.  Return 0 if successful, -1 if no emulation
+ * required, otherwise a signal number causing a frame setup failure.
+ */
 int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
 {
 	struct emuframe __user *fr;
 	int err;
 
+	/* NOP is easy */
 	if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) ||
-		(ir == 0)) {
-		/* NOP is easy */
-		regs->cp0_epc = cpc;
-		clear_delay_slot(regs);
-		return 0;
-	}
+	    (ir == 0))
+		return -1;
 
 	pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
 




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

  Powered by Linux