diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-03-25 09:51:51 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-26 06:33:57 -0400 |
commit | ea8e61b7bbc4a2faef77db34eb2db2a2c2372ff6 (patch) | |
tree | df2998225dc10245ce3d392576a724ab788e456c /arch/x86 | |
parent | faa4602e47690fb11221e00f9b9697c8dc0d4b19 (diff) |
x86, ptrace: Fix block-step
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>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/processor.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/thread_info.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes.c | 14 | ||||
-rw-r--r-- | arch/x86/kernel/process.c | 11 | ||||
-rw-r--r-- | arch/x86/kernel/step.c | 24 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 5 |
6 files changed, 53 insertions, 9 deletions
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 5bec21a66dc5..32428b410b55 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -799,7 +799,7 @@ extern void cpu_init(void); | |||
799 | 799 | ||
800 | static inline unsigned long get_debugctlmsr(void) | 800 | static inline unsigned long get_debugctlmsr(void) |
801 | { | 801 | { |
802 | unsigned long debugctlmsr = 0; | 802 | unsigned long debugctlmsr = 0; |
803 | 803 | ||
804 | #ifndef CONFIG_X86_DEBUGCTLMSR | 804 | #ifndef CONFIG_X86_DEBUGCTLMSR |
805 | if (boot_cpu_data.x86 < 6) | 805 | if (boot_cpu_data.x86 < 6) |
@@ -807,7 +807,7 @@ static inline unsigned long get_debugctlmsr(void) | |||
807 | #endif | 807 | #endif |
808 | rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); | 808 | rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); |
809 | 809 | ||
810 | return debugctlmsr; | 810 | return debugctlmsr; |
811 | } | 811 | } |
812 | 812 | ||
813 | static inline void update_debugctlmsr(unsigned long debugctlmsr) | 813 | static inline void update_debugctlmsr(unsigned long debugctlmsr) |
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index dc85e12d1405..d017ed5502e2 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h | |||
@@ -92,6 +92,7 @@ struct thread_info { | |||
92 | #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ | 92 | #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ |
93 | #define TIF_FREEZE 23 /* is freezing for suspend */ | 93 | #define TIF_FREEZE 23 /* is freezing for suspend */ |
94 | #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ | 94 | #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ |
95 | #define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */ | ||
95 | #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ | 96 | #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ |
96 | #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ | 97 | #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ |
97 | 98 | ||
@@ -113,6 +114,7 @@ struct thread_info { | |||
113 | #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) | 114 | #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) |
114 | #define _TIF_FREEZE (1 << TIF_FREEZE) | 115 | #define _TIF_FREEZE (1 << TIF_FREEZE) |
115 | #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) | 116 | #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) |
117 | #define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) | ||
116 | #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) | 118 | #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) |
117 | #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) | 119 | #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) |
118 | 120 | ||
@@ -143,7 +145,7 @@ struct thread_info { | |||
143 | 145 | ||
144 | /* flags to check in __switch_to() */ | 146 | /* flags to check in __switch_to() */ |
145 | #define _TIF_WORK_CTXSW \ | 147 | #define _TIF_WORK_CTXSW \ |
146 | (_TIF_IO_BITMAP|_TIF_NOTSC) | 148 | (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP) |
147 | 149 | ||
148 | #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) | 150 | #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) |
149 | #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) | 151 | #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) |
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 7a880ad3a208..f2f56c0967b6 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -422,12 +422,22 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, | |||
422 | 422 | ||
423 | static void __kprobes clear_btf(void) | 423 | static void __kprobes clear_btf(void) |
424 | { | 424 | { |
425 | /* XXX */ | 425 | if (test_thread_flag(TIF_BLOCKSTEP)) { |
426 | unsigned long debugctl = get_debugctlmsr(); | ||
427 | |||
428 | debugctl &= ~DEBUGCTLMSR_BTF; | ||
429 | update_debugctlmsr(debugctl); | ||
430 | } | ||
426 | } | 431 | } |
427 | 432 | ||
428 | static void __kprobes restore_btf(void) | 433 | static void __kprobes restore_btf(void) |
429 | { | 434 | { |
430 | /* XXX */ | 435 | if (test_thread_flag(TIF_BLOCKSTEP)) { |
436 | unsigned long debugctl = get_debugctlmsr(); | ||
437 | |||
438 | debugctl |= DEBUGCTLMSR_BTF; | ||
439 | update_debugctlmsr(debugctl); | ||
440 | } | ||
431 | } | 441 | } |
432 | 442 | ||
433 | void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, | 443 | void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, |
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 1a60beb32ede..8328009416d7 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -195,6 +195,17 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | |||
195 | prev = &prev_p->thread; | 195 | prev = &prev_p->thread; |
196 | next = &next_p->thread; | 196 | next = &next_p->thread; |
197 | 197 | ||
198 | if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^ | ||
199 | test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) { | ||
200 | unsigned long debugctl = get_debugctlmsr(); | ||
201 | |||
202 | debugctl &= ~DEBUGCTLMSR_BTF; | ||
203 | if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) | ||
204 | debugctl |= DEBUGCTLMSR_BTF; | ||
205 | |||
206 | update_debugctlmsr(debugctl); | ||
207 | } | ||
208 | |||
198 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ | 209 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ |
199 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { | 210 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { |
200 | /* prev and next are different */ | 211 | /* prev and next are different */ |
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 7beba0769a8c..58de45ee08b6 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c | |||
@@ -169,9 +169,19 @@ static void enable_step(struct task_struct *child, bool block) | |||
169 | * So noone should try to use debugger block stepping in a program | 169 | * So noone should try to use debugger block stepping in a program |
170 | * that uses user-mode single stepping itself. | 170 | * that uses user-mode single stepping itself. |
171 | */ | 171 | */ |
172 | if (!enable_single_step(child)) | 172 | if (enable_single_step(child) && block) { |
173 | return; | 173 | unsigned long debugctl = get_debugctlmsr(); |
174 | /* XXX */ | 174 | |
175 | debugctl |= DEBUGCTLMSR_BTF; | ||
176 | update_debugctlmsr(debugctl); | ||
177 | set_tsk_thread_flag(child, TIF_BLOCKSTEP); | ||
178 | } else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { | ||
179 | unsigned long debugctl = get_debugctlmsr(); | ||
180 | |||
181 | debugctl &= ~DEBUGCTLMSR_BTF; | ||
182 | update_debugctlmsr(debugctl); | ||
183 | clear_tsk_thread_flag(child, TIF_BLOCKSTEP); | ||
184 | } | ||
175 | } | 185 | } |
176 | 186 | ||
177 | void user_enable_single_step(struct task_struct *child) | 187 | void user_enable_single_step(struct task_struct *child) |
@@ -189,7 +199,13 @@ void user_disable_single_step(struct task_struct *child) | |||
189 | /* | 199 | /* |
190 | * Make sure block stepping (BTF) is disabled. | 200 | * Make sure block stepping (BTF) is disabled. |
191 | */ | 201 | */ |
192 | /* XXX */ | 202 | if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { |
203 | unsigned long debugctl = get_debugctlmsr(); | ||
204 | |||
205 | debugctl &= ~DEBUGCTLMSR_BTF; | ||
206 | update_debugctlmsr(debugctl); | ||
207 | clear_tsk_thread_flag(child, TIF_BLOCKSTEP); | ||
208 | } | ||
193 | 209 | ||
194 | /* Always clear TIF_SINGLESTEP... */ | 210 | /* Always clear TIF_SINGLESTEP... */ |
195 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | 211 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index e3da5d726a37..36f1bd9f8e76 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -544,6 +544,11 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
544 | /* DR6 may or may not be cleared by the CPU */ | 544 | /* DR6 may or may not be cleared by the CPU */ |
545 | set_debugreg(0, 6); | 545 | set_debugreg(0, 6); |
546 | 546 | ||
547 | /* | ||
548 | * The processor cleared BTF, so don't mark that we need it set. | ||
549 | */ | ||
550 | clear_tsk_thread_flag(tsk, TIF_BLOCKSTEP); | ||
551 | |||
547 | /* Store the virtualized DR6 value */ | 552 | /* Store the virtualized DR6 value */ |
548 | tsk->thread.debugreg6 = dr6; | 553 | tsk->thread.debugreg6 = dr6; |
549 | 554 | ||