[RFC PATCH] riscv: clarify vector state semantics on syscall and context switch

From: daichengrong

Date: Thu Mar 19 2026 - 03:40:01 EST


The RISC-V vector specification states that executing a system call
causes all caller-saved vector registers (v0-v31, vl, vtype) and vstart
to become unspecified.

Currently, after calling riscv_v_vstate_discard(), the vector state
may still be marked as DIRTY, which can mislead the context switch
logic into treating the registers as containing valid user data.

This patch clarifies and tightens the kernel-side semantics:

1. On syscall entry, the kernel checks the vector state via mstatus
and discards it if necessary. After discard, the state is explicitly
set to INIT instead of DIRTY, indicating that the vector registers
no longer contain meaningful user data.

2. During context switch, the vector state is interpreted as follows:
- INIT: no valid user data is present, so vector register data does
not need to be saved.
- non-INIT (e.g. DIRTY): vector register data must be saved.

3. On restore, if the state is INIT, the vector registers are treated
as invalid and are not restored from memory. Instead, they are
overwritten with a known initial value to avoid potential data
leakage from a previous task.

This aligns the kernel's vector state tracking with the architectural
"unspecified" semantics while ensuring correct lazy context switching
and preventing cross-task data leakage.

Signed-off-by: daichengrong <daichengrong@xxxxxxxxxxx>
---
arch/riscv/include/asm/vector.h | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 00cb9c0982b1..93c68a549b72 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -298,8 +298,9 @@ static inline void __riscv_v_vstate_discard(void)
static inline void riscv_v_vstate_discard(struct pt_regs *regs)
{
if (riscv_v_vstate_query(regs)) {
- __riscv_v_vstate_discard();
- __riscv_v_vstate_dirty(regs);
+ if (!__riscv_v_vstate_check(regs->status, INITIAL))
+ __riscv_v_vstate_discard();
+ riscv_v_vstate_on(regs);
}
}

@@ -315,19 +316,17 @@ static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
struct pt_regs *regs)
{
- if (riscv_v_vstate_query(regs)) {
+ if (__riscv_v_vstate_check(regs->status, INITIAL))
+ __riscv_v_vstate_discard();
+ else if (__riscv_v_vstate_check(regs->status, CLEAN))
__riscv_v_vstate_restore(vstate, vstate->datap);
- __riscv_v_vstate_clean(regs);
- }
}

static inline void riscv_v_vstate_set_restore(struct task_struct *task,
struct pt_regs *regs)
{
- if (riscv_v_vstate_query(regs)) {
+ if (riscv_v_vstate_query(regs))
set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE);
- riscv_v_vstate_on(regs);
- }
}

#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
--
2.25.1