diff options
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r-- | arch/x86_64/kernel/traps.c | 33 |
1 files changed, 28 insertions, 5 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 2700b1375c1f..cea335e8746c 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -102,6 +102,8 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) | |||
102 | { | 102 | { |
103 | if (regs->eflags & X86_EFLAGS_IF) | 103 | if (regs->eflags & X86_EFLAGS_IF) |
104 | local_irq_disable(); | 104 | local_irq_disable(); |
105 | /* Make sure to not schedule here because we could be running | ||
106 | on an exception stack. */ | ||
105 | preempt_enable_no_resched(); | 107 | preempt_enable_no_resched(); |
106 | } | 108 | } |
107 | 109 | ||
@@ -385,6 +387,7 @@ void out_of_line_bug(void) | |||
385 | 387 | ||
386 | static DEFINE_SPINLOCK(die_lock); | 388 | static DEFINE_SPINLOCK(die_lock); |
387 | static int die_owner = -1; | 389 | static int die_owner = -1; |
390 | static unsigned int die_nest_count; | ||
388 | 391 | ||
389 | unsigned __kprobes long oops_begin(void) | 392 | unsigned __kprobes long oops_begin(void) |
390 | { | 393 | { |
@@ -399,6 +402,7 @@ unsigned __kprobes long oops_begin(void) | |||
399 | else | 402 | else |
400 | spin_lock(&die_lock); | 403 | spin_lock(&die_lock); |
401 | } | 404 | } |
405 | die_nest_count++; | ||
402 | die_owner = cpu; | 406 | die_owner = cpu; |
403 | console_verbose(); | 407 | console_verbose(); |
404 | bust_spinlocks(1); | 408 | bust_spinlocks(1); |
@@ -409,7 +413,13 @@ void __kprobes oops_end(unsigned long flags) | |||
409 | { | 413 | { |
410 | die_owner = -1; | 414 | die_owner = -1; |
411 | bust_spinlocks(0); | 415 | bust_spinlocks(0); |
412 | spin_unlock_irqrestore(&die_lock, flags); | 416 | die_nest_count--; |
417 | if (die_nest_count) | ||
418 | /* We still own the lock */ | ||
419 | local_irq_restore(flags); | ||
420 | else | ||
421 | /* Nest count reaches zero, release the lock. */ | ||
422 | spin_unlock_irqrestore(&die_lock, flags); | ||
413 | if (panic_on_oops) | 423 | if (panic_on_oops) |
414 | panic("Oops"); | 424 | panic("Oops"); |
415 | } | 425 | } |
@@ -464,6 +474,8 @@ void __kprobes die_nmi(char *str, struct pt_regs *regs) | |||
464 | panic("nmi watchdog"); | 474 | panic("nmi watchdog"); |
465 | printk("console shuts up ...\n"); | 475 | printk("console shuts up ...\n"); |
466 | oops_end(flags); | 476 | oops_end(flags); |
477 | nmi_exit(); | ||
478 | local_irq_enable(); | ||
467 | do_exit(SIGSEGV); | 479 | do_exit(SIGSEGV); |
468 | } | 480 | } |
469 | 481 | ||
@@ -473,8 +485,6 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, | |||
473 | { | 485 | { |
474 | struct task_struct *tsk = current; | 486 | struct task_struct *tsk = current; |
475 | 487 | ||
476 | conditional_sti(regs); | ||
477 | |||
478 | tsk->thread.error_code = error_code; | 488 | tsk->thread.error_code = error_code; |
479 | tsk->thread.trap_no = trapnr; | 489 | tsk->thread.trap_no = trapnr; |
480 | 490 | ||
@@ -511,6 +521,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ | |||
511 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 521 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ |
512 | == NOTIFY_STOP) \ | 522 | == NOTIFY_STOP) \ |
513 | return; \ | 523 | return; \ |
524 | conditional_sti(regs); \ | ||
514 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ | 525 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ |
515 | } | 526 | } |
516 | 527 | ||
@@ -525,6 +536,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ | |||
525 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 536 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ |
526 | == NOTIFY_STOP) \ | 537 | == NOTIFY_STOP) \ |
527 | return; \ | 538 | return; \ |
539 | conditional_sti(regs); \ | ||
528 | do_trap(trapnr, signr, str, regs, error_code, &info); \ | 540 | do_trap(trapnr, signr, str, regs, error_code, &info); \ |
529 | } | 541 | } |
530 | 542 | ||
@@ -538,7 +550,17 @@ DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) | |||
538 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) | 550 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) |
539 | DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) | 551 | DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) |
540 | DO_ERROR(18, SIGSEGV, "reserved", reserved) | 552 | DO_ERROR(18, SIGSEGV, "reserved", reserved) |
541 | DO_ERROR(12, SIGBUS, "stack segment", stack_segment) | 553 | |
554 | /* Runs on IST stack */ | ||
555 | asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code) | ||
556 | { | ||
557 | if (notify_die(DIE_TRAP, "stack segment", regs, error_code, | ||
558 | 12, SIGBUS) == NOTIFY_STOP) | ||
559 | return; | ||
560 | preempt_conditional_sti(regs); | ||
561 | do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL); | ||
562 | preempt_conditional_cli(regs); | ||
563 | } | ||
542 | 564 | ||
543 | asmlinkage void do_double_fault(struct pt_regs * regs, long error_code) | 565 | asmlinkage void do_double_fault(struct pt_regs * regs, long error_code) |
544 | { | 566 | { |
@@ -672,8 +694,9 @@ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code) | |||
672 | if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) { | 694 | if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) { |
673 | return; | 695 | return; |
674 | } | 696 | } |
697 | preempt_conditional_sti(regs); | ||
675 | do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); | 698 | do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); |
676 | return; | 699 | preempt_conditional_cli(regs); |
677 | } | 700 | } |
678 | 701 | ||
679 | /* Help handler running on IST stack to switch back to user stack | 702 | /* Help handler running on IST stack to switch back to user stack |