Path: blob/master/arch/riscv/kernel/kernel_mode_vector.c
26424 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2012 ARM Ltd.3* Author: Catalin Marinas <[email protected]>4* Copyright (C) 2017 Linaro Ltd. <[email protected]>5* Copyright (C) 2021 SiFive6*/7#include <linux/compiler.h>8#include <linux/irqflags.h>9#include <linux/percpu.h>10#include <linux/preempt.h>11#include <linux/types.h>1213#include <asm/vector.h>14#include <asm/switch_to.h>15#include <asm/simd.h>16#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE17#include <asm/asm-prototypes.h>18#endif1920static inline void riscv_v_flags_set(u32 flags)21{22WRITE_ONCE(current->thread.riscv_v_flags, flags);23}2425static inline void riscv_v_start(u32 flags)26{27int orig;2829orig = riscv_v_flags();30BUG_ON((orig & flags) != 0);31riscv_v_flags_set(orig | flags);32barrier();33}3435static inline void riscv_v_stop(u32 flags)36{37int orig;3839barrier();40orig = riscv_v_flags();41BUG_ON((orig & flags) == 0);42riscv_v_flags_set(orig & ~flags);43}4445/*46* Claim ownership of the CPU vector context for use by the calling context.47*48* The caller may freely manipulate the vector context metadata until49* put_cpu_vector_context() is called.50*/51void get_cpu_vector_context(void)52{53/*54* disable softirqs so it is impossible for softirqs to nest55* get_cpu_vector_context() when kernel is actively using Vector.56*/57if (!IS_ENABLED(CONFIG_PREEMPT_RT))58local_bh_disable();59else60preempt_disable();6162riscv_v_start(RISCV_KERNEL_MODE_V);63}6465/*66* Release the CPU vector context.67*68* Must be called from a context in which get_cpu_vector_context() was69* previously called, with no call to put_cpu_vector_context() in the70* meantime.71*/72void put_cpu_vector_context(void)73{74riscv_v_stop(RISCV_KERNEL_MODE_V);7576if (!IS_ENABLED(CONFIG_PREEMPT_RT))77local_bh_enable();78else79preempt_enable();80}8182#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE83static __always_inline u32 *riscv_v_flags_ptr(void)84{85return ¤t->thread.riscv_v_flags;86}8788static inline void riscv_preempt_v_set_dirty(void)89{90*riscv_v_flags_ptr() |= RISCV_PREEMPT_V_DIRTY;91}9293static inline void riscv_preempt_v_reset_flags(void)94{95*riscv_v_flags_ptr() &= ~(RISCV_PREEMPT_V_DIRTY | RISCV_PREEMPT_V_NEED_RESTORE);96}9798static inline void riscv_v_ctx_depth_inc(void)99{100*riscv_v_flags_ptr() += RISCV_V_CTX_UNIT_DEPTH;101}102103static inline void riscv_v_ctx_depth_dec(void)104{105*riscv_v_flags_ptr() -= RISCV_V_CTX_UNIT_DEPTH;106}107108static inline u32 riscv_v_ctx_get_depth(void)109{110return *riscv_v_flags_ptr() & RISCV_V_CTX_DEPTH_MASK;111}112113static int riscv_v_stop_kernel_context(void)114{115if (riscv_v_ctx_get_depth() != 0 || !riscv_preempt_v_started(current))116return 1;117118riscv_preempt_v_clear_dirty(current);119riscv_v_stop(RISCV_PREEMPT_V);120return 0;121}122123static int riscv_v_start_kernel_context(bool *is_nested)124{125struct __riscv_v_ext_state *kvstate, *uvstate;126127kvstate = ¤t->thread.kernel_vstate;128if (!kvstate->datap)129return -ENOENT;130131if (riscv_preempt_v_started(current)) {132WARN_ON(riscv_v_ctx_get_depth() == 0);133*is_nested = true;134get_cpu_vector_context();135if (riscv_preempt_v_dirty(current)) {136__riscv_v_vstate_save(kvstate, kvstate->datap);137riscv_preempt_v_clear_dirty(current);138}139riscv_preempt_v_set_restore(current);140return 0;141}142143/* Transfer the ownership of V from user to kernel, then save */144riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);145if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) {146uvstate = ¤t->thread.vstate;147__riscv_v_vstate_save(uvstate, uvstate->datap);148}149riscv_preempt_v_clear_dirty(current);150return 0;151}152153/* low-level V context handling code, called with irq disabled */154asmlinkage void riscv_v_context_nesting_start(struct pt_regs *regs)155{156int depth;157158if (!riscv_preempt_v_started(current))159return;160161depth = riscv_v_ctx_get_depth();162if (depth == 0 && __riscv_v_vstate_check(regs->status, DIRTY))163riscv_preempt_v_set_dirty();164165riscv_v_ctx_depth_inc();166}167168asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)169{170struct __riscv_v_ext_state *vstate = ¤t->thread.kernel_vstate;171u32 depth;172173WARN_ON(!irqs_disabled());174175if (!riscv_preempt_v_started(current))176return;177178riscv_v_ctx_depth_dec();179depth = riscv_v_ctx_get_depth();180if (depth == 0) {181if (riscv_preempt_v_restore(current)) {182__riscv_v_vstate_restore(vstate, vstate->datap);183__riscv_v_vstate_clean(regs);184riscv_preempt_v_reset_flags();185}186}187}188#else189#define riscv_v_start_kernel_context(nested) (-ENOENT)190#define riscv_v_stop_kernel_context() (-ENOENT)191#endif /* CONFIG_RISCV_ISA_V_PREEMPTIVE */192193/*194* kernel_vector_begin(): obtain the CPU vector registers for use by the calling195* context196*197* Must not be called unless may_use_simd() returns true.198* Task context in the vector registers is saved back to memory as necessary.199*200* A matching call to kernel_vector_end() must be made before returning from the201* calling context.202*203* The caller may freely use the vector registers until kernel_vector_end() is204* called.205*/206void kernel_vector_begin(void)207{208bool nested = false;209210if (WARN_ON(!(has_vector() || has_xtheadvector())))211return;212213BUG_ON(!may_use_simd());214215if (riscv_v_start_kernel_context(&nested)) {216get_cpu_vector_context();217riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current));218}219220if (!nested)221riscv_v_vstate_set_restore(current, task_pt_regs(current));222223riscv_v_enable();224}225EXPORT_SYMBOL_GPL(kernel_vector_begin);226227/*228* kernel_vector_end(): give the CPU vector registers back to the current task229*230* Must be called from a context in which kernel_vector_begin() was previously231* called, with no call to kernel_vector_end() in the meantime.232*233* The caller must not use the vector registers after this function is called,234* unless kernel_vector_begin() is called again in the meantime.235*/236void kernel_vector_end(void)237{238if (WARN_ON(!(has_vector() || has_xtheadvector())))239return;240241riscv_v_disable();242243if (riscv_v_stop_kernel_context())244put_cpu_vector_context();245}246EXPORT_SYMBOL_GPL(kernel_vector_end);247248249