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.

[1]: s390x ELF ABI,
     https://github.com/IBM/s390x-abi/releases

Signed-off-by: Jens Remus <jre...@linux.ibm.com>
---
 arch/s390/Kconfig                          |  2 +
 arch/s390/include/asm/unwind_user.h        | 83 ++++++++++++++++++++++
 arch/s390/include/asm/unwind_user_sframe.h | 37 ++++++++++
 3 files changed, 122 insertions(+)
 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 f4ea52c1f0ba..8b29a8f0f9c3 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -239,6 +239,8 @@ config S390
        select HAVE_SETUP_PER_CPU_AREA
        select HAVE_SOFTIRQ_ON_OWN_STACK
        select HAVE_SYSCALL_TRACEPOINTS
+       select HAVE_UNWIND_USER_LOC_REG
+       select HAVE_UNWIND_USER_SFRAME
        select HAVE_USER_RA_REG
        select HAVE_VIRT_CPU_ACCOUNTING
        select HAVE_VIRT_CPU_ACCOUNTING_IDLE
diff --git a/arch/s390/include/asm/unwind_user.h 
b/arch/s390/include/asm/unwind_user.h
new file mode 100644
index 000000000000..daae1545e203
--- /dev/null
+++ b/arch/s390/include/asm/unwind_user.h
@@ -0,0 +1,83 @@
+/* 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>
+
+static inline void __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:
+               *val = 0;
+       }
+}
+
+static inline int s390_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];
+       } else if (16 <= regnum && regnum <= 31) {
+               __s390_get_dwarf_fpr(val, regnum);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define unwind_user_get_reg s390_unwind_user_get_reg
+
+#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..2216e6921fd8
--- /dev/null
+++ b/arch/s390/include/asm/unwind_user_sframe.h
@@ -0,0 +1,37 @@
+/* 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>
+
+static inline s32 arch_sframe_cfa_offset_decode(s32 offset)
+{
+       return (offset << 3) + 160;
+}
+
+static inline void arch_sframe_set_frame_reginfo(
+       struct unwind_user_reginfo *reginfo,
+       s32 offset)
+{
+       if (offset & 1) {
+               reginfo->loc = UNWIND_USER_LOC_REG;
+               reginfo->regnum = offset >> 1;
+       } else if (offset) {
+               reginfo->loc = UNWIND_USER_LOC_STACK;
+               reginfo->frame_off = offset;
+       } else {
+               reginfo->loc = UNWIND_USER_LOC_NONE;
+       }
+}
+
+static inline s32 arch_sframe_sp_val_off(void)
+{
+       return -160;
+}
+
+#define sframe_cfa_offset_decode arch_sframe_cfa_offset_decode
+#define sframe_set_frame_reginfo arch_sframe_set_frame_reginfo
+#define sframe_sp_val_off arch_sframe_sp_val_off
+
+#endif /* _ASM_S390_UNWIND_USER_SFRAME_H */
-- 
2.48.1


Reply via email to