Commit 86f2552b authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Martin Schwidefsky

[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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent cd3b70f5
...@@ -328,8 +328,8 @@ struct pt_regs ...@@ -328,8 +328,8 @@ struct pt_regs
psw_t psw; psw_t psw;
unsigned long gprs[NUM_GPRS]; unsigned long gprs[NUM_GPRS];
unsigned long orig_gpr2; unsigned long orig_gpr2;
unsigned short svcnr;
unsigned short ilc; unsigned short ilc;
unsigned short svcnr;
}; };
#endif #endif
...@@ -436,6 +436,7 @@ typedef struct ...@@ -436,6 +436,7 @@ typedef struct
#define PTRACE_PEEKDATA_AREA 0x5003 #define PTRACE_PEEKDATA_AREA 0x5003
#define PTRACE_POKETEXT_AREA 0x5004 #define PTRACE_POKETEXT_AREA 0x5004
#define PTRACE_POKEDATA_AREA 0x5005 #define PTRACE_POKEDATA_AREA 0x5005
#define PTRACE_GET_LAST_BREAK 0x5006
/* /*
* PT_PROT definition is loosely based on hppa bsd definition in * PT_PROT definition is loosely based on hppa bsd definition in
......
...@@ -50,6 +50,7 @@ struct thread_info { ...@@ -50,6 +50,7 @@ struct thread_info {
struct restart_block restart_block; struct restart_block restart_block;
__u64 user_timer; __u64 user_timer;
__u64 system_timer; __u64 system_timer;
unsigned long last_break; /* last breaking-event-address. */
}; };
/* /*
......
...@@ -39,6 +39,7 @@ int main(void) ...@@ -39,6 +39,7 @@ int main(void)
DEFINE(__TI_precount, offsetof(struct thread_info, preempt_count)); DEFINE(__TI_precount, offsetof(struct thread_info, preempt_count));
DEFINE(__TI_user_timer, offsetof(struct thread_info, user_timer)); DEFINE(__TI_user_timer, offsetof(struct thread_info, user_timer));
DEFINE(__TI_system_timer, offsetof(struct thread_info, system_timer)); DEFINE(__TI_system_timer, offsetof(struct thread_info, system_timer));
DEFINE(__TI_last_break, offsetof(struct thread_info, last_break));
BLANK(); BLANK();
DEFINE(__PT_ARGS, offsetof(struct pt_regs, args)); DEFINE(__PT_ARGS, offsetof(struct pt_regs, args));
DEFINE(__PT_PSW, offsetof(struct pt_regs, psw)); DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
......
...@@ -180,9 +180,9 @@ STACK_SIZE = 1 << STACK_SHIFT ...@@ -180,9 +180,9 @@ STACK_SIZE = 1 << STACK_SHIFT
s %r15,BASED(.Lc_spsize) # make room for registers & psw s %r15,BASED(.Lc_spsize) # make room for registers & psw
mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack
st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2
icm %r12,3,__LC_SVC_ILC icm %r12,12,__LC_SVC_ILC
stm %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack stm %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack
st %r12,SP_SVCNR(%r15) st %r12,SP_ILC(%r15)
mvc SP_R12(16,%r15),\savearea # move %r12-%r15 to stack mvc SP_R12(16,%r15),\savearea # move %r12-%r15 to stack
la %r12,0 la %r12,0
st %r12,__SF_BACKCHAIN(%r15) # clear back chain st %r12,__SF_BACKCHAIN(%r15) # clear back chain
......
This diff is collapsed.
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
enum s390_regset { enum s390_regset {
REGSET_GENERAL, REGSET_GENERAL,
REGSET_FP, REGSET_FP,
REGSET_LAST_BREAK,
REGSET_GENERAL_EXTENDED, REGSET_GENERAL_EXTENDED,
}; };
...@@ -381,6 +382,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -381,6 +382,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
copied += sizeof(unsigned long); copied += sizeof(unsigned long);
} }
return 0; return 0;
case PTRACE_GET_LAST_BREAK:
put_user(task_thread_info(child)->last_break,
(unsigned long __user *) data);
return 0;
default: default:
/* Removing high order bit from addr (only for 31 bit). */ /* Removing high order bit from addr (only for 31 bit). */
addr &= PSW_ADDR_INSN; addr &= PSW_ADDR_INSN;
...@@ -633,6 +638,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ...@@ -633,6 +638,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
copied += sizeof(unsigned int); copied += sizeof(unsigned int);
} }
return 0; return 0;
case PTRACE_GET_LAST_BREAK:
put_user(task_thread_info(child)->last_break,
(unsigned int __user *) data);
return 0;
} }
return compat_ptrace_request(child, request, addr, data); return compat_ptrace_request(child, request, addr, data);
} }
...@@ -797,6 +806,28 @@ static int s390_fpregs_set(struct task_struct *target, ...@@ -797,6 +806,28 @@ static int s390_fpregs_set(struct task_struct *target,
return rc; return rc;
} }
#ifdef CONFIG_64BIT
static int s390_last_break_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
if (count > 0) {
if (kbuf) {
unsigned long *k = kbuf;
*k = task_thread_info(target)->last_break;
} else {
unsigned long __user *u = ubuf;
if (__put_user(task_thread_info(target)->last_break, u))
return -EFAULT;
}
}
return 0;
}
#endif
static const struct user_regset s390_regsets[] = { static const struct user_regset s390_regsets[] = {
[REGSET_GENERAL] = { [REGSET_GENERAL] = {
.core_note_type = NT_PRSTATUS, .core_note_type = NT_PRSTATUS,
...@@ -814,6 +845,15 @@ static const struct user_regset s390_regsets[] = { ...@@ -814,6 +845,15 @@ static const struct user_regset s390_regsets[] = {
.get = s390_fpregs_get, .get = s390_fpregs_get,
.set = s390_fpregs_set, .set = s390_fpregs_set,
}, },
#ifdef CONFIG_64BIT
[REGSET_LAST_BREAK] = {
.core_note_type = NT_S390_LAST_BREAK,
.n = 1,
.size = sizeof(long),
.align = sizeof(long),
.get = s390_last_break_get,
},
#endif
}; };
static const struct user_regset_view user_s390_view = { static const struct user_regset_view user_s390_view = {
...@@ -948,6 +988,27 @@ static int s390_compat_regs_high_set(struct task_struct *target, ...@@ -948,6 +988,27 @@ static int s390_compat_regs_high_set(struct task_struct *target,
return rc; return rc;
} }
static int s390_compat_last_break_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
compat_ulong_t last_break;
if (count > 0) {
last_break = task_thread_info(target)->last_break;
if (kbuf) {
unsigned long *k = kbuf;
*k = last_break;
} else {
unsigned long __user *u = ubuf;
if (__put_user(last_break, u))
return -EFAULT;
}
}
return 0;
}
static const struct user_regset s390_compat_regsets[] = { static const struct user_regset s390_compat_regsets[] = {
[REGSET_GENERAL] = { [REGSET_GENERAL] = {
.core_note_type = NT_PRSTATUS, .core_note_type = NT_PRSTATUS,
...@@ -965,6 +1026,13 @@ static const struct user_regset s390_compat_regsets[] = { ...@@ -965,6 +1026,13 @@ static const struct user_regset s390_compat_regsets[] = {
.get = s390_fpregs_get, .get = s390_fpregs_get,
.set = s390_fpregs_set, .set = s390_fpregs_set,
}, },
[REGSET_LAST_BREAK] = {
.core_note_type = NT_S390_LAST_BREAK,
.n = 1,
.size = sizeof(long),
.align = sizeof(long),
.get = s390_compat_last_break_get,
},
[REGSET_GENERAL_EXTENDED] = { [REGSET_GENERAL_EXTENDED] = {
.core_note_type = NT_S390_HIGH_GPRS, .core_note_type = NT_S390_HIGH_GPRS,
.n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
......
...@@ -313,6 +313,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, ...@@ -313,6 +313,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
To avoid breaking binary compatibility, they are passed as args. */ To avoid breaking binary compatibility, they are passed as args. */
regs->gprs[4] = current->thread.trap_no; regs->gprs[4] = current->thread.trap_no;
regs->gprs[5] = current->thread.prot_addr; regs->gprs[5] = current->thread.prot_addr;
regs->gprs[6] = task_thread_info(current)->last_break;
/* Place signal number on stack to allow backtrace from handler. */ /* Place signal number on stack to allow backtrace from handler. */
if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
...@@ -376,6 +377,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -376,6 +377,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->gprs[2] = map_signal(sig); regs->gprs[2] = map_signal(sig);
regs->gprs[3] = (unsigned long) &frame->info; regs->gprs[3] = (unsigned long) &frame->info;
regs->gprs[4] = (unsigned long) &frame->uc; regs->gprs[4] = (unsigned long) &frame->uc;
regs->gprs[5] = task_thread_info(current)->last_break;
return 0; return 0;
give_sigsegv: give_sigsegv:
......
...@@ -394,6 +394,7 @@ typedef struct elf64_shdr { ...@@ -394,6 +394,7 @@ typedef struct elf64_shdr {
#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ #define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
#define NT_S390_CTRS 0x304 /* s390 control registers */ #define NT_S390_CTRS 0x304 /* s390 control registers */
#define NT_S390_PREFIX 0x305 /* s390 prefix register */ #define NT_S390_PREFIX 0x305 /* s390 prefix register */
#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
/* Note header in a PT_NOTE section */ /* Note header in a PT_NOTE section */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment