diff options
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/ptrace-common.h | 23 | ||||
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 148 | ||||
-rw-r--r-- | arch/powerpc/kernel/ptrace32.c | 204 |
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 | ||
55 | static 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 | |||
66 | static 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 | */ | ||
66 | static 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 | |||
62 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 118 | long 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 | */ | ||
48 | static 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 | |||
44 | long compat_sys_ptrace(int request, int pid, unsigned long addr, | 88 | long 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); |