aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
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
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')
-rw-r--r--arch/powerpc/kernel/ptrace-common.h23
-rw-r--r--arch/powerpc/kernel/ptrace.c148
-rw-r--r--arch/powerpc/kernel/ptrace32.c204
3 files changed, 209 insertions, 166 deletions
diff --git a/arch/powerpc/kernel/ptrace-common.h b/arch/powerpc/kernel/ptrace-common.h
index f0746eca8f44..21884535fee6 100644
--- a/arch/powerpc/kernel/ptrace-common.h
+++ b/arch/powerpc/kernel/ptrace-common.h
@@ -52,6 +52,29 @@ static inline int put_reg(struct task_struct *task, int regno,
52} 52}
53 53
54 54
55static inline int get_fpregs(void __user *data,
56 struct task_struct *task,
57 int has_fpscr)
58{
59 unsigned int count = has_fpscr ? 33 : 32;
60
61 if (copy_to_user(data, task->thread.fpr, count * sizeof(double)))
62 return -EFAULT;
63 return 0;
64}
65
66static inline int set_fpregs(void __user *data,
67 struct task_struct *task,
68 int has_fpscr)
69{
70 unsigned int count = has_fpscr ? 33 : 32;
71
72 if (copy_from_user(task->thread.fpr, data, count * sizeof(double)))
73 return -EFAULT;
74 return 0;
75}
76
77
55#ifdef CONFIG_ALTIVEC 78#ifdef CONFIG_ALTIVEC
56/* 79/*
57 * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. 80 * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go.
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index da53b0d4114b..230d5f5bfab6 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -59,6 +59,62 @@ void ptrace_disable(struct task_struct *child)
59 clear_single_step(child); 59 clear_single_step(child);
60} 60}
61 61
62/*
63 * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
64 * we mark them as obsolete now, they will be removed in a future version
65 */
66static long arch_ptrace_old(struct task_struct *child, long request, long addr,
67 long data)
68{
69 int ret = -EPERM;
70
71 switch(request) {
72 case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
73 int i;
74 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
75 unsigned long __user *tmp = (unsigned long __user *)addr;
76
77 for (i = 0; i < 32; i++) {
78 ret = put_user(*reg, tmp);
79 if (ret)
80 break;
81 reg++;
82 tmp++;
83 }
84 break;
85 }
86
87 case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
88 int i;
89 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
90 unsigned long __user *tmp = (unsigned long __user *)addr;
91
92 for (i = 0; i < 32; i++) {
93 ret = get_user(*reg, tmp);
94 if (ret)
95 break;
96 reg++;
97 tmp++;
98 }
99 break;
100 }
101
102 case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
103 flush_fp_to_thread(child);
104 ret = get_fpregs((void __user *)addr, child, 0);
105 break;
106 }
107
108 case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
109 flush_fp_to_thread(child);
110 ret = set_fpregs((void __user *)addr, child, 0);
111 break;
112 }
113
114 }
115 return ret;
116}
117
62long arch_ptrace(struct task_struct *child, long request, long addr, long data) 118long arch_ptrace(struct task_struct *child, long request, long addr, long data)
63{ 119{
64 int ret = -EPERM; 120 int ret = -EPERM;
@@ -214,71 +270,58 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
214 ret = ptrace_detach(child, data); 270 ret = ptrace_detach(child, data);
215 break; 271 break;
216 272
217 case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ 273#ifdef CONFIG_PPC64
218 int i; 274 case PTRACE_GETREGS64:
219 unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; 275#endif
220 unsigned long __user *tmp = (unsigned long __user *)addr; 276 case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
221 277 int ui;
222 for (i = 0; i < 32; i++) { 278 if (!access_ok(VERIFY_WRITE, (void __user *)data,
223 ret = put_user(*reg, tmp); 279 sizeof(struct pt_regs))) {
224 if (ret) 280 ret = -EIO;
225 break; 281 break;
226 reg++; 282 }
227 tmp++; 283 ret = 0;
284 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
285 ret |= __put_user(get_reg(child, ui),
286 (unsigned long __user *) data);
287 data += sizeof(long);
228 } 288 }
229 break; 289 break;
230 } 290 }
231 291
232 case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ 292#ifdef CONFIG_PPC64
233 int i; 293 case PTRACE_SETREGS64:
234 unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; 294#endif
235 unsigned long __user *tmp = (unsigned long __user *)addr; 295 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
236 296 unsigned long tmp;
237 for (i = 0; i < 32; i++) { 297 int ui;
238 ret = get_user(*reg, tmp); 298 if (!access_ok(VERIFY_READ, (void __user *)data,
299 sizeof(struct pt_regs))) {
300 ret = -EIO;
301 break;
302 }
303 ret = 0;
304 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
305 ret = __get_user(tmp, (unsigned long __user *) data);
239 if (ret) 306 if (ret)
240 break; 307 break;
241 reg++; 308 put_reg(child, ui, tmp);
242 tmp++; 309 data += sizeof(long);
243 } 310 }
244 break; 311 break;
245 } 312 }
246 313
247#ifdef CONFIG_PPC64 314 case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */
248 case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
249 int i;
250 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
251 unsigned long __user *tmp = (unsigned long __user *)addr;
252
253 flush_fp_to_thread(child); 315 flush_fp_to_thread(child);
254 316 ret = get_fpregs((void __user *)data, child, 1);
255 for (i = 0; i < 32; i++) {
256 ret = put_user(*reg, tmp);
257 if (ret)
258 break;
259 reg++;
260 tmp++;
261 }
262 break; 317 break;
263 } 318 }
264 319
265 case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ 320 case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */
266 int i;
267 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
268 unsigned long __user *tmp = (unsigned long __user *)addr;
269
270 flush_fp_to_thread(child); 321 flush_fp_to_thread(child);
271 322 ret = set_fpregs((void __user *)data, child, 1);
272 for (i = 0; i < 32; i++) {
273 ret = get_user(*reg, tmp);
274 if (ret)
275 break;
276 reg++;
277 tmp++;
278 }
279 break; 323 break;
280 } 324 }
281#endif /* CONFIG_PPC64 */
282 325
283#ifdef CONFIG_ALTIVEC 326#ifdef CONFIG_ALTIVEC
284 case PTRACE_GETVRREGS: 327 case PTRACE_GETVRREGS:
@@ -311,11 +354,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
311 break; 354 break;
312#endif 355#endif
313 356
357 /* Old reverse args ptrace callss */
358 case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
359 case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
360 case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
361 case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
362 ret = arch_ptrace_old(child, request, addr, data);
363 break;
364
314 default: 365 default:
315 ret = ptrace_request(child, request, addr, data); 366 ret = ptrace_request(child, request, addr, data);
316 break; 367 break;
317 } 368 }
318
319 return ret; 369 return ret;
320} 370}
321 371
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);