Add s390 support for unwinding of user space using SFrame. This leverages the previous commits to address the following s390 particularities:
- The CFA is defined as the value of the stack pointer (SP) at call site in the previous frame + 160. Therefore the SP unwinds as SP = CFA - 160. Therefore use a SP value offset from CFA of -160. - The return address (RA) is not saved on the stack at function entry. It is also not saved in the function prologue, when in leaf functions. Therefore the RA does not necessarily need to be unwound in the first unwinding step for the topmost frame. - The frame pointer (FP) and/or return address (RA) may be saved in other registers when in leaf functions. GCC effectively uses floating-point registers (FPR) for this purpose. Therefore DWARF register numbers may be encoded in the SFrame FP/RA offsets. - To make use of the signed 8-bit SFrame offset size and effectively reduce the .sframe section size the SFrame CFA offset values are encoded as (CFA - 160) / 8. This is because the lowest CFA offset value on s390 is by definition +160 (= value at function entry), which does not fit into a signed 8-bit SFrame offset. Therefore the CFA offset values are stored adjusted by -160. Additionally they are scaled by the s390-specific DWARF data scaling factor of 8. The s390x ELF ABI [1] guarantees that the CFA offset values are always aligned on an 8-byte boundary. Add s390-specific SFrame format definitions. Note that SFRAME_ABI_* (and thus SFRAME_ABI_S390_ENDIAN_BIG) is currently unused. Include <asm/unwind_user_sframe.h> after "sframe.h" to make those s390-specific definitions available to architecture-specific unwind user sframe code, particularly the s390-specific one. [1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases Signed-off-by: Jens Remus <[email protected]> --- Notes (jremus): Changes in RFC v3: - Adjust to rename of UNWIND_USER_LOC_NONE to UNWIND_USER_LOC_RETAIN. - Adjust s390-specific unwind_user_word_size() to changes in the x86-specific (see patch 4). Changes in RFC v2: - Provide unwind_user_word_size() to satisfy new unwind user need. Note that support for COMPAT has not been implemented as s390 support for COMPAT is expected to be removed with v6.19: https://lore.kernel.org/all/[email protected]/ - Adjust to changes in preceding patches in this series that enable support in unwind user (sframe) for s390 particularities. Alternatively the s390-specific definitions could also be added to the s390-specific unwind user sframe header. The current implementation follows Binutils approach to have all SFrame format definitions in one central header file. arch/s390/Kconfig | 1 + arch/s390/include/asm/unwind_user.h | 100 +++++++++++++++++++++ arch/s390/include/asm/unwind_user_sframe.h | 33 +++++++ kernel/unwind/sframe.c | 2 +- kernel/unwind/sframe.h | 14 +++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 arch/s390/include/asm/unwind_user.h create mode 100644 arch/s390/include/asm/unwind_user_sframe.h diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index df22b10d9141..52d3f3b3e086 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -246,6 +246,7 @@ config S390 select HAVE_SETUP_PER_CPU_AREA select HAVE_SOFTIRQ_ON_OWN_STACK select HAVE_SYSCALL_TRACEPOINTS + select HAVE_UNWIND_USER_SFRAME select HAVE_VIRT_CPU_ACCOUNTING select HAVE_VIRT_CPU_ACCOUNTING_IDLE select HOTPLUG_SMT diff --git a/arch/s390/include/asm/unwind_user.h b/arch/s390/include/asm/unwind_user.h new file mode 100644 index 000000000000..3a95be1eb886 --- /dev/null +++ b/arch/s390/include/asm/unwind_user.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_UNWIND_USER_H +#define _ASM_S390_UNWIND_USER_H + +#include <linux/sched/task_stack.h> +#include <linux/types.h> +#include <asm/fpu-insn.h> + +#ifdef CONFIG_UNWIND_USER + +static inline int unwind_user_word_size(struct pt_regs *regs) +{ + return 8; +} + +static inline int arch_unwind_user_get_ra_reg(unsigned long *val) +{ + struct pt_regs *regs = task_pt_regs(current); + *val = regs->gprs[14]; + return 0; +} +#define unwind_user_get_ra_reg arch_unwind_user_get_ra_reg + +static inline int __s390_get_dwarf_fpr(unsigned long *val, int regnum) +{ + switch (regnum) { + case 16: + fpu_std(0, (freg_t *)val); + break; + case 17: + fpu_std(2, (freg_t *)val); + break; + case 18: + fpu_std(4, (freg_t *)val); + break; + case 19: + fpu_std(6, (freg_t *)val); + break; + case 20: + fpu_std(1, (freg_t *)val); + break; + case 21: + fpu_std(3, (freg_t *)val); + break; + case 22: + fpu_std(5, (freg_t *)val); + break; + case 23: + fpu_std(7, (freg_t *)val); + break; + case 24: + fpu_std(8, (freg_t *)val); + break; + case 25: + fpu_std(10, (freg_t *)val); + break; + case 26: + fpu_std(12, (freg_t *)val); + break; + case 27: + fpu_std(14, (freg_t *)val); + break; + case 28: + fpu_std(9, (freg_t *)val); + break; + case 29: + fpu_std(11, (freg_t *)val); + break; + case 30: + fpu_std(13, (freg_t *)val); + break; + case 31: + fpu_std(15, (freg_t *)val); + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline int arch_unwind_user_get_reg(unsigned long *val, int regnum) +{ + if (0 <= regnum && regnum <= 15) { + struct pt_regs *regs = task_pt_regs(current); + *val = regs->gprs[regnum]; + return 0; + } else if (16 <= regnum && regnum <= 31) { + return __s390_get_dwarf_fpr(val, regnum); + } + + return -EINVAL; +} +#define unwind_user_get_reg arch_unwind_user_get_reg + +#endif /* CONFIG_UNWIND_USER */ + +#include <asm-generic/unwind_user.h> + +#endif /* _ASM_S390_UNWIND_USER_H */ diff --git a/arch/s390/include/asm/unwind_user_sframe.h b/arch/s390/include/asm/unwind_user_sframe.h new file mode 100644 index 000000000000..af650596cb5d --- /dev/null +++ b/arch/s390/include/asm/unwind_user_sframe.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_UNWIND_USER_SFRAME_H +#define _ASM_S390_UNWIND_USER_SFRAME_H + +#include <linux/unwind_user.h> +#include <linux/types.h> + +#define SFRAME_SP_OFFSET SFRAME_S390X_SP_VAL_OFFSET + +static inline s32 arch_sframe_cfa_offset_decode(s32 offset) +{ + return SFRAME_V2_S390X_CFA_OFFSET_DECODE(offset); +} +#define sframe_cfa_offset_decode arch_sframe_cfa_offset_decode + +static inline void +arch_sframe_init_reginfo(struct unwind_user_reginfo *reginfo, s32 offset) +{ + if (SFRAME_V2_S390X_OFFSET_IS_REGNUM(offset)) { + reginfo->loc = UNWIND_USER_LOC_REG; + reginfo->regnum = SFRAME_V2_S390X_OFFSET_DECODE_REGNUM(offset); + } else if (offset) { + reginfo->loc = UNWIND_USER_LOC_STACK; + reginfo->offset = offset; + } else { + reginfo->loc = UNWIND_USER_LOC_RETAIN; + } +} +#define sframe_init_reginfo arch_sframe_init_reginfo + +#include <asm-generic/unwind_user_sframe.h> + +#endif /* _ASM_S390_UNWIND_USER_SFRAME_H */ diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c index 92f770fc21f6..bd446d55b552 100644 --- a/kernel/unwind/sframe.c +++ b/kernel/unwind/sframe.c @@ -12,11 +12,11 @@ #include <linux/mm.h> #include <linux/string_helpers.h> #include <linux/sframe.h> -#include <asm/unwind_user_sframe.h> #include <linux/unwind_user_types.h> #include "sframe.h" #include "sframe_debug.h" +#include <asm/unwind_user_sframe.h> struct sframe_fde_internal { unsigned long func_start_addr; diff --git a/kernel/unwind/sframe.h b/kernel/unwind/sframe.h index 69ce0d5b9694..c09f25fbaa2f 100644 --- a/kernel/unwind/sframe.h +++ b/kernel/unwind/sframe.h @@ -18,6 +18,7 @@ #define SFRAME_ABI_AARCH64_ENDIAN_BIG 1 #define SFRAME_ABI_AARCH64_ENDIAN_LITTLE 2 #define SFRAME_ABI_AMD64_ENDIAN_LITTLE 3 +#define SFRAME_ABI_S390X_ENDIAN_BIG 4 /* s390 64-bit (s390x) */ #define SFRAME_FDE_TYPE_PCINC 0 #define SFRAME_FDE_TYPE_PCMASK 1 @@ -69,4 +70,17 @@ struct sframe_fde { #define SFRAME_FRE_OFFSET_SIZE(data) ((data >> 5) & 0x3) #define SFRAME_FRE_MANGLED_RA_P(data) ((data >> 7) & 0x1) +/* s390 64-bit (s390x) */ + +#define SFRAME_S390X_SP_VAL_OFFSET (-160) + +#define SFRAME_S390X_CFA_OFFSET_ADJUSTMENT SFRAME_S390X_SP_VAL_OFFSET +#define SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR 8 +#define SFRAME_V2_S390X_CFA_OFFSET_DECODE(offset) \ + (((offset) * SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR) \ + - SFRAME_S390X_CFA_OFFSET_ADJUSTMENT) + +#define SFRAME_V2_S390X_OFFSET_IS_REGNUM(offset) ((offset) & 1) +#define SFRAME_V2_S390X_OFFSET_DECODE_REGNUM(offset) ((offset) >> 1) + #endif /* _SFRAME_H */ -- 2.51.0
