Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions arch/riscv/include/asm/kvm_vcpu_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
#include <asm/vector.h>
#include <asm/kvm_host.h>

static __always_inline void __kvm_riscv_vector_save(struct kvm_cpu_context *context)
static __always_inline void kvm_riscv_vector_save(struct kvm_cpu_context *context)
{
riscv_v_enable();
__riscv_v_vstate_save(&context->vector, context->vector.datap);
riscv_v_disable();
}

static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context)
static __always_inline void kvm_riscv_vector_restore(struct kvm_cpu_context *context)
{
riscv_v_enable();
__riscv_v_vstate_restore(&context->vector, context->vector.datap);
riscv_v_disable();
}

void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu);
Expand Down
45 changes: 24 additions & 21 deletions arch/riscv/include/asm/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -95,7 +96,7 @@ static inline void riscv_v_vstate_off(struct pt_regs *regs)
regs->status = __riscv_v_vstate_or(regs->status, OFF);
}

static inline void riscv_v_vstate_on(struct pt_regs *regs)
static inline void riscv_v_vstate_init(struct pt_regs *regs)
{
regs->status = __riscv_v_vstate_or(regs->status, INITIAL);
}
Expand Down Expand Up @@ -198,7 +199,6 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
{
unsigned long vl;

riscv_v_enable();
__vstate_csr_save(save_to);
if (has_xtheadvector()) {
asm volatile (
Expand Down Expand Up @@ -227,15 +227,13 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
".option pop\n\t"
: "=&r" (vl) : "r" (datap) : "memory");
}
riscv_v_disable();
}

static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
void *datap)
{
unsigned long vl;

riscv_v_enable();
if (has_xtheadvector()) {
asm volatile (
"mv t0, %0\n\t"
Expand Down Expand Up @@ -264,14 +262,12 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_
: "=&r" (vl) : "r" (datap) : "memory");
}
__vstate_csr_restore(restore_from);
riscv_v_disable();
}

static inline void __riscv_v_vstate_discard(void)
{
unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);

riscv_v_enable();
if (has_xtheadvector())
asm volatile (THEAD_VSETVLI_T4X0E8M8D1 : : : "t4");
else
Expand All @@ -291,42 +287,45 @@ static inline void __riscv_v_vstate_discard(void)
"vsetvl %0, x0, %1\n\t"
".option pop\n\t"
: "=&r" (vl) : "r" (vtype_inval));

riscv_v_disable();
}

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);
}
}

static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
struct pt_regs *regs)
{
if (__riscv_v_vstate_check(regs->status, DIRTY)) {
riscv_v_enable();
__riscv_v_vstate_save(vstate, vstate->datap);
riscv_v_disable();
__riscv_v_vstate_clean(regs);
}
}

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_enable();
__riscv_v_vstate_discard();
riscv_v_disable();
} else if (__riscv_v_vstate_check(regs->status, CLEAN)) {
riscv_v_enable();
__riscv_v_vstate_restore(vstate, vstate->datap);
__riscv_v_vstate_clean(regs);
riscv_v_disable();
}
}

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);
}

static inline void riscv_v_vstate_discard(struct pt_regs *regs)
{
if (riscv_v_vstate_query(regs)) {
riscv_v_vstate_set_restore(current, regs);
riscv_v_vstate_init(regs);
}
}

Expand Down Expand Up @@ -378,8 +377,10 @@ static inline void __switch_to_vector(struct task_struct *prev,
prev->thread.riscv_v_flags |= RISCV_PREEMPT_V_IN_SCHEDULE;
}
if (riscv_preempt_v_dirty(prev)) {
riscv_v_enable();
__riscv_v_vstate_save(&prev->thread.kernel_vstate,
prev->thread.kernel_vstate.datap);
riscv_v_disable();
riscv_preempt_v_clear_dirty(prev);
}
} else {
Expand All @@ -395,6 +396,7 @@ static inline void __switch_to_vector(struct task_struct *prev,
riscv_preempt_v_set_restore(next);
}
} else {
/* VS is never DIRTY at this point, there's no need to alter vstate here */
riscv_v_vstate_set_restore(next, task_pt_regs(next));
}
}
Expand All @@ -420,12 +422,13 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
#define riscv_v_vstate_restore(vstate, regs) do {} while (0)
#define __switch_to_vector(__prev, __next) do {} while (0)
#define riscv_v_vstate_off(regs) do {} while (0)
#define riscv_v_vstate_on(regs) do {} while (0)
#define riscv_v_vstate_init(regs) do {} while (0)
#define riscv_v_thread_free(tsk) do {} while (0)
#define riscv_v_setup_ctx_cache() do {} while (0)
#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 */
Expand Down
27 changes: 22 additions & 5 deletions arch/riscv/kernel/kernel_mode_vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,35 @@ static int riscv_v_start_kernel_context(bool *is_nested)
*is_nested = true;
get_cpu_vector_context();
if (riscv_preempt_v_dirty(current)) {
riscv_v_enable();
__riscv_v_vstate_save(kvstate, kvstate->datap);
riscv_v_disable();
riscv_preempt_v_clear_dirty(current);
}
riscv_preempt_v_set_restore(current);
return 0;
}

/* Transfer the ownership of V from user to kernel, then save */
riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);
/*
* Skip saving user's context if it is not DIRTY. We would have to start KMV in "dirty" if
* this check is performed after KMV starts, to protect user's ctx. Then, we could waste
* time saving already "clean" context once KMV is started in "dirty".
*/
if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) {
uvstate = &current->thread.vstate;
__riscv_v_vstate_save(uvstate, uvstate->datap);
/* Transfer the ownership of V from user to kernel, then save */
riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);
/*
* Calling the guarded version of vstate_save to make the code cleaner. Also, the
* vstate check within the call is necessary as context switch may happen between
* __riscv_v_vstate_check and riscv_v_start. In such case we are not supposed to
* save the context again.
*/
riscv_v_vstate_save(&current->thread.vstate, task_pt_regs(current));
riscv_preempt_v_clear_dirty(current);
return 0;
}
riscv_preempt_v_clear_dirty(current);

riscv_v_start(RISCV_PREEMPT_V);
return 0;
}

Expand Down Expand Up @@ -180,7 +195,9 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)
depth = riscv_v_ctx_get_depth();
if (depth == 0) {
if (riscv_preempt_v_restore(current)) {
riscv_v_enable();
__riscv_v_vstate_restore(vstate, vstate->datap);
riscv_v_disable();
__riscv_v_vstate_clean(regs);
riscv_preempt_v_reset_flags();
}
Expand Down
13 changes: 7 additions & 6 deletions arch/riscv/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&current->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;
Expand Down Expand Up @@ -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));
Expand Down
11 changes: 7 additions & 4 deletions arch/riscv/kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&current->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, &current->thread.vstate,
Expand Down Expand Up @@ -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(&current->thread.vstate, &state->v_state,
Expand Down
39 changes: 38 additions & 1 deletion arch/riscv/kernel/vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -221,7 +258,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs)
return true;
}

riscv_v_vstate_on(regs);
__riscv_v_vstate_clean(regs);
riscv_v_vstate_set_restore(current, regs);

return true;
Expand Down
8 changes: 4 additions & 4 deletions arch/riscv/kvm/vcpu_vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
{
if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
if (riscv_isa_extension_available(isa, v))
__kvm_riscv_vector_save(cntx);
kvm_riscv_vector_save(cntx);
kvm_riscv_vcpu_vector_clean(cntx);
}
}
Expand All @@ -56,7 +56,7 @@ void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
{
if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
if (riscv_isa_extension_available(isa, v))
__kvm_riscv_vector_restore(cntx);
kvm_riscv_vector_restore(cntx);
kvm_riscv_vcpu_vector_clean(cntx);
}
}
Expand All @@ -65,13 +65,13 @@ void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
{
/* No need to check host sstatus as it can be modified outside */
if (!kvm_riscv_isa_check_host(V))
__kvm_riscv_vector_save(cntx);
kvm_riscv_vector_save(cntx);
}

void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
{
if (!kvm_riscv_isa_check_host(V))
__kvm_riscv_vector_restore(cntx);
kvm_riscv_vector_restore(cntx);
}

int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
Expand Down
Loading
Loading