diff options
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r-- | arch/x86_64/kernel/traps.c | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 6bda322d3caf..cea335e8746c 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
31 | #include <linux/nmi.h> | 31 | #include <linux/nmi.h> |
32 | #include <linux/kprobes.h> | 32 | #include <linux/kprobes.h> |
33 | #include <linux/kexec.h> | ||
33 | 34 | ||
34 | #include <asm/system.h> | 35 | #include <asm/system.h> |
35 | #include <asm/uaccess.h> | 36 | #include <asm/uaccess.h> |
@@ -101,6 +102,8 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) | |||
101 | { | 102 | { |
102 | if (regs->eflags & X86_EFLAGS_IF) | 103 | if (regs->eflags & X86_EFLAGS_IF) |
103 | local_irq_disable(); | 104 | local_irq_disable(); |
105 | /* Make sure to not schedule here because we could be running | ||
106 | on an exception stack. */ | ||
104 | preempt_enable_no_resched(); | 107 | preempt_enable_no_resched(); |
105 | } | 108 | } |
106 | 109 | ||
@@ -384,6 +387,7 @@ void out_of_line_bug(void) | |||
384 | 387 | ||
385 | static DEFINE_SPINLOCK(die_lock); | 388 | static DEFINE_SPINLOCK(die_lock); |
386 | static int die_owner = -1; | 389 | static int die_owner = -1; |
390 | static unsigned int die_nest_count; | ||
387 | 391 | ||
388 | unsigned __kprobes long oops_begin(void) | 392 | unsigned __kprobes long oops_begin(void) |
389 | { | 393 | { |
@@ -398,6 +402,7 @@ unsigned __kprobes long oops_begin(void) | |||
398 | else | 402 | else |
399 | spin_lock(&die_lock); | 403 | spin_lock(&die_lock); |
400 | } | 404 | } |
405 | die_nest_count++; | ||
401 | die_owner = cpu; | 406 | die_owner = cpu; |
402 | console_verbose(); | 407 | console_verbose(); |
403 | bust_spinlocks(1); | 408 | bust_spinlocks(1); |
@@ -408,7 +413,13 @@ void __kprobes oops_end(unsigned long flags) | |||
408 | { | 413 | { |
409 | die_owner = -1; | 414 | die_owner = -1; |
410 | bust_spinlocks(0); | 415 | bust_spinlocks(0); |
411 | 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); | ||
412 | if (panic_on_oops) | 423 | if (panic_on_oops) |
413 | panic("Oops"); | 424 | panic("Oops"); |
414 | } | 425 | } |
@@ -433,6 +444,8 @@ void __kprobes __die(const char * str, struct pt_regs * regs, long err) | |||
433 | printk(KERN_ALERT "RIP "); | 444 | printk(KERN_ALERT "RIP "); |
434 | printk_address(regs->rip); | 445 | printk_address(regs->rip); |
435 | printk(" RSP <%016lx>\n", regs->rsp); | 446 | printk(" RSP <%016lx>\n", regs->rsp); |
447 | if (kexec_should_crash(current)) | ||
448 | crash_kexec(regs); | ||
436 | } | 449 | } |
437 | 450 | ||
438 | void die(const char * str, struct pt_regs * regs, long err) | 451 | void die(const char * str, struct pt_regs * regs, long err) |
@@ -455,10 +468,14 @@ void __kprobes die_nmi(char *str, struct pt_regs *regs) | |||
455 | */ | 468 | */ |
456 | printk(str, safe_smp_processor_id()); | 469 | printk(str, safe_smp_processor_id()); |
457 | show_registers(regs); | 470 | show_registers(regs); |
471 | if (kexec_should_crash(current)) | ||
472 | crash_kexec(regs); | ||
458 | if (panic_on_timeout || panic_on_oops) | 473 | if (panic_on_timeout || panic_on_oops) |
459 | panic("nmi watchdog"); | 474 | panic("nmi watchdog"); |
460 | printk("console shuts up ...\n"); | 475 | printk("console shuts up ...\n"); |
461 | oops_end(flags); | 476 | oops_end(flags); |
477 | nmi_exit(); | ||
478 | local_irq_enable(); | ||
462 | do_exit(SIGSEGV); | 479 | do_exit(SIGSEGV); |
463 | } | 480 | } |
464 | 481 | ||
@@ -468,8 +485,6 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, | |||
468 | { | 485 | { |
469 | struct task_struct *tsk = current; | 486 | struct task_struct *tsk = current; |
470 | 487 | ||
471 | conditional_sti(regs); | ||
472 | |||
473 | tsk->thread.error_code = error_code; | 488 | tsk->thread.error_code = error_code; |
474 | tsk->thread.trap_no = trapnr; | 489 | tsk->thread.trap_no = trapnr; |
475 | 490 | ||
@@ -506,6 +521,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ | |||
506 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 521 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ |
507 | == NOTIFY_STOP) \ | 522 | == NOTIFY_STOP) \ |
508 | return; \ | 523 | return; \ |
524 | conditional_sti(regs); \ | ||
509 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ | 525 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ |
510 | } | 526 | } |
511 | 527 | ||
@@ -520,6 +536,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ | |||
520 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 536 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ |
521 | == NOTIFY_STOP) \ | 537 | == NOTIFY_STOP) \ |
522 | return; \ | 538 | return; \ |
539 | conditional_sti(regs); \ | ||
523 | do_trap(trapnr, signr, str, regs, error_code, &info); \ | 540 | do_trap(trapnr, signr, str, regs, error_code, &info); \ |
524 | } | 541 | } |
525 | 542 | ||
@@ -533,7 +550,17 @@ DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) | |||
533 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) | 550 | DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) |
534 | 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) |
535 | DO_ERROR(18, SIGSEGV, "reserved", reserved) | 552 | DO_ERROR(18, SIGSEGV, "reserved", reserved) |
536 | 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 | } | ||
537 | 564 | ||
538 | 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) |
539 | { | 566 | { |
@@ -667,8 +694,9 @@ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code) | |||
667 | 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) { |
668 | return; | 695 | return; |
669 | } | 696 | } |
697 | preempt_conditional_sti(regs); | ||
670 | do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); | 698 | do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); |
671 | return; | 699 | preempt_conditional_cli(regs); |
672 | } | 700 | } |
673 | 701 | ||
674 | /* 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 |