aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/traps.h2
-rw-r--r--arch/x86/kernel/irqinit.c2
-rw-r--r--arch/x86/kernel/traps.c100
3 files changed, 34 insertions, 70 deletions
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 4da91ad69e0d..f66cda56781d 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -79,7 +79,7 @@ static inline int get_si_code(unsigned long condition)
79 79
80extern int panic_on_unrecovered_nmi; 80extern int panic_on_unrecovered_nmi;
81 81
82void math_error(void __user *); 82void math_error(struct pt_regs *, int, int);
83void math_emulate(struct math_emu_info *); 83void math_emulate(struct math_emu_info *);
84#ifndef CONFIG_X86_32 84#ifndef CONFIG_X86_32
85asmlinkage void smp_thermal_interrupt(void); 85asmlinkage void smp_thermal_interrupt(void);
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index 0ed2d300cd46..990ae7cfc578 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -60,7 +60,7 @@ static irqreturn_t math_error_irq(int cpl, void *dev_id)
60 outb(0, 0xF0); 60 outb(0, 0xF0);
61 if (ignore_fpu_irq || !boot_cpu_data.hard_math) 61 if (ignore_fpu_irq || !boot_cpu_data.hard_math)
62 return IRQ_NONE; 62 return IRQ_NONE;
63 math_error((void __user *)get_irq_regs()->ip); 63 math_error(get_irq_regs(), 0, 16);
64 return IRQ_HANDLED; 64 return IRQ_HANDLED;
65} 65}
66 66
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index a16c9dfe6b70..a472992cecdc 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -595,36 +595,48 @@ static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr)
595 * the correct behaviour even in the presence of the asynchronous 595 * the correct behaviour even in the presence of the asynchronous
596 * IRQ13 behaviour 596 * IRQ13 behaviour
597 */ 597 */
598void math_error(void __user *ip) 598void math_error(struct pt_regs *regs, int error_code, int trapnr)
599{ 599{
600 struct task_struct *task; 600 struct task_struct *task;
601 siginfo_t info; 601 siginfo_t info;
602 unsigned short cwd, swd, err; 602 unsigned short err;
603 603
604 /* 604 /*
605 * Save the info for the exception handler and clear the error. 605 * Save the info for the exception handler and clear the error.
606 */ 606 */
607 task = current; 607 task = current;
608 save_init_fpu(task); 608 save_init_fpu(task);
609 task->thread.trap_no = 16; 609 task->thread.trap_no = trapnr;
610 task->thread.error_code = 0; 610 task->thread.error_code = error_code;
611 info.si_signo = SIGFPE; 611 info.si_signo = SIGFPE;
612 info.si_errno = 0; 612 info.si_errno = 0;
613 info.si_addr = ip; 613 info.si_addr = (void __user *)regs->ip;
614 /* 614 if (trapnr == 16) {
615 * (~cwd & swd) will mask out exceptions that are not set to unmasked 615 unsigned short cwd, swd;
616 * status. 0x3f is the exception bits in these regs, 0x200 is the 616 /*
617 * C1 reg you need in case of a stack fault, 0x040 is the stack 617 * (~cwd & swd) will mask out exceptions that are not set to unmasked
618 * fault bit. We should only be taking one exception at a time, 618 * status. 0x3f is the exception bits in these regs, 0x200 is the
619 * so if this combination doesn't produce any single exception, 619 * C1 reg you need in case of a stack fault, 0x040 is the stack
620 * then we have a bad program that isn't synchronizing its FPU usage 620 * fault bit. We should only be taking one exception at a time,
621 * and it will suffer the consequences since we won't be able to 621 * so if this combination doesn't produce any single exception,
622 * fully reproduce the context of the exception 622 * then we have a bad program that isn't synchronizing its FPU usage
623 */ 623 * and it will suffer the consequences since we won't be able to
624 cwd = get_fpu_cwd(task); 624 * fully reproduce the context of the exception
625 swd = get_fpu_swd(task); 625 */
626 cwd = get_fpu_cwd(task);
627 swd = get_fpu_swd(task);
626 628
627 err = swd & ~cwd; 629 err = swd & ~cwd;
630 } else {
631 /*
632 * The SIMD FPU exceptions are handled a little differently, as there
633 * is only a single status/control register. Thus, to determine which
634 * unmasked exception was caught we must mask the exception mask bits
635 * at 0x1f80, and then use these to mask the exception bits at 0x3f.
636 */
637 unsigned short mxcsr = get_fpu_mxcsr(task);
638 err = ~(mxcsr >> 7) & mxcsr;
639 }
628 640
629 if (err & 0x001) { /* Invalid op */ 641 if (err & 0x001) { /* Invalid op */
630 /* 642 /*
@@ -663,55 +675,7 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
663 return; 675 return;
664#endif 676#endif
665 677
666 math_error((void __user *)regs->ip); 678 math_error(regs, error_code, 16);
667}
668
669static void simd_math_error(void __user *ip)
670{
671 struct task_struct *task;
672 siginfo_t info;
673 unsigned short mxcsr;
674
675 /*
676 * Save the info for the exception handler and clear the error.
677 */
678 task = current;
679 save_init_fpu(task);
680 task->thread.trap_no = 19;
681 task->thread.error_code = 0;
682 info.si_signo = SIGFPE;
683 info.si_errno = 0;
684 info.si_code = __SI_FAULT;
685 info.si_addr = ip;
686 /*
687 * The SIMD FPU exceptions are handled a little differently, as there
688 * is only a single status/control register. Thus, to determine which
689 * unmasked exception was caught we must mask the exception mask bits
690 * at 0x1f80, and then use these to mask the exception bits at 0x3f.
691 */
692 mxcsr = get_fpu_mxcsr(task);
693 switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) {
694 case 0x000:
695 default:
696 break;
697 case 0x001: /* Invalid Op */
698 info.si_code = FPE_FLTINV;
699 break;
700 case 0x002: /* Denormalize */
701 case 0x010: /* Underflow */
702 info.si_code = FPE_FLTUND;
703 break;
704 case 0x004: /* Zero Divide */
705 info.si_code = FPE_FLTDIV;
706 break;
707 case 0x008: /* Overflow */
708 info.si_code = FPE_FLTOVF;
709 break;
710 case 0x020: /* Precision */
711 info.si_code = FPE_FLTRES;
712 break;
713 }
714 force_sig_info(SIGFPE, &info, task);
715} 679}
716 680
717dotraplinkage void 681dotraplinkage void
@@ -727,7 +691,7 @@ do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
727 return; 691 return;
728#endif 692#endif
729 693
730 simd_math_error((void __user *)regs->ip); 694 math_error(regs, error_code, 19);
731} 695}
732 696
733dotraplinkage void 697dotraplinkage void