aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel/traps.c
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2005-04-16 18:25:03 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:25:03 -0400
commit6fefb0d17599d63e9d30c23bcbe2d4e06477cd55 (patch)
treed513ab7e0edfb2ee74adf15f2c7714ccdb179978 /arch/x86_64/kernel/traps.c
parent11b854b2f10f398f9a18c65e202853f929dd3185 (diff)
[PATCH] x86_64: Regularize exception stack handling
This fixes various issues in the return path for "paranoid" handlers (= running on a private exception stack that act like NMIs). Generalize previous hack to switch back to process stack for scheduling/signal handling purposes. Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r--arch/x86_64/kernel/traps.c57
1 files changed, 28 insertions, 29 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
index 09e4a6460e74..1b06ab24538b 100644
--- a/arch/x86_64/kernel/traps.c
+++ b/arch/x86_64/kernel/traps.c
@@ -488,24 +488,8 @@ DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
488DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) 488DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
489DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) 489DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
490DO_ERROR(18, SIGSEGV, "reserved", reserved) 490DO_ERROR(18, SIGSEGV, "reserved", reserved)
491 491DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
492#define DO_ERROR_STACK(trapnr, signr, str, name) \ 492DO_ERROR( 8, SIGSEGV, "double fault", double_fault)
493asmlinkage void *do_##name(struct pt_regs * regs, long error_code) \
494{ \
495 struct pt_regs *pr = ((struct pt_regs *)(current->thread.rsp0))-1; \
496 if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
497 == NOTIFY_STOP) \
498 return regs; \
499 if (regs->cs & 3) { \
500 memcpy(pr, regs, sizeof(struct pt_regs)); \
501 regs = pr; \
502 } \
503 do_trap(trapnr, signr, str, regs, error_code, NULL); \
504 return regs; \
505}
506
507DO_ERROR_STACK(12, SIGBUS, "stack segment", stack_segment)
508DO_ERROR_STACK( 8, SIGSEGV, "double fault", double_fault)
509 493
510asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) 494asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
511{ 495{
@@ -584,6 +568,8 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
584 printk("Do you have a strange power saving mode enabled?\n"); 568 printk("Do you have a strange power saving mode enabled?\n");
585} 569}
586 570
571/* Runs on IST stack. This code must keep interrupts off all the time.
572 Nested NMIs are prevented by the CPU. */
587asmlinkage void default_do_nmi(struct pt_regs *regs) 573asmlinkage void default_do_nmi(struct pt_regs *regs)
588{ 574{
589 unsigned char reason = 0; 575 unsigned char reason = 0;
@@ -629,20 +615,34 @@ asmlinkage void do_int3(struct pt_regs * regs, long error_code)
629 return; 615 return;
630} 616}
631 617
618/* Help handler running on IST stack to switch back to user stack
619 for scheduling or signal handling. The actual stack switch is done in
620 entry.S */
621asmlinkage struct pt_regs *sync_regs(struct pt_regs *eregs)
622{
623 struct pt_regs *regs = eregs;
624 /* Did already sync */
625 if (eregs == (struct pt_regs *)eregs->rsp)
626 ;
627 /* Exception from user space */
628 else if (eregs->cs & 3)
629 regs = ((struct pt_regs *)current->thread.rsp0) - 1;
630 /* Exception from kernel and interrupts are enabled. Move to
631 kernel process stack. */
632 else if (eregs->eflags & X86_EFLAGS_IF)
633 regs = (struct pt_regs *)(eregs->rsp -= sizeof(struct pt_regs));
634 if (eregs != regs)
635 *regs = *eregs;
636 return regs;
637}
638
632/* runs on IST stack. */ 639/* runs on IST stack. */
633asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code) 640asmlinkage void do_debug(struct pt_regs * regs, unsigned long error_code)
634{ 641{
635 struct pt_regs *pr;
636 unsigned long condition; 642 unsigned long condition;
637 struct task_struct *tsk = current; 643 struct task_struct *tsk = current;
638 siginfo_t info; 644 siginfo_t info;
639 645
640 pr = (struct pt_regs *)(current->thread.rsp0)-1;
641 if (regs->cs & 3) {
642 memcpy(pr, regs, sizeof(struct pt_regs));
643 regs = pr;
644 }
645
646#ifdef CONFIG_CHECKING 646#ifdef CONFIG_CHECKING
647 { 647 {
648 /* RED-PEN interaction with debugger - could destroy gs */ 648 /* RED-PEN interaction with debugger - could destroy gs */
@@ -660,7 +660,7 @@ asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code)
660 660
661 if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, 661 if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
662 SIGTRAP) == NOTIFY_STOP) { 662 SIGTRAP) == NOTIFY_STOP) {
663 return regs; 663 return;
664 } 664 }
665 conditional_sti(regs); 665 conditional_sti(regs);
666 666
@@ -712,7 +712,7 @@ asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code)
712clear_dr7: 712clear_dr7:
713 asm volatile("movq %0,%%db7"::"r"(0UL)); 713 asm volatile("movq %0,%%db7"::"r"(0UL));
714 notify_die(DIE_DEBUG, "debug", regs, condition, 1, SIGTRAP); 714 notify_die(DIE_DEBUG, "debug", regs, condition, 1, SIGTRAP);
715 return regs; 715 return;
716 716
717clear_TF_reenable: 717clear_TF_reenable:
718 set_tsk_thread_flag(tsk, TIF_SINGLESTEP); 718 set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
@@ -722,7 +722,6 @@ clear_TF:
722 if (notify_die(DIE_DEBUG, "debug2", regs, condition, 1, SIGTRAP) 722 if (notify_die(DIE_DEBUG, "debug2", regs, condition, 1, SIGTRAP)
723 != NOTIFY_STOP) 723 != NOTIFY_STOP)
724 regs->eflags &= ~TF_MASK; 724 regs->eflags &= ~TF_MASK;
725 return regs;
726} 725}
727 726
728static int kernel_math_error(struct pt_regs *regs, char *str) 727static int kernel_math_error(struct pt_regs *regs, char *str)