xref: /linux/arch/riscv/include/asm/vector.h (revision ba6ec09911b805778a2fed6d626bfe77b011a717)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Copyright (C) 2020 SiFive
4  */
5 
6 #ifndef __ASM_RISCV_VECTOR_H
7 #define __ASM_RISCV_VECTOR_H
8 
9 #include <linux/types.h>
10 #include <uapi/asm-generic/errno.h>
11 
12 #ifdef CONFIG_RISCV_ISA_V
13 
14 #include <linux/stringify.h>
15 #include <linux/sched.h>
16 #include <linux/sched/task_stack.h>
17 #include <asm/ptrace.h>
18 #include <asm/cpufeature.h>
19 #include <asm/csr.h>
20 #include <asm/asm.h>
21 #include <asm/vendorid_list.h>
22 #include <asm/vendor_extensions.h>
23 #include <asm/vendor_extensions/thead.h>
24 
25 #define __riscv_v_vstate_or(_val, TYPE) ({				\
26 	typeof(_val) _res = _val;					\
27 	if (has_xtheadvector()) \
28 		_res = (_res & ~SR_VS_THEAD) | SR_VS_##TYPE##_THEAD;	\
29 	else								\
30 		_res = (_res & ~SR_VS) | SR_VS_##TYPE;			\
31 	_res;								\
32 })
33 
34 #define __riscv_v_vstate_check(_val, TYPE) ({				\
35 	bool _res;							\
36 	if (has_xtheadvector()) \
37 		_res = ((_val) & SR_VS_THEAD) == SR_VS_##TYPE##_THEAD;	\
38 	else								\
39 		_res = ((_val) & SR_VS) == SR_VS_##TYPE;		\
40 	_res;								\
41 })
42 
43 extern unsigned long riscv_v_vsize;
44 int riscv_v_setup_vsize(void);
45 bool insn_is_vector(u32 insn_buf);
46 bool riscv_v_first_use_handler(struct pt_regs *regs);
47 void kernel_vector_begin(void);
48 void kernel_vector_end(void);
49 void get_cpu_vector_context(void);
50 void put_cpu_vector_context(void);
51 void riscv_v_thread_free(struct task_struct *tsk);
52 void __init riscv_v_setup_ctx_cache(void);
53 void riscv_v_thread_alloc(struct task_struct *tsk);
54 
riscv_v_flags(void)55 static inline u32 riscv_v_flags(void)
56 {
57 	return READ_ONCE(current->thread.riscv_v_flags);
58 }
59 
has_vector(void)60 static __always_inline bool has_vector(void)
61 {
62 	return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
63 }
64 
has_xtheadvector_no_alternatives(void)65 static __always_inline bool has_xtheadvector_no_alternatives(void)
66 {
67 	if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
68 		return riscv_isa_vendor_extension_available(THEAD_VENDOR_ID, XTHEADVECTOR);
69 	else
70 		return false;
71 }
72 
has_xtheadvector(void)73 static __always_inline bool has_xtheadvector(void)
74 {
75 	if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
76 		return riscv_has_vendor_extension_unlikely(THEAD_VENDOR_ID,
77 							   RISCV_ISA_VENDOR_EXT_XTHEADVECTOR);
78 	else
79 		return false;
80 }
81 
__riscv_v_vstate_clean(struct pt_regs * regs)82 static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
83 {
84 	regs->status = __riscv_v_vstate_or(regs->status, CLEAN);
85 }
86 
__riscv_v_vstate_dirty(struct pt_regs * regs)87 static inline void __riscv_v_vstate_dirty(struct pt_regs *regs)
88 {
89 	regs->status = __riscv_v_vstate_or(regs->status, DIRTY);
90 }
91 
riscv_v_vstate_off(struct pt_regs * regs)92 static inline void riscv_v_vstate_off(struct pt_regs *regs)
93 {
94 	regs->status = __riscv_v_vstate_or(regs->status, OFF);
95 }
96 
riscv_v_vstate_on(struct pt_regs * regs)97 static inline void riscv_v_vstate_on(struct pt_regs *regs)
98 {
99 	regs->status = __riscv_v_vstate_or(regs->status, INITIAL);
100 }
101 
riscv_v_vstate_query(struct pt_regs * regs)102 static inline bool riscv_v_vstate_query(struct pt_regs *regs)
103 {
104 	return !__riscv_v_vstate_check(regs->status, OFF);
105 }
106 
riscv_v_enable(void)107 static __always_inline void riscv_v_enable(void)
108 {
109 	if (has_xtheadvector())
110 		csr_set(CSR_SSTATUS, SR_VS_THEAD);
111 	else
112 		csr_set(CSR_SSTATUS, SR_VS);
113 }
114 
riscv_v_disable(void)115 static __always_inline void riscv_v_disable(void)
116 {
117 	if (has_xtheadvector())
118 		csr_clear(CSR_SSTATUS, SR_VS_THEAD);
119 	else
120 		csr_clear(CSR_SSTATUS, SR_VS);
121 }
122 
__vstate_csr_save(struct __riscv_v_ext_state * dest)123 static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
124 {
125 	asm volatile (
126 		"csrr	%0, " __stringify(CSR_VSTART) "\n\t"
127 		"csrr	%1, " __stringify(CSR_VTYPE) "\n\t"
128 		"csrr	%2, " __stringify(CSR_VL) "\n\t"
129 		: "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
130 		"=r" (dest->vcsr) : :);
131 
132 	if (has_xtheadvector()) {
133 		unsigned long status;
134 
135 		/*
136 		 * CSR_VCSR is defined as
137 		 * [2:1] - vxrm[1:0]
138 		 * [0] - vxsat
139 		 * The earlier vector spec implemented by T-Head uses separate
140 		 * registers for the same bit-elements, so just combine those
141 		 * into the existing output field.
142 		 *
143 		 * Additionally T-Head cores need FS to be enabled when accessing
144 		 * the VXRM and VXSAT CSRs, otherwise ending in illegal instructions.
145 		 * Though the cores do not implement the VXRM and VXSAT fields in the
146 		 * FCSR CSR that vector-0.7.1 specifies.
147 		 */
148 		status = csr_read_set(CSR_STATUS, SR_FS_DIRTY);
149 		dest->vcsr = csr_read(CSR_VXSAT) | csr_read(CSR_VXRM) << CSR_VXRM_SHIFT;
150 
151 		dest->vlenb = riscv_v_vsize / 32;
152 
153 		if ((status & SR_FS) != SR_FS_DIRTY)
154 			csr_write(CSR_STATUS, status);
155 	} else {
156 		dest->vcsr = csr_read(CSR_VCSR);
157 		dest->vlenb = csr_read(CSR_VLENB);
158 	}
159 }
160 
__vstate_csr_restore(struct __riscv_v_ext_state * src)161 static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src)
162 {
163 	asm volatile (
164 		".option push\n\t"
165 		".option arch, +zve32x\n\t"
166 		"vsetvl	 x0, %2, %1\n\t"
167 		".option pop\n\t"
168 		"csrw	" __stringify(CSR_VSTART) ", %0\n\t"
169 		: : "r" (src->vstart), "r" (src->vtype), "r" (src->vl));
170 
171 	if (has_xtheadvector()) {
172 		unsigned long status = csr_read(CSR_SSTATUS);
173 
174 		/*
175 		 * Similar to __vstate_csr_save above, restore values for the
176 		 * separate VXRM and VXSAT CSRs from the vcsr variable.
177 		 */
178 		status = csr_read_set(CSR_STATUS, SR_FS_DIRTY);
179 
180 		csr_write(CSR_VXRM, (src->vcsr >> CSR_VXRM_SHIFT) & CSR_VXRM_MASK);
181 		csr_write(CSR_VXSAT, src->vcsr & CSR_VXSAT_MASK);
182 
183 		if ((status & SR_FS) != SR_FS_DIRTY)
184 			csr_write(CSR_STATUS, status);
185 	} else {
186 		csr_write(CSR_VCSR, src->vcsr);
187 	}
188 }
189 
__riscv_v_vstate_save(struct __riscv_v_ext_state * save_to,void * datap)190 static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
191 					 void *datap)
192 {
193 	unsigned long vl;
194 
195 	riscv_v_enable();
196 	__vstate_csr_save(save_to);
197 	if (has_xtheadvector()) {
198 		asm volatile (
199 			"mv t0, %0\n\t"
200 			THEAD_VSETVLI_T4X0E8M8D1
201 			THEAD_VSB_V_V0T0
202 			"add		t0, t0, t4\n\t"
203 			THEAD_VSB_V_V0T0
204 			"add		t0, t0, t4\n\t"
205 			THEAD_VSB_V_V0T0
206 			"add		t0, t0, t4\n\t"
207 			THEAD_VSB_V_V0T0
208 			: : "r" (datap) : "memory", "t0", "t4");
209 	} else {
210 		asm volatile (
211 			".option push\n\t"
212 			".option arch, +zve32x\n\t"
213 			"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
214 			"vse8.v		v0, (%1)\n\t"
215 			"add		%1, %1, %0\n\t"
216 			"vse8.v		v8, (%1)\n\t"
217 			"add		%1, %1, %0\n\t"
218 			"vse8.v		v16, (%1)\n\t"
219 			"add		%1, %1, %0\n\t"
220 			"vse8.v		v24, (%1)\n\t"
221 			".option pop\n\t"
222 			: "=&r" (vl) : "r" (datap) : "memory");
223 	}
224 	riscv_v_disable();
225 }
226 
__riscv_v_vstate_restore(struct __riscv_v_ext_state * restore_from,void * datap)227 static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
228 					    void *datap)
229 {
230 	unsigned long vl;
231 
232 	riscv_v_enable();
233 	if (has_xtheadvector()) {
234 		asm volatile (
235 			"mv t0, %0\n\t"
236 			THEAD_VSETVLI_T4X0E8M8D1
237 			THEAD_VLB_V_V0T0
238 			"add		t0, t0, t4\n\t"
239 			THEAD_VLB_V_V0T0
240 			"add		t0, t0, t4\n\t"
241 			THEAD_VLB_V_V0T0
242 			"add		t0, t0, t4\n\t"
243 			THEAD_VLB_V_V0T0
244 			: : "r" (datap) : "memory", "t0", "t4");
245 	} else {
246 		asm volatile (
247 			".option push\n\t"
248 			".option arch, +zve32x\n\t"
249 			"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
250 			"vle8.v		v0, (%1)\n\t"
251 			"add		%1, %1, %0\n\t"
252 			"vle8.v		v8, (%1)\n\t"
253 			"add		%1, %1, %0\n\t"
254 			"vle8.v		v16, (%1)\n\t"
255 			"add		%1, %1, %0\n\t"
256 			"vle8.v		v24, (%1)\n\t"
257 			".option pop\n\t"
258 			: "=&r" (vl) : "r" (datap) : "memory");
259 	}
260 	__vstate_csr_restore(restore_from);
261 	riscv_v_disable();
262 }
263 
__riscv_v_vstate_discard(void)264 static inline void __riscv_v_vstate_discard(void)
265 {
266 	unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
267 
268 	riscv_v_enable();
269 	if (has_xtheadvector())
270 		asm volatile (THEAD_VSETVLI_T4X0E8M8D1 : : : "t4");
271 	else
272 		asm volatile (
273 			".option push\n\t"
274 			".option arch, +zve32x\n\t"
275 			"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
276 			".option pop\n\t": "=&r" (vl));
277 
278 	asm volatile (
279 		".option push\n\t"
280 		".option arch, +zve32x\n\t"
281 		"vmv.v.i	v0, -1\n\t"
282 		"vmv.v.i	v8, -1\n\t"
283 		"vmv.v.i	v16, -1\n\t"
284 		"vmv.v.i	v24, -1\n\t"
285 		"vsetvl		%0, x0, %1\n\t"
286 		".option pop\n\t"
287 		: "=&r" (vl) : "r" (vtype_inval));
288 
289 	riscv_v_disable();
290 }
291 
riscv_v_vstate_discard(struct pt_regs * regs)292 static inline void riscv_v_vstate_discard(struct pt_regs *regs)
293 {
294 	if (riscv_v_vstate_query(regs)) {
295 		__riscv_v_vstate_discard();
296 		__riscv_v_vstate_dirty(regs);
297 	}
298 }
299 
riscv_v_vstate_save(struct __riscv_v_ext_state * vstate,struct pt_regs * regs)300 static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
301 				       struct pt_regs *regs)
302 {
303 	if (__riscv_v_vstate_check(regs->status, DIRTY)) {
304 		__riscv_v_vstate_save(vstate, vstate->datap);
305 		__riscv_v_vstate_clean(regs);
306 	}
307 }
308 
riscv_v_vstate_restore(struct __riscv_v_ext_state * vstate,struct pt_regs * regs)309 static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
310 					  struct pt_regs *regs)
311 {
312 	if (riscv_v_vstate_query(regs)) {
313 		__riscv_v_vstate_restore(vstate, vstate->datap);
314 		__riscv_v_vstate_clean(regs);
315 	}
316 }
317 
riscv_v_vstate_set_restore(struct task_struct * task,struct pt_regs * regs)318 static inline void riscv_v_vstate_set_restore(struct task_struct *task,
319 					      struct pt_regs *regs)
320 {
321 	if (riscv_v_vstate_query(regs)) {
322 		set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE);
323 		riscv_v_vstate_on(regs);
324 	}
325 }
326 
327 #ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
riscv_preempt_v_dirty(struct task_struct * task)328 static inline bool riscv_preempt_v_dirty(struct task_struct *task)
329 {
330 	return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V_DIRTY);
331 }
332 
riscv_preempt_v_restore(struct task_struct * task)333 static inline bool riscv_preempt_v_restore(struct task_struct *task)
334 {
335 	return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V_NEED_RESTORE);
336 }
337 
riscv_preempt_v_clear_dirty(struct task_struct * task)338 static inline void riscv_preempt_v_clear_dirty(struct task_struct *task)
339 {
340 	barrier();
341 	task->thread.riscv_v_flags &= ~RISCV_PREEMPT_V_DIRTY;
342 }
343 
riscv_preempt_v_set_restore(struct task_struct * task)344 static inline void riscv_preempt_v_set_restore(struct task_struct *task)
345 {
346 	barrier();
347 	task->thread.riscv_v_flags |= RISCV_PREEMPT_V_NEED_RESTORE;
348 }
349 
riscv_preempt_v_started(struct task_struct * task)350 static inline bool riscv_preempt_v_started(struct task_struct *task)
351 {
352 	return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V);
353 }
354 
355 #else /* !CONFIG_RISCV_ISA_V_PREEMPTIVE */
riscv_preempt_v_dirty(struct task_struct * task)356 static inline bool riscv_preempt_v_dirty(struct task_struct *task) { return false; }
riscv_preempt_v_restore(struct task_struct * task)357 static inline bool riscv_preempt_v_restore(struct task_struct *task) { return false; }
riscv_preempt_v_started(struct task_struct * task)358 static inline bool riscv_preempt_v_started(struct task_struct *task) { return false; }
359 #define riscv_preempt_v_clear_dirty(tsk)	do {} while (0)
360 #define riscv_preempt_v_set_restore(tsk)	do {} while (0)
361 #endif /* CONFIG_RISCV_ISA_V_PREEMPTIVE */
362 
__switch_to_vector(struct task_struct * prev,struct task_struct * next)363 static inline void __switch_to_vector(struct task_struct *prev,
364 				      struct task_struct *next)
365 {
366 	struct pt_regs *regs;
367 
368 	if (riscv_preempt_v_started(prev)) {
369 		if (riscv_preempt_v_dirty(prev)) {
370 			__riscv_v_vstate_save(&prev->thread.kernel_vstate,
371 					      prev->thread.kernel_vstate.datap);
372 			riscv_preempt_v_clear_dirty(prev);
373 		}
374 	} else {
375 		regs = task_pt_regs(prev);
376 		riscv_v_vstate_save(&prev->thread.vstate, regs);
377 	}
378 
379 	if (riscv_preempt_v_started(next))
380 		riscv_preempt_v_set_restore(next);
381 	else
382 		riscv_v_vstate_set_restore(next, task_pt_regs(next));
383 }
384 
385 void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
386 bool riscv_v_vstate_ctrl_user_allowed(void);
387 
388 #else /* ! CONFIG_RISCV_ISA_V  */
389 
390 struct pt_regs;
391 
riscv_v_setup_vsize(void)392 static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
has_vector(void)393 static __always_inline bool has_vector(void) { return false; }
insn_is_vector(u32 insn_buf)394 static __always_inline bool insn_is_vector(u32 insn_buf) { return false; }
has_xtheadvector_no_alternatives(void)395 static __always_inline bool has_xtheadvector_no_alternatives(void) { return false; }
has_xtheadvector(void)396 static __always_inline bool has_xtheadvector(void) { return false; }
riscv_v_first_use_handler(struct pt_regs * regs)397 static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
riscv_v_vstate_query(struct pt_regs * regs)398 static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
riscv_v_vstate_ctrl_user_allowed(void)399 static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
400 #define riscv_v_vsize (0)
401 #define riscv_v_vstate_discard(regs)		do {} while (0)
402 #define riscv_v_vstate_save(vstate, regs)	do {} while (0)
403 #define riscv_v_vstate_restore(vstate, regs)	do {} while (0)
404 #define __switch_to_vector(__prev, __next)	do {} while (0)
405 #define riscv_v_vstate_off(regs)		do {} while (0)
406 #define riscv_v_vstate_on(regs)			do {} while (0)
407 #define riscv_v_thread_free(tsk)		do {} while (0)
408 #define  riscv_v_setup_ctx_cache()		do {} while (0)
409 #define riscv_v_thread_alloc(tsk)		do {} while (0)
410 
411 #endif /* CONFIG_RISCV_ISA_V */
412 
413 /*
414  * Return the implementation's vlen value.
415  *
416  * riscv_v_vsize contains the value of "32 vector registers with vlenb length"
417  * so rebuild the vlen value in bits from it.
418  */
riscv_vector_vlen(void)419 static inline int riscv_vector_vlen(void)
420 {
421 	return riscv_v_vsize / 32 * 8;
422 }
423 
424 #endif /* ! __ASM_RISCV_VECTOR_H */
425