MIPS: Ensure FCSR cause bits are clear after invoking FPU emulator

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>
This commit is contained in:
Paul Burton 2015-01-30 12:09:35 +00:00 коммит произвёл Ralf Baechle
Родитель 091be550a7
Коммит ad70c13a93
1 изменённых файлов: 9 добавлений и 8 удалений

Просмотреть файл

@ -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;
@ -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;