diff options
author | Roland McGrath <roland@redhat.com> | 2007-12-20 06:58:08 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-02-07 04:40:16 -0500 |
commit | fa8f5cb0c980e9fe3e04bc937fbd13417b52c046 (patch) | |
tree | 22bb8496ac654e57b134223e23ea899fbb5cfb6e /arch/powerpc/kernel/ptrace.c | |
parent | 80fdf4709497a276a826c9d8426ef1effc8f8e33 (diff) |
[POWERPC] Add user_regset compat support
This extends task_user_regset_view CONFIG_PPC64 with support for the
32-bit view of register state, compatible with what a CONFIG_PPC32
kernel provides. This will enable generic machine-independent code to
access user-mode threads' registers for debugging and dumping.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/ptrace.c')
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index eb00274e84ba..60de9ee3701d 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
@@ -501,8 +501,170 @@ static const struct user_regset_view user_ppc_native_view = { | |||
501 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) | 501 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) |
502 | }; | 502 | }; |
503 | 503 | ||
504 | #ifdef CONFIG_PPC64 | ||
505 | #include <linux/compat.h> | ||
506 | |||
507 | static int gpr32_get(struct task_struct *target, | ||
508 | const struct user_regset *regset, | ||
509 | unsigned int pos, unsigned int count, | ||
510 | void *kbuf, void __user *ubuf) | ||
511 | { | ||
512 | const unsigned long *regs = &target->thread.regs->gpr[0]; | ||
513 | compat_ulong_t *k = kbuf; | ||
514 | compat_ulong_t __user *u = ubuf; | ||
515 | compat_ulong_t reg; | ||
516 | |||
517 | if (target->thread.regs == NULL) | ||
518 | return -EIO; | ||
519 | |||
520 | CHECK_FULL_REGS(target->thread.regs); | ||
521 | |||
522 | pos /= sizeof(reg); | ||
523 | count /= sizeof(reg); | ||
524 | |||
525 | if (kbuf) | ||
526 | for (; count > 0 && pos < PT_MSR; --count) | ||
527 | *k++ = regs[pos++]; | ||
528 | else | ||
529 | for (; count > 0 && pos < PT_MSR; --count) | ||
530 | if (__put_user((compat_ulong_t) regs[pos++], u++)) | ||
531 | return -EFAULT; | ||
532 | |||
533 | if (count > 0 && pos == PT_MSR) { | ||
534 | reg = get_user_msr(target); | ||
535 | if (kbuf) | ||
536 | *k++ = reg; | ||
537 | else if (__put_user(reg, u++)) | ||
538 | return -EFAULT; | ||
539 | ++pos; | ||
540 | --count; | ||
541 | } | ||
542 | |||
543 | if (kbuf) | ||
544 | for (; count > 0 && pos < PT_REGS_COUNT; --count) | ||
545 | *k++ = regs[pos++]; | ||
546 | else | ||
547 | for (; count > 0 && pos < PT_REGS_COUNT; --count) | ||
548 | if (__put_user((compat_ulong_t) regs[pos++], u++)) | ||
549 | return -EFAULT; | ||
550 | |||
551 | kbuf = k; | ||
552 | ubuf = u; | ||
553 | pos *= sizeof(reg); | ||
554 | count *= sizeof(reg); | ||
555 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
556 | PT_REGS_COUNT * sizeof(reg), -1); | ||
557 | } | ||
558 | |||
559 | static int gpr32_set(struct task_struct *target, | ||
560 | const struct user_regset *regset, | ||
561 | unsigned int pos, unsigned int count, | ||
562 | const void *kbuf, const void __user *ubuf) | ||
563 | { | ||
564 | unsigned long *regs = &target->thread.regs->gpr[0]; | ||
565 | const compat_ulong_t *k = kbuf; | ||
566 | const compat_ulong_t __user *u = ubuf; | ||
567 | compat_ulong_t reg; | ||
568 | |||
569 | if (target->thread.regs == NULL) | ||
570 | return -EIO; | ||
571 | |||
572 | CHECK_FULL_REGS(target->thread.regs); | ||
573 | |||
574 | pos /= sizeof(reg); | ||
575 | count /= sizeof(reg); | ||
576 | |||
577 | if (kbuf) | ||
578 | for (; count > 0 && pos < PT_MSR; --count) | ||
579 | regs[pos++] = *k++; | ||
580 | else | ||
581 | for (; count > 0 && pos < PT_MSR; --count) { | ||
582 | if (__get_user(reg, u++)) | ||
583 | return -EFAULT; | ||
584 | regs[pos++] = reg; | ||
585 | } | ||
586 | |||
587 | |||
588 | if (count > 0 && pos == PT_MSR) { | ||
589 | if (kbuf) | ||
590 | reg = *k++; | ||
591 | else if (__get_user(reg, u++)) | ||
592 | return -EFAULT; | ||
593 | set_user_msr(target, reg); | ||
594 | ++pos; | ||
595 | --count; | ||
596 | } | ||
597 | |||
598 | if (kbuf) | ||
599 | for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) | ||
600 | regs[pos++] = *k++; | ||
601 | else | ||
602 | for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) { | ||
603 | if (__get_user(reg, u++)) | ||
604 | return -EFAULT; | ||
605 | regs[pos++] = reg; | ||
606 | } | ||
607 | |||
608 | if (count > 0 && pos == PT_TRAP) { | ||
609 | if (kbuf) | ||
610 | reg = *k++; | ||
611 | else if (__get_user(reg, u++)) | ||
612 | return -EFAULT; | ||
613 | set_user_trap(target, reg); | ||
614 | ++pos; | ||
615 | --count; | ||
616 | } | ||
617 | |||
618 | kbuf = k; | ||
619 | ubuf = u; | ||
620 | pos *= sizeof(reg); | ||
621 | count *= sizeof(reg); | ||
622 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
623 | (PT_TRAP + 1) * sizeof(reg), -1); | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * These are the regset flavors matching the CONFIG_PPC32 native set. | ||
628 | */ | ||
629 | static const struct user_regset compat_regsets[] = { | ||
630 | [REGSET_GPR] = { | ||
631 | .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, | ||
632 | .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), | ||
633 | .get = gpr32_get, .set = gpr32_set | ||
634 | }, | ||
635 | [REGSET_FPR] = { | ||
636 | .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, | ||
637 | .size = sizeof(double), .align = sizeof(double), | ||
638 | .get = fpr_get, .set = fpr_set | ||
639 | }, | ||
640 | #ifdef CONFIG_ALTIVEC | ||
641 | [REGSET_VMX] = { | ||
642 | .core_note_type = NT_PPC_VMX, .n = 34, | ||
643 | .size = sizeof(vector128), .align = sizeof(vector128), | ||
644 | .active = vr_active, .get = vr_get, .set = vr_set | ||
645 | }, | ||
646 | #endif | ||
647 | #ifdef CONFIG_SPE | ||
648 | [REGSET_SPE] = { | ||
649 | .n = 35, | ||
650 | .size = sizeof(u32), .align = sizeof(u32), | ||
651 | .active = evr_active, .get = evr_get, .set = evr_set | ||
652 | }, | ||
653 | #endif | ||
654 | }; | ||
655 | |||
656 | static const struct user_regset_view user_ppc_compat_view = { | ||
657 | .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI, | ||
658 | .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) | ||
659 | }; | ||
660 | #endif /* CONFIG_PPC64 */ | ||
661 | |||
504 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 662 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
505 | { | 663 | { |
664 | #ifdef CONFIG_PPC64 | ||
665 | if (test_tsk_thread_flag(task, TIF_32BIT)) | ||
666 | return &user_ppc_compat_view; | ||
667 | #endif | ||
506 | return &user_ppc_native_view; | 668 | return &user_ppc_native_view; |
507 | } | 669 | } |
508 | 670 | ||