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