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
riscv_v_flags_set(u32 flags)61 static inline void riscv_v_flags_set(u32 flags)
62 {
63 WRITE_ONCE(current->thread.riscv_v_flags, flags);
64 }
65
has_vector(void)66 static __always_inline bool has_vector(void)
67 {
68 return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
69 }
70
has_xtheadvector_no_alternatives(void)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
has_xtheadvector(void)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
__riscv_v_vstate_clean(struct pt_regs * regs)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
__riscv_v_vstate_dirty(struct pt_regs * regs)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
riscv_v_vstate_off(struct pt_regs * regs)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
riscv_v_vstate_on(struct pt_regs * regs)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
riscv_v_vstate_query(struct pt_regs * regs)108 static inline bool riscv_v_vstate_query(struct pt_regs *regs)
109 {
110 return !__riscv_v_vstate_check(regs->status, OFF);
111 }
112
riscv_v_enable(void)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
riscv_v_disable(void)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
riscv_v_is_on(void)129 static __always_inline bool riscv_v_is_on(void)
130 {
131 return !!(csr_read(CSR_SSTATUS) & SR_VS);
132 }
133
__vstate_csr_save(struct __riscv_v_ext_state * dest)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 : :);
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
__vstate_csr_restore(struct __riscv_v_ext_state * src)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
__riscv_v_vstate_save(struct __riscv_v_ext_state * save_to,void * datap)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
__riscv_v_vstate_restore(struct __riscv_v_ext_state * restore_from,void * datap)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
__riscv_v_vstate_discard(void)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
riscv_v_vstate_discard(struct pt_regs * regs)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
riscv_v_vstate_save(struct __riscv_v_ext_state * vstate,struct pt_regs * regs)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
riscv_v_vstate_restore(struct __riscv_v_ext_state * vstate,struct pt_regs * regs)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
riscv_v_vstate_set_restore(struct task_struct * task,struct pt_regs * regs)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
riscv_preempt_v_dirty(struct task_struct * task)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
riscv_preempt_v_restore(struct task_struct * task)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
riscv_preempt_v_clear_dirty(struct task_struct * task)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
riscv_preempt_v_set_restore(struct task_struct * task)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
riscv_preempt_v_started(struct task_struct * task)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 */
riscv_preempt_v_dirty(struct task_struct * task)367 static inline bool riscv_preempt_v_dirty(struct task_struct *task) { return false; }
riscv_preempt_v_restore(struct task_struct * task)368 static inline bool riscv_preempt_v_restore(struct task_struct *task) { return false; }
riscv_preempt_v_started(struct task_struct * task)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
__switch_to_vector(struct task_struct * prev,struct task_struct * next)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
riscv_v_setup_vsize(void)414 static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
has_vector(void)415 static __always_inline bool has_vector(void) { return false; }
insn_is_vector(u32 insn_buf)416 static __always_inline bool insn_is_vector(u32 insn_buf) { return false; }
has_xtheadvector_no_alternatives(void)417 static __always_inline bool has_xtheadvector_no_alternatives(void) { return false; }
has_xtheadvector(void)418 static __always_inline bool has_xtheadvector(void) { return false; }
riscv_v_first_use_handler(struct pt_regs * regs)419 static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
riscv_v_vstate_query(struct pt_regs * regs)420 static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
riscv_v_vstate_ctrl_user_allowed(void)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 */
riscv_vector_vlen(void)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