Skip to content

arch: riscv: pass main thread to custom stack guard without multithreading - #115704

Open
hongquan-prog wants to merge 1 commit into
zephyrproject-rtos:mainfrom
hongquan-prog:fix-113190-riscv-no-mt-stack-guard-a0
Open

arch: riscv: pass main thread to custom stack guard without multithreading#115704
hongquan-prog wants to merge 1 commit into
zephyrproject-rtos:mainfrom
hongquan-prog:fix-113190-riscv-no-mt-stack-guard-a0

Conversation

@hongquan-prog

@hongquan-prog hongquan-prog commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Problem

With CONFIG_MULTITHREADING=n and CONFIG_CUSTOM_STACK_GUARD=y, the inline
assembly in z_riscv_switch_to_main_no_multithreading() calls
z_riscv_custom_stack_guard_enable(struct k_thread *thread) without setting
a0 to the thread argument required by the function's contract
(arch/riscv/include/kernel_arch_func.h). All other call sites
(arch/riscv/core/isr.S, arch/riscv/core/switch.S) load a valid
k_thread * into a0 before the call.

The in-tree Andes HSP implementation only survives because its
!CONFIG_MULTITHREADING branch ignores thread; any vendor implementation
that follows the contract and dereferences thread (e.g. reads
thread->stack_info.start) faults with a Load access fault in
no-multithreading mode.

Fix

Pass a static k_thread descriptor whose stack_info describes
z_main_stack (z_main_thread is only defined with
CONFIG_MULTITHREADING), bound to a0 with the fixed-register pattern used
by z_riscv_userspace_enter(). A zero-initialized descriptor behaves
correctly through every field the in-tree implementation inspects
(thread_state, user_options, stack_info.start).

Also pin main_entry to the callee-saved register s1 so the jalr target
is guaranteed to survive the call; previously this relied on the compiler
happening to pick a callee-saved register.

Fixes #113190

…ading

In !CONFIG_MULTITHREADING mode the inline assembly in
z_riscv_switch_to_main_no_multithreading() calls
z_riscv_custom_stack_guard_enable() without setting a0 to the
k_thread * the callee's contract requires. The Andes implementation
only survives because it ignores the argument; any implementation
that dereferences thread faults.

Pass a static k_thread whose stack_info describes z_main_stack
(z_main_thread does not exist in this mode), bound to a0 with the
fixed-register pattern. Also pin main_entry to callee-saved s1 so
the jalr target survives the call.

Fixes zephyrproject-rtos#113190

Signed-off-by: Hongquan Li <hongquan.li@processmission.com>
@sonarqubecloud

sonarqubecloud Bot commented Aug 8, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: RISCV RISCV Architecture (32-bit & 64-bit)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

arch: riscv: z_riscv_custom_stack_guard_enable() called without a0 argument when CONFIG_MULTITHREADING=n

3 participants