From 1975ed43ce67542befed188f6aed6a82cfaffd0b Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 7 Nov 2018 23:14:00 +0000 Subject: MIPS: Ensure emulated FP sets PF_USED_MATH 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 Patchwork: https://patchwork.linux-mips.org/patch/21003/ Cc: linux-mips@linux-mips.org --- 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 6092cdf7f8d9..cb22a558431e 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 @@ fpu_emul: regs->regs[31] = r31; regs->cp0_epc = epc; - if (!init_fp_ctx(current)) - lose_fpu(1); - err = fpu_emulator_cop1Handler(regs, ¤t->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, ¤t->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, ¤t->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, ¤t->thread.fpu, 1, &fault_addr); own_fpu(1); /* Restore FPU state. */ @@ -1733,7 +1732,6 @@ fpu_emul: BUG_ON(!used_math()); BUG_ON(!is_fpu_owner()); - lose_fpu(1); /* save the FPU state for the emulator */ res = fpu_emulator_cop1Handler(regs, ¤t->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; -- cgit v1.2.3-58-ga151