diff options
-rw-r--r-- | arch/x86/include/asm/traps.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/irqinit.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 100 |
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 | ||
80 | extern int panic_on_unrecovered_nmi; | 80 | extern int panic_on_unrecovered_nmi; |
81 | 81 | ||
82 | void math_error(void __user *); | 82 | void math_error(struct pt_regs *, int, int); |
83 | void math_emulate(struct math_emu_info *); | 83 | void math_emulate(struct math_emu_info *); |
84 | #ifndef CONFIG_X86_32 | 84 | #ifndef CONFIG_X86_32 |
85 | asmlinkage void smp_thermal_interrupt(void); | 85 | asmlinkage 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 | */ |
598 | void math_error(void __user *ip) | 598 | void 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 | |||
669 | static 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 | ||
717 | dotraplinkage void | 681 | dotraplinkage 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 | ||
733 | dotraplinkage void | 697 | dotraplinkage void |