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/kernel | |
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/kernel')
-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 |
4 files changed, 48 insertions, 6 deletions
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 | ||