17017858eSGreentime Hu // SPDX-License-Identifier: GPL-2.0-or-later
27017858eSGreentime Hu /*
37017858eSGreentime Hu * Copyright (C) 2023 SiFive
47017858eSGreentime Hu * Author: Andy Chiu <andy.chiu@sifive.com>
57017858eSGreentime Hu */
67017858eSGreentime Hu #include <linux/export.h>
7cd054837SAndy Chiu #include <linux/sched/signal.h>
8cd054837SAndy Chiu #include <linux/types.h>
9cd054837SAndy Chiu #include <linux/slab.h>
10cd054837SAndy Chiu #include <linux/sched.h>
11cd054837SAndy Chiu #include <linux/uaccess.h>
121fd96a3eSAndy Chiu #include <linux/prctl.h>
137017858eSGreentime Hu
14cd054837SAndy Chiu #include <asm/thread_info.h>
15cd054837SAndy Chiu #include <asm/processor.h>
16cd054837SAndy Chiu #include <asm/insn.h>
177017858eSGreentime Hu #include <asm/vector.h>
187017858eSGreentime Hu #include <asm/csr.h>
197017858eSGreentime Hu #include <asm/elf.h>
20cd054837SAndy Chiu #include <asm/ptrace.h>
217017858eSGreentime Hu #include <asm/bug.h>
227017858eSGreentime Hu
231fd96a3eSAndy Chiu static bool riscv_v_implicit_uacc = IS_ENABLED(CONFIG_RISCV_ISA_V_DEFAULT_ENABLE);
241fd96a3eSAndy Chiu
257017858eSGreentime Hu unsigned long riscv_v_vsize __read_mostly;
267017858eSGreentime Hu EXPORT_SYMBOL_GPL(riscv_v_vsize);
277017858eSGreentime Hu
riscv_v_setup_vsize(void)287017858eSGreentime Hu int riscv_v_setup_vsize(void)
297017858eSGreentime Hu {
307017858eSGreentime Hu unsigned long this_vsize;
317017858eSGreentime Hu
327017858eSGreentime Hu /* There are 32 vector registers with vlenb length. */
337017858eSGreentime Hu riscv_v_enable();
347017858eSGreentime Hu this_vsize = csr_read(CSR_VLENB) * 32;
357017858eSGreentime Hu riscv_v_disable();
367017858eSGreentime Hu
377017858eSGreentime Hu if (!riscv_v_vsize) {
387017858eSGreentime Hu riscv_v_vsize = this_vsize;
397017858eSGreentime Hu return 0;
407017858eSGreentime Hu }
417017858eSGreentime Hu
427017858eSGreentime Hu if (riscv_v_vsize != this_vsize) {
437017858eSGreentime Hu WARN(1, "RISCV_ISA_V only supports one vlenb on SMP systems");
447017858eSGreentime Hu return -EOPNOTSUPP;
457017858eSGreentime Hu }
467017858eSGreentime Hu
477017858eSGreentime Hu return 0;
487017858eSGreentime Hu }
49cd054837SAndy Chiu
insn_is_vector(u32 insn_buf)50cd054837SAndy Chiu static bool insn_is_vector(u32 insn_buf)
51cd054837SAndy Chiu {
52cd054837SAndy Chiu u32 opcode = insn_buf & __INSN_OPCODE_MASK;
53cd054837SAndy Chiu u32 width, csr;
54cd054837SAndy Chiu
55cd054837SAndy Chiu /*
56cd054837SAndy Chiu * All V-related instructions, including CSR operations are 4-Byte. So,
57cd054837SAndy Chiu * do not handle if the instruction length is not 4-Byte.
58cd054837SAndy Chiu */
59cd054837SAndy Chiu if (unlikely(GET_INSN_LENGTH(insn_buf) != 4))
60cd054837SAndy Chiu return false;
61cd054837SAndy Chiu
62cd054837SAndy Chiu switch (opcode) {
63cd054837SAndy Chiu case RVV_OPCODE_VECTOR:
64cd054837SAndy Chiu return true;
65cd054837SAndy Chiu case RVV_OPCODE_VL:
66cd054837SAndy Chiu case RVV_OPCODE_VS:
67cd054837SAndy Chiu width = RVV_EXRACT_VL_VS_WIDTH(insn_buf);
68cd054837SAndy Chiu if (width == RVV_VL_VS_WIDTH_8 || width == RVV_VL_VS_WIDTH_16 ||
69cd054837SAndy Chiu width == RVV_VL_VS_WIDTH_32 || width == RVV_VL_VS_WIDTH_64)
70cd054837SAndy Chiu return true;
71cd054837SAndy Chiu
72cd054837SAndy Chiu break;
73cd054837SAndy Chiu case RVG_OPCODE_SYSTEM:
74cd054837SAndy Chiu csr = RVG_EXTRACT_SYSTEM_CSR(insn_buf);
75cd054837SAndy Chiu if ((csr >= CSR_VSTART && csr <= CSR_VCSR) ||
76cd054837SAndy Chiu (csr >= CSR_VL && csr <= CSR_VLENB))
77cd054837SAndy Chiu return true;
78cd054837SAndy Chiu }
79cd054837SAndy Chiu
80cd054837SAndy Chiu return false;
81cd054837SAndy Chiu }
82cd054837SAndy Chiu
riscv_v_thread_zalloc(void)83cd054837SAndy Chiu static int riscv_v_thread_zalloc(void)
84cd054837SAndy Chiu {
85cd054837SAndy Chiu void *datap;
86cd054837SAndy Chiu
87cd054837SAndy Chiu datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
88cd054837SAndy Chiu if (!datap)
89cd054837SAndy Chiu return -ENOMEM;
90cd054837SAndy Chiu
91cd054837SAndy Chiu current->thread.vstate.datap = datap;
92cd054837SAndy Chiu memset(¤t->thread.vstate, 0, offsetof(struct __riscv_v_ext_state,
93cd054837SAndy Chiu datap));
94cd054837SAndy Chiu return 0;
95cd054837SAndy Chiu }
96cd054837SAndy Chiu
971fd96a3eSAndy Chiu #define VSTATE_CTRL_GET_CUR(x) ((x) & PR_RISCV_V_VSTATE_CTRL_CUR_MASK)
981fd96a3eSAndy Chiu #define VSTATE_CTRL_GET_NEXT(x) (((x) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2)
991fd96a3eSAndy Chiu #define VSTATE_CTRL_MAKE_NEXT(x) (((x) << 2) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK)
1001fd96a3eSAndy Chiu #define VSTATE_CTRL_GET_INHERIT(x) (!!((x) & PR_RISCV_V_VSTATE_CTRL_INHERIT))
riscv_v_ctrl_get_cur(struct task_struct * tsk)1011fd96a3eSAndy Chiu static inline int riscv_v_ctrl_get_cur(struct task_struct *tsk)
1021fd96a3eSAndy Chiu {
1031fd96a3eSAndy Chiu return VSTATE_CTRL_GET_CUR(tsk->thread.vstate_ctrl);
1041fd96a3eSAndy Chiu }
1051fd96a3eSAndy Chiu
riscv_v_ctrl_get_next(struct task_struct * tsk)1061fd96a3eSAndy Chiu static inline int riscv_v_ctrl_get_next(struct task_struct *tsk)
1071fd96a3eSAndy Chiu {
1081fd96a3eSAndy Chiu return VSTATE_CTRL_GET_NEXT(tsk->thread.vstate_ctrl);
1091fd96a3eSAndy Chiu }
1101fd96a3eSAndy Chiu
riscv_v_ctrl_test_inherit(struct task_struct * tsk)1111fd96a3eSAndy Chiu static inline bool riscv_v_ctrl_test_inherit(struct task_struct *tsk)
1121fd96a3eSAndy Chiu {
1131fd96a3eSAndy Chiu return VSTATE_CTRL_GET_INHERIT(tsk->thread.vstate_ctrl);
1141fd96a3eSAndy Chiu }
1151fd96a3eSAndy Chiu
riscv_v_ctrl_set(struct task_struct * tsk,int cur,int nxt,bool inherit)1161fd96a3eSAndy Chiu static inline void riscv_v_ctrl_set(struct task_struct *tsk, int cur, int nxt,
1171fd96a3eSAndy Chiu bool inherit)
1181fd96a3eSAndy Chiu {
1191fd96a3eSAndy Chiu unsigned long ctrl;
1201fd96a3eSAndy Chiu
1211fd96a3eSAndy Chiu ctrl = cur & PR_RISCV_V_VSTATE_CTRL_CUR_MASK;
1221fd96a3eSAndy Chiu ctrl |= VSTATE_CTRL_MAKE_NEXT(nxt);
1231fd96a3eSAndy Chiu if (inherit)
1241fd96a3eSAndy Chiu ctrl |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
1251fd96a3eSAndy Chiu tsk->thread.vstate_ctrl = ctrl;
1261fd96a3eSAndy Chiu }
1271fd96a3eSAndy Chiu
riscv_v_vstate_ctrl_user_allowed(void)1281fd96a3eSAndy Chiu bool riscv_v_vstate_ctrl_user_allowed(void)
1291fd96a3eSAndy Chiu {
1301fd96a3eSAndy Chiu return riscv_v_ctrl_get_cur(current) == PR_RISCV_V_VSTATE_CTRL_ON;
1311fd96a3eSAndy Chiu }
1321fd96a3eSAndy Chiu EXPORT_SYMBOL_GPL(riscv_v_vstate_ctrl_user_allowed);
1331fd96a3eSAndy Chiu
riscv_v_first_use_handler(struct pt_regs * regs)134cd054837SAndy Chiu bool riscv_v_first_use_handler(struct pt_regs *regs)
135cd054837SAndy Chiu {
136cd054837SAndy Chiu u32 __user *epc = (u32 __user *)regs->epc;
137cd054837SAndy Chiu u32 insn = (u32)regs->badaddr;
138cd054837SAndy Chiu
139cd054837SAndy Chiu /* Do not handle if V is not supported, or disabled */
140cd054837SAndy Chiu if (!(ELF_HWCAP & COMPAT_HWCAP_ISA_V))
141cd054837SAndy Chiu return false;
142cd054837SAndy Chiu
143cd054837SAndy Chiu /* If V has been enabled then it is not the first-use trap */
144cd054837SAndy Chiu if (riscv_v_vstate_query(regs))
145cd054837SAndy Chiu return false;
146cd054837SAndy Chiu
147cd054837SAndy Chiu /* Get the instruction */
148cd054837SAndy Chiu if (!insn) {
149cd054837SAndy Chiu if (__get_user(insn, epc))
150cd054837SAndy Chiu return false;
151cd054837SAndy Chiu }
152cd054837SAndy Chiu
153cd054837SAndy Chiu /* Filter out non-V instructions */
154cd054837SAndy Chiu if (!insn_is_vector(insn))
155cd054837SAndy Chiu return false;
156cd054837SAndy Chiu
157cd054837SAndy Chiu /* Sanity check. datap should be null by the time of the first-use trap */
158cd054837SAndy Chiu WARN_ON(current->thread.vstate.datap);
159cd054837SAndy Chiu
160cd054837SAndy Chiu /*
161cd054837SAndy Chiu * Now we sure that this is a V instruction. And it executes in the
162cd054837SAndy Chiu * context where VS has been off. So, try to allocate the user's V
163cd054837SAndy Chiu * context and resume execution.
164cd054837SAndy Chiu */
165cd054837SAndy Chiu if (riscv_v_thread_zalloc()) {
166cd054837SAndy Chiu force_sig(SIGBUS);
167cd054837SAndy Chiu return true;
168cd054837SAndy Chiu }
169cd054837SAndy Chiu riscv_v_vstate_on(regs);
170*75b59f2aSAndy Chiu riscv_v_vstate_restore(current, regs);
171cd054837SAndy Chiu return true;
172cd054837SAndy Chiu }
1731fd96a3eSAndy Chiu
riscv_v_vstate_ctrl_init(struct task_struct * tsk)1741fd96a3eSAndy Chiu void riscv_v_vstate_ctrl_init(struct task_struct *tsk)
1751fd96a3eSAndy Chiu {
1761fd96a3eSAndy Chiu bool inherit;
1771fd96a3eSAndy Chiu int cur, next;
1781fd96a3eSAndy Chiu
1791fd96a3eSAndy Chiu if (!has_vector())
1801fd96a3eSAndy Chiu return;
1811fd96a3eSAndy Chiu
1821fd96a3eSAndy Chiu next = riscv_v_ctrl_get_next(tsk);
1831fd96a3eSAndy Chiu if (!next) {
1847ca7a7b9SAndy Chiu if (READ_ONCE(riscv_v_implicit_uacc))
1851fd96a3eSAndy Chiu cur = PR_RISCV_V_VSTATE_CTRL_ON;
1861fd96a3eSAndy Chiu else
1871fd96a3eSAndy Chiu cur = PR_RISCV_V_VSTATE_CTRL_OFF;
1881fd96a3eSAndy Chiu } else {
1891fd96a3eSAndy Chiu cur = next;
1901fd96a3eSAndy Chiu }
1911fd96a3eSAndy Chiu /* Clear next mask if inherit-bit is not set */
1921fd96a3eSAndy Chiu inherit = riscv_v_ctrl_test_inherit(tsk);
1931fd96a3eSAndy Chiu if (!inherit)
1941fd96a3eSAndy Chiu next = PR_RISCV_V_VSTATE_CTRL_DEFAULT;
1951fd96a3eSAndy Chiu
1961fd96a3eSAndy Chiu riscv_v_ctrl_set(tsk, cur, next, inherit);
1971fd96a3eSAndy Chiu }
1981fd96a3eSAndy Chiu
riscv_v_vstate_ctrl_get_current(void)1991fd96a3eSAndy Chiu long riscv_v_vstate_ctrl_get_current(void)
2001fd96a3eSAndy Chiu {
2011fd96a3eSAndy Chiu if (!has_vector())
2021fd96a3eSAndy Chiu return -EINVAL;
2031fd96a3eSAndy Chiu
2041fd96a3eSAndy Chiu return current->thread.vstate_ctrl & PR_RISCV_V_VSTATE_CTRL_MASK;
2051fd96a3eSAndy Chiu }
2061fd96a3eSAndy Chiu
riscv_v_vstate_ctrl_set_current(unsigned long arg)2071fd96a3eSAndy Chiu long riscv_v_vstate_ctrl_set_current(unsigned long arg)
2081fd96a3eSAndy Chiu {
2091fd96a3eSAndy Chiu bool inherit;
2101fd96a3eSAndy Chiu int cur, next;
2111fd96a3eSAndy Chiu
2121fd96a3eSAndy Chiu if (!has_vector())
2131fd96a3eSAndy Chiu return -EINVAL;
2141fd96a3eSAndy Chiu
2151fd96a3eSAndy Chiu if (arg & ~PR_RISCV_V_VSTATE_CTRL_MASK)
2161fd96a3eSAndy Chiu return -EINVAL;
2171fd96a3eSAndy Chiu
2181fd96a3eSAndy Chiu cur = VSTATE_CTRL_GET_CUR(arg);
2191fd96a3eSAndy Chiu switch (cur) {
2201fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_OFF:
2211fd96a3eSAndy Chiu /* Do not allow user to turn off V if current is not off */
2221fd96a3eSAndy Chiu if (riscv_v_ctrl_get_cur(current) != PR_RISCV_V_VSTATE_CTRL_OFF)
2231fd96a3eSAndy Chiu return -EPERM;
2241fd96a3eSAndy Chiu
2251fd96a3eSAndy Chiu break;
2261fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_ON:
2271fd96a3eSAndy Chiu break;
2281fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
2291fd96a3eSAndy Chiu cur = riscv_v_ctrl_get_cur(current);
2301fd96a3eSAndy Chiu break;
2311fd96a3eSAndy Chiu default:
2321fd96a3eSAndy Chiu return -EINVAL;
2331fd96a3eSAndy Chiu }
2341fd96a3eSAndy Chiu
2351fd96a3eSAndy Chiu next = VSTATE_CTRL_GET_NEXT(arg);
2361fd96a3eSAndy Chiu inherit = VSTATE_CTRL_GET_INHERIT(arg);
2371fd96a3eSAndy Chiu switch (next) {
2381fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
2391fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_OFF:
2401fd96a3eSAndy Chiu case PR_RISCV_V_VSTATE_CTRL_ON:
2411fd96a3eSAndy Chiu riscv_v_ctrl_set(current, cur, next, inherit);
2421fd96a3eSAndy Chiu return 0;
2431fd96a3eSAndy Chiu }
2441fd96a3eSAndy Chiu
2451fd96a3eSAndy Chiu return -EINVAL;
2461fd96a3eSAndy Chiu }
2477ca7a7b9SAndy Chiu
2487ca7a7b9SAndy Chiu #ifdef CONFIG_SYSCTL
2497ca7a7b9SAndy Chiu
2507ca7a7b9SAndy Chiu static struct ctl_table riscv_v_default_vstate_table[] = {
2517ca7a7b9SAndy Chiu {
2527ca7a7b9SAndy Chiu .procname = "riscv_v_default_allow",
2537ca7a7b9SAndy Chiu .data = &riscv_v_implicit_uacc,
2547ca7a7b9SAndy Chiu .maxlen = sizeof(riscv_v_implicit_uacc),
2557ca7a7b9SAndy Chiu .mode = 0644,
2567ca7a7b9SAndy Chiu .proc_handler = proc_dobool,
2577ca7a7b9SAndy Chiu },
2587ca7a7b9SAndy Chiu { }
2597ca7a7b9SAndy Chiu };
2607ca7a7b9SAndy Chiu
riscv_v_sysctl_init(void)2617ca7a7b9SAndy Chiu static int __init riscv_v_sysctl_init(void)
2627ca7a7b9SAndy Chiu {
2637ca7a7b9SAndy Chiu if (has_vector())
2647ca7a7b9SAndy Chiu if (!register_sysctl("abi", riscv_v_default_vstate_table))
2657ca7a7b9SAndy Chiu return -EINVAL;
2667ca7a7b9SAndy Chiu return 0;
2677ca7a7b9SAndy Chiu }
2687ca7a7b9SAndy Chiu
2697ca7a7b9SAndy Chiu #else /* ! CONFIG_SYSCTL */
riscv_v_sysctl_init(void)2707ca7a7b9SAndy Chiu static int __init riscv_v_sysctl_init(void) { return 0; }
2717ca7a7b9SAndy Chiu #endif /* ! CONFIG_SYSCTL */
2727ca7a7b9SAndy Chiu
riscv_v_init(void)2737ca7a7b9SAndy Chiu static int riscv_v_init(void)
2747ca7a7b9SAndy Chiu {
2757ca7a7b9SAndy Chiu return riscv_v_sysctl_init();
2767ca7a7b9SAndy Chiu }
2777ca7a7b9SAndy Chiu core_initcall(riscv_v_init);
278