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 | |
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')
-rw-r--r-- | arch/x86_64/kernel/entry.S | 49 | ||||
-rw-r--r-- | arch/x86_64/kernel/traps.c | 57 |
2 files changed, 55 insertions, 51 deletions
diff --git a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S index 12a307100929..3e888c2e5a06 100644 --- a/arch/x86_64/kernel/entry.S +++ b/arch/x86_64/kernel/entry.S | |||
@@ -579,6 +579,7 @@ ENTRY(spurious_interrupt) | |||
579 | movq ORIG_RAX(%rsp),%rsi | 579 | movq ORIG_RAX(%rsp),%rsi |
580 | movq $-1,ORIG_RAX(%rsp) | 580 | movq $-1,ORIG_RAX(%rsp) |
581 | call \sym | 581 | call \sym |
582 | cli | ||
582 | .endm | 583 | .endm |
583 | 584 | ||
584 | /* | 585 | /* |
@@ -794,10 +795,6 @@ ENTRY(debug) | |||
794 | pushq $0 | 795 | pushq $0 |
795 | CFI_ADJUST_CFA_OFFSET 8 | 796 | CFI_ADJUST_CFA_OFFSET 8 |
796 | paranoidentry do_debug | 797 | paranoidentry do_debug |
797 | /* switch back to process stack to restore the state ptrace touched */ | ||
798 | movq %rax,%rsp | ||
799 | testl $3,CS(%rsp) | ||
800 | jnz paranoid_userspace | ||
801 | jmp paranoid_exit | 798 | jmp paranoid_exit |
802 | CFI_ENDPROC | 799 | CFI_ENDPROC |
803 | 800 | ||
@@ -807,35 +804,49 @@ ENTRY(nmi) | |||
807 | pushq $-1 | 804 | pushq $-1 |
808 | CFI_ADJUST_CFA_OFFSET 8 | 805 | CFI_ADJUST_CFA_OFFSET 8 |
809 | paranoidentry do_nmi | 806 | paranoidentry do_nmi |
807 | /* | ||
808 | * "Paranoid" exit path from exception stack. | ||
809 | * Paranoid because this is used by NMIs and cannot take | ||
810 | * any kernel state for granted. | ||
811 | * We don't do kernel preemption checks here, because only | ||
812 | * NMI should be common and it does not enable IRQs and | ||
813 | * cannot get reschedule ticks. | ||
814 | */ | ||
810 | /* ebx: no swapgs flag */ | 815 | /* ebx: no swapgs flag */ |
811 | paranoid_exit: | 816 | paranoid_exit: |
812 | testl %ebx,%ebx /* swapgs needed? */ | 817 | testl %ebx,%ebx /* swapgs needed? */ |
813 | jnz paranoid_restore | 818 | jnz paranoid_restore |
819 | testl $3,CS(%rsp) | ||
820 | jnz paranoid_userspace | ||
814 | paranoid_swapgs: | 821 | paranoid_swapgs: |
815 | cli | ||
816 | swapgs | 822 | swapgs |
817 | paranoid_restore: | 823 | paranoid_restore: |
818 | RESTORE_ALL 8 | 824 | RESTORE_ALL 8 |
819 | iretq | 825 | iretq |
820 | paranoid_userspace: | 826 | paranoid_userspace: |
821 | cli | ||
822 | GET_THREAD_INFO(%rcx) | 827 | GET_THREAD_INFO(%rcx) |
823 | movl threadinfo_flags(%rcx),%edx | 828 | movl threadinfo_flags(%rcx),%ebx |
824 | testl $_TIF_WORK_MASK,%edx | 829 | andl $_TIF_WORK_MASK,%ebx |
825 | jz paranoid_swapgs | 830 | jz paranoid_swapgs |
826 | testl $_TIF_NEED_RESCHED,%edx | 831 | movq %rsp,%rdi /* &pt_regs */ |
827 | jnz paranoid_resched | 832 | call sync_regs |
833 | movq %rax,%rsp /* switch stack for scheduling */ | ||
834 | testl $_TIF_NEED_RESCHED,%ebx | ||
835 | jnz paranoid_schedule | ||
836 | movl %ebx,%edx /* arg3: thread flags */ | ||
828 | sti | 837 | sti |
829 | xorl %esi,%esi /* oldset */ | 838 | xorl %esi,%esi /* arg2: oldset */ |
830 | movq %rsp,%rdi /* &pt_regs */ | 839 | movq %rsp,%rdi /* arg1: &pt_regs */ |
831 | call do_notify_resume | 840 | call do_notify_resume |
832 | jmp paranoid_exit | 841 | cli |
833 | paranoid_resched: | 842 | jmp paranoid_userspace |
843 | paranoid_schedule: | ||
834 | sti | 844 | sti |
835 | call schedule | 845 | call schedule |
836 | jmp paranoid_exit | 846 | cli |
847 | jmp paranoid_userspace | ||
837 | CFI_ENDPROC | 848 | CFI_ENDPROC |
838 | 849 | ||
839 | ENTRY(int3) | 850 | ENTRY(int3) |
840 | zeroentry do_int3 | 851 | zeroentry do_int3 |
841 | 852 | ||
@@ -858,9 +869,6 @@ ENTRY(reserved) | |||
858 | ENTRY(double_fault) | 869 | ENTRY(double_fault) |
859 | CFI_STARTPROC | 870 | CFI_STARTPROC |
860 | paranoidentry do_double_fault | 871 | paranoidentry do_double_fault |
861 | movq %rax,%rsp | ||
862 | testl $3,CS(%rsp) | ||
863 | jnz paranoid_userspace | ||
864 | jmp paranoid_exit | 872 | jmp paranoid_exit |
865 | CFI_ENDPROC | 873 | CFI_ENDPROC |
866 | 874 | ||
@@ -874,9 +882,6 @@ ENTRY(segment_not_present) | |||
874 | ENTRY(stack_segment) | 882 | ENTRY(stack_segment) |
875 | CFI_STARTPROC | 883 | CFI_STARTPROC |
876 | paranoidentry do_stack_segment | 884 | paranoidentry do_stack_segment |
877 | movq %rax,%rsp | ||
878 | testl $3,CS(%rsp) | ||
879 | jnz paranoid_userspace | ||
880 | jmp paranoid_exit | 885 | jmp paranoid_exit |
881 | CFI_ENDPROC | 886 | CFI_ENDPROC |
882 | 887 | ||
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) |