diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 07:30:52 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:30:52 -0500 |
commit | 962ff3804d31a4d090bbcbd3d06a4b63e3a5b5fd (patch) | |
tree | 46e45a1076777b7454fd026f006f7517229b34b7 /arch | |
parent | e4aed6cc45f06acd35e3dfbbaf632c5d5aa897c0 (diff) |
x86: x86-64 ptrace debugreg cleanup
This cleans up the 64-bit ptrace code to separate the guts of the
debug register access from the implementation of PTRACE_PEEKUSR and
PTRACE_POKEUSR. The new functions ptrace_[gs]et_debugreg are made
global so that the ia32 code can later be changed to call them too.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/ptrace_64.c | 140 |
1 files changed, 66 insertions, 74 deletions
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c index d0a0aeaaa0c2..4ba66d8af717 100644 --- a/arch/x86/kernel/ptrace_64.c +++ b/arch/x86/kernel/ptrace_64.c | |||
@@ -183,9 +183,63 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno) | |||
183 | 183 | ||
184 | } | 184 | } |
185 | 185 | ||
186 | unsigned long ptrace_get_debugreg(struct task_struct *child, int n) | ||
187 | { | ||
188 | switch (n) { | ||
189 | case 0: return child->thread.debugreg0; | ||
190 | case 1: return child->thread.debugreg1; | ||
191 | case 2: return child->thread.debugreg2; | ||
192 | case 3: return child->thread.debugreg3; | ||
193 | case 6: return child->thread.debugreg6; | ||
194 | case 7: return child->thread.debugreg7; | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long data) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | if (n < 4) { | ||
204 | int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7; | ||
205 | if (unlikely(data >= TASK_SIZE_OF(child) - dsize)) | ||
206 | return -EIO; | ||
207 | } | ||
208 | |||
209 | switch (n) { | ||
210 | case 0: child->thread.debugreg0 = data; break; | ||
211 | case 1: child->thread.debugreg1 = data; break; | ||
212 | case 2: child->thread.debugreg2 = data; break; | ||
213 | case 3: child->thread.debugreg3 = data; break; | ||
214 | |||
215 | case 6: | ||
216 | if (data >> 32) | ||
217 | return -EIO; | ||
218 | child->thread.debugreg6 = data; | ||
219 | break; | ||
220 | |||
221 | case 7: | ||
222 | /* | ||
223 | * See ptrace_32.c for an explanation of this awkward check. | ||
224 | */ | ||
225 | data &= ~DR_CONTROL_RESERVED; | ||
226 | for (i = 0; i < 4; i++) | ||
227 | if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1) | ||
228 | return -EIO; | ||
229 | child->thread.debugreg7 = data; | ||
230 | if (data) | ||
231 | set_tsk_thread_flag(child, TIF_DEBUG); | ||
232 | else | ||
233 | clear_tsk_thread_flag(child, TIF_DEBUG); | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
186 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 240 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
187 | { | 241 | { |
188 | long i, ret; | 242 | long ret; |
189 | unsigned ui; | 243 | unsigned ui; |
190 | 244 | ||
191 | switch (request) { | 245 | switch (request) { |
@@ -204,32 +258,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
204 | addr > sizeof(struct user) - 7) | 258 | addr > sizeof(struct user) - 7) |
205 | break; | 259 | break; |
206 | 260 | ||
207 | switch (addr) { | 261 | tmp = 0; |
208 | case 0 ... sizeof(struct user_regs_struct) - sizeof(long): | 262 | if (addr < sizeof(struct user_regs_struct)) |
209 | tmp = getreg(child, addr); | 263 | tmp = getreg(child, addr); |
210 | break; | 264 | else if (addr >= offsetof(struct user, u_debugreg[0])) { |
211 | case offsetof(struct user, u_debugreg[0]): | 265 | addr -= offsetof(struct user, u_debugreg[0]); |
212 | tmp = child->thread.debugreg0; | 266 | tmp = ptrace_get_debugreg(child, addr / sizeof(long)); |
213 | break; | ||
214 | case offsetof(struct user, u_debugreg[1]): | ||
215 | tmp = child->thread.debugreg1; | ||
216 | break; | ||
217 | case offsetof(struct user, u_debugreg[2]): | ||
218 | tmp = child->thread.debugreg2; | ||
219 | break; | ||
220 | case offsetof(struct user, u_debugreg[3]): | ||
221 | tmp = child->thread.debugreg3; | ||
222 | break; | ||
223 | case offsetof(struct user, u_debugreg[6]): | ||
224 | tmp = child->thread.debugreg6; | ||
225 | break; | ||
226 | case offsetof(struct user, u_debugreg[7]): | ||
227 | tmp = child->thread.debugreg7; | ||
228 | break; | ||
229 | default: | ||
230 | tmp = 0; | ||
231 | break; | ||
232 | } | 267 | } |
268 | |||
233 | ret = put_user(tmp,(unsigned long __user *) data); | 269 | ret = put_user(tmp,(unsigned long __user *) data); |
234 | break; | 270 | break; |
235 | } | 271 | } |
@@ -241,63 +277,19 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
241 | break; | 277 | break; |
242 | 278 | ||
243 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | 279 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ |
244 | { | ||
245 | int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7; | ||
246 | ret = -EIO; | 280 | ret = -EIO; |
247 | if ((addr & 7) || | 281 | if ((addr & 7) || |
248 | addr > sizeof(struct user) - 7) | 282 | addr > sizeof(struct user) - 7) |
249 | break; | 283 | break; |
250 | 284 | ||
251 | switch (addr) { | 285 | if (addr < sizeof(struct user_regs_struct)) |
252 | case 0 ... sizeof(struct user_regs_struct) - sizeof(long): | ||
253 | ret = putreg(child, addr, data); | 286 | ret = putreg(child, addr, data); |
254 | break; | 287 | else if (addr >= offsetof(struct user, u_debugreg[0])) { |
255 | /* Disallows to set a breakpoint into the vsyscall */ | 288 | addr -= offsetof(struct user, u_debugreg[0]); |
256 | case offsetof(struct user, u_debugreg[0]): | 289 | ret = ptrace_set_debugreg(child, |
257 | if (data >= TASK_SIZE_OF(child) - dsize) break; | 290 | addr / sizeof(long), data); |
258 | child->thread.debugreg0 = data; | ||
259 | ret = 0; | ||
260 | break; | ||
261 | case offsetof(struct user, u_debugreg[1]): | ||
262 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
263 | child->thread.debugreg1 = data; | ||
264 | ret = 0; | ||
265 | break; | ||
266 | case offsetof(struct user, u_debugreg[2]): | ||
267 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
268 | child->thread.debugreg2 = data; | ||
269 | ret = 0; | ||
270 | break; | ||
271 | case offsetof(struct user, u_debugreg[3]): | ||
272 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
273 | child->thread.debugreg3 = data; | ||
274 | ret = 0; | ||
275 | break; | ||
276 | case offsetof(struct user, u_debugreg[6]): | ||
277 | if (data >> 32) | ||
278 | break; | ||
279 | child->thread.debugreg6 = data; | ||
280 | ret = 0; | ||
281 | break; | ||
282 | case offsetof(struct user, u_debugreg[7]): | ||
283 | /* See arch/i386/kernel/ptrace.c for an explanation of | ||
284 | * this awkward check.*/ | ||
285 | data &= ~DR_CONTROL_RESERVED; | ||
286 | for(i=0; i<4; i++) | ||
287 | if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1) | ||
288 | break; | ||
289 | if (i == 4) { | ||
290 | child->thread.debugreg7 = data; | ||
291 | if (data) | ||
292 | set_tsk_thread_flag(child, TIF_DEBUG); | ||
293 | else | ||
294 | clear_tsk_thread_flag(child, TIF_DEBUG); | ||
295 | ret = 0; | ||
296 | } | ||
297 | break; | ||
298 | } | 291 | } |
299 | break; | 292 | break; |
300 | } | ||
301 | 293 | ||
302 | #ifdef CONFIG_IA32_EMULATION | 294 | #ifdef CONFIG_IA32_EMULATION |
303 | /* This makes only sense with 32bit programs. Allow a | 295 | /* This makes only sense with 32bit programs. Allow a |