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:
parent
40d2e76315
commit
9b6dba9e07
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue