x86, ptrace: Fix block-step
authorPeter Zijlstra <a.p.zijlstra@chello.nl>
Thu, 25 Mar 2010 13:51:51 +0000 (14:51 +0100)
committerIngo Molnar <mingo@elte.hu>
Fri, 26 Mar 2010 10:33:57 +0000 (11:33 +0100)
Implement ptrace-block-step using TIF_BLOCKSTEP which will set
DEBUGCTLMSR_BTF when set for a task while preserving any other
DEBUGCTLMSR bits.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <20100325135414.017536066@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/processor.h
arch/x86/include/asm/thread_info.h
arch/x86/kernel/kprobes.c
arch/x86/kernel/process.c
arch/x86/kernel/step.c
arch/x86/kernel/traps.c

index 5bec21a66dc5885b925e40c4e309c08630253bce..32428b410b55007efd526e902d5f5e5b330474f0 100644 (file)
@@ -799,7 +799,7 @@ extern void cpu_init(void);
 
 static inline unsigned long get_debugctlmsr(void)
 {
-    unsigned long debugctlmsr = 0;
+       unsigned long debugctlmsr = 0;
 
 #ifndef CONFIG_X86_DEBUGCTLMSR
        if (boot_cpu_data.x86 < 6)
@@ -807,7 +807,7 @@ static inline unsigned long get_debugctlmsr(void)
 #endif
        rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
 
-    return debugctlmsr;
+       return debugctlmsr;
 }
 
 static inline void update_debugctlmsr(unsigned long debugctlmsr)
index dc85e12d1405c0d9c79a965eb820086b55fc3279..d017ed5502e27d747a99e048884e28c3316f0bd0 100644 (file)
@@ -92,6 +92,7 @@ struct thread_info {
 #define TIF_IO_BITMAP          22      /* uses I/O bitmap */
 #define TIF_FREEZE             23      /* is freezing for suspend */
 #define TIF_FORCED_TF          24      /* true if TF in eflags artificially */
+#define TIF_BLOCKSTEP          25      /* set when we want DEBUGCTLMSR_BTF */
 #define TIF_LAZY_MMU_UPDATES   27      /* task is updating the mmu lazily */
 #define TIF_SYSCALL_TRACEPOINT 28      /* syscall tracepoint instrumentation */
 
@@ -113,6 +114,7 @@ struct thread_info {
 #define _TIF_IO_BITMAP         (1 << TIF_IO_BITMAP)
 #define _TIF_FREEZE            (1 << TIF_FREEZE)
 #define _TIF_FORCED_TF         (1 << TIF_FORCED_TF)
+#define _TIF_BLOCKSTEP         (1 << TIF_BLOCKSTEP)
 #define _TIF_LAZY_MMU_UPDATES  (1 << TIF_LAZY_MMU_UPDATES)
 #define _TIF_SYSCALL_TRACEPOINT        (1 << TIF_SYSCALL_TRACEPOINT)
 
@@ -143,7 +145,7 @@ struct thread_info {
 
 /* flags to check in __switch_to() */
 #define _TIF_WORK_CTXSW                                                        \
-       (_TIF_IO_BITMAP|_TIF_NOTSC)
+       (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP)
 
 #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
 #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
index 7a880ad3a2084adda851d87ee8600d5a712a7cb2..f2f56c0967b66e5723b293d41946084281fba223 100644 (file)
@@ -422,12 +422,22 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
 
 static void __kprobes clear_btf(void)
 {
-       /* XXX */
+       if (test_thread_flag(TIF_BLOCKSTEP)) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl &= ~DEBUGCTLMSR_BTF;
+               update_debugctlmsr(debugctl);
+       }
 }
 
 static void __kprobes restore_btf(void)
 {
-       /* XXX */
+       if (test_thread_flag(TIF_BLOCKSTEP)) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl |= DEBUGCTLMSR_BTF;
+               update_debugctlmsr(debugctl);
+       }
 }
 
 void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
index 1a60beb32ede8126cec362d0f351644154aea5ee..8328009416d72b30c69a50d3a443021a87750a4d 100644 (file)
@@ -195,6 +195,17 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
        prev = &prev_p->thread;
        next = &next_p->thread;
 
+       if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^
+           test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl &= ~DEBUGCTLMSR_BTF;
+               if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP))
+                       debugctl |= DEBUGCTLMSR_BTF;
+
+               update_debugctlmsr(debugctl);
+       }
+
        if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
            test_tsk_thread_flag(next_p, TIF_NOTSC)) {
                /* prev and next are different */
index 7beba0769a8cfac34f23ad120f597e20f3ab819d..58de45ee08b6f76638cd70011972f645cf9ea25e 100644 (file)
@@ -169,9 +169,19 @@ static void enable_step(struct task_struct *child, bool block)
         * So noone should try to use debugger block stepping in a program
         * that uses user-mode single stepping itself.
         */
-       if (!enable_single_step(child))
-               return;
-       /* XXX */
+       if (enable_single_step(child) && block) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl |= DEBUGCTLMSR_BTF;
+               update_debugctlmsr(debugctl);
+               set_tsk_thread_flag(child, TIF_BLOCKSTEP);
+       } else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl &= ~DEBUGCTLMSR_BTF;
+               update_debugctlmsr(debugctl);
+               clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
+       }
 }
 
 void user_enable_single_step(struct task_struct *child)
@@ -189,7 +199,13 @@ void user_disable_single_step(struct task_struct *child)
        /*
         * Make sure block stepping (BTF) is disabled.
         */
-       /* XXX */
+       if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
+               unsigned long debugctl = get_debugctlmsr();
+
+               debugctl &= ~DEBUGCTLMSR_BTF;
+               update_debugctlmsr(debugctl);
+               clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
+       }
 
        /* Always clear TIF_SINGLESTEP... */
        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
index e3da5d726a377d3fb5368f81a38e5855092c1c55..36f1bd9f8e76b04a927f3f74bd0e52ffc0990019 100644 (file)
@@ -544,6 +544,11 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        /* DR6 may or may not be cleared by the CPU */
        set_debugreg(0, 6);
 
+       /*
+        * The processor cleared BTF, so don't mark that we need it set.
+        */
+       clear_tsk_thread_flag(tsk, TIF_BLOCKSTEP);
+
        /* Store the virtualized DR6 value */
        tsk->thread.debugreg6 = dr6;