* [PATCH 0/7] MIPS: math-emu: Branch delay slot emulation fixes
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Hi,
This small patch series addresses the following issues with branch delay
slot emulation in our floating-point emulator:
- NOP emulation sometimes causes SIGILL (Aurelien's bug),
- microMIPS emulation always goes astray,
- microMIPS emulation of ADDIUPC always returns the wrong result.
Also included are a bunch of code clean-ups and comment fixes. See
individual patch descriptions for further details.
I attempted to move clean-ups to the end, so that they do not interfere
with backporting, except with 2/7 which, if reordered, would require 3/7
to become ill-formatted. I hope this is OK. Changes 5-7/7 do not require
backporting.
This series has been validated with a MIPS M5150 processor.
Maciej
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 0/7] MIPS: math-emu: Branch delay slot emulation fixes
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Hi,
This small patch series addresses the following issues with branch delay
slot emulation in our floating-point emulator:
- NOP emulation sometimes causes SIGILL (Aurelien's bug),
- microMIPS emulation always goes astray,
- microMIPS emulation of ADDIUPC always returns the wrong result.
Also included are a bunch of code clean-ups and comment fixes. See
individual patch descriptions for further details.
I attempted to move clean-ups to the end, so that they do not interfere
with backporting, except with 2/7 which, if reordered, would require 3/7
to become ill-formatted. I hope this is OK. Changes 5-7/7 do not require
backporting.
This series has been validated with a MIPS M5150 processor.
Maciej
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/7] MIPS: math-emu: Correctly handle NOP emulation
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
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@imgtec.com>
---
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);
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/7] MIPS: math-emu: Correctly handle NOP emulation
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
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@imgtec.com>
---
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);
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 2/7] MIPS: math-emu: dsemul: Fix ill formatting of microMIPS part
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Correct formatting breakage introduced with commit 102cedc32a6e ("MIPS:
microMIPS: Floating point support."), so that further changes to this
code can be consistent.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
Please apply; as I noted in 0/7 the structure of this code would become
inconsistent with the next change or the change itself would have to break
our formatting rules, to say nothing of legibility. Consequently this
change needs backporting to stable branches so that 3/7 can be backported
as well.
I'm all but happy about the style of this code itself BTW, the chains of
casts of casts are not really needed, but I'll save a further clean-up for
another day.
Maciej
linux-umips-dsemul-format.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:21:57.194943000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:57:12.640605000 +0000
@@ -75,10 +75,14 @@ int mips_dsemul(struct pt_regs *regs, mi
return SIGBUS;
if (get_isa16_mode(regs->cp0_epc)) {
- err = __put_user(ir >> 16, (u16 __user *)(&fr->emul));
- err |= __put_user(ir & 0xffff, (u16 __user *)((long)(&fr->emul) + 2));
- err |= __put_user(BREAK_MATH >> 16, (u16 __user *)(&fr->badinst));
- err |= __put_user(BREAK_MATH & 0xffff, (u16 __user *)((long)(&fr->badinst) + 2));
+ err = __put_user(ir >> 16,
+ (u16 __user *)(&fr->emul));
+ err |= __put_user(ir & 0xffff,
+ (u16 __user *)((long)(&fr->emul) + 2));
+ err |= __put_user(BREAK_MATH >> 16,
+ (u16 __user *)(&fr->badinst));
+ 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);
@@ -125,8 +129,10 @@ int do_dsemulret(struct pt_regs *xcp)
* - Is the following memory word the BD_COOKIE?
*/
if (get_isa16_mode(xcp->cp0_epc)) {
- err = __get_user(instr[0], (u16 __user *)(&fr->badinst));
- err |= __get_user(instr[1], (u16 __user *)((long)(&fr->badinst) + 2));
+ err = __get_user(instr[0],
+ (u16 __user *)(&fr->badinst));
+ err |= __get_user(instr[1],
+ (u16 __user *)((long)(&fr->badinst) + 2));
insn = (instr[0] << 16) | instr[1];
} else {
err = __get_user(insn, &fr->badinst);
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 2/7] MIPS: math-emu: dsemul: Fix ill formatting of microMIPS part
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Correct formatting breakage introduced with commit 102cedc32a6e ("MIPS:
microMIPS: Floating point support."), so that further changes to this
code can be consistent.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
Please apply; as I noted in 0/7 the structure of this code would become
inconsistent with the next change or the change itself would have to break
our formatting rules, to say nothing of legibility. Consequently this
change needs backporting to stable branches so that 3/7 can be backported
as well.
I'm all but happy about the style of this code itself BTW, the chains of
casts of casts are not really needed, but I'll save a further clean-up for
another day.
Maciej
linux-umips-dsemul-format.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:21:57.194943000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:57:12.640605000 +0000
@@ -75,10 +75,14 @@ int mips_dsemul(struct pt_regs *regs, mi
return SIGBUS;
if (get_isa16_mode(regs->cp0_epc)) {
- err = __put_user(ir >> 16, (u16 __user *)(&fr->emul));
- err |= __put_user(ir & 0xffff, (u16 __user *)((long)(&fr->emul) + 2));
- err |= __put_user(BREAK_MATH >> 16, (u16 __user *)(&fr->badinst));
- err |= __put_user(BREAK_MATH & 0xffff, (u16 __user *)((long)(&fr->badinst) + 2));
+ err = __put_user(ir >> 16,
+ (u16 __user *)(&fr->emul));
+ err |= __put_user(ir & 0xffff,
+ (u16 __user *)((long)(&fr->emul) + 2));
+ err |= __put_user(BREAK_MATH >> 16,
+ (u16 __user *)(&fr->badinst));
+ 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);
@@ -125,8 +129,10 @@ int do_dsemulret(struct pt_regs *xcp)
* - Is the following memory word the BD_COOKIE?
*/
if (get_isa16_mode(xcp->cp0_epc)) {
- err = __get_user(instr[0], (u16 __user *)(&fr->badinst));
- err |= __get_user(instr[1], (u16 __user *)((long)(&fr->badinst) + 2));
+ err = __get_user(instr[0],
+ (u16 __user *)(&fr->badinst));
+ err |= __get_user(instr[1],
+ (u16 __user *)((long)(&fr->badinst) + 2));
insn = (instr[0] << 16) | instr[1];
} else {
err = __get_user(insn, &fr->badinst);
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 3/7] MIPS: math-emu: Make microMIPS branch delay slot emulation work
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
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>
---
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;
}
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 3/7] MIPS: math-emu: Make microMIPS branch delay slot emulation work
@ 2016-01-22 5:20 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:20 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
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>
---
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;
}
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 4/7] MIPS: math-emu: Correct the emulation of microMIPS ADDIUPC instruction
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Emulate the microMIPS ADDIUPC instruction directly in `mips_dsemul'. If
executed in the emulation frame, this instruction produces an incorrect
result, because the value of the PC there is not the same as where the
instruction originated.
Reshape code so as to handle all microMIPS cases together.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
Please apply and backport to stable branches. This is the program I used
to validate this change:
#include <stdio.h>
struct pc {
void *pc0;
void *pc1;
};
static struct pc addiupc(double x, double y, int i, int unaligned)
{
struct pc pc;
asm(
" .set push \n"
" .set noreorder \n"
" c.eq.d %2, %3 \n"
" .align 2 \n"
" .rept %5 \n"
" nop16 \n"
" .endr \n"
" bc1t 1f \n"
"0: .fill 0 \n"
" addiu %0, $pc, %4 \n"
"1: la %1, 0b \n"
" addu %1, %1, %z4 \n"
" .set pop "
: "=&u" (pc.pc0), "=&r" (pc.pc1)
: "f" (x), "f" (y), "i" (i), "i" (unaligned));
return pc;
}
int main(int argc, char **argv)
{
struct pc pc;
pc = addiupc(1.0, 2.0, 0, 0);
printf("al ne 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 0, 0);
printf("al eq 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 0, 1);
printf("un ne 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 0, 1);
printf("un eq 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16, 0);
printf("al ne 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16, 0);
printf("al eq 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16, 1);
printf("un ne 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16, 1);
printf("un eq 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16, 0);
printf("al ne -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16, 0);
printf("al eq -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16, 1);
printf("un ne -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16, 1);
printf("un eq -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16777212, 0);
printf("al ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16777212, 0);
printf("al eq 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16777212, 1);
printf("un ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16777212, 1);
printf("un eq 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16777216, 0);
printf("al ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16777216, 0);
printf("al eq -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16777216, 1);
printf("un ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16777216, 1);
printf("un eq -16777216: %p, %p\n", pc.pc0, pc.pc1);
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):
al ne 0: 0x40052c, 0x40052c
Illegal instruction
(because of the issue fixed with 3/7) with 3/7 applied it prints:
al ne 0: 0x40052c, 0x40052c
al eq 0: 0x7fe144f8, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x7fe144f8, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x7fe14508, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x7fe14508, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x7fe144e8, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x7fe144e8, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x80e144f4, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x80e144f4, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0x7ee144f8, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0x7ee144f8, 0xff400822
(notice the addresses offsetted from the emulation frame in the "eq" case,
printed on the left) whereas with the change applied it prints:
al ne 0: 0x40052c, 0x40052c
al eq 0: 0x400550, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x400598, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x4005f0, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x40063c, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x400668, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x4006b4, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x1400714, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x1400770, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0xff4007cc, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0xff400820, 0xff400822
instead, as expected (notice the 4-byte alignment of the results, absent
from the reference values calculated with the LA/ADDU sequence, printed on
the right).
Further changes will be required to correctly handle PC-relative MIPSr6
instructions. I do not intend to make such changes anytime soon, people
are encouraged to make them themselves if interested in MIPSr6 execution
environments.
If adding further cases to `mips_dsemul' I suggest factoring out code to
handle direct emulation (-1 return code).
Maciej
linux-umips-dsemul-addiupc.diff
Index: linux-sfr-sead/arch/mips/include/uapi/asm/inst.h
===================================================================
--- linux-sfr-sead.orig/arch/mips/include/uapi/asm/inst.h 2016-01-22 01:07:02.925179000 +0000
+++ linux-sfr-sead/arch/mips/include/uapi/asm/inst.h 2016-01-22 01:10:10.472810000 +0000
@@ -799,6 +799,13 @@ struct mm_x_format { /* Scaled indexed
;)))))
};
+struct mm_a_format { /* ADDIUPC format (microMIPS) */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 3,
+ __BITFIELD_FIELD(signed int simmediate : 23,
+ ;)))
+};
+
/*
* microMIPS instruction formats (16-bit length)
*/
@@ -940,6 +947,7 @@ union mips_instruction {
struct mm_i_format mm_i_format;
struct mm_m_format mm_m_format;
struct mm_x_format mm_x_format;
+ struct mm_a_format mm_a_format;
struct mm_b0_format mm_b0_format;
struct mm_b1_format mm_b1_format;
struct mm16_m_format mm16_m_format ;
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:09:07.914682000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:10.476810000 +0000
@@ -43,10 +43,30 @@ int mips_dsemul(struct pt_regs *regs, mi
int err;
/* NOP is easy */
- if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) ||
- (ir == 0))
+ if (ir == 0)
return -1;
+ /* microMIPS instructions */
+ if (get_isa16_mode(regs->cp0_epc)) {
+ union mips_instruction insn = { .word = ir };
+
+ /* NOP16 aka MOVE16 $0, $0 */
+ if ((ir >> 16) == MM_NOP16)
+ return -1;
+
+ /* ADDIUPC */
+ if (insn.mm_a_format.opcode == mm_addiupc_op) {
+ unsigned int rs;
+ s32 v;
+
+ rs = (((insn.mm_a_format.rs + 0x1e) & 0xf) + 2);
+ v = regs->cp0_epc & ~3;
+ v += insn.mm_a_format.simmediate << 2;
+ regs->regs[rs] = (long)v;
+ return -1;
+ }
+ }
+
pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
/*
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 4/7] MIPS: math-emu: Correct the emulation of microMIPS ADDIUPC instruction
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Emulate the microMIPS ADDIUPC instruction directly in `mips_dsemul'. If
executed in the emulation frame, this instruction produces an incorrect
result, because the value of the PC there is not the same as where the
instruction originated.
Reshape code so as to handle all microMIPS cases together.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
Please apply and backport to stable branches. This is the program I used
to validate this change:
#include <stdio.h>
struct pc {
void *pc0;
void *pc1;
};
static struct pc addiupc(double x, double y, int i, int unaligned)
{
struct pc pc;
asm(
" .set push \n"
" .set noreorder \n"
" c.eq.d %2, %3 \n"
" .align 2 \n"
" .rept %5 \n"
" nop16 \n"
" .endr \n"
" bc1t 1f \n"
"0: .fill 0 \n"
" addiu %0, $pc, %4 \n"
"1: la %1, 0b \n"
" addu %1, %1, %z4 \n"
" .set pop "
: "=&u" (pc.pc0), "=&r" (pc.pc1)
: "f" (x), "f" (y), "i" (i), "i" (unaligned));
return pc;
}
int main(int argc, char **argv)
{
struct pc pc;
pc = addiupc(1.0, 2.0, 0, 0);
printf("al ne 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 0, 0);
printf("al eq 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 0, 1);
printf("un ne 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 0, 1);
printf("un eq 0: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16, 0);
printf("al ne 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16, 0);
printf("al eq 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16, 1);
printf("un ne 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16, 1);
printf("un eq 16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16, 0);
printf("al ne -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16, 0);
printf("al eq -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16, 1);
printf("un ne -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16, 1);
printf("un eq -16: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16777212, 0);
printf("al ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16777212, 0);
printf("al eq 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, 16777212, 1);
printf("un ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, 16777212, 1);
printf("un eq 16777212: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16777216, 0);
printf("al ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16777216, 0);
printf("al eq -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(1.0, 2.0, -16777216, 1);
printf("un ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
pc = addiupc(0.0, 0.0, -16777216, 1);
printf("un eq -16777216: %p, %p\n", pc.pc0, pc.pc1);
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):
al ne 0: 0x40052c, 0x40052c
Illegal instruction
(because of the issue fixed with 3/7) with 3/7 applied it prints:
al ne 0: 0x40052c, 0x40052c
al eq 0: 0x7fe144f8, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x7fe144f8, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x7fe14508, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x7fe14508, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x7fe144e8, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x7fe144e8, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x80e144f4, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x80e144f4, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0x7ee144f8, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0x7ee144f8, 0xff400822
(notice the addresses offsetted from the emulation frame in the "eq" case,
printed on the left) whereas with the change applied it prints:
al ne 0: 0x40052c, 0x40052c
al eq 0: 0x400550, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x400598, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x4005f0, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x40063c, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x400668, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x4006b4, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x1400714, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x1400770, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0xff4007cc, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0xff400820, 0xff400822
instead, as expected (notice the 4-byte alignment of the results, absent
from the reference values calculated with the LA/ADDU sequence, printed on
the right).
Further changes will be required to correctly handle PC-relative MIPSr6
instructions. I do not intend to make such changes anytime soon, people
are encouraged to make them themselves if interested in MIPSr6 execution
environments.
If adding further cases to `mips_dsemul' I suggest factoring out code to
handle direct emulation (-1 return code).
Maciej
linux-umips-dsemul-addiupc.diff
Index: linux-sfr-sead/arch/mips/include/uapi/asm/inst.h
===================================================================
--- linux-sfr-sead.orig/arch/mips/include/uapi/asm/inst.h 2016-01-22 01:07:02.925179000 +0000
+++ linux-sfr-sead/arch/mips/include/uapi/asm/inst.h 2016-01-22 01:10:10.472810000 +0000
@@ -799,6 +799,13 @@ struct mm_x_format { /* Scaled indexed
;)))))
};
+struct mm_a_format { /* ADDIUPC format (microMIPS) */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 3,
+ __BITFIELD_FIELD(signed int simmediate : 23,
+ ;)))
+};
+
/*
* microMIPS instruction formats (16-bit length)
*/
@@ -940,6 +947,7 @@ union mips_instruction {
struct mm_i_format mm_i_format;
struct mm_m_format mm_m_format;
struct mm_x_format mm_x_format;
+ struct mm_a_format mm_a_format;
struct mm_b0_format mm_b0_format;
struct mm_b1_format mm_b1_format;
struct mm16_m_format mm16_m_format ;
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:09:07.914682000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:10.476810000 +0000
@@ -43,10 +43,30 @@ int mips_dsemul(struct pt_regs *regs, mi
int err;
/* NOP is easy */
- if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) ||
- (ir == 0))
+ if (ir == 0)
return -1;
+ /* microMIPS instructions */
+ if (get_isa16_mode(regs->cp0_epc)) {
+ union mips_instruction insn = { .word = ir };
+
+ /* NOP16 aka MOVE16 $0, $0 */
+ if ((ir >> 16) == MM_NOP16)
+ return -1;
+
+ /* ADDIUPC */
+ if (insn.mm_a_format.opcode == mm_addiupc_op) {
+ unsigned int rs;
+ s32 v;
+
+ rs = (((insn.mm_a_format.rs + 0x1e) & 0xf) + 2);
+ v = regs->cp0_epc & ~3;
+ v += insn.mm_a_format.simmediate << 2;
+ regs->regs[rs] = (long)v;
+ return -1;
+ }
+ }
+
pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
/*
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 5/7] MIPS: math-emu: dsemul: Correct description of the emulation frame
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Remove irrelevant content from the description of the emulation frame in
`mips_dsemul', referring to bare-metal configurations. Update the text,
reflecting the change made with commit ba3049ed4086 ("MIPS: Switch FPU
emulator trap to BREAK instruction."), where we switched from using an
address error exception on an unaligned access to the use of a BREAK 514
instruction causing a breakpoint exception instead.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
linux-mips-dsemul-comment.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:10.476810000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:44.420413000 +0000
@@ -78,13 +78,8 @@ int mips_dsemul(struct pt_regs *regs, mi
* Algorithmics used a system call instruction, and
* borrowed that vector. MIPS/Linux version is a bit
* more heavyweight in the interests of portability and
- * multiprocessor support. For Linux we generate a
- * an unaligned access and force an address error exception.
- *
- * For embedded systems (stand-alone) we prefer to use a
- * non-existing CP1 instruction. This prevents us from emulating
- * branches, but gives us a cleaner interface to the exception
- * handler (single entry point).
+ * multiprocessor support. For Linux we use a BREAK 514
+ * instruction causing a breakpoint exception.
*/
break_math = BREAK_MATH(get_isa16_mode(regs->cp0_epc));
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 5/7] MIPS: math-emu: dsemul: Correct description of the emulation frame
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Remove irrelevant content from the description of the emulation frame in
`mips_dsemul', referring to bare-metal configurations. Update the text,
reflecting the change made with commit ba3049ed4086 ("MIPS: Switch FPU
emulator trap to BREAK instruction."), where we switched from using an
address error exception on an unaligned access to the use of a BREAK 514
instruction causing a breakpoint exception instead.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
linux-mips-dsemul-comment.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:10.476810000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:10:44.420413000 +0000
@@ -78,13 +78,8 @@ int mips_dsemul(struct pt_regs *regs, mi
* Algorithmics used a system call instruction, and
* borrowed that vector. MIPS/Linux version is a bit
* more heavyweight in the interests of portability and
- * multiprocessor support. For Linux we generate a
- * an unaligned access and force an address error exception.
- *
- * For embedded systems (stand-alone) we prefer to use a
- * non-existing CP1 instruction. This prevents us from emulating
- * branches, but gives us a cleaner interface to the exception
- * handler (single entry point).
+ * multiprocessor support. For Linux we use a BREAK 514
+ * instruction causing a breakpoint exception.
*/
break_math = BREAK_MATH(get_isa16_mode(regs->cp0_epc));
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 6/7] MIPS: inst.h: Fix some instruction descriptions
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Fix the description of the microMIPS NOP16 encoding or MM_NOP16, which
is not equivalent to the MIPS16 NOP instruction. This is 0x0c00 and
represents the microMIPS `MOVE16 $0, $0' operation, whereas MIPS16 NOP
is encoded as 0x6500, representing `MOVE $0, $16'.
Also fix a typo in `mm_fp0_format' description.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
I don't think these changes are worth two separate patches. Please
apply.
Maciej
linux-mips-inst-comment-fix.diff
Index: linux-sfr-test/arch/mips/include/uapi/asm/inst.h
===================================================================
--- linux-sfr-test.orig/arch/mips/include/uapi/asm/inst.h 2016-01-20 21:52:36.000000000 +0000
+++ linux-sfr-test/arch/mips/include/uapi/asm/inst.h 2016-01-20 21:54:36.191277000 +0000
@@ -529,7 +529,7 @@ enum MIPS6e_i8_func {
};
/*
- * (microMIPS & MIPS16e) NOP instruction.
+ * (microMIPS) NOP instruction.
*/
#define MM_NOP16 0x0c00
@@ -679,7 +679,7 @@ struct fp0_format { /* FPU multiply and
;))))))
};
-struct mm_fp0_format { /* FPU multipy and add format (microMIPS) */
+struct mm_fp0_format { /* FPU multiply and add format (microMIPS) */
__BITFIELD_FIELD(unsigned int opcode : 6,
__BITFIELD_FIELD(unsigned int ft : 5,
__BITFIELD_FIELD(unsigned int fs : 5,
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 6/7] MIPS: inst.h: Fix some instruction descriptions
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Fix the description of the microMIPS NOP16 encoding or MM_NOP16, which
is not equivalent to the MIPS16 NOP instruction. This is 0x0c00 and
represents the microMIPS `MOVE16 $0, $0' operation, whereas MIPS16 NOP
is encoded as 0x6500, representing `MOVE $0, $16'.
Also fix a typo in `mm_fp0_format' description.
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
Ralf,
I don't think these changes are worth two separate patches. Please
apply.
Maciej
linux-mips-inst-comment-fix.diff
Index: linux-sfr-test/arch/mips/include/uapi/asm/inst.h
===================================================================
--- linux-sfr-test.orig/arch/mips/include/uapi/asm/inst.h 2016-01-20 21:52:36.000000000 +0000
+++ linux-sfr-test/arch/mips/include/uapi/asm/inst.h 2016-01-20 21:54:36.191277000 +0000
@@ -529,7 +529,7 @@ enum MIPS6e_i8_func {
};
/*
- * (microMIPS & MIPS16e) NOP instruction.
+ * (microMIPS) NOP instruction.
*/
#define MM_NOP16 0x0c00
@@ -679,7 +679,7 @@ struct fp0_format { /* FPU multiply and
;))))))
};
-struct mm_fp0_format { /* FPU multipy and add format (microMIPS) */
+struct mm_fp0_format { /* FPU multiply and add format (microMIPS) */
__BITFIELD_FIELD(unsigned int opcode : 6,
__BITFIELD_FIELD(unsigned int ft : 5,
__BITFIELD_FIELD(unsigned int fs : 5,
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 7/7] MIPS: math-emu: dsemul: Reduce `get_isa16_mode' clutter
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
linux-mips-dsemul-isa16.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:58:16.000000000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:59:52.315227000 +0000
@@ -38,6 +38,7 @@ struct emuframe {
*/
int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
{
+ int isa16 = get_isa16_mode(regs->cp0_epc);
mips_instruction break_math;
struct emuframe __user *fr;
int err;
@@ -47,7 +48,7 @@ int mips_dsemul(struct pt_regs *regs, mi
return -1;
/* microMIPS instructions */
- if (get_isa16_mode(regs->cp0_epc)) {
+ if (isa16) {
union mips_instruction insn = { .word = ir };
/* NOP16 aka MOVE16 $0, $0 */
@@ -81,7 +82,7 @@ int mips_dsemul(struct pt_regs *regs, mi
* multiprocessor support. For Linux we use a BREAK 514
* instruction causing a breakpoint exception.
*/
- break_math = BREAK_MATH(get_isa16_mode(regs->cp0_epc));
+ break_math = BREAK_MATH(isa16);
/* Ensure that the two instructions are in the same cache line */
fr = (struct emuframe __user *)
@@ -91,7 +92,7 @@ int mips_dsemul(struct pt_regs *regs, mi
if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe))))
return SIGBUS;
- if (get_isa16_mode(regs->cp0_epc)) {
+ if (isa16) {
err = __put_user(ir >> 16,
(u16 __user *)(&fr->emul));
err |= __put_user(ir & 0xffff,
@@ -113,8 +114,7 @@ int mips_dsemul(struct pt_regs *regs, mi
return SIGBUS;
}
- regs->cp0_epc = ((unsigned long) &fr->emul) |
- get_isa16_mode(regs->cp0_epc);
+ regs->cp0_epc = (unsigned long)&fr->emul | isa16;
flush_cache_sigtramp((unsigned long)&fr->emul);
@@ -123,6 +123,7 @@ int mips_dsemul(struct pt_regs *regs, mi
int do_dsemulret(struct pt_regs *xcp)
{
+ int isa16 = get_isa16_mode(xcp->cp0_epc);
struct emuframe __user *fr;
unsigned long epc;
u32 insn, cookie;
@@ -145,7 +146,7 @@ int do_dsemulret(struct pt_regs *xcp)
* - Is the instruction pointed to by the EPC an BREAK_MATH?
* - Is the following memory word the BD_COOKIE?
*/
- if (get_isa16_mode(xcp->cp0_epc)) {
+ if (isa16) {
err = __get_user(instr[0],
(u16 __user *)(&fr->badinst));
err |= __get_user(instr[1],
@@ -156,8 +157,8 @@ int do_dsemulret(struct pt_regs *xcp)
}
err |= __get_user(cookie, &fr->cookie);
- if (unlikely(err || insn != BREAK_MATH(get_isa16_mode(xcp->cp0_epc)) ||
- cookie != BD_COOKIE)) {
+ if (unlikely(err ||
+ insn != BREAK_MATH(isa16) || cookie != BD_COOKIE)) {
MIPS_FPU_EMU_INC_STATS(errors);
return 0;
}
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 7/7] MIPS: math-emu: dsemul: Reduce `get_isa16_mode' clutter
@ 2016-01-22 5:21 ` Maciej W. Rozycki
0 siblings, 0 replies; 17+ messages in thread
From: Maciej W. Rozycki @ 2016-01-22 5:21 UTC (permalink / raw)
To: Ralf Baechle; +Cc: Aurelien Jarno, linux-mips
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
---
linux-mips-dsemul-isa16.diff
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c 2016-01-22 01:58:16.000000000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c 2016-01-22 01:59:52.315227000 +0000
@@ -38,6 +38,7 @@ struct emuframe {
*/
int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
{
+ int isa16 = get_isa16_mode(regs->cp0_epc);
mips_instruction break_math;
struct emuframe __user *fr;
int err;
@@ -47,7 +48,7 @@ int mips_dsemul(struct pt_regs *regs, mi
return -1;
/* microMIPS instructions */
- if (get_isa16_mode(regs->cp0_epc)) {
+ if (isa16) {
union mips_instruction insn = { .word = ir };
/* NOP16 aka MOVE16 $0, $0 */
@@ -81,7 +82,7 @@ int mips_dsemul(struct pt_regs *regs, mi
* multiprocessor support. For Linux we use a BREAK 514
* instruction causing a breakpoint exception.
*/
- break_math = BREAK_MATH(get_isa16_mode(regs->cp0_epc));
+ break_math = BREAK_MATH(isa16);
/* Ensure that the two instructions are in the same cache line */
fr = (struct emuframe __user *)
@@ -91,7 +92,7 @@ int mips_dsemul(struct pt_regs *regs, mi
if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe))))
return SIGBUS;
- if (get_isa16_mode(regs->cp0_epc)) {
+ if (isa16) {
err = __put_user(ir >> 16,
(u16 __user *)(&fr->emul));
err |= __put_user(ir & 0xffff,
@@ -113,8 +114,7 @@ int mips_dsemul(struct pt_regs *regs, mi
return SIGBUS;
}
- regs->cp0_epc = ((unsigned long) &fr->emul) |
- get_isa16_mode(regs->cp0_epc);
+ regs->cp0_epc = (unsigned long)&fr->emul | isa16;
flush_cache_sigtramp((unsigned long)&fr->emul);
@@ -123,6 +123,7 @@ int mips_dsemul(struct pt_regs *regs, mi
int do_dsemulret(struct pt_regs *xcp)
{
+ int isa16 = get_isa16_mode(xcp->cp0_epc);
struct emuframe __user *fr;
unsigned long epc;
u32 insn, cookie;
@@ -145,7 +146,7 @@ int do_dsemulret(struct pt_regs *xcp)
* - Is the instruction pointed to by the EPC an BREAK_MATH?
* - Is the following memory word the BD_COOKIE?
*/
- if (get_isa16_mode(xcp->cp0_epc)) {
+ if (isa16) {
err = __get_user(instr[0],
(u16 __user *)(&fr->badinst));
err |= __get_user(instr[1],
@@ -156,8 +157,8 @@ int do_dsemulret(struct pt_regs *xcp)
}
err |= __get_user(cookie, &fr->cookie);
- if (unlikely(err || insn != BREAK_MATH(get_isa16_mode(xcp->cp0_epc)) ||
- cookie != BD_COOKIE)) {
+ if (unlikely(err ||
+ insn != BREAK_MATH(isa16) || cookie != BD_COOKIE)) {
MIPS_FPU_EMU_INC_STATS(errors);
return 0;
}
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/7] MIPS: math-emu: Branch delay slot emulation fixes
2016-01-22 5:20 ` Maciej W. Rozycki
` (7 preceding siblings ...)
(?)
@ 2016-01-23 12:39 ` Aurelien Jarno
-1 siblings, 0 replies; 17+ messages in thread
From: Aurelien Jarno @ 2016-01-23 12:39 UTC (permalink / raw)
To: Maciej W. Rozycki; +Cc: Ralf Baechle, linux-mips
Hi,
On 2016-01-22 05:20, Maciej W. Rozycki wrote:
> Hi,
>
> This small patch series addresses the following issues with branch delay
> slot emulation in our floating-point emulator:
>
> - NOP emulation sometimes causes SIGILL (Aurelien's bug),
>
> - microMIPS emulation always goes astray,
>
> - microMIPS emulation of ADDIUPC always returns the wrong result.
>
> Also included are a bunch of code clean-ups and comment fixes. See
> individual patch descriptions for further details.
>
> I attempted to move clean-ups to the end, so that they do not interfere
> with backporting, except with 2/7 which, if reordered, would require 3/7
> to become ill-formatted. I hope this is OK. Changes 5-7/7 do not require
> backporting.
>
> This series has been validated with a MIPS M5150 processor.
Thanks for working on that. I have tested this series, and I confirm
this fixes the issue and works well, though I haven't tested them on
a system with microMIPS support.
So for all the patches:
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
And for patch 1:
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Aurelien
--
Aurelien Jarno GPG: 4096R/1DDD8C9B
aurelien@aurel32.net http://www.aurel32.net
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2016-01-23 12:39 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-22 5:20 [PATCH 0/7] MIPS: math-emu: Branch delay slot emulation fixes Maciej W. Rozycki
2016-01-22 5:20 ` Maciej W. Rozycki
2016-01-22 5:20 ` [PATCH 1/7] MIPS: math-emu: Correctly handle NOP emulation Maciej W. Rozycki
2016-01-22 5:20 ` Maciej W. Rozycki
2016-01-22 5:20 ` [PATCH 2/7] MIPS: math-emu: dsemul: Fix ill formatting of microMIPS part Maciej W. Rozycki
2016-01-22 5:20 ` Maciej W. Rozycki
2016-01-22 5:20 ` [PATCH 3/7] MIPS: math-emu: Make microMIPS branch delay slot emulation work Maciej W. Rozycki
2016-01-22 5:20 ` Maciej W. Rozycki
2016-01-22 5:21 ` [PATCH 4/7] MIPS: math-emu: Correct the emulation of microMIPS ADDIUPC instruction Maciej W. Rozycki
2016-01-22 5:21 ` Maciej W. Rozycki
2016-01-22 5:21 ` [PATCH 5/7] MIPS: math-emu: dsemul: Correct description of the emulation frame Maciej W. Rozycki
2016-01-22 5:21 ` Maciej W. Rozycki
2016-01-22 5:21 ` [PATCH 6/7] MIPS: inst.h: Fix some instruction descriptions Maciej W. Rozycki
2016-01-22 5:21 ` Maciej W. Rozycki
2016-01-22 5:21 ` [PATCH 7/7] MIPS: math-emu: dsemul: Reduce `get_isa16_mode' clutter Maciej W. Rozycki
2016-01-22 5:21 ` Maciej W. Rozycki
2016-01-23 12:39 ` [PATCH 0/7] MIPS: math-emu: Branch delay slot emulation fixes Aurelien Jarno
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.