Skip to content

Commit

Permalink
MIPS: Ensure FCSR cause bits are clear after invoking FPU emulator
Browse files Browse the repository at this point in the history
When running the emulator to handle an instruction that raised an FP
unimplemented operation exception, the FCSR cause bits were being
cleared. This is done to ensure that the kernel does not take an FP
exception when later restoring FP context to registers. However, this
was not being done when the emulator is invoked in response to a
coprocessor unusable exception. This happens in 2 cases:

  - There is no FPU present in the system. In this case things were
    OK, since the FP context is never restored to hardware registers
    and thus no FP exception may be raised when restoring FCSR.

  - The FPU could not be configured to the mode required by the task.
    In this case it would be possible for the emulator to set cause
    bits which are later restored to hardware if the task migrates
    to a CPU whose associated FPU does support its mode requirements,
    or if the tasks FP mode requirements change.

Consistently clear the cause bits after invoking the emulator, by moving
the clearing to process_fpemu_return and ensuring this is always called
before the tasks FP context is restored. This will make it easier to
catch further paths invoking the emulator in future, as will be
introduced in further patches.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9165/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Paul Burton authored and Ralf Baechle committed Mar 27, 2015
1 parent 091be55 commit ad70c13
Showing 1 changed file with 9 additions and 8 deletions.
17 changes: 9 additions & 8 deletions arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,13 @@ asmlinkage void do_ov(struct pt_regs *regs)

int process_fpemu_return(int sig, void __user *fault_addr)
{
/*
* We can't allow the emulated instruction to leave any of the cause
* bits set in FCSR. If they were then the kernel would take an FP
* exception when restoring FP context.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;

if (sig == SIGSEGV || sig == SIGBUS) {
struct siginfo si = {0};
si.si_addr = fault_addr;
Expand Down Expand Up @@ -804,18 +811,12 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
&fault_addr);

/*
* We can't allow the emulated instruction to leave any of
* the cause bit set in $fcr31.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
/* If something went wrong, signal */
process_fpemu_return(sig, fault_addr);

/* Restore the hardware register state */
own_fpu(1); /* Using the FPU again. */

/* If something went wrong, signal */
process_fpemu_return(sig, fault_addr);

goto out;
} else if (fcr31 & FPU_CSR_INV_X)
info.si_code = FPE_FLTINV;
Expand Down

0 comments on commit ad70c13

Please sign in to comment.