aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/ptrace32.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2007-06-04 01:15:43 -0400
committerPaul Mackerras <paulus@samba.org>2007-06-14 08:29:56 -0400
commite17666ba48f78ff10162d7448e7c92d668d8faf6 (patch)
tree7d3e2c99d383a968491178439b638dc1062e5269 /arch/powerpc/kernel/ptrace32.c
parentacd89828484db6371202f5d292781ae6f832eda2 (diff)
[POWERPC] ptrace updates & new, better requests
The powerpc ptrace interface is dodgy at best. We have defined our "own" versions of GETREGS/SETREGS/GETFPREGS/SETFPREGS that strangely take arguments in reverse order from other archs (in addition to having different request numbers) and have subtle issue, like not accessing all of the registers in their respective categories. This patch moves the implementation of those to a separate function in order to facilitate their deprecation in the future, and provides new ptrace requests that mirror the x86 and sparc ones and use the same numbers: PTRACE_GETREGS : returns an entire pt_regs (the whole thing, not only the 32 GPRs, though that doesn't include the FPRs etc... There's a compat version for 32 bits that returns a 32 bits compatible pt_regs (44 uints) PTRACE_SETREGS : sets an entire pt_regs (the whole thing, not only the 32 GPRs, though that doesn't include the FPRs etc... Some registers cannot be written to and will just be dropped, this is the same as with POKEUSR, that is anything above MQ on 32 bits and CCR on 64 bits. There is a compat version as well. PTRACE_GETFPREGS : returns all the FP registers -including- the FPSCR that is 33 doubles (regardless of 32/64 bits) PTRACE_SETFPREGS : sets all the FP registers -including- the FPSCR that is 33 doubles (regardless of 32/64 bits) And two that only exist on 64 bits kernels: PTRACE_GETREGS64 : Same as PTRACE_GETREGS, except there is no compat function, a 32 bits process will obtain the full 64 bits registers PTRACE_SETREGS64 : Same as PTRACE_SETREGS, except there is no compat function, a 32 bits process will set the full 64 bits registers The two later ones makes things easier to have a 32 bits debugger on a 64 bits program (or on a 32 bits program that uses the full 64 bits of the GPRs, which is possible though has issues that will be fixed in a later patch). Finally, while at it, the patch removes a whole bunch of code duplication between ptrace32.c and ptrace.c, in large part by having the former call into the later for all requests that don't need any special "compat" treatment. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/ptrace32.c')
-rw-r--r--arch/powerpc/kernel/ptrace32.c204
1 files changed, 87 insertions, 117 deletions
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 1bf1f450e1ab..98b1580a2bc2 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -41,6 +41,50 @@
41 * in exit.c or in signal.c. 41 * in exit.c or in signal.c.
42 */ 42 */
43 43
44/*
45 * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
46 * we mark them as obsolete now, they will be removed in a future version
47 */
48static long compat_ptrace_old(struct task_struct *child, long request,
49 long addr, long data)
50{
51 int ret = -EPERM;
52
53 switch(request) {
54 case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
55 int i;
56 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
57 unsigned int __user *tmp = (unsigned int __user *)addr;
58
59 for (i = 0; i < 32; i++) {
60 ret = put_user(*reg, tmp);
61 if (ret)
62 break;
63 reg++;
64 tmp++;
65 }
66 break;
67 }
68
69 case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
70 int i;
71 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
72 unsigned int __user *tmp = (unsigned int __user *)addr;
73
74 for (i = 0; i < 32; i++) {
75 ret = get_user(*reg, tmp);
76 if (ret)
77 break;
78 reg++;
79 tmp++;
80 }
81 break;
82 }
83
84 }
85 return ret;
86}
87
44long compat_sys_ptrace(int request, int pid, unsigned long addr, 88long compat_sys_ptrace(int request, int pid, unsigned long addr,
45 unsigned long data) 89 unsigned long data)
46{ 90{
@@ -280,52 +324,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
280 break; 324 break;
281 } 325 }
282 326
283 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
284 case PTRACE_CONT: { /* restart after signal. */
285 ret = -EIO;
286 if (!valid_signal(data))
287 break;
288 if (request == PTRACE_SYSCALL)
289 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
290 else
291 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
292 child->exit_code = data;
293 /* make sure the single step bit is not set. */
294 clear_single_step(child);
295 wake_up_process(child);
296 ret = 0;
297 break;
298 }
299
300 /*
301 * make the child exit. Best I can do is send it a sigkill.
302 * perhaps it should be put in the status that it wants to
303 * exit.
304 */
305 case PTRACE_KILL: {
306 ret = 0;
307 if (child->exit_state == EXIT_ZOMBIE) /* already dead */
308 break;
309 child->exit_code = SIGKILL;
310 /* make sure the single step bit is not set. */
311 clear_single_step(child);
312 wake_up_process(child);
313 break;
314 }
315
316 case PTRACE_SINGLESTEP: { /* set the trap flag. */
317 ret = -EIO;
318 if (!valid_signal(data))
319 break;
320 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
321 set_single_step(child);
322 child->exit_code = data;
323 /* give it a chance to run. */
324 wake_up_process(child);
325 ret = 0;
326 break;
327 }
328
329 case PTRACE_GET_DEBUGREG: { 327 case PTRACE_GET_DEBUGREG: {
330 ret = -EINVAL; 328 ret = -EINVAL;
331 /* We only support one DABR and no IABRS at the moment */ 329 /* We only support one DABR and no IABRS at the moment */
@@ -335,95 +333,67 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr,
335 break; 333 break;
336 } 334 }
337 335
338 case PTRACE_SET_DEBUGREG: 336 case PTRACE_GETEVENTMSG:
339 ret = ptrace_set_debugreg(child, addr, data); 337 ret = put_user(child->ptrace_message, (unsigned int __user *) data);
340 break;
341
342 case PTRACE_DETACH:
343 ret = ptrace_detach(child, data);
344 break; 338 break;
345 339
346 case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ 340 case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
347 int i; 341 int ui;
348 unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; 342 if (!access_ok(VERIFY_WRITE, (void __user *)data,
349 unsigned int __user *tmp = (unsigned int __user *)addr; 343 PT_REGS_COUNT * sizeof(int))) {
350 344 ret = -EIO;
351 for (i = 0; i < 32; i++) { 345 break;
352 ret = put_user(*reg, tmp);
353 if (ret)
354 break;
355 reg++;
356 tmp++;
357 } 346 }
358 break; 347 ret = 0;
359 } 348 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
360 349 ret |= __put_user(get_reg(child, ui),
361 case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ 350 (unsigned int __user *) data);
362 int i; 351 data += sizeof(int);
363 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
364 unsigned int __user *tmp = (unsigned int __user *)addr;
365
366 for (i = 0; i < 32; i++) {
367 ret = get_user(*reg, tmp);
368 if (ret)
369 break;
370 reg++;
371 tmp++;
372 } 352 }
373 break; 353 break;
374 } 354 }
375 355
376 case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ 356 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
377 int i; 357 unsigned long tmp;
378 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; 358 int ui;
379 unsigned int __user *tmp = (unsigned int __user *)addr; 359 if (!access_ok(VERIFY_READ, (void __user *)data,
380 360 PT_REGS_COUNT * sizeof(int))) {
381 flush_fp_to_thread(child); 361 ret = -EIO;
382 362 break;
383 for (i = 0; i < 32; i++) {
384 ret = put_user(*reg, tmp);
385 if (ret)
386 break;
387 reg++;
388 tmp++;
389 } 363 }
390 break; 364 ret = 0;
391 } 365 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
392 366 ret = __get_user(tmp, (unsigned int __user *) data);
393 case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
394 int i;
395 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
396 unsigned int __user *tmp = (unsigned int __user *)addr;
397
398 flush_fp_to_thread(child);
399
400 for (i = 0; i < 32; i++) {
401 ret = get_user(*reg, tmp);
402 if (ret) 367 if (ret)
403 break; 368 break;
404 reg++; 369 put_reg(child, ui, tmp);
405 tmp++; 370 data += sizeof(int);
406 } 371 }
407 break; 372 break;
408 } 373 }
409 374
410 case PTRACE_GETEVENTMSG: 375 case PTRACE_GETFPREGS:
411 ret = put_user(child->ptrace_message, (unsigned int __user *) data); 376 case PTRACE_SETFPREGS:
412 break;
413
414#ifdef CONFIG_ALTIVEC
415 case PTRACE_GETVRREGS: 377 case PTRACE_GETVRREGS:
416 /* Get the child altivec register state. */ 378 case PTRACE_SETVRREGS:
417 flush_altivec_to_thread(child); 379 case PTRACE_GETREGS64:
418 ret = get_vrregs((unsigned long __user *)data, child); 380 case PTRACE_SETREGS64:
381 case PPC_PTRACE_GETFPREGS:
382 case PPC_PTRACE_SETFPREGS:
383 case PTRACE_KILL:
384 case PTRACE_SINGLESTEP:
385 case PTRACE_DETACH:
386 case PTRACE_SET_DEBUGREG:
387 case PTRACE_SYSCALL:
388 case PTRACE_CONT:
389 ret = arch_ptrace(child, request, addr, data);
419 break; 390 break;
420 391
421 case PTRACE_SETVRREGS: 392 /* Old reverse args ptrace callss */
422 /* Set the child altivec register state. */ 393 case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
423 flush_altivec_to_thread(child); 394 case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
424 ret = set_vrregs(child, (unsigned long __user *)data); 395 ret = compat_ptrace_old(child, request, addr, data);
425 break; 396 break;
426#endif
427 397
428 default: 398 default:
429 ret = ptrace_request(child, request, addr, data); 399 ret = ptrace_request(child, request, addr, data);