diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2010-02-11 14:51:00 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-11 18:08:33 -0500 |
commit | 2225a122ae26d542bdce523d9d87a4a7ba10e07b (patch) | |
tree | 861117cc1711cdf3c10f76212afe2e57b05d34c4 /kernel/ptrace.c | |
parent | 5b3efd500854d45d305b53c54c97db5970959980 (diff) |
ptrace: Add support for generic PTRACE_GETREGSET/PTRACE_SETREGSET
Generic support for PTRACE_GETREGSET/PTRACE_SETREGSET commands which
export the regsets supported by each architecture using the correponding
NT_* types. These NT_* types are already part of the userland ABI, used
in representing the architecture specific register sets as different NOTES
in an ELF core file.
'addr' parameter for the ptrace system call encode the REGSET type (using
the corresppnding NT_* type) and the 'data' parameter points to the
struct iovec having the user buffer and the length of that buffer.
struct iovec iov = { buf, len};
ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov);
On successful completion, iov.len will be updated by the kernel specifying
how much the kernel has written/read to/from the user's iov.buf.
x86 extended state registers are primarily exported using this interface.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
LKML-Reference: <20100211195614.886724710@sbs-t61.sc.intel.com>
Acked-by: Hongjiu Lu <hjl.tools@gmail.com>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 23bd09cd042e..13b4554d8fbb 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/pid_namespace.h> | 22 | #include <linux/pid_namespace.h> |
23 | #include <linux/syscalls.h> | 23 | #include <linux/syscalls.h> |
24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | #include <linux/regset.h> | ||
25 | 26 | ||
26 | 27 | ||
27 | /* | 28 | /* |
@@ -511,6 +512,47 @@ static int ptrace_resume(struct task_struct *child, long request, long data) | |||
511 | return 0; | 512 | return 0; |
512 | } | 513 | } |
513 | 514 | ||
515 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
516 | |||
517 | static const struct user_regset * | ||
518 | find_regset(const struct user_regset_view *view, unsigned int type) | ||
519 | { | ||
520 | const struct user_regset *regset; | ||
521 | int n; | ||
522 | |||
523 | for (n = 0; n < view->n; ++n) { | ||
524 | regset = view->regsets + n; | ||
525 | if (regset->core_note_type == type) | ||
526 | return regset; | ||
527 | } | ||
528 | |||
529 | return NULL; | ||
530 | } | ||
531 | |||
532 | static int ptrace_regset(struct task_struct *task, int req, unsigned int type, | ||
533 | struct iovec *kiov) | ||
534 | { | ||
535 | const struct user_regset_view *view = task_user_regset_view(task); | ||
536 | const struct user_regset *regset = find_regset(view, type); | ||
537 | int regset_no; | ||
538 | |||
539 | if (!regset || (kiov->iov_len % regset->size) != 0) | ||
540 | return -EIO; | ||
541 | |||
542 | regset_no = regset - view->regsets; | ||
543 | kiov->iov_len = min(kiov->iov_len, | ||
544 | (__kernel_size_t) (regset->n * regset->size)); | ||
545 | |||
546 | if (req == PTRACE_GETREGSET) | ||
547 | return copy_regset_to_user(task, view, regset_no, 0, | ||
548 | kiov->iov_len, kiov->iov_base); | ||
549 | else | ||
550 | return copy_regset_from_user(task, view, regset_no, 0, | ||
551 | kiov->iov_len, kiov->iov_base); | ||
552 | } | ||
553 | |||
554 | #endif | ||
555 | |||
514 | int ptrace_request(struct task_struct *child, long request, | 556 | int ptrace_request(struct task_struct *child, long request, |
515 | long addr, long data) | 557 | long addr, long data) |
516 | { | 558 | { |
@@ -573,6 +615,26 @@ int ptrace_request(struct task_struct *child, long request, | |||
573 | return 0; | 615 | return 0; |
574 | return ptrace_resume(child, request, SIGKILL); | 616 | return ptrace_resume(child, request, SIGKILL); |
575 | 617 | ||
618 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
619 | case PTRACE_GETREGSET: | ||
620 | case PTRACE_SETREGSET: | ||
621 | { | ||
622 | struct iovec kiov; | ||
623 | struct iovec __user *uiov = (struct iovec __user *) data; | ||
624 | |||
625 | if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov))) | ||
626 | return -EFAULT; | ||
627 | |||
628 | if (__get_user(kiov.iov_base, &uiov->iov_base) || | ||
629 | __get_user(kiov.iov_len, &uiov->iov_len)) | ||
630 | return -EFAULT; | ||
631 | |||
632 | ret = ptrace_regset(child, request, addr, &kiov); | ||
633 | if (!ret) | ||
634 | ret = __put_user(kiov.iov_len, &uiov->iov_len); | ||
635 | break; | ||
636 | } | ||
637 | #endif | ||
576 | default: | 638 | default: |
577 | break; | 639 | break; |
578 | } | 640 | } |
@@ -711,6 +773,32 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
711 | else | 773 | else |
712 | ret = ptrace_setsiginfo(child, &siginfo); | 774 | ret = ptrace_setsiginfo(child, &siginfo); |
713 | break; | 775 | break; |
776 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
777 | case PTRACE_GETREGSET: | ||
778 | case PTRACE_SETREGSET: | ||
779 | { | ||
780 | struct iovec kiov; | ||
781 | struct compat_iovec __user *uiov = | ||
782 | (struct compat_iovec __user *) datap; | ||
783 | compat_uptr_t ptr; | ||
784 | compat_size_t len; | ||
785 | |||
786 | if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov))) | ||
787 | return -EFAULT; | ||
788 | |||
789 | if (__get_user(ptr, &uiov->iov_base) || | ||
790 | __get_user(len, &uiov->iov_len)) | ||
791 | return -EFAULT; | ||
792 | |||
793 | kiov.iov_base = compat_ptr(ptr); | ||
794 | kiov.iov_len = len; | ||
795 | |||
796 | ret = ptrace_regset(child, request, addr, &kiov); | ||
797 | if (!ret) | ||
798 | ret = __put_user(kiov.iov_len, &uiov->iov_len); | ||
799 | break; | ||
800 | } | ||
801 | #endif | ||
714 | 802 | ||
715 | default: | 803 | default: |
716 | ret = ptrace_request(child, request, addr, data); | 804 | ret = ptrace_request(child, request, addr, data); |