[PATCH 04/20] MIPS: Ensure emulated FP sets PF_USED_MATH

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

 



Emulated floating point instructions don't ensure that the PF_USED_MATH
flag is set for the task. This results in a couple of inconsistencies:

  - ptrace will return the default initial state of FP registers rather
    than the values actually stored in struct thread_struct, hiding
    state that has been updated by emulated floating point instructions.

  - If a task migrates to a CPU with an FPU after having emulated
    floating point instructions then its floating point register state
    will be reset to the default ~0 bit pattern, losing state from the
    emulated instructions.

Fix this by calling init_fp_ctx() from fpu_emulator_cop1Handler() to
consistently initialize FP state if it was previously uninitialized,
setting the PF_USED_MATH flag in the process.

All callers of fpu_emulator_cop1Handler() either call lose_fpu(1) before
it in order to save any live FPU registers to struct thread_struct, or
in the case of do_cpu() already know that the task does not own an FPU
so lose_fpu(1) would be a no-op. Since we know that saving FP context
will be unnecessary in the case where FP context was just initialized we
move this call into fpu_emulator_cop1Handler() too, providing
consistency & avoiding needless duplication.

Calls to own_fpu(1) are common after return from
fpu_emulator_cop1Handler() too, but this would not be a no-op in the
do_cpu() case so these are left as-is. A potential future improvement
could be to have fpu_emulator_cop1Handler() restore FPU state
automatically only if it saved it, though this may not be optimal if
some callers are better off without their current calls to own_fpu(1).
One potential example of this could be mipsr2_decoder() which as-is
could end up saving & restoring FP context repeatedly & unnecessarily if
emulating multiple FP instructions.

Signed-off-by: Paul Burton <paul.burton@xxxxxxxx>
---

 arch/mips/kernel/mips-r2-to-r6-emul.c | 3 ---
 arch/mips/kernel/traps.c              | 5 -----
 arch/mips/kernel/unaligned.c          | 2 --
 arch/mips/math-emu/cp1emu.c           | 7 +++++++
 4 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index 9c1690235896..1468d1dfb793 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -1175,9 +1175,6 @@ int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31)
 		regs->regs[31] = r31;
 		regs->cp0_epc = epc;
 
-		if (!init_fp_ctx(current))
-			lose_fpu(1);
-
 		err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
 					       &fault_addr);
 
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 650b9dd05b8e..db52d30eacec 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -794,9 +794,6 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
 	regs->cp0_epc = old_epc;
 	regs->regs[31] = old_ra;
 
-	/* Save the FP context to struct thread_struct */
-	lose_fpu(1);
-
 	/* Run the emulator */
 	sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
 				       &fault_addr);
@@ -848,8 +845,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
 		 * register operands before invoking the emulator, which seems
 		 * a bit extreme for what should be an infrequent event.
 		 */
-		/* Ensure 'resume' not overwrite saved fp context again. */
-		lose_fpu(1);
 
 		/* Run the emulator */
 		sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index ce446eed62d2..4c7de9f6373d 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -1220,7 +1220,6 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		die_if_kernel("Unaligned FP access in kernel code", regs);
 		BUG_ON(!used_math());
 
-		lose_fpu(1);	/* Save FPU state for the emulator. */
 		res = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
 					       &fault_addr);
 		own_fpu(1);	/* Restore FPU state. */
@@ -1733,7 +1732,6 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
 		BUG_ON(!used_math());
 		BUG_ON(!is_fpu_owner());
 
-		lose_fpu(1);	/* save the FPU state for the emulator */
 		res = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
 					       &fault_addr);
 		own_fpu(1);	/* restore FPU state */
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 62deb025970b..82e2993c1a2c 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -2831,6 +2831,13 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 	u16 *instr_ptr;
 	int sig = 0;
 
+	/*
+	 * Initialize context if it hasn't been used already, otherwise ensure
+	 * it has been saved to struct thread_struct.
+	 */
+	if (!init_fp_ctx(current))
+		lose_fpu(1);
+
 	oldepc = xcp->cp0_epc;
 	do {
 		prevepc = xcp->cp0_epc;
-- 
2.19.1



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

  Powered by Linux