|
@@ -708,8 +708,8 @@ NOKPROBE_SYMBOL(do_debug);
|
|
static void math_error(struct pt_regs *regs, int error_code, int trapnr)
|
|
static void math_error(struct pt_regs *regs, int error_code, int trapnr)
|
|
{
|
|
{
|
|
struct task_struct *task = current;
|
|
struct task_struct *task = current;
|
|
|
|
+ struct fpu *fpu = &task->thread.fpu;
|
|
siginfo_t info;
|
|
siginfo_t info;
|
|
- unsigned short err;
|
|
|
|
char *str = (trapnr == X86_TRAP_MF) ? "fpu exception" :
|
|
char *str = (trapnr == X86_TRAP_MF) ? "fpu exception" :
|
|
"simd exception";
|
|
"simd exception";
|
|
|
|
|
|
@@ -717,8 +717,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
|
|
return;
|
|
return;
|
|
conditional_sti(regs);
|
|
conditional_sti(regs);
|
|
|
|
|
|
- if (!user_mode(regs))
|
|
|
|
- {
|
|
|
|
|
|
+ if (!user_mode(regs)) {
|
|
if (!fixup_exception(regs)) {
|
|
if (!fixup_exception(regs)) {
|
|
task->thread.error_code = error_code;
|
|
task->thread.error_code = error_code;
|
|
task->thread.trap_nr = trapnr;
|
|
task->thread.trap_nr = trapnr;
|
|
@@ -730,62 +729,20 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
|
|
/*
|
|
/*
|
|
* Save the info for the exception handler and clear the error.
|
|
* Save the info for the exception handler and clear the error.
|
|
*/
|
|
*/
|
|
- fpu__save(&task->thread.fpu);
|
|
|
|
- task->thread.trap_nr = trapnr;
|
|
|
|
|
|
+ fpu__save(fpu);
|
|
|
|
+
|
|
|
|
+ task->thread.trap_nr = trapnr;
|
|
task->thread.error_code = error_code;
|
|
task->thread.error_code = error_code;
|
|
- info.si_signo = SIGFPE;
|
|
|
|
- info.si_errno = 0;
|
|
|
|
- info.si_addr = (void __user *)uprobe_get_trap_addr(regs);
|
|
|
|
- if (trapnr == X86_TRAP_MF) {
|
|
|
|
- unsigned short cwd, swd;
|
|
|
|
- /*
|
|
|
|
- * (~cwd & swd) will mask out exceptions that are not set to unmasked
|
|
|
|
- * status. 0x3f is the exception bits in these regs, 0x200 is the
|
|
|
|
- * C1 reg you need in case of a stack fault, 0x040 is the stack
|
|
|
|
- * fault bit. We should only be taking one exception at a time,
|
|
|
|
- * so if this combination doesn't produce any single exception,
|
|
|
|
- * then we have a bad program that isn't synchronizing its FPU usage
|
|
|
|
- * and it will suffer the consequences since we won't be able to
|
|
|
|
- * fully reproduce the context of the exception
|
|
|
|
- */
|
|
|
|
- cwd = get_fpu_cwd(task);
|
|
|
|
- swd = get_fpu_swd(task);
|
|
|
|
|
|
+ info.si_signo = SIGFPE;
|
|
|
|
+ info.si_errno = 0;
|
|
|
|
+ info.si_addr = (void __user *)uprobe_get_trap_addr(regs);
|
|
|
|
|
|
- err = swd & ~cwd;
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * The SIMD FPU exceptions are handled a little differently, as there
|
|
|
|
- * is only a single status/control register. Thus, to determine which
|
|
|
|
- * unmasked exception was caught we must mask the exception mask bits
|
|
|
|
- * at 0x1f80, and then use these to mask the exception bits at 0x3f.
|
|
|
|
- */
|
|
|
|
- unsigned short mxcsr = get_fpu_mxcsr(task);
|
|
|
|
- err = ~(mxcsr >> 7) & mxcsr;
|
|
|
|
- }
|
|
|
|
|
|
+ info.si_code = fpu__exception_code(fpu, trapnr);
|
|
|
|
|
|
- if (err & 0x001) { /* Invalid op */
|
|
|
|
- /*
|
|
|
|
- * swd & 0x240 == 0x040: Stack Underflow
|
|
|
|
- * swd & 0x240 == 0x240: Stack Overflow
|
|
|
|
- * User must clear the SF bit (0x40) if set
|
|
|
|
- */
|
|
|
|
- info.si_code = FPE_FLTINV;
|
|
|
|
- } else if (err & 0x004) { /* Divide by Zero */
|
|
|
|
- info.si_code = FPE_FLTDIV;
|
|
|
|
- } else if (err & 0x008) { /* Overflow */
|
|
|
|
- info.si_code = FPE_FLTOVF;
|
|
|
|
- } else if (err & 0x012) { /* Denormal, Underflow */
|
|
|
|
- info.si_code = FPE_FLTUND;
|
|
|
|
- } else if (err & 0x020) { /* Precision */
|
|
|
|
- info.si_code = FPE_FLTRES;
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * If we're using IRQ 13, or supposedly even some trap
|
|
|
|
- * X86_TRAP_MF implementations, it's possible
|
|
|
|
- * we get a spurious trap, which is not an error.
|
|
|
|
- */
|
|
|
|
|
|
+ /* Retry when we get spurious exceptions: */
|
|
|
|
+ if (!info.si_code)
|
|
return;
|
|
return;
|
|
- }
|
|
|
|
|
|
+
|
|
force_sig_info(SIGFPE, &info, task);
|
|
force_sig_info(SIGFPE, &info, task);
|
|
}
|
|
}
|
|
|
|
|