aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/ptrace.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2010-05-17 04:00:05 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-05-17 04:00:15 -0400
commit86f2552bbd0e17b19bb5e9881042533eaea553c7 (patch)
treef520909d953f5d8c7db3dd4c4f2582bd7f7473aa /arch/s390/kernel/ptrace.c
parentcd3b70f5d4d82f85d1e1d6e822f38ae098cf7c72 (diff)
[S390] add breaking event address for user space
Copy the last breaking event address from the lowcore to a new field in the thread_struct on each system entry. Add a new ptrace request PTRACE_GET_LAST_BREAK and a new utrace regset REGSET_LAST_BREAK to query the last breaking event. This is useful for debugging wild branches in user space code. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/ptrace.c')
-rw-r--r--arch/s390/kernel/ptrace.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 9f654da4cecc..83339d33c4b1 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -57,6 +57,7 @@
57enum s390_regset { 57enum s390_regset {
58 REGSET_GENERAL, 58 REGSET_GENERAL,
59 REGSET_FP, 59 REGSET_FP,
60 REGSET_LAST_BREAK,
60 REGSET_GENERAL_EXTENDED, 61 REGSET_GENERAL_EXTENDED,
61}; 62};
62 63
@@ -381,6 +382,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
381 copied += sizeof(unsigned long); 382 copied += sizeof(unsigned long);
382 } 383 }
383 return 0; 384 return 0;
385 case PTRACE_GET_LAST_BREAK:
386 put_user(task_thread_info(child)->last_break,
387 (unsigned long __user *) data);
388 return 0;
384 default: 389 default:
385 /* Removing high order bit from addr (only for 31 bit). */ 390 /* Removing high order bit from addr (only for 31 bit). */
386 addr &= PSW_ADDR_INSN; 391 addr &= PSW_ADDR_INSN;
@@ -633,6 +638,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
633 copied += sizeof(unsigned int); 638 copied += sizeof(unsigned int);
634 } 639 }
635 return 0; 640 return 0;
641 case PTRACE_GET_LAST_BREAK:
642 put_user(task_thread_info(child)->last_break,
643 (unsigned int __user *) data);
644 return 0;
636 } 645 }
637 return compat_ptrace_request(child, request, addr, data); 646 return compat_ptrace_request(child, request, addr, data);
638} 647}
@@ -797,6 +806,28 @@ static int s390_fpregs_set(struct task_struct *target,
797 return rc; 806 return rc;
798} 807}
799 808
809#ifdef CONFIG_64BIT
810
811static int s390_last_break_get(struct task_struct *target,
812 const struct user_regset *regset,
813 unsigned int pos, unsigned int count,
814 void *kbuf, void __user *ubuf)
815{
816 if (count > 0) {
817 if (kbuf) {
818 unsigned long *k = kbuf;
819 *k = task_thread_info(target)->last_break;
820 } else {
821 unsigned long __user *u = ubuf;
822 if (__put_user(task_thread_info(target)->last_break, u))
823 return -EFAULT;
824 }
825 }
826 return 0;
827}
828
829#endif
830
800static const struct user_regset s390_regsets[] = { 831static const struct user_regset s390_regsets[] = {
801 [REGSET_GENERAL] = { 832 [REGSET_GENERAL] = {
802 .core_note_type = NT_PRSTATUS, 833 .core_note_type = NT_PRSTATUS,
@@ -814,6 +845,15 @@ static const struct user_regset s390_regsets[] = {
814 .get = s390_fpregs_get, 845 .get = s390_fpregs_get,
815 .set = s390_fpregs_set, 846 .set = s390_fpregs_set,
816 }, 847 },
848#ifdef CONFIG_64BIT
849 [REGSET_LAST_BREAK] = {
850 .core_note_type = NT_S390_LAST_BREAK,
851 .n = 1,
852 .size = sizeof(long),
853 .align = sizeof(long),
854 .get = s390_last_break_get,
855 },
856#endif
817}; 857};
818 858
819static const struct user_regset_view user_s390_view = { 859static const struct user_regset_view user_s390_view = {
@@ -948,6 +988,27 @@ static int s390_compat_regs_high_set(struct task_struct *target,
948 return rc; 988 return rc;
949} 989}
950 990
991static int s390_compat_last_break_get(struct task_struct *target,
992 const struct user_regset *regset,
993 unsigned int pos, unsigned int count,
994 void *kbuf, void __user *ubuf)
995{
996 compat_ulong_t last_break;
997
998 if (count > 0) {
999 last_break = task_thread_info(target)->last_break;
1000 if (kbuf) {
1001 unsigned long *k = kbuf;
1002 *k = last_break;
1003 } else {
1004 unsigned long __user *u = ubuf;
1005 if (__put_user(last_break, u))
1006 return -EFAULT;
1007 }
1008 }
1009 return 0;
1010}
1011
951static const struct user_regset s390_compat_regsets[] = { 1012static const struct user_regset s390_compat_regsets[] = {
952 [REGSET_GENERAL] = { 1013 [REGSET_GENERAL] = {
953 .core_note_type = NT_PRSTATUS, 1014 .core_note_type = NT_PRSTATUS,
@@ -965,6 +1026,13 @@ static const struct user_regset s390_compat_regsets[] = {
965 .get = s390_fpregs_get, 1026 .get = s390_fpregs_get,
966 .set = s390_fpregs_set, 1027 .set = s390_fpregs_set,
967 }, 1028 },
1029 [REGSET_LAST_BREAK] = {
1030 .core_note_type = NT_S390_LAST_BREAK,
1031 .n = 1,
1032 .size = sizeof(long),
1033 .align = sizeof(long),
1034 .get = s390_compat_last_break_get,
1035 },
968 [REGSET_GENERAL_EXTENDED] = { 1036 [REGSET_GENERAL_EXTENDED] = {
969 .core_note_type = NT_S390_HIGH_GPRS, 1037 .core_note_type = NT_S390_HIGH_GPRS,
970 .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), 1038 .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),