diff options
Diffstat (limited to 'arch/powerpc/kernel/ptrace32.c')
-rw-r--r-- | arch/powerpc/kernel/ptrace32.c | 239 |
1 files changed, 104 insertions, 135 deletions
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 9b9a230349bc..9e6baeac0fb1 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c | |||
@@ -33,13 +33,55 @@ | |||
33 | #include <asm/pgtable.h> | 33 | #include <asm/pgtable.h> |
34 | #include <asm/system.h> | 34 | #include <asm/system.h> |
35 | 35 | ||
36 | #include "ptrace-common.h" | ||
37 | |||
38 | /* | 36 | /* |
39 | * does not yet catch signals sent when the child dies. | 37 | * does not yet catch signals sent when the child dies. |
40 | * in exit.c or in signal.c. | 38 | * in exit.c or in signal.c. |
41 | */ | 39 | */ |
42 | 40 | ||
41 | /* | ||
42 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, | ||
43 | * we mark them as obsolete now, they will be removed in a future version | ||
44 | */ | ||
45 | static long compat_ptrace_old(struct task_struct *child, long request, | ||
46 | long addr, long data) | ||
47 | { | ||
48 | int ret = -EPERM; | ||
49 | |||
50 | switch(request) { | ||
51 | case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ | ||
52 | int i; | ||
53 | unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; | ||
54 | unsigned int __user *tmp = (unsigned int __user *)addr; | ||
55 | |||
56 | for (i = 0; i < 32; i++) { | ||
57 | ret = put_user(*reg, tmp); | ||
58 | if (ret) | ||
59 | break; | ||
60 | reg++; | ||
61 | tmp++; | ||
62 | } | ||
63 | break; | ||
64 | } | ||
65 | |||
66 | case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ | ||
67 | int i; | ||
68 | unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; | ||
69 | unsigned int __user *tmp = (unsigned int __user *)addr; | ||
70 | |||
71 | for (i = 0; i < 32; i++) { | ||
72 | ret = get_user(*reg, tmp); | ||
73 | if (ret) | ||
74 | break; | ||
75 | reg++; | ||
76 | tmp++; | ||
77 | } | ||
78 | break; | ||
79 | } | ||
80 | |||
81 | } | ||
82 | return ret; | ||
83 | } | ||
84 | |||
43 | long compat_sys_ptrace(int request, int pid, unsigned long addr, | 85 | long compat_sys_ptrace(int request, int pid, unsigned long addr, |
44 | unsigned long data) | 86 | unsigned long data) |
45 | { | 87 | { |
@@ -123,7 +165,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
123 | break; | 165 | break; |
124 | 166 | ||
125 | if (index < PT_FPR0) { | 167 | if (index < PT_FPR0) { |
126 | tmp = get_reg(child, index); | 168 | tmp = ptrace_get_reg(child, index); |
127 | } else { | 169 | } else { |
128 | flush_fp_to_thread(child); | 170 | flush_fp_to_thread(child); |
129 | /* | 171 | /* |
@@ -162,7 +204,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
162 | else | 204 | else |
163 | part = 0; /* want the 1st half of the register (left-most). */ | 205 | part = 0; /* want the 1st half of the register (left-most). */ |
164 | 206 | ||
165 | /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ | 207 | /* Validate the input - check to see if address is on the wrong boundary |
208 | * or beyond the end of the user area | ||
209 | */ | ||
166 | if ((addr & 3) || numReg > PT_FPSCR) | 210 | if ((addr & 3) || numReg > PT_FPSCR) |
167 | break; | 211 | break; |
168 | 212 | ||
@@ -170,7 +214,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
170 | flush_fp_to_thread(child); | 214 | flush_fp_to_thread(child); |
171 | tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; | 215 | tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; |
172 | } else { /* register within PT_REGS struct */ | 216 | } else { /* register within PT_REGS struct */ |
173 | tmp = get_reg(child, numReg); | 217 | tmp = ptrace_get_reg(child, numReg); |
174 | } | 218 | } |
175 | reg32bits = ((u32*)&tmp)[part]; | 219 | reg32bits = ((u32*)&tmp)[part]; |
176 | ret = put_user(reg32bits, (u32 __user *)data); | 220 | ret = put_user(reg32bits, (u32 __user *)data); |
@@ -226,10 +270,8 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
226 | if ((addr & 3) || (index > PT_FPSCR32)) | 270 | if ((addr & 3) || (index > PT_FPSCR32)) |
227 | break; | 271 | break; |
228 | 272 | ||
229 | if (index == PT_ORIG_R3) | ||
230 | break; | ||
231 | if (index < PT_FPR0) { | 273 | if (index < PT_FPR0) { |
232 | ret = put_reg(child, index, data); | 274 | ret = ptrace_put_reg(child, index, data); |
233 | } else { | 275 | } else { |
234 | flush_fp_to_thread(child); | 276 | flush_fp_to_thread(child); |
235 | /* | 277 | /* |
@@ -258,70 +300,25 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
258 | /* Determine which register the user wants */ | 300 | /* Determine which register the user wants */ |
259 | index = (u64)addr >> 2; | 301 | index = (u64)addr >> 2; |
260 | numReg = index / 2; | 302 | numReg = index / 2; |
303 | |||
261 | /* | 304 | /* |
262 | * Validate the input - check to see if address is on the | 305 | * Validate the input - check to see if address is on the |
263 | * wrong boundary or beyond the end of the user area | 306 | * wrong boundary or beyond the end of the user area |
264 | */ | 307 | */ |
265 | if ((addr & 3) || (numReg > PT_FPSCR)) | 308 | if ((addr & 3) || (numReg > PT_FPSCR)) |
266 | break; | 309 | break; |
267 | /* Insure it is a register we let them change */ | 310 | if (numReg < PT_FPR0) { |
268 | if ((numReg == PT_ORIG_R3) | 311 | unsigned long freg = ptrace_get_reg(child, numReg); |
269 | || ((numReg > PT_CCR) && (numReg < PT_FPR0))) | 312 | if (index % 2) |
270 | break; | 313 | freg = (freg & ~0xfffffffful) | (data & 0xfffffffful); |
271 | if (numReg >= PT_FPR0) { | 314 | else |
315 | freg = (freg & 0xfffffffful) | (data << 32); | ||
316 | ret = ptrace_put_reg(child, numReg, freg); | ||
317 | } else { | ||
272 | flush_fp_to_thread(child); | 318 | flush_fp_to_thread(child); |
319 | ((unsigned int *)child->thread.regs)[index] = data; | ||
320 | ret = 0; | ||
273 | } | 321 | } |
274 | if (numReg == PT_MSR) | ||
275 | data = (data & MSR_DEBUGCHANGE) | ||
276 | | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); | ||
277 | ((u32*)child->thread.regs)[index] = data; | ||
278 | ret = 0; | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | ||
283 | case PTRACE_CONT: { /* restart after signal. */ | ||
284 | ret = -EIO; | ||
285 | if (!valid_signal(data)) | ||
286 | break; | ||
287 | if (request == PTRACE_SYSCALL) | ||
288 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
289 | else | ||
290 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
291 | child->exit_code = data; | ||
292 | /* make sure the single step bit is not set. */ | ||
293 | clear_single_step(child); | ||
294 | wake_up_process(child); | ||
295 | ret = 0; | ||
296 | break; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * make the child exit. Best I can do is send it a sigkill. | ||
301 | * perhaps it should be put in the status that it wants to | ||
302 | * exit. | ||
303 | */ | ||
304 | case PTRACE_KILL: { | ||
305 | ret = 0; | ||
306 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | ||
307 | break; | ||
308 | child->exit_code = SIGKILL; | ||
309 | /* make sure the single step bit is not set. */ | ||
310 | clear_single_step(child); | ||
311 | wake_up_process(child); | ||
312 | break; | ||
313 | } | ||
314 | |||
315 | case PTRACE_SINGLESTEP: { /* set the trap flag. */ | ||
316 | ret = -EIO; | ||
317 | if (!valid_signal(data)) | ||
318 | break; | ||
319 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
320 | set_single_step(child); | ||
321 | child->exit_code = data; | ||
322 | /* give it a chance to run. */ | ||
323 | wake_up_process(child); | ||
324 | ret = 0; | ||
325 | break; | 322 | break; |
326 | } | 323 | } |
327 | 324 | ||
@@ -334,95 +331,67 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, | |||
334 | break; | 331 | break; |
335 | } | 332 | } |
336 | 333 | ||
337 | case PTRACE_SET_DEBUGREG: | 334 | case PTRACE_GETEVENTMSG: |
338 | ret = ptrace_set_debugreg(child, addr, data); | 335 | ret = put_user(child->ptrace_message, (unsigned int __user *) data); |
339 | break; | ||
340 | |||
341 | case PTRACE_DETACH: | ||
342 | ret = ptrace_detach(child, data); | ||
343 | break; | 336 | break; |
344 | 337 | ||
345 | case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ | 338 | case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ |
346 | int i; | 339 | int ui; |
347 | unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; | 340 | if (!access_ok(VERIFY_WRITE, (void __user *)data, |
348 | unsigned int __user *tmp = (unsigned int __user *)addr; | 341 | PT_REGS_COUNT * sizeof(int))) { |
349 | 342 | ret = -EIO; | |
350 | for (i = 0; i < 32; i++) { | 343 | break; |
351 | ret = put_user(*reg, tmp); | ||
352 | if (ret) | ||
353 | break; | ||
354 | reg++; | ||
355 | tmp++; | ||
356 | } | 344 | } |
357 | break; | 345 | ret = 0; |
358 | } | 346 | for (ui = 0; ui < PT_REGS_COUNT; ui ++) { |
359 | 347 | ret |= __put_user(ptrace_get_reg(child, ui), | |
360 | case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ | 348 | (unsigned int __user *) data); |
361 | int i; | 349 | data += sizeof(int); |
362 | unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; | ||
363 | unsigned int __user *tmp = (unsigned int __user *)addr; | ||
364 | |||
365 | for (i = 0; i < 32; i++) { | ||
366 | ret = get_user(*reg, tmp); | ||
367 | if (ret) | ||
368 | break; | ||
369 | reg++; | ||
370 | tmp++; | ||
371 | } | 350 | } |
372 | break; | 351 | break; |
373 | } | 352 | } |
374 | 353 | ||
375 | case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ | 354 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ |
376 | int i; | 355 | unsigned long tmp; |
377 | unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; | 356 | int ui; |
378 | unsigned int __user *tmp = (unsigned int __user *)addr; | 357 | if (!access_ok(VERIFY_READ, (void __user *)data, |
379 | 358 | PT_REGS_COUNT * sizeof(int))) { | |
380 | flush_fp_to_thread(child); | 359 | ret = -EIO; |
381 | 360 | break; | |
382 | for (i = 0; i < 32; i++) { | ||
383 | ret = put_user(*reg, tmp); | ||
384 | if (ret) | ||
385 | break; | ||
386 | reg++; | ||
387 | tmp++; | ||
388 | } | 361 | } |
389 | break; | 362 | ret = 0; |
390 | } | 363 | for (ui = 0; ui < PT_REGS_COUNT; ui ++) { |
391 | 364 | ret = __get_user(tmp, (unsigned int __user *) data); | |
392 | case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ | ||
393 | int i; | ||
394 | unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; | ||
395 | unsigned int __user *tmp = (unsigned int __user *)addr; | ||
396 | |||
397 | flush_fp_to_thread(child); | ||
398 | |||
399 | for (i = 0; i < 32; i++) { | ||
400 | ret = get_user(*reg, tmp); | ||
401 | if (ret) | 365 | if (ret) |
402 | break; | 366 | break; |
403 | reg++; | 367 | ptrace_put_reg(child, ui, tmp); |
404 | tmp++; | 368 | data += sizeof(int); |
405 | } | 369 | } |
406 | break; | 370 | break; |
407 | } | 371 | } |
408 | 372 | ||
409 | case PTRACE_GETEVENTMSG: | 373 | case PTRACE_GETFPREGS: |
410 | ret = put_user(child->ptrace_message, (unsigned int __user *) data); | 374 | case PTRACE_SETFPREGS: |
411 | break; | ||
412 | |||
413 | #ifdef CONFIG_ALTIVEC | ||
414 | case PTRACE_GETVRREGS: | 375 | case PTRACE_GETVRREGS: |
415 | /* Get the child altivec register state. */ | 376 | case PTRACE_SETVRREGS: |
416 | flush_altivec_to_thread(child); | 377 | case PTRACE_GETREGS64: |
417 | ret = get_vrregs((unsigned long __user *)data, child); | 378 | case PTRACE_SETREGS64: |
379 | case PPC_PTRACE_GETFPREGS: | ||
380 | case PPC_PTRACE_SETFPREGS: | ||
381 | case PTRACE_KILL: | ||
382 | case PTRACE_SINGLESTEP: | ||
383 | case PTRACE_DETACH: | ||
384 | case PTRACE_SET_DEBUGREG: | ||
385 | case PTRACE_SYSCALL: | ||
386 | case PTRACE_CONT: | ||
387 | ret = arch_ptrace(child, request, addr, data); | ||
418 | break; | 388 | break; |
419 | 389 | ||
420 | case PTRACE_SETVRREGS: | 390 | /* Old reverse args ptrace callss */ |
421 | /* Set the child altivec register state. */ | 391 | case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ |
422 | flush_altivec_to_thread(child); | 392 | case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ |
423 | ret = set_vrregs(child, (unsigned long __user *)data); | 393 | ret = compat_ptrace_old(child, request, addr, data); |
424 | break; | 394 | break; |
425 | #endif | ||
426 | 395 | ||
427 | default: | 396 | default: |
428 | ret = ptrace_request(child, request, addr, data); | 397 | ret = ptrace_request(child, request, addr, data); |