aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2010-02-11 14:51:00 -0500
committerH. Peter Anvin <hpa@zytor.com>2010-02-11 18:08:33 -0500
commit2225a122ae26d542bdce523d9d87a4a7ba10e07b (patch)
tree861117cc1711cdf3c10f76212afe2e57b05d34c4
parent5b3efd500854d45d305b53c54c97db5970959980 (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>
-rw-r--r--include/linux/elf.h6
-rw-r--r--include/linux/ptrace.h15
-rw-r--r--kernel/ptrace.c88
3 files changed, 108 insertions, 1 deletions
diff --git a/include/linux/elf.h b/include/linux/elf.h
index a8c4af073ce9..d8e6e61ad9ff 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -349,7 +349,11 @@ typedef struct elf64_shdr {
349#define ELF_OSABI ELFOSABI_NONE 349#define ELF_OSABI ELFOSABI_NONE
350#endif 350#endif
351 351
352/* Notes used in ET_CORE */ 352/*
353 * Notes used in ET_CORE. Architectures export some of the arch register sets
354 * using the corresponding note types via the PTRACE_GETREGSET and
355 * PTRACE_SETREGSET requests.
356 */
353#define NT_PRSTATUS 1 357#define NT_PRSTATUS 1
354#define NT_PRFPREG 2 358#define NT_PRFPREG 2
355#define NT_PRPSINFO 3 359#define NT_PRPSINFO 3
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 56f2d63a5cbb..dbfa821d5a6e 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -27,6 +27,21 @@
27#define PTRACE_GETSIGINFO 0x4202 27#define PTRACE_GETSIGINFO 0x4202
28#define PTRACE_SETSIGINFO 0x4203 28#define PTRACE_SETSIGINFO 0x4203
29 29
30/*
31 * Generic ptrace interface that exports the architecture specific regsets
32 * using the corresponding NT_* types (which are also used in the core dump).
33 *
34 * This interface usage is as follows:
35 * struct iovec iov = { buf, len};
36 *
37 * ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov);
38 *
39 * On the successful completion, iov.len will be updated by the kernel,
40 * specifying how much the kernel has written/read to/from the user's iov.buf.
41 */
42#define PTRACE_GETREGSET 0x4204
43#define PTRACE_SETREGSET 0x4205
44
30/* options set using PTRACE_SETOPTIONS */ 45/* options set using PTRACE_SETOPTIONS */
31#define PTRACE_O_TRACESYSGOOD 0x00000001 46#define PTRACE_O_TRACESYSGOOD 0x00000001
32#define PTRACE_O_TRACEFORK 0x00000002 47#define PTRACE_O_TRACEFORK 0x00000002
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
517static const struct user_regset *
518find_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
532static 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
514int ptrace_request(struct task_struct *child, long request, 556int 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);