diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 07:30:54 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:30:54 -0500 |
commit | 10faa81e102e2b7695f386812055cd2ef9e44b4c (patch) | |
tree | bd21d2094eacca5b07d5ee5977cb0ad57ebc5c02 | |
parent | 7e9916040b3020d0f36d68bb7512e3b80b623097 (diff) |
x86: debugctlmsr arch_has_block_step
This implements user-mode step-until-branch on x86 using the BTF bit
in MSR_IA32_DEBUGCTLMSR. It's just like single-step, only less so.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/kernel/step.c | 64 | ||||
-rw-r--r-- | arch/x86/kernel/traps_32.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/traps_64.c | 6 | ||||
-rw-r--r-- | include/asm-x86/ptrace.h | 7 |
4 files changed, 80 insertions, 3 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 243bff650ca5..cf4b9dac4a05 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c | |||
@@ -107,7 +107,10 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs) | |||
107 | return 0; | 107 | return 0; |
108 | } | 108 | } |
109 | 109 | ||
110 | void user_enable_single_step(struct task_struct *child) | 110 | /* |
111 | * Enable single-stepping. Return nonzero if user mode is not using TF itself. | ||
112 | */ | ||
113 | static int enable_single_step(struct task_struct *child) | ||
111 | { | 114 | { |
112 | struct pt_regs *regs = task_pt_regs(child); | 115 | struct pt_regs *regs = task_pt_regs(child); |
113 | 116 | ||
@@ -122,7 +125,7 @@ void user_enable_single_step(struct task_struct *child) | |||
122 | * If TF was already set, don't do anything else | 125 | * If TF was already set, don't do anything else |
123 | */ | 126 | */ |
124 | if (regs->eflags & X86_EFLAGS_TF) | 127 | if (regs->eflags & X86_EFLAGS_TF) |
125 | return; | 128 | return 0; |
126 | 129 | ||
127 | /* Set TF on the kernel stack.. */ | 130 | /* Set TF on the kernel stack.. */ |
128 | regs->eflags |= X86_EFLAGS_TF; | 131 | regs->eflags |= X86_EFLAGS_TF; |
@@ -133,13 +136,68 @@ void user_enable_single_step(struct task_struct *child) | |||
133 | * won't clear it by hand later. | 136 | * won't clear it by hand later. |
134 | */ | 137 | */ |
135 | if (is_setting_trap_flag(child, regs)) | 138 | if (is_setting_trap_flag(child, regs)) |
136 | return; | 139 | return 0; |
137 | 140 | ||
138 | set_tsk_thread_flag(child, TIF_FORCED_TF); | 141 | set_tsk_thread_flag(child, TIF_FORCED_TF); |
142 | |||
143 | return 1; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Install this value in MSR_IA32_DEBUGCTLMSR whenever child is running. | ||
148 | */ | ||
149 | static void write_debugctlmsr(struct task_struct *child, unsigned long val) | ||
150 | { | ||
151 | child->thread.debugctlmsr = val; | ||
152 | |||
153 | if (child != current) | ||
154 | return; | ||
155 | |||
156 | #ifdef CONFIG_X86_64 | ||
157 | wrmsrl(MSR_IA32_DEBUGCTLMSR, val); | ||
158 | #else | ||
159 | wrmsr(MSR_IA32_DEBUGCTLMSR, val, 0); | ||
160 | #endif | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Enable single or block step. | ||
165 | */ | ||
166 | static void enable_step(struct task_struct *child, bool block) | ||
167 | { | ||
168 | /* | ||
169 | * Make sure block stepping (BTF) is not enabled unless it should be. | ||
170 | * Note that we don't try to worry about any is_setting_trap_flag() | ||
171 | * instructions after the first when using block stepping. | ||
172 | * So noone should try to use debugger block stepping in a program | ||
173 | * that uses user-mode single stepping itself. | ||
174 | */ | ||
175 | if (enable_single_step(child) && block) { | ||
176 | set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | ||
177 | write_debugctlmsr(child, DEBUGCTLMSR_BTF); | ||
178 | } else if (test_and_clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR)) { | ||
179 | write_debugctlmsr(child, 0); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | void user_enable_single_step(struct task_struct *child) | ||
184 | { | ||
185 | enable_step(child, 0); | ||
186 | } | ||
187 | |||
188 | void user_enable_block_step(struct task_struct *child) | ||
189 | { | ||
190 | enable_step(child, 1); | ||
139 | } | 191 | } |
140 | 192 | ||
141 | void user_disable_single_step(struct task_struct *child) | 193 | void user_disable_single_step(struct task_struct *child) |
142 | { | 194 | { |
195 | /* | ||
196 | * Make sure block stepping (BTF) is disabled. | ||
197 | */ | ||
198 | if (test_and_clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR)) | ||
199 | write_debugctlmsr(child, 0); | ||
200 | |||
143 | /* Always clear TIF_SINGLESTEP... */ | 201 | /* Always clear TIF_SINGLESTEP... */ |
144 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | 202 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); |
145 | 203 | ||
diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps_32.c index 02d1e1e58e81..9b0bbd508cd5 100644 --- a/arch/x86/kernel/traps_32.c +++ b/arch/x86/kernel/traps_32.c | |||
@@ -837,6 +837,12 @@ fastcall void __kprobes do_debug(struct pt_regs * regs, long error_code) | |||
837 | 837 | ||
838 | get_debugreg(condition, 6); | 838 | get_debugreg(condition, 6); |
839 | 839 | ||
840 | /* | ||
841 | * The processor cleared BTF, so don't mark that we need it set. | ||
842 | */ | ||
843 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); | ||
844 | tsk->thread.debugctlmsr = 0; | ||
845 | |||
840 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, | 846 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, |
841 | SIGTRAP) == NOTIFY_STOP) | 847 | SIGTRAP) == NOTIFY_STOP) |
842 | return; | 848 | return; |
diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c index 874aca397b02..610a64d6bdf0 100644 --- a/arch/x86/kernel/traps_64.c +++ b/arch/x86/kernel/traps_64.c | |||
@@ -850,6 +850,12 @@ asmlinkage void __kprobes do_debug(struct pt_regs * regs, | |||
850 | 850 | ||
851 | get_debugreg(condition, 6); | 851 | get_debugreg(condition, 6); |
852 | 852 | ||
853 | /* | ||
854 | * The processor cleared BTF, so don't mark that we need it set. | ||
855 | */ | ||
856 | clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); | ||
857 | tsk->thread.debugctlmsr = 0; | ||
858 | |||
853 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, | 859 | if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, |
854 | SIGTRAP) == NOTIFY_STOP) | 860 | SIGTRAP) == NOTIFY_STOP) |
855 | return; | 861 | return; |
diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h index d223decd7b01..04204f359298 100644 --- a/include/asm-x86/ptrace.h +++ b/include/asm-x86/ptrace.h | |||
@@ -150,6 +150,13 @@ enum { | |||
150 | extern void user_enable_single_step(struct task_struct *); | 150 | extern void user_enable_single_step(struct task_struct *); |
151 | extern void user_disable_single_step(struct task_struct *); | 151 | extern void user_disable_single_step(struct task_struct *); |
152 | 152 | ||
153 | extern void user_enable_block_step(struct task_struct *); | ||
154 | #ifdef CONFIG_X86_DEBUGCTLMSR | ||
155 | #define arch_has_block_step() (1) | ||
156 | #else | ||
157 | #define arch_has_block_step() (boot_cpu_data.x86 >= 6) | ||
158 | #endif | ||
159 | |||
153 | struct user_desc; | 160 | struct user_desc; |
154 | extern int do_get_thread_area(struct task_struct *p, int idx, | 161 | extern int do_get_thread_area(struct task_struct *p, int idx, |
155 | struct user_desc __user *info); | 162 | struct user_desc __user *info); |