mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-01 16:14:13 +08:00
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 <paul.burton@mips.com> Patchwork: https://patchwork.linux-mips.org/patch/21003/ Cc: linux-mips@linux-mips.org
This commit is contained in:
parent
cc97ab235f
commit
1975ed43ce
@ -1175,9 +1175,6 @@ fpu_emul:
|
|||||||
regs->regs[31] = r31;
|
regs->regs[31] = r31;
|
||||||
regs->cp0_epc = epc;
|
regs->cp0_epc = epc;
|
||||||
|
|
||||||
if (!init_fp_ctx(current))
|
|
||||||
lose_fpu(1);
|
|
||||||
|
|
||||||
err = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0,
|
err = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0,
|
||||||
&fault_addr);
|
&fault_addr);
|
||||||
|
|
||||||
|
@ -794,9 +794,6 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
|
|||||||
regs->cp0_epc = old_epc;
|
regs->cp0_epc = old_epc;
|
||||||
regs->regs[31] = old_ra;
|
regs->regs[31] = old_ra;
|
||||||
|
|
||||||
/* Save the FP context to struct thread_struct */
|
|
||||||
lose_fpu(1);
|
|
||||||
|
|
||||||
/* Run the emulator */
|
/* Run the emulator */
|
||||||
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
||||||
&fault_addr);
|
&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
|
* register operands before invoking the emulator, which seems
|
||||||
* a bit extreme for what should be an infrequent event.
|
* a bit extreme for what should be an infrequent event.
|
||||||
*/
|
*/
|
||||||
/* Ensure 'resume' not overwrite saved fp context again. */
|
|
||||||
lose_fpu(1);
|
|
||||||
|
|
||||||
/* Run the emulator */
|
/* Run the emulator */
|
||||||
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
||||||
|
@ -1220,7 +1220,6 @@ static void emulate_load_store_insn(struct pt_regs *regs,
|
|||||||
die_if_kernel("Unaligned FP access in kernel code", regs);
|
die_if_kernel("Unaligned FP access in kernel code", regs);
|
||||||
BUG_ON(!used_math());
|
BUG_ON(!used_math());
|
||||||
|
|
||||||
lose_fpu(1); /* Save FPU state for the emulator. */
|
|
||||||
res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
||||||
&fault_addr);
|
&fault_addr);
|
||||||
own_fpu(1); /* Restore FPU state. */
|
own_fpu(1); /* Restore FPU state. */
|
||||||
@ -1733,7 +1732,6 @@ fpu_emul:
|
|||||||
BUG_ON(!used_math());
|
BUG_ON(!used_math());
|
||||||
BUG_ON(!is_fpu_owner());
|
BUG_ON(!is_fpu_owner());
|
||||||
|
|
||||||
lose_fpu(1); /* save the FPU state for the emulator */
|
|
||||||
res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
||||||
&fault_addr);
|
&fault_addr);
|
||||||
own_fpu(1); /* restore FPU state */
|
own_fpu(1); /* restore FPU state */
|
||||||
|
@ -2831,6 +2831,13 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||||||
u16 *instr_ptr;
|
u16 *instr_ptr;
|
||||||
int sig = 0;
|
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;
|
oldepc = xcp->cp0_epc;
|
||||||
do {
|
do {
|
||||||
prevepc = xcp->cp0_epc;
|
prevepc = xcp->cp0_epc;
|
||||||
|
Loading…
Reference in New Issue
Block a user