aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/ptrace.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/ptrace.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/ptrace.c')
-rw-r--r--arch/powerpc/kernel/ptrace.c148
1 files changed, 99 insertions, 49 deletions
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