diff options
author | K.Prasad <prasad@linux.vnet.ibm.com> | 2009-06-01 14:14:08 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-06-02 16:46:58 -0400 |
commit | 08d68323d1f0c34452e614263b212ca556dae47f (patch) | |
tree | d020110151fe59d1b38eb17f7f40c43301677c81 /arch/x86/kernel/traps.c | |
parent | 0067f1297241ea567f2b22a455519752d70fcca9 (diff) |
hw-breakpoints: modifying generic debug exception to use thread-specific debug registers
This patch modifies the breakpoint exception handler code to use the new
abstract debug register names.
[ fweisbec@gmail.com: fix conflict against kmemcheck ]
[ Impact: refactor and cleanup x86 debug exception handler ]
Original-patch-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'arch/x86/kernel/traps.c')
-rw-r--r-- | arch/x86/kernel/traps.c | 69 |
1 files changed, 24 insertions, 45 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index a1d288327ff0..de9913247dd0 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -529,73 +529,52 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) | |||
529 | dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | 529 | dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) |
530 | { | 530 | { |
531 | struct task_struct *tsk = current; | 531 | struct task_struct *tsk = current; |
532 | unsigned long condition; | 532 | unsigned long dr6; |
533 | int si_code; | 533 | int si_code; |
534 | 534 | ||
535 | get_debugreg(condition, 6); | 535 | get_debugreg(dr6, 6); |
536 | 536 | ||
537 | /* DR6 may or may not be cleared by the CPU */ | ||
538 | set_debugreg(0, 6); | ||
537 | /* | 539 | /* |
538 | * The processor cleared BTF, so don't mark that we need it set. | 540 | * The processor cleared BTF, so don't mark that we need it set. |
539 | */ | 541 | */ |
540 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); | 542 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); |
541 | tsk->thread.debugctlmsr = 0; | 543 | tsk->thread.debugctlmsr = 0; |
542 | 544 | ||
543 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, | 545 | /* Store the virtualized DR6 value */ |
546 | tsk->thread.debugreg6 = dr6; | ||
547 | |||
548 | if (notify_die(DIE_DEBUG, "debug", regs, dr6, error_code, | ||
544 | SIGTRAP) == NOTIFY_STOP) | 549 | SIGTRAP) == NOTIFY_STOP) |
545 | return; | 550 | return; |
546 | 551 | ||
547 | /* It's safe to allow irq's after DR6 has been saved */ | 552 | /* It's safe to allow irq's after DR6 has been saved */ |
548 | preempt_conditional_sti(regs); | 553 | preempt_conditional_sti(regs); |
549 | 554 | ||
550 | /* Mask out spurious debug traps due to lazy DR7 setting */ | 555 | if (regs->flags & X86_VM_MASK) { |
551 | if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { | 556 | handle_vm86_trap((struct kernel_vm86_regs *) regs, |
552 | if (!tsk->thread.debugreg7) | 557 | error_code, 1); |
553 | goto clear_dr7; | 558 | return; |
554 | } | 559 | } |
555 | 560 | ||
556 | #ifdef CONFIG_X86_32 | ||
557 | if (regs->flags & X86_VM_MASK) | ||
558 | goto debug_vm86; | ||
559 | #endif | ||
560 | |||
561 | /* Save debug status register where ptrace can see it */ | ||
562 | tsk->thread.debugreg6 = condition; | ||
563 | |||
564 | /* | 561 | /* |
565 | * Single-stepping through TF: make sure we ignore any events in | 562 | * Single-stepping through system calls: ignore any exceptions in |
566 | * kernel space (but re-enable TF when returning to user mode). | 563 | * kernel space, but re-enable TF when returning to user mode. |
564 | * | ||
565 | * We already checked v86 mode above, so we can check for kernel mode | ||
566 | * by just checking the CPL of CS. | ||
567 | */ | 567 | */ |
568 | if (condition & DR_STEP) { | 568 | if ((dr6 & DR_STEP) && !user_mode(regs)) { |
569 | if (!user_mode(regs)) | 569 | tsk->thread.debugreg6 &= ~DR_STEP; |
570 | goto clear_TF_reenable; | 570 | set_tsk_thread_flag(tsk, TIF_SINGLESTEP); |
571 | regs->flags &= ~X86_EFLAGS_TF; | ||
571 | } | 572 | } |
572 | 573 | si_code = get_si_code(tsk->thread.debugreg6); | |
573 | si_code = get_si_code(condition); | 574 | if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS)) |
574 | /* Ok, finally something we can handle */ | 575 | send_sigtrap(tsk, regs, error_code, si_code); |
575 | send_sigtrap(tsk, regs, error_code, si_code); | ||
576 | |||
577 | /* | ||
578 | * Disable additional traps. They'll be re-enabled when | ||
579 | * the signal is delivered. | ||
580 | */ | ||
581 | clear_dr7: | ||
582 | set_debugreg(0, 7); | ||
583 | preempt_conditional_cli(regs); | 576 | preempt_conditional_cli(regs); |
584 | return; | ||
585 | 577 | ||
586 | #ifdef CONFIG_X86_32 | ||
587 | debug_vm86: | ||
588 | /* reenable preemption: handle_vm86_trap() might sleep */ | ||
589 | dec_preempt_count(); | ||
590 | handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1); | ||
591 | conditional_cli(regs); | ||
592 | return; | ||
593 | #endif | ||
594 | |||
595 | clear_TF_reenable: | ||
596 | set_tsk_thread_flag(tsk, TIF_SINGLESTEP); | ||
597 | regs->flags &= ~X86_EFLAGS_TF; | ||
598 | preempt_conditional_cli(regs); | ||
599 | return; | 578 | return; |
600 | } | 579 | } |
601 | 580 | ||