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