diff options
Diffstat (limited to 'arch/mips/kernel/ptrace.c')
-rw-r--r-- | arch/mips/kernel/ptrace.c | 223 |
1 files changed, 177 insertions, 46 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index f639ccd5060c..645b3c4fcfba 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <linux/ptrace.h> | 24 | #include <linux/ptrace.h> |
25 | #include <linux/regset.h> | 25 | #include <linux/regset.h> |
26 | #include <linux/smp.h> | 26 | #include <linux/smp.h> |
27 | #include <linux/user.h> | ||
28 | #include <linux/security.h> | 27 | #include <linux/security.h> |
29 | #include <linux/tracehook.h> | 28 | #include <linux/tracehook.h> |
30 | #include <linux/audit.h> | 29 | #include <linux/audit.h> |
@@ -63,7 +62,7 @@ void ptrace_disable(struct task_struct *child) | |||
63 | * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. | 62 | * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. |
64 | * Registers are sign extended to fill the available space. | 63 | * Registers are sign extended to fill the available space. |
65 | */ | 64 | */ |
66 | int ptrace_getregs(struct task_struct *child, __s64 __user *data) | 65 | int ptrace_getregs(struct task_struct *child, struct user_pt_regs __user *data) |
67 | { | 66 | { |
68 | struct pt_regs *regs; | 67 | struct pt_regs *regs; |
69 | int i; | 68 | int i; |
@@ -74,13 +73,13 @@ int ptrace_getregs(struct task_struct *child, __s64 __user *data) | |||
74 | regs = task_pt_regs(child); | 73 | regs = task_pt_regs(child); |
75 | 74 | ||
76 | for (i = 0; i < 32; i++) | 75 | for (i = 0; i < 32; i++) |
77 | __put_user((long)regs->regs[i], data + i); | 76 | __put_user((long)regs->regs[i], (__s64 __user *)&data->regs[i]); |
78 | __put_user((long)regs->lo, data + EF_LO - EF_R0); | 77 | __put_user((long)regs->lo, (__s64 __user *)&data->lo); |
79 | __put_user((long)regs->hi, data + EF_HI - EF_R0); | 78 | __put_user((long)regs->hi, (__s64 __user *)&data->hi); |
80 | __put_user((long)regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | 79 | __put_user((long)regs->cp0_epc, (__s64 __user *)&data->cp0_epc); |
81 | __put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0); | 80 | __put_user((long)regs->cp0_badvaddr, (__s64 __user *)&data->cp0_badvaddr); |
82 | __put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0); | 81 | __put_user((long)regs->cp0_status, (__s64 __user *)&data->cp0_status); |
83 | __put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0); | 82 | __put_user((long)regs->cp0_cause, (__s64 __user *)&data->cp0_cause); |
84 | 83 | ||
85 | return 0; | 84 | return 0; |
86 | } | 85 | } |
@@ -90,7 +89,7 @@ int ptrace_getregs(struct task_struct *child, __s64 __user *data) | |||
90 | * the 64-bit format. On a 32-bit kernel only the lower order half | 89 | * the 64-bit format. On a 32-bit kernel only the lower order half |
91 | * (according to endianness) will be used. | 90 | * (according to endianness) will be used. |
92 | */ | 91 | */ |
93 | int ptrace_setregs(struct task_struct *child, __s64 __user *data) | 92 | int ptrace_setregs(struct task_struct *child, struct user_pt_regs __user *data) |
94 | { | 93 | { |
95 | struct pt_regs *regs; | 94 | struct pt_regs *regs; |
96 | int i; | 95 | int i; |
@@ -101,10 +100,10 @@ int ptrace_setregs(struct task_struct *child, __s64 __user *data) | |||
101 | regs = task_pt_regs(child); | 100 | regs = task_pt_regs(child); |
102 | 101 | ||
103 | for (i = 0; i < 32; i++) | 102 | for (i = 0; i < 32; i++) |
104 | __get_user(regs->regs[i], data + i); | 103 | __get_user(regs->regs[i], (__s64 __user *)&data->regs[i]); |
105 | __get_user(regs->lo, data + EF_LO - EF_R0); | 104 | __get_user(regs->lo, (__s64 __user *)&data->lo); |
106 | __get_user(regs->hi, data + EF_HI - EF_R0); | 105 | __get_user(regs->hi, (__s64 __user *)&data->hi); |
107 | __get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | 106 | __get_user(regs->cp0_epc, (__s64 __user *)&data->cp0_epc); |
108 | 107 | ||
109 | /* badvaddr, status, and cause may not be written. */ | 108 | /* badvaddr, status, and cause may not be written. */ |
110 | 109 | ||
@@ -129,7 +128,7 @@ int ptrace_getfpregs(struct task_struct *child, __u32 __user *data) | |||
129 | } | 128 | } |
130 | 129 | ||
131 | __put_user(child->thread.fpu.fcr31, data + 64); | 130 | __put_user(child->thread.fpu.fcr31, data + 64); |
132 | __put_user(current_cpu_data.fpu_id, data + 65); | 131 | __put_user(boot_cpu_data.fpu_id, data + 65); |
133 | 132 | ||
134 | return 0; | 133 | return 0; |
135 | } | 134 | } |
@@ -151,6 +150,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) | |||
151 | } | 150 | } |
152 | 151 | ||
153 | __get_user(child->thread.fpu.fcr31, data + 64); | 152 | __get_user(child->thread.fpu.fcr31, data + 64); |
153 | child->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; | ||
154 | 154 | ||
155 | /* FIR may not be written. */ | 155 | /* FIR may not be written. */ |
156 | 156 | ||
@@ -246,36 +246,160 @@ int ptrace_set_watch_regs(struct task_struct *child, | |||
246 | 246 | ||
247 | /* regset get/set implementations */ | 247 | /* regset get/set implementations */ |
248 | 248 | ||
249 | static int gpr_get(struct task_struct *target, | 249 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) |
250 | const struct user_regset *regset, | 250 | |
251 | unsigned int pos, unsigned int count, | 251 | static int gpr32_get(struct task_struct *target, |
252 | void *kbuf, void __user *ubuf) | 252 | const struct user_regset *regset, |
253 | unsigned int pos, unsigned int count, | ||
254 | void *kbuf, void __user *ubuf) | ||
253 | { | 255 | { |
254 | struct pt_regs *regs = task_pt_regs(target); | 256 | struct pt_regs *regs = task_pt_regs(target); |
257 | u32 uregs[ELF_NGREG] = {}; | ||
258 | unsigned i; | ||
259 | |||
260 | for (i = MIPS32_EF_R1; i <= MIPS32_EF_R31; i++) { | ||
261 | /* k0/k1 are copied as zero. */ | ||
262 | if (i == MIPS32_EF_R26 || i == MIPS32_EF_R27) | ||
263 | continue; | ||
264 | |||
265 | uregs[i] = regs->regs[i - MIPS32_EF_R0]; | ||
266 | } | ||
255 | 267 | ||
256 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 268 | uregs[MIPS32_EF_LO] = regs->lo; |
257 | regs, 0, sizeof(*regs)); | 269 | uregs[MIPS32_EF_HI] = regs->hi; |
270 | uregs[MIPS32_EF_CP0_EPC] = regs->cp0_epc; | ||
271 | uregs[MIPS32_EF_CP0_BADVADDR] = regs->cp0_badvaddr; | ||
272 | uregs[MIPS32_EF_CP0_STATUS] = regs->cp0_status; | ||
273 | uregs[MIPS32_EF_CP0_CAUSE] = regs->cp0_cause; | ||
274 | |||
275 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, | ||
276 | sizeof(uregs)); | ||
258 | } | 277 | } |
259 | 278 | ||
260 | static int gpr_set(struct task_struct *target, | 279 | static int gpr32_set(struct task_struct *target, |
261 | const struct user_regset *regset, | 280 | const struct user_regset *regset, |
262 | unsigned int pos, unsigned int count, | 281 | unsigned int pos, unsigned int count, |
263 | const void *kbuf, const void __user *ubuf) | 282 | const void *kbuf, const void __user *ubuf) |
264 | { | 283 | { |
265 | struct pt_regs newregs; | 284 | struct pt_regs *regs = task_pt_regs(target); |
266 | int ret; | 285 | u32 uregs[ELF_NGREG]; |
286 | unsigned start, num_regs, i; | ||
287 | int err; | ||
288 | |||
289 | start = pos / sizeof(u32); | ||
290 | num_regs = count / sizeof(u32); | ||
291 | |||
292 | if (start + num_regs > ELF_NGREG) | ||
293 | return -EIO; | ||
294 | |||
295 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0, | ||
296 | sizeof(uregs)); | ||
297 | if (err) | ||
298 | return err; | ||
299 | |||
300 | for (i = start; i < num_regs; i++) { | ||
301 | /* | ||
302 | * Cast all values to signed here so that if this is a 64-bit | ||
303 | * kernel, the supplied 32-bit values will be sign extended. | ||
304 | */ | ||
305 | switch (i) { | ||
306 | case MIPS32_EF_R1 ... MIPS32_EF_R25: | ||
307 | /* k0/k1 are ignored. */ | ||
308 | case MIPS32_EF_R28 ... MIPS32_EF_R31: | ||
309 | regs->regs[i - MIPS32_EF_R0] = (s32)uregs[i]; | ||
310 | break; | ||
311 | case MIPS32_EF_LO: | ||
312 | regs->lo = (s32)uregs[i]; | ||
313 | break; | ||
314 | case MIPS32_EF_HI: | ||
315 | regs->hi = (s32)uregs[i]; | ||
316 | break; | ||
317 | case MIPS32_EF_CP0_EPC: | ||
318 | regs->cp0_epc = (s32)uregs[i]; | ||
319 | break; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | #endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */ | ||
327 | |||
328 | #ifdef CONFIG_64BIT | ||
329 | |||
330 | static int gpr64_get(struct task_struct *target, | ||
331 | const struct user_regset *regset, | ||
332 | unsigned int pos, unsigned int count, | ||
333 | void *kbuf, void __user *ubuf) | ||
334 | { | ||
335 | struct pt_regs *regs = task_pt_regs(target); | ||
336 | u64 uregs[ELF_NGREG] = {}; | ||
337 | unsigned i; | ||
338 | |||
339 | for (i = MIPS64_EF_R1; i <= MIPS64_EF_R31; i++) { | ||
340 | /* k0/k1 are copied as zero. */ | ||
341 | if (i == MIPS64_EF_R26 || i == MIPS64_EF_R27) | ||
342 | continue; | ||
343 | |||
344 | uregs[i] = regs->regs[i - MIPS64_EF_R0]; | ||
345 | } | ||
346 | |||
347 | uregs[MIPS64_EF_LO] = regs->lo; | ||
348 | uregs[MIPS64_EF_HI] = regs->hi; | ||
349 | uregs[MIPS64_EF_CP0_EPC] = regs->cp0_epc; | ||
350 | uregs[MIPS64_EF_CP0_BADVADDR] = regs->cp0_badvaddr; | ||
351 | uregs[MIPS64_EF_CP0_STATUS] = regs->cp0_status; | ||
352 | uregs[MIPS64_EF_CP0_CAUSE] = regs->cp0_cause; | ||
353 | |||
354 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, | ||
355 | sizeof(uregs)); | ||
356 | } | ||
267 | 357 | ||
268 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 358 | static int gpr64_set(struct task_struct *target, |
269 | &newregs, | 359 | const struct user_regset *regset, |
270 | 0, sizeof(newregs)); | 360 | unsigned int pos, unsigned int count, |
271 | if (ret) | 361 | const void *kbuf, const void __user *ubuf) |
272 | return ret; | 362 | { |
363 | struct pt_regs *regs = task_pt_regs(target); | ||
364 | u64 uregs[ELF_NGREG]; | ||
365 | unsigned start, num_regs, i; | ||
366 | int err; | ||
367 | |||
368 | start = pos / sizeof(u64); | ||
369 | num_regs = count / sizeof(u64); | ||
273 | 370 | ||
274 | *task_pt_regs(target) = newregs; | 371 | if (start + num_regs > ELF_NGREG) |
372 | return -EIO; | ||
373 | |||
374 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0, | ||
375 | sizeof(uregs)); | ||
376 | if (err) | ||
377 | return err; | ||
378 | |||
379 | for (i = start; i < num_regs; i++) { | ||
380 | switch (i) { | ||
381 | case MIPS64_EF_R1 ... MIPS64_EF_R25: | ||
382 | /* k0/k1 are ignored. */ | ||
383 | case MIPS64_EF_R28 ... MIPS64_EF_R31: | ||
384 | regs->regs[i - MIPS64_EF_R0] = uregs[i]; | ||
385 | break; | ||
386 | case MIPS64_EF_LO: | ||
387 | regs->lo = uregs[i]; | ||
388 | break; | ||
389 | case MIPS64_EF_HI: | ||
390 | regs->hi = uregs[i]; | ||
391 | break; | ||
392 | case MIPS64_EF_CP0_EPC: | ||
393 | regs->cp0_epc = uregs[i]; | ||
394 | break; | ||
395 | } | ||
396 | } | ||
275 | 397 | ||
276 | return 0; | 398 | return 0; |
277 | } | 399 | } |
278 | 400 | ||
401 | #endif /* CONFIG_64BIT */ | ||
402 | |||
279 | static int fpr_get(struct task_struct *target, | 403 | static int fpr_get(struct task_struct *target, |
280 | const struct user_regset *regset, | 404 | const struct user_regset *regset, |
281 | unsigned int pos, unsigned int count, | 405 | unsigned int pos, unsigned int count, |
@@ -337,14 +461,16 @@ enum mips_regset { | |||
337 | REGSET_FPR, | 461 | REGSET_FPR, |
338 | }; | 462 | }; |
339 | 463 | ||
464 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) | ||
465 | |||
340 | static const struct user_regset mips_regsets[] = { | 466 | static const struct user_regset mips_regsets[] = { |
341 | [REGSET_GPR] = { | 467 | [REGSET_GPR] = { |
342 | .core_note_type = NT_PRSTATUS, | 468 | .core_note_type = NT_PRSTATUS, |
343 | .n = ELF_NGREG, | 469 | .n = ELF_NGREG, |
344 | .size = sizeof(unsigned int), | 470 | .size = sizeof(unsigned int), |
345 | .align = sizeof(unsigned int), | 471 | .align = sizeof(unsigned int), |
346 | .get = gpr_get, | 472 | .get = gpr32_get, |
347 | .set = gpr_set, | 473 | .set = gpr32_set, |
348 | }, | 474 | }, |
349 | [REGSET_FPR] = { | 475 | [REGSET_FPR] = { |
350 | .core_note_type = NT_PRFPREG, | 476 | .core_note_type = NT_PRFPREG, |
@@ -364,14 +490,18 @@ static const struct user_regset_view user_mips_view = { | |||
364 | .n = ARRAY_SIZE(mips_regsets), | 490 | .n = ARRAY_SIZE(mips_regsets), |
365 | }; | 491 | }; |
366 | 492 | ||
493 | #endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */ | ||
494 | |||
495 | #ifdef CONFIG_64BIT | ||
496 | |||
367 | static const struct user_regset mips64_regsets[] = { | 497 | static const struct user_regset mips64_regsets[] = { |
368 | [REGSET_GPR] = { | 498 | [REGSET_GPR] = { |
369 | .core_note_type = NT_PRSTATUS, | 499 | .core_note_type = NT_PRSTATUS, |
370 | .n = ELF_NGREG, | 500 | .n = ELF_NGREG, |
371 | .size = sizeof(unsigned long), | 501 | .size = sizeof(unsigned long), |
372 | .align = sizeof(unsigned long), | 502 | .align = sizeof(unsigned long), |
373 | .get = gpr_get, | 503 | .get = gpr64_get, |
374 | .set = gpr_set, | 504 | .set = gpr64_set, |
375 | }, | 505 | }, |
376 | [REGSET_FPR] = { | 506 | [REGSET_FPR] = { |
377 | .core_note_type = NT_PRFPREG, | 507 | .core_note_type = NT_PRFPREG, |
@@ -384,25 +514,26 @@ static const struct user_regset mips64_regsets[] = { | |||
384 | }; | 514 | }; |
385 | 515 | ||
386 | static const struct user_regset_view user_mips64_view = { | 516 | static const struct user_regset_view user_mips64_view = { |
387 | .name = "mips", | 517 | .name = "mips64", |
388 | .e_machine = ELF_ARCH, | 518 | .e_machine = ELF_ARCH, |
389 | .ei_osabi = ELF_OSABI, | 519 | .ei_osabi = ELF_OSABI, |
390 | .regsets = mips64_regsets, | 520 | .regsets = mips64_regsets, |
391 | .n = ARRAY_SIZE(mips_regsets), | 521 | .n = ARRAY_SIZE(mips64_regsets), |
392 | }; | 522 | }; |
393 | 523 | ||
524 | #endif /* CONFIG_64BIT */ | ||
525 | |||
394 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 526 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
395 | { | 527 | { |
396 | #ifdef CONFIG_32BIT | 528 | #ifdef CONFIG_32BIT |
397 | return &user_mips_view; | 529 | return &user_mips_view; |
398 | #endif | 530 | #else |
399 | |||
400 | #ifdef CONFIG_MIPS32_O32 | 531 | #ifdef CONFIG_MIPS32_O32 |
401 | if (test_thread_flag(TIF_32BIT_REGS)) | 532 | if (test_tsk_thread_flag(task, TIF_32BIT_REGS)) |
402 | return &user_mips_view; | 533 | return &user_mips_view; |
403 | #endif | 534 | #endif |
404 | |||
405 | return &user_mips64_view; | 535 | return &user_mips64_view; |
536 | #endif | ||
406 | } | 537 | } |
407 | 538 | ||
408 | long arch_ptrace(struct task_struct *child, long request, | 539 | long arch_ptrace(struct task_struct *child, long request, |
@@ -480,7 +611,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
480 | break; | 611 | break; |
481 | case FPC_EIR: | 612 | case FPC_EIR: |
482 | /* implementation / version register */ | 613 | /* implementation / version register */ |
483 | tmp = current_cpu_data.fpu_id; | 614 | tmp = boot_cpu_data.fpu_id; |
484 | break; | 615 | break; |
485 | case DSP_BASE ... DSP_BASE + 5: { | 616 | case DSP_BASE ... DSP_BASE + 5: { |
486 | dspreg_t *dregs; | 617 | dspreg_t *dregs; |
@@ -565,7 +696,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
565 | break; | 696 | break; |
566 | #endif | 697 | #endif |
567 | case FPC_CSR: | 698 | case FPC_CSR: |
568 | child->thread.fpu.fcr31 = data; | 699 | child->thread.fpu.fcr31 = data & ~FPU_CSR_ALL_X; |
569 | break; | 700 | break; |
570 | case DSP_BASE ... DSP_BASE + 5: { | 701 | case DSP_BASE ... DSP_BASE + 5: { |
571 | dspreg_t *dregs; | 702 | dspreg_t *dregs; |