diff options
author | Andi Kleen <ak@suse.de> | 2005-04-16 18:25:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:25:03 -0400 |
commit | 6fefb0d17599d63e9d30c23bcbe2d4e06477cd55 (patch) | |
tree | d513ab7e0edfb2ee74adf15f2c7714ccdb179978 /arch/x86_64/kernel/traps.c | |
parent | 11b854b2f10f398f9a18c65e202853f929dd3185 (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.c | 57 |
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) | |||
488 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) | 488 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) |
489 | DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) | 489 | DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) |
490 | DO_ERROR(18, SIGSEGV, "reserved", reserved) | 490 | DO_ERROR(18, SIGSEGV, "reserved", reserved) |
491 | 491 | DO_ERROR(12, SIGBUS, "stack segment", stack_segment) | |
492 | #define DO_ERROR_STACK(trapnr, signr, str, name) \ | 492 | DO_ERROR( 8, SIGSEGV, "double fault", double_fault) |
493 | asmlinkage 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 | |||
507 | DO_ERROR_STACK(12, SIGBUS, "stack segment", stack_segment) | ||
508 | DO_ERROR_STACK( 8, SIGSEGV, "double fault", double_fault) | ||
509 | 493 | ||
510 | asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) | 494 | asmlinkage 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. */ | ||
587 | asmlinkage void default_do_nmi(struct pt_regs *regs) | 573 | asmlinkage 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 */ | ||
621 | asmlinkage 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. */ |
633 | asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code) | 640 | asmlinkage 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) | |||
712 | clear_dr7: | 712 | clear_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 | ||
717 | clear_TF_reenable: | 717 | clear_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 | ||
728 | static int kernel_math_error(struct pt_regs *regs, char *str) | 727 | static int kernel_math_error(struct pt_regs *regs, char *str) |