diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index 00cb9c0982b1ae..35a6b98680a47b 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -52,6 +52,7 @@ void riscv_v_thread_free(struct task_struct *tsk); void __init riscv_v_setup_ctx_cache(void); void riscv_v_thread_alloc(struct task_struct *tsk); void __init update_regset_vector_info(unsigned long size); +void riscv_v_ucontext_save(struct task_struct *tsk); static inline u32 riscv_v_flags(void) { @@ -426,6 +427,7 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; } #define riscv_v_thread_alloc(tsk) do {} while (0) #define get_cpu_vector_context() do {} while (0) #define put_cpu_vector_context() do {} while (0) +#define riscv_v_ucontext_save(tsk) do {} while (0) #define riscv_v_vstate_set_restore(task, regs) do {} while (0) #endif /* CONFIG_RISCV_ISA_V */ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index f336a183667eb8..9276485b6cac3e 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -109,11 +109,7 @@ static int riscv_vr_get(struct task_struct *target, * Ensure the vector registers have been saved to the memory before * copying them to membuf. */ - if (target == current) { - get_cpu_vector_context(); - riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current)); - put_cpu_vector_context(); - } + riscv_v_ucontext_save(target); ptrace_vstate.vstart = vstate->vstart; ptrace_vstate.vl = vstate->vl; @@ -222,13 +218,18 @@ static int riscv_vr_set(struct task_struct *target, int ret; struct __riscv_v_ext_state *vstate = &target->thread.vstate; struct __riscv_v_regset_state ptrace_vstate; + struct pt_regs *regs = task_pt_regs(target); if (!(has_vector() || has_xtheadvector())) return -EINVAL; - if (!riscv_v_vstate_query(task_pt_regs(target))) + if (!riscv_v_vstate_query(regs)) return -ENODATA; + /* Silently drop the modification to tracee as no vreg lives across a syscall */ + if (__riscv_v_vstate_check(regs->status, INITIAL)) + return 0; + /* Copy rest of the vstate except datap */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0, sizeof(struct __riscv_v_regset_state)); diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 59784dc117e454..0da352310b841b 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -89,9 +89,7 @@ static long save_v_state(struct pt_regs *regs, void __user *sc_vec) /* datap is designed to be 16 byte aligned for better performance */ WARN_ON(!IS_ALIGNED((unsigned long)datap, 16)); - get_cpu_vector_context(); - riscv_v_vstate_save(¤t->thread.vstate, regs); - put_cpu_vector_context(); + riscv_v_ucontext_save(current); /* Copy everything of vstate but datap. */ err = __copy_to_user(&state->v_state, ¤t->thread.vstate, @@ -121,9 +119,14 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec) /* * Mark the vstate as clean prior performing the actual copy, * to avoid getting the vstate incorrectly clobbered by the - * discarded vector state. + * discarded vector state. + * + * This also allows user to modify vregs through the signal + * interface at a syscall stop. e.g. to support user space + * context switching. */ riscv_v_vstate_set_restore(current, regs); + __riscv_v_vstate_clean(regs); /* Copy everything of __sc_riscv_v_state except datap. */ err = __copy_from_user(¤t->thread.vstate, &state->v_state, diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index b112166d51e9f5..dfddbb18ea170f 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -29,6 +29,43 @@ static struct kmem_cache *riscv_v_kernel_cachep; unsigned long riscv_v_vsize __read_mostly; EXPORT_SYMBOL_GPL(riscv_v_vsize); +/* + * Context memory is not coherent to register when sstatus.vs is set to INITIAL. This function + * take the INITIAL state into consideration and reflect the nulled state into context memory. + * Assume the target task is not actively running when tsk != current + */ +void riscv_v_ucontext_save(struct task_struct *tsk) +{ + struct __riscv_v_ext_state *vstate = &tsk->thread.vstate; + struct pt_regs *regs = task_pt_regs(tsk); + + /* + * Do not set vstate as clean when it is INITIAL, otherwise we lose track of the nulled + * state in ptrace. + */ + if (tsk == current) { + get_cpu_vector_context(); + if (__riscv_v_vstate_check(regs->status, INITIAL)) { + riscv_v_enable(); + __riscv_v_vstate_discard(); + __riscv_v_vstate_save(vstate, vstate->datap); + riscv_v_disable(); + } else { + riscv_v_vstate_save(vstate, regs); + } + put_cpu_vector_context(); + } else if (__riscv_v_vstate_check(regs->status, INITIAL)) { + /* + * If we are not current and VS == INITIAL, null out the context memory for tsk + * using kernel mode vector. + */ + kernel_vector_begin(); + __riscv_v_vstate_discard(); + __riscv_v_vstate_save(vstate, vstate->datap); + kernel_vector_end(); + } +} + int riscv_v_setup_vsize(void) { unsigned long this_vsize;