Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
43extern unsigned long riscv_v_vsize;
44int riscv_v_setup_vsize(void);
45bool insn_is_vector(u32 insn_buf);
46bool riscv_v_first_use_handler(struct pt_regs *regs);
47void kernel_vector_begin(void);
48void kernel_vector_end(void);
49void get_cpu_vector_context(void);
50void put_cpu_vector_context(void);
51void riscv_v_thread_free(struct task_struct *tsk);
52void __init riscv_v_setup_ctx_cache(void);
53void riscv_v_thread_alloc(struct task_struct *tsk);
54
55static inline u32 riscv_v_flags(void)
56{
57 return READ_ONCE(current->thread.riscv_v_flags);
58}
59
60static __always_inline bool has_vector(void)
61{
62 return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
63}
64
65static __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
73static __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
82static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
83{
84 regs->status = __riscv_v_vstate_or(regs->status, CLEAN);
85}
86
87static inline void __riscv_v_vstate_dirty(struct pt_regs *regs)
88{
89 regs->status = __riscv_v_vstate_or(regs->status, DIRTY);
90}
91
92static inline void riscv_v_vstate_off(struct pt_regs *regs)
93{
94 regs->status = __riscv_v_vstate_or(regs->status, OFF);
95}
96
97static inline void riscv_v_vstate_on(struct pt_regs *regs)
98{
99 regs->status = __riscv_v_vstate_or(regs->status, INITIAL);
100}
101
102static inline bool riscv_v_vstate_query(struct pt_regs *regs)
103{
104 return !__riscv_v_vstate_check(regs->status, OFF);
105}
106
107static __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
115static __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
123static __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
161static __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
190static 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
227static 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
264static 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
292static 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
300static 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
309static 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
318static 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
328static inline bool riscv_preempt_v_dirty(struct task_struct *task)
329{
330 return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V_DIRTY);
331}
332
333static 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
338static 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
344static 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
350static 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 */
356static inline bool riscv_preempt_v_dirty(struct task_struct *task) { return false; }
357static inline bool riscv_preempt_v_restore(struct task_struct *task) { return false; }
358static 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
363static 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
385void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
386bool riscv_v_vstate_ctrl_user_allowed(void);
387
388#else /* ! CONFIG_RISCV_ISA_V */
389
390struct pt_regs;
391
392static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
393static __always_inline bool has_vector(void) { return false; }
394static __always_inline bool insn_is_vector(u32 insn_buf) { return false; }
395static __always_inline bool has_xtheadvector_no_alternatives(void) { return false; }
396static __always_inline bool has_xtheadvector(void) { return false; }
397static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
398static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
399static 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 */
419static inline int riscv_vector_vlen(void)
420{
421 return riscv_v_vsize / 32 * 8;
422}
423
424#endif /* ! __ASM_RISCV_VECTOR_H */