x86: Merge simd_math_error() into math_error()

The only difference between FPU and SIMD exceptions is where the
status bits are read from (cwd/swd vs. mxcsr).  This also fixes
the discrepency introduced by commit adf77bac, which fixed FPU
but not SIMD.

Signed-off-by: Brian Gerst <brgerst@gmail.com>
LKML-Reference: <1269176446-2489-3-git-send-email-brgerst@gmail.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
Brian Gerst 2010-03-21 09:00:44 -04:00 committed by H. Peter Anvin
parent 40d2e76315
commit 9b6dba9e07
3 changed files with 34 additions and 70 deletions

View File

@ -79,7 +79,7 @@ static inline int get_si_code(unsigned long condition)
extern int panic_on_unrecovered_nmi; extern int panic_on_unrecovered_nmi;
void math_error(void __user *); void math_error(struct pt_regs *, int, int);
void math_emulate(struct math_emu_info *); void math_emulate(struct math_emu_info *);
#ifndef CONFIG_X86_32 #ifndef CONFIG_X86_32
asmlinkage void smp_thermal_interrupt(void); asmlinkage void smp_thermal_interrupt(void);

View File

@ -60,7 +60,7 @@ static irqreturn_t math_error_irq(int cpl, void *dev_id)
outb(0, 0xF0); outb(0, 0xF0);
if (ignore_fpu_irq || !boot_cpu_data.hard_math) if (ignore_fpu_irq || !boot_cpu_data.hard_math)
return IRQ_NONE; return IRQ_NONE;
math_error((void __user *)get_irq_regs()->ip); math_error(get_irq_regs(), 0, 16);
return IRQ_HANDLED; return IRQ_HANDLED;
} }

View File

@ -595,36 +595,48 @@ static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr)
* the correct behaviour even in the presence of the asynchronous * the correct behaviour even in the presence of the asynchronous
* IRQ13 behaviour * IRQ13 behaviour
*/ */
void math_error(void __user *ip) void math_error(struct pt_regs *regs, int error_code, int trapnr)
{ {
struct task_struct *task; struct task_struct *task;
siginfo_t info; siginfo_t info;
unsigned short cwd, swd, err; unsigned short err;
/* /*
* Save the info for the exception handler and clear the error. * Save the info for the exception handler and clear the error.
*/ */
task = current; task = current;
save_init_fpu(task); save_init_fpu(task);
task->thread.trap_no = 16; task->thread.trap_no = trapnr;
task->thread.error_code = 0; task->thread.error_code = error_code;
info.si_signo = SIGFPE; info.si_signo = SIGFPE;
info.si_errno = 0; info.si_errno = 0;
info.si_addr = ip; info.si_addr = (void __user *)regs->ip;
/* if (trapnr == 16) {
* (~cwd & swd) will mask out exceptions that are not set to unmasked unsigned short cwd, swd;
* 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 * (~cwd & swd) will mask out exceptions that are not set to unmasked
* fault bit. We should only be taking one exception at a time, * status. 0x3f is the exception bits in these regs, 0x200 is the
* so if this combination doesn't produce any single exception, * C1 reg you need in case of a stack fault, 0x040 is the stack
* then we have a bad program that isn't synchronizing its FPU usage * fault bit. We should only be taking one exception at a time,
* and it will suffer the consequences since we won't be able to * so if this combination doesn't produce any single exception,
* fully reproduce the context of the 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
cwd = get_fpu_cwd(task); * fully reproduce the context of the exception
swd = get_fpu_swd(task); */
cwd = get_fpu_cwd(task);
swd = get_fpu_swd(task);
err = swd & ~cwd; 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;
}
if (err & 0x001) { /* Invalid op */ if (err & 0x001) { /* Invalid op */
/* /*
@ -663,55 +675,7 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
return; return;
#endif #endif
math_error((void __user *)regs->ip); math_error(regs, error_code, 16);
}
static void simd_math_error(void __user *ip)
{
struct task_struct *task;
siginfo_t info;
unsigned short mxcsr;
/*
* Save the info for the exception handler and clear the error.
*/
task = current;
save_init_fpu(task);
task->thread.trap_no = 19;
task->thread.error_code = 0;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = __SI_FAULT;
info.si_addr = ip;
/*
* 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.
*/
mxcsr = get_fpu_mxcsr(task);
switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) {
case 0x000:
default:
break;
case 0x001: /* Invalid Op */
info.si_code = FPE_FLTINV;
break;
case 0x002: /* Denormalize */
case 0x010: /* Underflow */
info.si_code = FPE_FLTUND;
break;
case 0x004: /* Zero Divide */
info.si_code = FPE_FLTDIV;
break;
case 0x008: /* Overflow */
info.si_code = FPE_FLTOVF;
break;
case 0x020: /* Precision */
info.si_code = FPE_FLTRES;
break;
}
force_sig_info(SIGFPE, &info, task);
} }
dotraplinkage void dotraplinkage void
@ -727,7 +691,7 @@ do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
return; return;
#endif #endif
simd_math_error((void __user *)regs->ip); math_error(regs, error_code, 19);
} }
dotraplinkage void dotraplinkage void