aboutsummaryrefslogtreecommitdiffstats
path: root/arch
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
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')
-rw-r--r--arch/x86_64/kernel/entry.S49
-rw-r--r--arch/x86_64/kernel/traps.c57
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 */
811paranoid_exit: 816paranoid_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
814paranoid_swapgs: 821paranoid_swapgs:
815 cli
816 swapgs 822 swapgs
817paranoid_restore: 823paranoid_restore:
818 RESTORE_ALL 8 824 RESTORE_ALL 8
819 iretq 825 iretq
820paranoid_userspace: 826paranoid_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
833paranoid_resched: 842 jmp paranoid_userspace
843paranoid_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
839ENTRY(int3) 850ENTRY(int3)
840 zeroentry do_int3 851 zeroentry do_int3
841 852
@@ -858,9 +869,6 @@ ENTRY(reserved)
858ENTRY(double_fault) 869ENTRY(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)
874ENTRY(stack_segment) 882ENTRY(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)
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)