Provide a s390-specific implementation of unwind_user_signal_next(), that unwinds from a (RT) signal frame.
Signed-off-by: Jens Remus <[email protected]> --- arch/s390/include/asm/unwind_user.h | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/arch/s390/include/asm/unwind_user.h b/arch/s390/include/asm/unwind_user.h index a7b97ea01c26..8f5524a24ae1 100644 --- a/arch/s390/include/asm/unwind_user.h +++ b/arch/s390/include/asm/unwind_user.h @@ -7,6 +7,7 @@ #include <linux/types.h> #include <asm/asm-offsets.h> #include <asm/fpu.h> +#include <asm/sigframe.h> #include <asm/stacktrace.h> #include <linux/unwind_user_types.h> @@ -67,6 +68,62 @@ static inline int arch_unwind_user_get_reg(unsigned long *val, } #define unwind_user_get_reg arch_unwind_user_get_reg +#define SVC_OPCODE 0x0a +#define INSN_SVC_SIGRETURN ((SVC_OPCODE << 8) | __NR_sigreturn) +#define INSN_SVC_RT_SIGRETURN ((SVC_OPCODE << 8) | __NR_rt_sigreturn) + +static inline int unwind_user_signal_next(struct unwind_user_state *state) +{ + unsigned short insn; + const _sigregs __user *sr; + unsigned long sp, fp, ra; + + /* ABI requires IP to be 2-byte aligned. */ + if (state->ip & 1) + return -EINVAL; + + if (__get_user(insn, (unsigned short __user *)state->ip)) + return -EINVAL; + + /* + * A signal frame has the instruction pointer pointing to + * svc $__NR_sigreturn or svc $__NR_rt_sigreturn + */ + switch (insn) { + case INSN_SVC_SIGRETURN: + /* New-style non-RT frame. */ + const struct sigframe __user *sf; + + sf = (struct sigframe __user *)state->sp; + if (__get_user(sr, (_sigregs __user **)&sf->sc.sregs)) + return -EINVAL; + break; + case INSN_SVC_RT_SIGRETURN: + /* New-style RT frame. */ + const struct rt_sigframe __user *rt_sf; + + rt_sf = (struct rt_sigframe __user *)state->sp; + sr = (_sigregs __user *)&rt_sf->uc.uc_mcontext; + break; + default: + return -EINVAL; + } + + if (__get_user(sp, (unsigned long __user *)&sr->regs.gprs[15])) + return -EINVAL; + if (__get_user(fp, (unsigned long __user *)&sr->regs.gprs[11])) + return -EINVAL; + if (__get_user(ra, (unsigned long __user *)&sr->regs.psw.addr)) + return -EINVAL; + + state->ip = ra; + state->sp = sp; + state->fp = fp; + state->topmost = false; + return 0; +} +#define unwind_user_signal_next unwind_user_signal_next + #endif /* CONFIG_UNWIND_USER */ #ifdef CONFIG_HAVE_UNWIND_USER_FP -- 2.51.0
