[PATCH 4/4] riscv: entry: Save a frame record for exceptions

From: Samuel Holland
Date: Wed May 29 2024 - 20:18:26 EST


This follows the frame pointer ABI and allows stack traces to cross
exception boundaries without a special case in the stack walking code.

Signed-off-by: Samuel Holland <samuel.holland@xxxxxxxxxx>
---

arch/riscv/include/asm/processor.h | 9 +++++++--
arch/riscv/include/asm/ptrace.h | 5 +++++
arch/riscv/include/asm/stacktrace.h | 5 -----
arch/riscv/kernel/asm-offsets.c | 6 +++---
arch/riscv/kernel/entry.S | 16 ++++++++++------
arch/riscv/kernel/head.S | 6 ++----
arch/riscv/kernel/stacktrace.c | 9 ---------
7 files changed, 27 insertions(+), 29 deletions(-)

diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
index 68c3432dc6ea..ccbb1e363c7f 100644
--- a/arch/riscv/include/asm/processor.h
+++ b/arch/riscv/include/asm/processor.h
@@ -144,9 +144,14 @@ static inline void arch_thread_struct_whitelist(unsigned long *offset,
.align_ctl = PR_UNALIGN_NOPRINT, \
}

+#ifdef CONFIG_FRAME_POINTER
+#define EXCEPTION_FRAME_SIZE ALIGN(sizeof(struct pt_regs) + sizeof(struct stackframe), STACK_ALIGN)
+#else
+#define EXCEPTION_FRAME_SIZE ALIGN(sizeof(struct pt_regs), STACK_ALIGN)
+#endif
+
#define task_pt_regs(tsk) \
- ((struct pt_regs *)(task_stack_page(tsk) + THREAD_SIZE \
- - ALIGN(sizeof(struct pt_regs), STACK_ALIGN)))
+ ((struct pt_regs *)(task_stack_page(tsk) + THREAD_SIZE - EXCEPTION_FRAME_SIZE))

#define KSTK_EIP(tsk) (task_pt_regs(tsk)->epc)
#define KSTK_ESP(tsk) (task_pt_regs(tsk)->sp)
diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
index b5b0adcc85c1..f475f6acec49 100644
--- a/arch/riscv/include/asm/ptrace.h
+++ b/arch/riscv/include/asm/ptrace.h
@@ -12,6 +12,11 @@

#ifndef __ASSEMBLY__

+struct stackframe {
+ unsigned long fp;
+ unsigned long ra;
+};
+
struct pt_regs {
unsigned long epc;
unsigned long ra;
diff --git a/arch/riscv/include/asm/stacktrace.h b/arch/riscv/include/asm/stacktrace.h
index b1495a7e06ce..3019558f747c 100644
--- a/arch/riscv/include/asm/stacktrace.h
+++ b/arch/riscv/include/asm/stacktrace.h
@@ -6,11 +6,6 @@
#include <linux/sched.h>
#include <asm/ptrace.h>

-struct stackframe {
- unsigned long fp;
- unsigned long ra;
-};
-
extern void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
bool (*fn)(void *, unsigned long), void *arg);
extern void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c
index 84c056f5ee09..582b52713e93 100644
--- a/arch/riscv/kernel/asm-offsets.c
+++ b/arch/riscv/kernel/asm-offsets.c
@@ -477,10 +477,10 @@ void asm_offsets(void)
);

/*
- * We allocate a pt_regs on the stack when entering the kernel. This
- * ensures the alignment is sane.
+ * We allocate a pt_regs and possibly a stackframe on the stack when
+ * entering the kernel. This ensures the alignment is sane.
*/
- DEFINE(PT_SIZE_ON_STACK, ALIGN(sizeof(struct pt_regs), STACK_ALIGN));
+ DEFINE(EXCEPTION_FRAME_SIZE, EXCEPTION_FRAME_SIZE);

OFFSET(KERNEL_MAP_VIRT_ADDR, kernel_mapping, virt_addr);
OFFSET(SBI_HART_BOOT_TASK_PTR_OFFSET, sbi_hart_boot_data, task_ptr);
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index bd1c5621df45..cdb58ce32cbb 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -33,7 +33,7 @@ SYM_CODE_START(handle_exception)
REG_S sp, TASK_TI_KERNEL_SP(tp)

#ifdef CONFIG_VMAP_STACK
- addi sp, sp, -(PT_SIZE_ON_STACK)
+ addi sp, sp, -EXCEPTION_FRAME_SIZE
srli sp, sp, THREAD_SHIFT
andi sp, sp, 0x1
bnez sp, handle_kernel_stack_overflow
@@ -43,7 +43,7 @@ SYM_CODE_START(handle_exception)
.Lsave_context:
REG_S sp, TASK_TI_USER_SP(tp)
REG_L sp, TASK_TI_KERNEL_SP(tp)
- addi sp, sp, -(PT_SIZE_ON_STACK)
+ addi sp, sp, -EXCEPTION_FRAME_SIZE
REG_S x1, PT_RA(sp)
REG_S x3, PT_GP(sp)
REG_S x5, PT_T0(sp)
@@ -83,6 +83,12 @@ SYM_CODE_START(handle_exception)
/* Load the kernel shadow call stack pointer if coming from userspace */
scs_load_current_if_task_changed s5

+#ifdef CONFIG_FRAME_POINTER
+ REG_S ra, (EXCEPTION_FRAME_SIZE + STACKFRAME_RA)(sp)
+ REG_S s0, (EXCEPTION_FRAME_SIZE + STACKFRAME_FP)(sp)
+ addi s0, sp, EXCEPTION_FRAME_SIZE
+#endif
+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
move a0, sp
call riscv_v_context_nesting_start
@@ -136,7 +142,7 @@ SYM_CODE_START_NOALIGN(ret_from_exception)
bnez t0, 1f

/* Save unwound kernel stack pointer in thread_info */
- addi t0, sp, PT_SIZE_ON_STACK
+ addi t0, sp, EXCEPTION_FRAME_SIZE
REG_S t0, TASK_TI_KERNEL_SP(tp)

/* Save the kernel shadow call stack pointer */
@@ -192,14 +198,12 @@ SYM_CODE_START_LOCAL(handle_kernel_stack_overflow)
/* we reach here from kernel context, sscratch must be 0 */
csrrw x31, CSR_SCRATCH, x31
asm_per_cpu sp, overflow_stack, x31
- li x31, OVERFLOW_STACK_SIZE
+ li x31, OVERFLOW_STACK_SIZE - EXCEPTION_FRAME_SIZE
add sp, sp, x31
/* zero out x31 again and restore x31 */
xor x31, x31, x31
csrrw x31, CSR_SCRATCH, x31

- addi sp, sp, -(PT_SIZE_ON_STACK)
-
//save context to overflow stack
REG_S x1, PT_RA(sp)
REG_S x3, PT_GP(sp)
diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
index 4236a69c35cb..09ee5e6c2a98 100644
--- a/arch/riscv/kernel/head.S
+++ b/arch/riscv/kernel/head.S
@@ -290,9 +290,8 @@ SYM_CODE_START(_start_kernel)

/* Initialize page tables and relocate to virtual addresses */
la tp, init_task
- la sp, init_thread_union + THREAD_SIZE
+ la sp, init_thread_union + THREAD_SIZE - EXCEPTION_FRAME_SIZE
XIP_FIXUP_OFFSET sp
- addi sp, sp, -PT_SIZE_ON_STACK
scs_load_init_stack
#ifdef CONFIG_BUILTIN_DTB
la a0, __dtb_start
@@ -310,8 +309,7 @@ SYM_CODE_START(_start_kernel)
call .Lsetup_trap_vector
/* Restore C environment */
la tp, init_task
- la sp, init_thread_union + THREAD_SIZE
- addi sp, sp, -PT_SIZE_ON_STACK
+ la sp, init_thread_union + THREAD_SIZE - EXCEPTION_FRAME_SIZE
scs_load_current

#ifdef CONFIG_KASAN
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index 528ec7cc9a62..6be8f8942f6b 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -16,8 +16,6 @@

#ifdef CONFIG_FRAME_POINTER

-extern asmlinkage void ret_from_exception(void);
-
static inline int fp_is_valid(unsigned long fp, unsigned long sp)
{
unsigned long low, high;
@@ -70,13 +68,6 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
fp = frame->fp;
pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
&frame->ra);
- if (pc == (unsigned long)ret_from_exception) {
- if (unlikely(!__kernel_text_address(pc) || !fn(arg, pc)))
- break;
-
- pc = ((struct pt_regs *)sp)->epc;
- fp = ((struct pt_regs *)sp)->s0;
- }
}

}
--
2.44.1