[PATCH] riscv: Fix 32-bit call_on_irq_stack() frame pointer ABI

Michael Ellerman mpe at kernel.org
Thu Jun 25 01:05:44 PDT 2026


On 24/6/26 9:31 pm, Rui Qi wrote:
> From: Samuel Holland <samuel.holland at sifive.com>
> 
> call_on_irq_stack() uses struct member offsets to set up its link in the
> frame record list. On riscv32, struct stackframe is the wrong size to
> maintain stack pointer alignment, so STACKFRAME_SIZE_ON_STACK includes
> padding. However, the ABI requires the frame record to be placed
> immediately below the address stored in s0, so the padding must come
> before the struct members.
> 
> Fix the layout by making STACKFRAME_FP and STACKFRAME_RA the negative
> offsets from s0, instead of the positive offsets from sp.

The fact that all uses of the defines need to add back STACKFRAME_SIZE_ON_STACK
makes me think the defines don't have the most useful values.

If the values were offset + padding then the uses could be left unchanged.

> diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c
> index af827448a609..a75f0cfea1e9 100644
> --- a/arch/riscv/kernel/asm-offsets.c
> +++ b/arch/riscv/kernel/asm-offsets.c
> @@ -501,8 +501,8 @@ void asm_offsets(void)
>   	OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr);
>   
>   	DEFINE(STACKFRAME_SIZE_ON_STACK, ALIGN(sizeof(struct stackframe), STACK_ALIGN));
> -	OFFSET(STACKFRAME_FP, stackframe, fp);
> -	OFFSET(STACKFRAME_RA, stackframe, ra);
> +	DEFINE(STACKFRAME_FP, offsetof(struct stackframe, fp) - sizeof(struct stackframe));
> +	DEFINE(STACKFRAME_RA, offsetof(struct stackframe, ra) - sizeof(struct stackframe));

ie. if they were defined as:

         DEFINE(STACKFRAME_FP, ALIGN(sizeof(struct stackframe), STACK_ALIGN) -
                               sizeof(struct stackframe) + offsetof(struct stackframe, fp));
         DEFINE(STACKFRAME_RA, ALIGN(sizeof(struct stackframe), STACK_ALIGN) -
                               sizeof(struct stackframe) + offsetof(struct stackframe, ra));

STACKFRAME_SIZE_ON_STACK - sizeof(stackframe) computes the size of the padding,
and then plus the offset gets you the right location.

That would allow the call sites to still use STACKFRAME_RA(sp).

I would probably also rename them to ON_STACK_RA or something, to make it clear
they are not offsets into struct stackframe any more, but maybe that's bike shedding :)

cheers

>   #ifdef CONFIG_FUNCTION_TRACER
>   	DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
>   #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
> diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
> index c6988983cdf7..08df724e13b9 100644
> --- a/arch/riscv/kernel/entry.S
> +++ b/arch/riscv/kernel/entry.S
> @@ -386,8 +386,8 @@ SYM_CODE_END(ret_from_fork_user_asm)
>   SYM_FUNC_START(call_on_irq_stack)
>   	/* Create a frame record to save ra and s0 (fp) */
>   	addi	sp, sp, -STACKFRAME_SIZE_ON_STACK
> -	REG_S	ra, STACKFRAME_RA(sp)
> -	REG_S	s0, STACKFRAME_FP(sp)
> +	REG_S	ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp)
> +	REG_S	s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp)
>   	addi	s0, sp, STACKFRAME_SIZE_ON_STACK
>   
>   	/* Switch to the per-CPU shadow call stack */
> @@ -405,8 +405,8 @@ SYM_FUNC_START(call_on_irq_stack)
>   
>   	/* Switch back to the thread stack and restore ra and s0 */
>   	addi	sp, s0, -STACKFRAME_SIZE_ON_STACK
> -	REG_L	ra, STACKFRAME_RA(sp)
> -	REG_L	s0, STACKFRAME_FP(sp)
> +	REG_L	ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp)
> +	REG_L	s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp)
>   	addi	sp, sp, STACKFRAME_SIZE_ON_STACK
>   
>   	ret



More information about the linux-riscv mailing list