diff options
Diffstat (limited to 'arch/x86/kernel/traps.c')
-rw-r--r-- | arch/x86/kernel/traps.c | 73 |
1 files changed, 26 insertions, 47 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 5204332f475..ae04589a579 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -532,77 +532,56 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) | |||
532 | dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | 532 | dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) |
533 | { | 533 | { |
534 | struct task_struct *tsk = current; | 534 | struct task_struct *tsk = current; |
535 | unsigned long condition; | 535 | unsigned long dr6; |
536 | int si_code; | 536 | int si_code; |
537 | 537 | ||
538 | get_debugreg(condition, 6); | 538 | get_debugreg(dr6, 6); |
539 | 539 | ||
540 | /* Catch kmemcheck conditions first of all! */ | 540 | /* Catch kmemcheck conditions first of all! */ |
541 | if (condition & DR_STEP && kmemcheck_trap(regs)) | 541 | if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) |
542 | return; | 542 | return; |
543 | 543 | ||
544 | /* DR6 may or may not be cleared by the CPU */ | ||
545 | set_debugreg(0, 6); | ||
544 | /* | 546 | /* |
545 | * The processor cleared BTF, so don't mark that we need it set. | 547 | * The processor cleared BTF, so don't mark that we need it set. |
546 | */ | 548 | */ |
547 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); | 549 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); |
548 | tsk->thread.debugctlmsr = 0; | 550 | tsk->thread.debugctlmsr = 0; |
549 | 551 | ||
550 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, | 552 | /* Store the virtualized DR6 value */ |
551 | SIGTRAP) == NOTIFY_STOP) | 553 | tsk->thread.debugreg6 = dr6; |
554 | |||
555 | if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, | ||
556 | SIGTRAP) == NOTIFY_STOP) | ||
552 | return; | 557 | return; |
553 | 558 | ||
554 | /* It's safe to allow irq's after DR6 has been saved */ | 559 | /* It's safe to allow irq's after DR6 has been saved */ |
555 | preempt_conditional_sti(regs); | 560 | preempt_conditional_sti(regs); |
556 | 561 | ||
557 | /* Mask out spurious debug traps due to lazy DR7 setting */ | 562 | if (regs->flags & X86_VM_MASK) { |
558 | if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { | 563 | handle_vm86_trap((struct kernel_vm86_regs *) regs, |
559 | if (!tsk->thread.debugreg7) | 564 | error_code, 1); |
560 | goto clear_dr7; | 565 | return; |
561 | } | 566 | } |
562 | 567 | ||
563 | #ifdef CONFIG_X86_32 | ||
564 | if (regs->flags & X86_VM_MASK) | ||
565 | goto debug_vm86; | ||
566 | #endif | ||
567 | |||
568 | /* Save debug status register where ptrace can see it */ | ||
569 | tsk->thread.debugreg6 = condition; | ||
570 | |||
571 | /* | 568 | /* |
572 | * Single-stepping through TF: make sure we ignore any events in | 569 | * Single-stepping through system calls: ignore any exceptions in |
573 | * kernel space (but re-enable TF when returning to user mode). | 570 | * kernel space, but re-enable TF when returning to user mode. |
571 | * | ||
572 | * We already checked v86 mode above, so we can check for kernel mode | ||
573 | * by just checking the CPL of CS. | ||
574 | */ | 574 | */ |
575 | if (condition & DR_STEP) { | 575 | if ((dr6 & DR_STEP) && !user_mode(regs)) { |
576 | if (!user_mode(regs)) | 576 | tsk->thread.debugreg6 &= ~DR_STEP; |
577 | goto clear_TF_reenable; | 577 | set_tsk_thread_flag(tsk, TIF_SINGLESTEP); |
578 | regs->flags &= ~X86_EFLAGS_TF; | ||
578 | } | 579 | } |
579 | 580 | si_code = get_si_code(tsk->thread.debugreg6); | |
580 | si_code = get_si_code(condition); | 581 | if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS)) |
581 | /* Ok, finally something we can handle */ | 582 | send_sigtrap(tsk, regs, error_code, si_code); |
582 | send_sigtrap(tsk, regs, error_code, si_code); | ||
583 | |||
584 | /* | ||
585 | * Disable additional traps. They'll be re-enabled when | ||
586 | * the signal is delivered. | ||
587 | */ | ||
588 | clear_dr7: | ||
589 | set_debugreg(0, 7); | ||
590 | preempt_conditional_cli(regs); | 583 | preempt_conditional_cli(regs); |
591 | return; | ||
592 | 584 | ||
593 | #ifdef CONFIG_X86_32 | ||
594 | debug_vm86: | ||
595 | /* reenable preemption: handle_vm86_trap() might sleep */ | ||
596 | dec_preempt_count(); | ||
597 | handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1); | ||
598 | conditional_cli(regs); | ||
599 | return; | ||
600 | #endif | ||
601 | |||
602 | clear_TF_reenable: | ||
603 | set_tsk_thread_flag(tsk, TIF_SINGLESTEP); | ||
604 | regs->flags &= ~X86_EFLAGS_TF; | ||
605 | preempt_conditional_cli(regs); | ||
606 | return; | 585 | return; |
607 | } | 586 | } |
608 | 587 | ||