1 /*-
2 * Copyright (c) 2015-2016 The FreeBSD Foundation
3 *
4 * This software was developed by Andrew Turner under
5 * sponsorship from the FreeBSD Foundation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #ifdef VFP
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/elf.h>
34 #include <sys/eventhandler.h>
35 #include <sys/limits.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/reg.h>
41 #include <sys/smp.h>
42
43 #include <vm/uma.h>
44
45 #include <machine/armreg.h>
46 #include <machine/md_var.h>
47 #include <machine/pcb.h>
48 #include <machine/vfp.h>
49
50 /* Sanity check we can store all the VFP registers */
51 CTASSERT(sizeof(((struct pcb *)0)->pcb_fpustate.vfp_regs) == 16 * 32);
52
53 static MALLOC_DEFINE(M_FPUKERN_CTX, "fpukern_ctx",
54 "Kernel contexts for VFP state");
55
56 struct fpu_kern_ctx {
57 struct vfpstate *prev;
58 #define FPU_KERN_CTX_DUMMY 0x01 /* avoided save for the kern thread */
59 #define FPU_KERN_CTX_INUSE 0x02
60 uint32_t flags;
61 struct vfpstate state;
62 };
63
64 static uma_zone_t fpu_save_area_zone;
65 static struct vfpstate *fpu_initialstate;
66
67 static u_int sve_max_vector_len;
68
69 static size_t
_sve_buf_size(u_int sve_len)70 _sve_buf_size(u_int sve_len)
71 {
72 size_t len;
73
74 /* 32 vector registers */
75 len = (size_t)sve_len * 32;
76 /*
77 * 16 predicate registers and the fault fault register, each 1/8th
78 * the size of a vector register.
79 */
80 len += ((size_t)sve_len * 17) / 8;
81 /*
82 * FPSR and FPCR
83 */
84 len += sizeof(uint64_t) * 2;
85
86 return (len);
87 }
88
89 size_t
sve_max_buf_size(void)90 sve_max_buf_size(void)
91 {
92 MPASS(sve_max_vector_len > 0);
93 return (_sve_buf_size(sve_max_vector_len));
94 }
95
96 size_t
sve_buf_size(struct thread * td)97 sve_buf_size(struct thread *td)
98 {
99 struct pcb *pcb;
100
101 pcb = td->td_pcb;
102 MPASS(pcb->pcb_svesaved != NULL);
103 MPASS(pcb->pcb_sve_len > 0);
104
105 return (_sve_buf_size(pcb->pcb_sve_len));
106 }
107
108 static void *
sve_alloc(void)109 sve_alloc(void)
110 {
111 void *buf;
112
113 buf = malloc(sve_max_buf_size(), M_FPUKERN_CTX, M_WAITOK | M_ZERO);
114
115 return (buf);
116 }
117
118 static void
sve_free(void * buf)119 sve_free(void *buf)
120 {
121 free(buf, M_FPUKERN_CTX);
122 }
123
124 void
vfp_enable(void)125 vfp_enable(void)
126 {
127 uint32_t cpacr;
128
129 cpacr = READ_SPECIALREG(cpacr_el1);
130 cpacr = (cpacr & ~CPACR_FPEN_MASK) | CPACR_FPEN_TRAP_NONE;
131 WRITE_SPECIALREG(cpacr_el1, cpacr);
132 isb();
133 }
134
135 static void
sve_enable(void)136 sve_enable(void)
137 {
138 uint32_t cpacr;
139
140 cpacr = READ_SPECIALREG(cpacr_el1);
141 /* Enable FP */
142 cpacr = (cpacr & ~CPACR_FPEN_MASK) | CPACR_FPEN_TRAP_NONE;
143 /* Enable SVE */
144 cpacr = (cpacr & ~CPACR_ZEN_MASK) | CPACR_ZEN_TRAP_NONE;
145 WRITE_SPECIALREG(cpacr_el1, cpacr);
146 isb();
147 }
148
149 void
vfp_disable(void)150 vfp_disable(void)
151 {
152 uint32_t cpacr;
153
154 cpacr = READ_SPECIALREG(cpacr_el1);
155 /* Disable FP */
156 cpacr = (cpacr & ~CPACR_FPEN_MASK) | CPACR_FPEN_TRAP_ALL1;
157 /* Disable SVE */
158 cpacr = (cpacr & ~CPACR_ZEN_MASK) | CPACR_ZEN_TRAP_ALL1;
159 WRITE_SPECIALREG(cpacr_el1, cpacr);
160 isb();
161 }
162
163 /*
164 * Called when the thread is dying or when discarding the kernel VFP state.
165 * If the thread was the last to use the VFP unit mark it as unused to tell
166 * the kernel the fp state is unowned. Ensure the VFP unit is off so we get
167 * an exception on the next access.
168 */
169 void
vfp_discard(struct thread * td)170 vfp_discard(struct thread *td)
171 {
172
173 #ifdef INVARIANTS
174 if (td != NULL)
175 CRITICAL_ASSERT(td);
176 #endif
177 if (PCPU_GET(fpcurthread) == td)
178 PCPU_SET(fpcurthread, NULL);
179
180 vfp_disable();
181 }
182
183 void
vfp_store(struct vfpstate * state)184 vfp_store(struct vfpstate *state)
185 {
186 __uint128_t *vfp_state;
187 uint64_t fpcr, fpsr;
188
189 vfp_state = state->vfp_regs;
190 __asm __volatile(
191 ".arch_extension fp\n"
192 "mrs %0, fpcr \n"
193 "mrs %1, fpsr \n"
194 "stp q0, q1, [%2, #16 * 0]\n"
195 "stp q2, q3, [%2, #16 * 2]\n"
196 "stp q4, q5, [%2, #16 * 4]\n"
197 "stp q6, q7, [%2, #16 * 6]\n"
198 "stp q8, q9, [%2, #16 * 8]\n"
199 "stp q10, q11, [%2, #16 * 10]\n"
200 "stp q12, q13, [%2, #16 * 12]\n"
201 "stp q14, q15, [%2, #16 * 14]\n"
202 "stp q16, q17, [%2, #16 * 16]\n"
203 "stp q18, q19, [%2, #16 * 18]\n"
204 "stp q20, q21, [%2, #16 * 20]\n"
205 "stp q22, q23, [%2, #16 * 22]\n"
206 "stp q24, q25, [%2, #16 * 24]\n"
207 "stp q26, q27, [%2, #16 * 26]\n"
208 "stp q28, q29, [%2, #16 * 28]\n"
209 "stp q30, q31, [%2, #16 * 30]\n"
210 ".arch_extension nofp\n"
211 : "=&r"(fpcr), "=&r"(fpsr) : "r"(vfp_state));
212
213 state->vfp_fpcr = fpcr;
214 state->vfp_fpsr = fpsr;
215 }
216
217 void
vfp_restore(struct vfpstate * state)218 vfp_restore(struct vfpstate *state)
219 {
220 __uint128_t *vfp_state;
221 uint64_t fpcr, fpsr;
222
223 vfp_state = state->vfp_regs;
224 fpcr = state->vfp_fpcr;
225 fpsr = state->vfp_fpsr;
226
227 __asm __volatile(
228 ".arch_extension fp\n"
229 "ldp q0, q1, [%2, #16 * 0]\n"
230 "ldp q2, q3, [%2, #16 * 2]\n"
231 "ldp q4, q5, [%2, #16 * 4]\n"
232 "ldp q6, q7, [%2, #16 * 6]\n"
233 "ldp q8, q9, [%2, #16 * 8]\n"
234 "ldp q10, q11, [%2, #16 * 10]\n"
235 "ldp q12, q13, [%2, #16 * 12]\n"
236 "ldp q14, q15, [%2, #16 * 14]\n"
237 "ldp q16, q17, [%2, #16 * 16]\n"
238 "ldp q18, q19, [%2, #16 * 18]\n"
239 "ldp q20, q21, [%2, #16 * 20]\n"
240 "ldp q22, q23, [%2, #16 * 22]\n"
241 "ldp q24, q25, [%2, #16 * 24]\n"
242 "ldp q26, q27, [%2, #16 * 26]\n"
243 "ldp q28, q29, [%2, #16 * 28]\n"
244 "ldp q30, q31, [%2, #16 * 30]\n"
245 "msr fpcr, %0 \n"
246 "msr fpsr, %1 \n"
247 ".arch_extension nofp\n"
248 : : "r"(fpcr), "r"(fpsr), "r"(vfp_state));
249 }
250
251 static void
sve_store(void * state,u_int sve_len)252 sve_store(void *state, u_int sve_len)
253 {
254 vm_offset_t f_start, p_start, z_start;
255 uint64_t fpcr, fpsr;
256
257 /*
258 * Calculate the start of each register groups. There are three
259 * groups depending on size, with the First Fault Register (FFR)
260 * stored with the predicate registers as we use one of them to
261 * temporarily hold it.
262 *
263 * +-------------------------+-------------------+
264 * | Contents | Register size |
265 * z_start -> +-------------------------+-------------------+
266 * | | |
267 * | 32 Z regs | sve_len |
268 * | | |
269 * p_start -> +-------------------------+-------------------+
270 * | | |
271 * | 16 Predicate registers | 1/8 size of Z reg |
272 * | 1 First Fault register | |
273 * | | |
274 * f_start -> +-------------------------+-------------------+
275 * | | |
276 * | FPSR/FPCR | 32 bit |
277 * | | |
278 * +-------------------------+-------------------+
279 */
280 z_start = (vm_offset_t)state;
281 p_start = z_start + sve_len * 32;
282 f_start = p_start + (sve_len / 8) * 17;
283
284 __asm __volatile(
285 ".arch_extension sve \n"
286 "str z0, [%0, #0, MUL VL] \n"
287 "str z1, [%0, #1, MUL VL] \n"
288 "str z2, [%0, #2, MUL VL] \n"
289 "str z3, [%0, #3, MUL VL] \n"
290 "str z4, [%0, #4, MUL VL] \n"
291 "str z5, [%0, #5, MUL VL] \n"
292 "str z6, [%0, #6, MUL VL] \n"
293 "str z7, [%0, #7, MUL VL] \n"
294 "str z8, [%0, #8, MUL VL] \n"
295 "str z9, [%0, #9, MUL VL] \n"
296 "str z10, [%0, #10, MUL VL] \n"
297 "str z11, [%0, #11, MUL VL] \n"
298 "str z12, [%0, #12, MUL VL] \n"
299 "str z13, [%0, #13, MUL VL] \n"
300 "str z14, [%0, #14, MUL VL] \n"
301 "str z15, [%0, #15, MUL VL] \n"
302 "str z16, [%0, #16, MUL VL] \n"
303 "str z17, [%0, #17, MUL VL] \n"
304 "str z18, [%0, #18, MUL VL] \n"
305 "str z19, [%0, #19, MUL VL] \n"
306 "str z20, [%0, #20, MUL VL] \n"
307 "str z21, [%0, #21, MUL VL] \n"
308 "str z22, [%0, #22, MUL VL] \n"
309 "str z23, [%0, #23, MUL VL] \n"
310 "str z24, [%0, #24, MUL VL] \n"
311 "str z25, [%0, #25, MUL VL] \n"
312 "str z26, [%0, #26, MUL VL] \n"
313 "str z27, [%0, #27, MUL VL] \n"
314 "str z28, [%0, #28, MUL VL] \n"
315 "str z29, [%0, #29, MUL VL] \n"
316 "str z30, [%0, #30, MUL VL] \n"
317 "str z31, [%0, #31, MUL VL] \n"
318 /* Store the predicate registers */
319 "str p0, [%1, #0, MUL VL] \n"
320 "str p1, [%1, #1, MUL VL] \n"
321 "str p2, [%1, #2, MUL VL] \n"
322 "str p3, [%1, #3, MUL VL] \n"
323 "str p4, [%1, #4, MUL VL] \n"
324 "str p5, [%1, #5, MUL VL] \n"
325 "str p6, [%1, #6, MUL VL] \n"
326 "str p7, [%1, #7, MUL VL] \n"
327 "str p8, [%1, #8, MUL VL] \n"
328 "str p9, [%1, #9, MUL VL] \n"
329 "str p10, [%1, #10, MUL VL] \n"
330 "str p11, [%1, #11, MUL VL] \n"
331 "str p12, [%1, #12, MUL VL] \n"
332 "str p13, [%1, #13, MUL VL] \n"
333 "str p14, [%1, #14, MUL VL] \n"
334 "str p15, [%1, #15, MUL VL] \n"
335 ".arch_extension nosve \n"
336 : : "r"(z_start), "r"(p_start));
337
338 /* Save the FFR if needed */
339 /* TODO: Skip if in SME streaming mode (when supported) */
340 __asm __volatile(
341 ".arch_extension sve \n"
342 "rdffr p0.b \n"
343 "str p0, [%0, #16, MUL VL] \n"
344 /*
345 * Load the old p0 value to ensure it is consistent if we enable
346 * without calling sve_restore, e.g. switch to a kernel thread and
347 * back.
348 */
349 "ldr p0, [%0, #0, MUL VL] \n"
350 ".arch_extension nosve \n"
351 : : "r"(p_start));
352
353 __asm __volatile(
354 ".arch_extension fp \n"
355 "mrs %0, fpsr \n"
356 "mrs %1, fpcr \n"
357 "stp %w0, %w1, [%2] \n"
358 ".arch_extension nofp \n"
359 : "=&r"(fpsr), "=&r"(fpcr) : "r"(f_start));
360 }
361
362 static void
sve_restore(void * state,u_int sve_len)363 sve_restore(void *state, u_int sve_len)
364 {
365 vm_offset_t f_start, p_start, z_start;
366 uint64_t fpcr, fpsr;
367
368 /* See sve_store for the layout of the state buffer */
369 z_start = (vm_offset_t)state;
370 p_start = z_start + sve_len * 32;
371 f_start = p_start + (sve_len / 8) * 17;
372
373 __asm __volatile(
374 ".arch_extension sve \n"
375 "ldr p0, [%0, #16, MUL VL] \n"
376 "wrffr p0.b \n"
377 ".arch_extension nosve \n"
378 : : "r"(p_start));
379
380 __asm __volatile(
381 ".arch_extension sve \n"
382 "ldr z0, [%0, #0, MUL VL] \n"
383 "ldr z1, [%0, #1, MUL VL] \n"
384 "ldr z2, [%0, #2, MUL VL] \n"
385 "ldr z3, [%0, #3, MUL VL] \n"
386 "ldr z4, [%0, #4, MUL VL] \n"
387 "ldr z5, [%0, #5, MUL VL] \n"
388 "ldr z6, [%0, #6, MUL VL] \n"
389 "ldr z7, [%0, #7, MUL VL] \n"
390 "ldr z8, [%0, #8, MUL VL] \n"
391 "ldr z9, [%0, #9, MUL VL] \n"
392 "ldr z10, [%0, #10, MUL VL] \n"
393 "ldr z11, [%0, #11, MUL VL] \n"
394 "ldr z12, [%0, #12, MUL VL] \n"
395 "ldr z13, [%0, #13, MUL VL] \n"
396 "ldr z14, [%0, #14, MUL VL] \n"
397 "ldr z15, [%0, #15, MUL VL] \n"
398 "ldr z16, [%0, #16, MUL VL] \n"
399 "ldr z17, [%0, #17, MUL VL] \n"
400 "ldr z18, [%0, #18, MUL VL] \n"
401 "ldr z19, [%0, #19, MUL VL] \n"
402 "ldr z20, [%0, #20, MUL VL] \n"
403 "ldr z21, [%0, #21, MUL VL] \n"
404 "ldr z22, [%0, #22, MUL VL] \n"
405 "ldr z23, [%0, #23, MUL VL] \n"
406 "ldr z24, [%0, #24, MUL VL] \n"
407 "ldr z25, [%0, #25, MUL VL] \n"
408 "ldr z26, [%0, #26, MUL VL] \n"
409 "ldr z27, [%0, #27, MUL VL] \n"
410 "ldr z28, [%0, #28, MUL VL] \n"
411 "ldr z29, [%0, #29, MUL VL] \n"
412 "ldr z30, [%0, #30, MUL VL] \n"
413 "ldr z31, [%0, #31, MUL VL] \n"
414 /* Store the predicate registers */
415 "ldr p0, [%1, #0, MUL VL] \n"
416 "ldr p1, [%1, #1, MUL VL] \n"
417 "ldr p2, [%1, #2, MUL VL] \n"
418 "ldr p3, [%1, #3, MUL VL] \n"
419 "ldr p4, [%1, #4, MUL VL] \n"
420 "ldr p5, [%1, #5, MUL VL] \n"
421 "ldr p6, [%1, #6, MUL VL] \n"
422 "ldr p7, [%1, #7, MUL VL] \n"
423 "ldr p8, [%1, #8, MUL VL] \n"
424 "ldr p9, [%1, #9, MUL VL] \n"
425 "ldr p10, [%1, #10, MUL VL] \n"
426 "ldr p11, [%1, #11, MUL VL] \n"
427 "ldr p12, [%1, #12, MUL VL] \n"
428 "ldr p13, [%1, #13, MUL VL] \n"
429 "ldr p14, [%1, #14, MUL VL] \n"
430 "ldr p15, [%1, #15, MUL VL] \n"
431 ".arch_extension nosve \n"
432 : : "r"(z_start), "r"(p_start));
433
434 __asm __volatile(
435 ".arch_extension fp \n"
436 "ldp %w0, %w1, [%2] \n"
437 "msr fpsr, %0 \n"
438 "msr fpcr, %1 \n"
439 ".arch_extension nofp \n"
440 : "=&r"(fpsr), "=&r"(fpcr) : "r"(f_start));
441 }
442
443 /*
444 * Sync the VFP registers to the SVE register state, e.g. in signal return
445 * when userspace may have changed the vfp register values and expect them
446 * to be used when the signal handler returns.
447 */
448 void
vfp_to_sve_sync(struct thread * td)449 vfp_to_sve_sync(struct thread *td)
450 {
451 struct pcb *pcb;
452 uint32_t *fpxr;
453
454 pcb = td->td_pcb;
455 if (pcb->pcb_svesaved == NULL)
456 return;
457
458 MPASS(pcb->pcb_fpusaved != NULL);
459
460 /* Copy the VFP registers to the SVE region */
461 for (int i = 0; i < nitems(pcb->pcb_fpusaved->vfp_regs); i++) {
462 __uint128_t *sve_reg;
463
464 sve_reg = (__uint128_t *)((uintptr_t)pcb->pcb_svesaved +
465 i * pcb->pcb_sve_len);
466 *sve_reg = pcb->pcb_fpusaved->vfp_regs[i];
467 }
468
469 fpxr = (uint32_t *)((uintptr_t)pcb->pcb_svesaved +
470 (32 * pcb->pcb_sve_len) + (17 * pcb->pcb_sve_len / 8));
471 fpxr[0] = pcb->pcb_fpusaved->vfp_fpsr;
472 fpxr[1] = pcb->pcb_fpusaved->vfp_fpcr;
473 }
474
475 /*
476 * Sync the SVE registers to the VFP register state.
477 */
478 void
sve_to_vfp_sync(struct thread * td)479 sve_to_vfp_sync(struct thread *td)
480 {
481 struct pcb *pcb;
482 uint32_t *fpxr;
483
484 pcb = td->td_pcb;
485 if (pcb->pcb_svesaved == NULL)
486 return;
487
488 MPASS(pcb->pcb_fpusaved == &pcb->pcb_fpustate);
489
490 /* Copy the SVE registers to the VFP saved state */
491 for (int i = 0; i < nitems(pcb->pcb_fpusaved->vfp_regs); i++) {
492 __uint128_t *sve_reg;
493
494 sve_reg = (__uint128_t *)((uintptr_t)pcb->pcb_svesaved +
495 i * pcb->pcb_sve_len);
496 pcb->pcb_fpusaved->vfp_regs[i] = *sve_reg;
497 }
498
499 fpxr = (uint32_t *)((uintptr_t)pcb->pcb_svesaved +
500 (32 * pcb->pcb_sve_len) + (17 * pcb->pcb_sve_len / 8));
501 pcb->pcb_fpusaved->vfp_fpsr = fpxr[0];
502 pcb->pcb_fpusaved->vfp_fpcr = fpxr[1];
503 }
504
505 static void
vfp_save_state_common(struct thread * td,struct pcb * pcb,bool full_save)506 vfp_save_state_common(struct thread *td, struct pcb *pcb, bool full_save)
507 {
508 uint32_t cpacr;
509 bool save_sve;
510
511 save_sve = false;
512
513 critical_enter();
514 /*
515 * Only store the registers if the VFP is enabled,
516 * i.e. return if we are trapping on FP access.
517 */
518 cpacr = READ_SPECIALREG(cpacr_el1);
519 if ((cpacr & CPACR_FPEN_MASK) != CPACR_FPEN_TRAP_NONE)
520 goto done;
521
522 KASSERT(PCPU_GET(fpcurthread) == td,
523 ("Storing an invalid VFP state"));
524
525 /*
526 * Also save the SVE state. As SVE depends on the VFP being
527 * enabled we can rely on only needing to check this when
528 * the VFP unit has been enabled.
529 */
530 if ((cpacr & CPACR_ZEN_MASK) == CPACR_ZEN_TRAP_NONE) {
531 /* If SVE is enabled it should be valid */
532 MPASS((pcb->pcb_fpflags & PCB_FP_SVEVALID) != 0);
533
534 /*
535 * If we are switching while in a system call skip saving
536 * SVE registers. The ABI allows us to drop them over any
537 * system calls, however doing so is expensive in SVE
538 * heavy userspace code. This would require us to disable
539 * SVE for all system calls and trap the next use of them.
540 * As an optimisation only disable SVE on context switch.
541 */
542 if (td->td_frame == NULL ||
543 (ESR_ELx_EXCEPTION(td->td_frame->tf_esr) != EXCP_SVC64 &&
544 td->td_sa.code != (u_int)-1))
545 save_sve = true;
546 }
547
548 if (save_sve) {
549 KASSERT(pcb->pcb_svesaved != NULL,
550 ("Storing to a NULL SVE state"));
551 sve_store(pcb->pcb_svesaved, pcb->pcb_sve_len);
552 if (full_save)
553 sve_to_vfp_sync(td);
554 } else {
555 pcb->pcb_fpflags &= ~PCB_FP_SVEVALID;
556 vfp_store(pcb->pcb_fpusaved);
557 }
558 dsb(ish);
559 vfp_disable();
560
561 done:
562 critical_exit();
563 }
564
565 void
vfp_save_state(struct thread * td,struct pcb * pcb)566 vfp_save_state(struct thread *td, struct pcb *pcb)
567 {
568 KASSERT(td != NULL, ("NULL vfp thread"));
569 KASSERT(pcb != NULL, ("NULL vfp pcb"));
570 KASSERT(td->td_pcb == pcb, ("Invalid vfp pcb"));
571
572 vfp_save_state_common(td, pcb, true);
573 }
574
575 void
vfp_save_state_savectx(struct pcb * pcb)576 vfp_save_state_savectx(struct pcb *pcb)
577 {
578 /*
579 * savectx() will be called on panic with dumppcb as an argument,
580 * dumppcb either has no pcb_fpusaved set or it was previously set
581 * to its own fpu state.
582 *
583 * In both cases we can set it here to the pcb fpu state.
584 */
585 MPASS(pcb->pcb_fpusaved == NULL ||
586 pcb->pcb_fpusaved == &pcb->pcb_fpustate);
587 pcb->pcb_fpusaved = &pcb->pcb_fpustate;
588
589 vfp_save_state_common(curthread, pcb, true);
590 }
591
592 void
vfp_save_state_switch(struct thread * td)593 vfp_save_state_switch(struct thread *td)
594 {
595 KASSERT(td != NULL, ("NULL vfp thread"));
596
597 vfp_save_state_common(td, td->td_pcb, false);
598 }
599
600 /*
601 * Update the VFP state for a forked process or new thread. The PCB will
602 * have been copied from the old thread.
603 */
604 void
vfp_new_thread(struct thread * newtd,struct thread * oldtd,bool fork)605 vfp_new_thread(struct thread *newtd, struct thread *oldtd, bool fork)
606 {
607 struct pcb *newpcb, *oldpcb;
608
609 newpcb = newtd->td_pcb;
610 oldpcb = oldtd->td_pcb;
611
612 /* Kernel threads start with clean VFP */
613 if ((oldtd->td_pflags & TDP_KTHREAD) != 0) {
614 newpcb->pcb_fpflags &=
615 ~(PCB_FP_STARTED | PCB_FP_SVEVALID | PCB_FP_KERN |
616 PCB_FP_NOSAVE);
617 } else {
618 MPASS((newpcb->pcb_fpflags & (PCB_FP_KERN|PCB_FP_NOSAVE)) == 0);
619
620 /*
621 * The only SVE register state to be guaranteed to be saved
622 * a system call is the lower bits of the Z registers as
623 * these are aliased with the existing FP registers. Because
624 * we can only create a new thread or fork through a system
625 * call it is safe to drop the SVE state in the new thread.
626 */
627 newpcb->pcb_fpflags &= ~PCB_FP_SVEVALID;
628 if (!fork) {
629 newpcb->pcb_fpflags &= ~PCB_FP_STARTED;
630 }
631 }
632
633 newpcb->pcb_svesaved = NULL;
634 if (oldpcb->pcb_svesaved == NULL)
635 newpcb->pcb_sve_len = sve_max_vector_len;
636 else
637 KASSERT(newpcb->pcb_sve_len == oldpcb->pcb_sve_len,
638 ("%s: pcb sve vector length differs: %x != %x", __func__,
639 newpcb->pcb_sve_len, oldpcb->pcb_sve_len));
640
641 newpcb->pcb_fpusaved = &newpcb->pcb_fpustate;
642 newpcb->pcb_vfpcpu = UINT_MAX;
643 }
644
645 /*
646 * Reset the FP state to avoid leaking state from the parent process across
647 * execve() (and to ensure that we get a consistent floating point environment
648 * in every new process).
649 */
650 void
vfp_reset_state(struct thread * td,struct pcb * pcb)651 vfp_reset_state(struct thread *td, struct pcb *pcb)
652 {
653 /* Discard the threads VFP state before resetting it */
654 critical_enter();
655 vfp_discard(td);
656 critical_exit();
657
658 /*
659 * Clear the thread state. The VFP is disabled and is not the current
660 * VFP thread so we won't change any of these on context switch.
661 */
662 bzero(&pcb->pcb_fpustate.vfp_regs, sizeof(pcb->pcb_fpustate.vfp_regs));
663 KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
664 ("pcb_fpusaved should point to pcb_fpustate."));
665 pcb->pcb_fpustate.vfp_fpcr = VFPCR_INIT;
666 pcb->pcb_fpustate.vfp_fpsr = 0;
667 /* XXX: Memory leak when using SVE between fork & exec? */
668 pcb->pcb_svesaved = NULL;
669 pcb->pcb_vfpcpu = UINT_MAX;
670 pcb->pcb_fpflags = 0;
671 }
672
673 static void
vfp_restore_state_common(struct thread * td,int flags)674 vfp_restore_state_common(struct thread *td, int flags)
675 {
676 struct pcb *curpcb;
677 u_int cpu;
678 bool restore_sve;
679
680 KASSERT(td == curthread, ("%s: Called with non-current thread",
681 __func__));
682 CRITICAL_ASSERT(td);
683
684 cpu = PCPU_GET(cpuid);
685 curpcb = td->td_pcb;
686
687 /*
688 * If SVE has been used and the base VFP state is in use then
689 * restore the SVE registers. A non-base VFP state should only
690 * be used by the kernel and SVE should onlu be used by userspace.
691 */
692 restore_sve = false;
693 if ((curpcb->pcb_fpflags & PCB_FP_SVEVALID) != 0 &&
694 curpcb->pcb_fpusaved == &curpcb->pcb_fpustate) {
695 MPASS(curpcb->pcb_svesaved != NULL);
696 /* SVE shouldn't be enabled in the kernel */
697 MPASS((flags & PCB_FP_KERN) == 0);
698 restore_sve = true;
699 }
700
701 if (restore_sve) {
702 MPASS((curpcb->pcb_fpflags & PCB_FP_SVEVALID) != 0);
703 sve_enable();
704 } else {
705 curpcb->pcb_fpflags |= PCB_FP_STARTED;
706 vfp_enable();
707 }
708
709 /*
710 * If the previous thread on this cpu to use the VFP was not the
711 * current thread, or the current thread last used it on a different
712 * cpu we need to restore the old state.
713 */
714 if (PCPU_GET(fpcurthread) != curthread || cpu != curpcb->pcb_vfpcpu) {
715 /*
716 * The VFP registers are the lower 128 bits of the SVE
717 * registers. Use the SVE store state if it was previously
718 * enabled.
719 */
720 if (restore_sve) {
721 MPASS(td->td_pcb->pcb_svesaved != NULL);
722 sve_restore(td->td_pcb->pcb_svesaved,
723 td->td_pcb->pcb_sve_len);
724 } else {
725 vfp_restore(td->td_pcb->pcb_fpusaved);
726 }
727 PCPU_SET(fpcurthread, td);
728 curpcb->pcb_vfpcpu = cpu;
729 }
730 }
731
732 void
vfp_restore_state(void)733 vfp_restore_state(void)
734 {
735 struct thread *td;
736
737 td = curthread;
738 critical_enter();
739 vfp_restore_state_common(td, td->td_pcb->pcb_fpflags);
740 critical_exit();
741 }
742
743 bool
sve_restore_state(struct thread * td)744 sve_restore_state(struct thread *td)
745 {
746 struct pcb *curpcb;
747 void *svesaved;
748 uint64_t cpacr;
749
750 KASSERT(td == curthread, ("%s: Called with non-current thread",
751 __func__));
752
753 curpcb = td->td_pcb;
754
755 /* The SVE state should alias the base VFP state */
756 MPASS(curpcb->pcb_fpusaved == &curpcb->pcb_fpustate);
757
758 /* SVE not enabled, tell the caller to raise a fault */
759 if (curpcb->pcb_sve_len == 0) {
760 /*
761 * The init pcb is created before we read the vector length.
762 * Set it to the default length.
763 */
764 if (sve_max_vector_len == 0)
765 return (false);
766
767 MPASS(curpcb->pcb_svesaved == NULL);
768 curpcb->pcb_sve_len = sve_max_vector_len;
769 }
770
771 if (curpcb->pcb_svesaved == NULL) {
772 /* SVE should be disabled so will be invalid */
773 MPASS((curpcb->pcb_fpflags & PCB_FP_SVEVALID) == 0);
774
775 /*
776 * Allocate the SVE buffer of this thread.
777 * Enable interrupts so the allocation can sleep
778 */
779 svesaved = sve_alloc();
780
781 critical_enter();
782
783 /* Restore the VFP state if needed */
784 cpacr = READ_SPECIALREG(cpacr_el1);
785 if ((cpacr & CPACR_FPEN_MASK) != CPACR_FPEN_TRAP_NONE) {
786 vfp_restore_state_common(td, curpcb->pcb_fpflags);
787 }
788
789 /*
790 * Set the flags after enabling the VFP as the SVE saved
791 * state will be invalid.
792 */
793 curpcb->pcb_svesaved = svesaved;
794 curpcb->pcb_fpflags |= PCB_FP_SVEVALID;
795 sve_enable();
796
797 critical_exit();
798 } else {
799 critical_enter();
800
801 vfp_restore_state_common(td, curpcb->pcb_fpflags);
802
803 /* Enable SVE if it wasn't previously enabled */
804 if ((curpcb->pcb_fpflags & PCB_FP_SVEVALID) == 0) {
805 MPASS(PCPU_GET(fpcurthread) == td);
806 sve_enable();
807 curpcb->pcb_fpflags |= PCB_FP_SVEVALID;
808 }
809 critical_exit();
810 }
811
812 return (true);
813 }
814
815 void
vfp_init_secondary(void)816 vfp_init_secondary(void)
817 {
818 uint64_t pfr;
819
820 /* Check if there is a vfp unit present */
821 pfr = READ_SPECIALREG(id_aa64pfr0_el1);
822 if ((pfr & ID_AA64PFR0_FP_MASK) == ID_AA64PFR0_FP_NONE)
823 return;
824
825 /* Disable to be enabled when it's used */
826 vfp_disable();
827 }
828
829 static void
vfp_init(const void * dummy __unused)830 vfp_init(const void *dummy __unused)
831 {
832 uint64_t pfr;
833
834 /* Check if there is a vfp unit present */
835 pfr = READ_SPECIALREG(id_aa64pfr0_el1);
836 if ((pfr & ID_AA64PFR0_FP_MASK) == ID_AA64PFR0_FP_NONE)
837 return;
838
839 fpu_save_area_zone = uma_zcreate("VFP_save_area",
840 sizeof(struct vfpstate), NULL, NULL, NULL, NULL,
841 _Alignof(struct vfpstate) - 1, 0);
842 fpu_initialstate = uma_zalloc(fpu_save_area_zone, M_WAITOK | M_ZERO);
843
844 /* Ensure the VFP is enabled before accessing it in vfp_store */
845 vfp_enable();
846 vfp_store(fpu_initialstate);
847
848 /* Disable to be enabled when it's used */
849 vfp_disable();
850
851 /* Zero the VFP registers but keep fpcr and fpsr */
852 bzero(fpu_initialstate->vfp_regs, sizeof(fpu_initialstate->vfp_regs));
853
854 thread0.td_pcb->pcb_fpusaved->vfp_fpcr = VFPCR_INIT;
855 }
856
857 SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, vfp_init, NULL);
858
859 static void
sve_thread_dtor(void * arg __unused,struct thread * td)860 sve_thread_dtor(void *arg __unused, struct thread *td)
861 {
862 sve_free(td->td_pcb->pcb_svesaved);
863 }
864
865 static void
sve_pcpu_read(void * arg)866 sve_pcpu_read(void *arg)
867 {
868 u_int *len;
869 uint64_t vl;
870
871 len = arg;
872
873 /* Enable SVE to read zcr_el1 and VFP for rdvl */
874 sve_enable();
875
876 /* Set the longest vector length */
877 WRITE_SPECIALREG(ZCR_EL1_REG, ZCR_LEN_MASK);
878 isb();
879
880 /* Read the real vector length */
881 __asm __volatile(
882 ".arch_extension sve \n"
883 "rdvl %0, #1 \n"
884 ".arch_extension nosve \n"
885 : "=&r"(vl));
886
887 vfp_disable();
888
889 len[PCPU_GET(cpuid)] = vl;
890 }
891
892 static void
sve_init(const void * dummy __unused)893 sve_init(const void *dummy __unused)
894 {
895 u_int *len_list;
896 uint64_t reg;
897 int i;
898
899 get_kernel_reg(ID_AA64PFR0_EL1, ®);
900 if (ID_AA64PFR0_SVE_VAL(reg) == ID_AA64PFR0_SVE_NONE)
901 return;
902
903 len_list = malloc(sizeof(*len_list) * (mp_maxid + 1), M_TEMP,
904 M_WAITOK | M_ZERO);
905 smp_rendezvous(NULL, sve_pcpu_read, NULL, len_list);
906
907 sve_max_vector_len = ZCR_LEN_BYTES(ZCR_LEN_MASK);
908 CPU_FOREACH(i) {
909 if (bootverbose)
910 printf("CPU%d SVE vector length: %u\n", i, len_list[i]);
911 sve_max_vector_len = MIN(sve_max_vector_len, len_list[i]);
912 }
913 free(len_list, M_TEMP);
914
915 if (bootverbose)
916 printf("SVE with %u byte vectors\n", sve_max_vector_len);
917
918 if (sve_max_vector_len > 0) {
919 EVENTHANDLER_REGISTER(thread_dtor, sve_thread_dtor, NULL,
920 EVENTHANDLER_PRI_ANY);
921 }
922 }
923 SYSINIT(sve, SI_SUB_SMP, SI_ORDER_ANY, sve_init, NULL);
924
925 static bool
get_arm64_sve(struct regset * rs,struct thread * td,void * buf,size_t * sizep)926 get_arm64_sve(struct regset *rs, struct thread *td, void *buf,
927 size_t *sizep)
928 {
929 struct svereg_header *header;
930 struct pcb *pcb;
931 size_t buf_size;
932 uint16_t sve_flags;
933
934 pcb = td->td_pcb;
935
936 if (td == curthread && (pcb->pcb_fpflags & PCB_FP_STARTED) != 0)
937 vfp_save_state(td, pcb);
938
939 /* If there is no SVE support in HW then we don't support NT_ARM_SVE */
940 if (pcb->pcb_sve_len == 0)
941 return (false);
942
943 sve_flags = 0;
944 if ((pcb->pcb_fpflags & PCB_FP_SVEVALID) == 0) {
945 /* If SVE hasn't been used yet provide the VFP registers */
946 buf_size = sizeof(struct fpreg);
947 sve_flags |= SVEREG_FLAG_FP;
948 } else {
949 /* We have SVE registers */
950 buf_size = sve_buf_size(td);
951 sve_flags |= SVEREG_FLAG_SVE;
952 KASSERT(pcb->pcb_svesaved != NULL, ("%s: no saved sve",
953 __func__));
954 }
955
956 if (buf != NULL) {
957 KASSERT(*sizep == sizeof(struct svereg_header) + buf_size,
958 ("%s: invalid size", __func__));
959
960 header = buf;
961 memset(header, 0, sizeof(*header));
962
963 header->sve_size = sizeof(struct svereg_header) + buf_size;
964 header->sve_maxsize = sizeof(struct svereg_header) +
965 sve_max_buf_size();
966 header->sve_vec_len = pcb->pcb_sve_len;
967 header->sve_max_vec_len = sve_max_vector_len;
968 header->sve_flags = sve_flags;
969
970 if ((sve_flags & SVEREG_FLAG_REGS_MASK) == SVEREG_FLAG_FP) {
971 struct fpreg *fpregs;
972
973 fpregs = (void *)(&header[1]);
974 memcpy(fpregs->fp_q, pcb->pcb_fpustate.vfp_regs,
975 sizeof(fpregs->fp_q));
976 fpregs->fp_cr = pcb->pcb_fpustate.vfp_fpcr;
977 fpregs->fp_sr = pcb->pcb_fpustate.vfp_fpsr;
978 } else {
979 memcpy((void *)(&header[1]), pcb->pcb_svesaved,
980 buf_size);
981 }
982 }
983 *sizep = sizeof(struct svereg_header) + buf_size;
984
985 return (true);
986 }
987
988 static bool
set_arm64_sve(struct regset * rs,struct thread * td,void * buf,size_t size)989 set_arm64_sve(struct regset *rs, struct thread *td, void *buf, size_t size)
990 {
991 struct svereg_header *header;
992 struct pcb *pcb;
993 size_t buf_size;
994 uint16_t sve_flags;
995
996 pcb = td->td_pcb;
997
998 /* If there is no SVE support in HW then we don't support NT_ARM_SVE */
999 if (pcb->pcb_sve_len == 0)
1000 return (false);
1001
1002 sve_flags = 0;
1003 if ((pcb->pcb_fpflags & PCB_FP_SVEVALID) == 0) {
1004 /*
1005 * If the SVE state is invalid it provide the FP registers.
1006 * This may be beause it hasn't been used, or it has but
1007 * was switched out in a system call.
1008 */
1009 buf_size = sizeof(struct fpreg);
1010 sve_flags |= SVEREG_FLAG_FP;
1011 } else {
1012 /* We have SVE registers */
1013 MPASS(pcb->pcb_svesaved != NULL);
1014 buf_size = sve_buf_size(td);
1015 sve_flags |= SVEREG_FLAG_SVE;
1016 KASSERT(pcb->pcb_svesaved != NULL, ("%s: no saved sve",
1017 __func__));
1018 }
1019
1020 if (size != sizeof(struct svereg_header) + buf_size)
1021 return (false);
1022
1023 header = buf;
1024 /* Sanity checks on the header */
1025 if (header->sve_size != sizeof(struct svereg_header) + buf_size)
1026 return (false);
1027
1028 if (header->sve_maxsize != sizeof(struct svereg_header) +
1029 sve_max_buf_size())
1030 return (false);
1031
1032 if (header->sve_vec_len != pcb->pcb_sve_len)
1033 return (false);
1034
1035 if (header->sve_max_vec_len != sve_max_vector_len)
1036 return (false);
1037
1038 if (header->sve_flags != sve_flags)
1039 return (false);
1040
1041 if ((sve_flags & SVEREG_FLAG_REGS_MASK) == SVEREG_FLAG_FP) {
1042 struct fpreg *fpregs;
1043
1044 fpregs = (void *)(&header[1]);
1045 memcpy(pcb->pcb_fpustate.vfp_regs, fpregs->fp_q,
1046 sizeof(fpregs->fp_q));
1047 pcb->pcb_fpustate.vfp_fpcr = fpregs->fp_cr;
1048 pcb->pcb_fpustate.vfp_fpsr = fpregs->fp_sr;
1049 } else {
1050 /* Restore the SVE registers */
1051 memcpy(pcb->pcb_svesaved, (void *)(&header[1]), buf_size);
1052 }
1053
1054 return (true);
1055 }
1056
1057 static struct regset regset_arm64_sve = {
1058 .note = NT_ARM_SVE,
1059 .get = get_arm64_sve,
1060 .set = set_arm64_sve,
1061 };
1062 ELF_REGSET(regset_arm64_sve);
1063
1064 struct fpu_kern_ctx *
fpu_kern_alloc_ctx(u_int flags)1065 fpu_kern_alloc_ctx(u_int flags)
1066 {
1067 struct fpu_kern_ctx *res;
1068 size_t sz;
1069
1070 sz = sizeof(struct fpu_kern_ctx);
1071 res = malloc(sz, M_FPUKERN_CTX, ((flags & FPU_KERN_NOWAIT) ?
1072 M_NOWAIT : M_WAITOK) | M_ZERO);
1073 return (res);
1074 }
1075
1076 void
fpu_kern_free_ctx(struct fpu_kern_ctx * ctx)1077 fpu_kern_free_ctx(struct fpu_kern_ctx *ctx)
1078 {
1079
1080 KASSERT((ctx->flags & FPU_KERN_CTX_INUSE) == 0, ("free'ing inuse ctx"));
1081 /* XXXAndrew clear the memory ? */
1082 free(ctx, M_FPUKERN_CTX);
1083 }
1084
1085 void
fpu_kern_enter(struct thread * td,struct fpu_kern_ctx * ctx,u_int flags)1086 fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
1087 {
1088 struct pcb *pcb;
1089
1090 pcb = td->td_pcb;
1091 KASSERT((flags & FPU_KERN_NOCTX) != 0 || ctx != NULL,
1092 ("ctx is required when !FPU_KERN_NOCTX"));
1093 KASSERT(ctx == NULL || (ctx->flags & FPU_KERN_CTX_INUSE) == 0,
1094 ("using inuse ctx"));
1095 KASSERT((pcb->pcb_fpflags & PCB_FP_NOSAVE) == 0,
1096 ("recursive fpu_kern_enter while in PCB_FP_NOSAVE state"));
1097
1098 if ((flags & FPU_KERN_NOCTX) != 0) {
1099 critical_enter();
1100 if (curthread == PCPU_GET(fpcurthread)) {
1101 vfp_save_state(curthread, pcb);
1102 }
1103 PCPU_SET(fpcurthread, NULL);
1104
1105 vfp_enable();
1106 pcb->pcb_fpflags |= PCB_FP_KERN | PCB_FP_NOSAVE |
1107 PCB_FP_STARTED;
1108 return;
1109 }
1110
1111 if ((flags & FPU_KERN_KTHR) != 0 && is_fpu_kern_thread(0)) {
1112 ctx->flags = FPU_KERN_CTX_DUMMY | FPU_KERN_CTX_INUSE;
1113 return;
1114 }
1115 /*
1116 * Check either we are already using the VFP in the kernel, or
1117 * the saved state points to the default user space.
1118 */
1119 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) != 0 ||
1120 pcb->pcb_fpusaved == &pcb->pcb_fpustate,
1121 ("Mangled pcb_fpusaved %x %p %p", pcb->pcb_fpflags, pcb->pcb_fpusaved, &pcb->pcb_fpustate));
1122 ctx->flags = FPU_KERN_CTX_INUSE;
1123 vfp_save_state(curthread, pcb);
1124 ctx->prev = pcb->pcb_fpusaved;
1125 pcb->pcb_fpusaved = &ctx->state;
1126 pcb->pcb_fpflags |= PCB_FP_KERN;
1127 pcb->pcb_fpflags &= ~PCB_FP_STARTED;
1128
1129 return;
1130 }
1131
1132 int
fpu_kern_leave(struct thread * td,struct fpu_kern_ctx * ctx)1133 fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
1134 {
1135 struct pcb *pcb;
1136
1137 pcb = td->td_pcb;
1138
1139 if ((pcb->pcb_fpflags & PCB_FP_NOSAVE) != 0) {
1140 KASSERT(ctx == NULL, ("non-null ctx after FPU_KERN_NOCTX"));
1141 KASSERT(PCPU_GET(fpcurthread) == NULL,
1142 ("non-NULL fpcurthread for PCB_FP_NOSAVE"));
1143 CRITICAL_ASSERT(td);
1144
1145 vfp_disable();
1146 pcb->pcb_fpflags &= ~(PCB_FP_NOSAVE | PCB_FP_STARTED);
1147 critical_exit();
1148 } else {
1149 KASSERT((ctx->flags & FPU_KERN_CTX_INUSE) != 0,
1150 ("FPU context not inuse"));
1151 ctx->flags &= ~FPU_KERN_CTX_INUSE;
1152
1153 if (is_fpu_kern_thread(0) &&
1154 (ctx->flags & FPU_KERN_CTX_DUMMY) != 0)
1155 return (0);
1156 KASSERT((ctx->flags & FPU_KERN_CTX_DUMMY) == 0, ("dummy ctx"));
1157 critical_enter();
1158 vfp_discard(td);
1159 critical_exit();
1160 pcb->pcb_fpflags &= ~PCB_FP_STARTED;
1161 pcb->pcb_fpusaved = ctx->prev;
1162 }
1163
1164 if (pcb->pcb_fpusaved == &pcb->pcb_fpustate) {
1165 pcb->pcb_fpflags &= ~PCB_FP_KERN;
1166 } else {
1167 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) != 0,
1168 ("unpaired fpu_kern_leave"));
1169 }
1170
1171 return (0);
1172 }
1173
1174 int
fpu_kern_thread(u_int flags __unused)1175 fpu_kern_thread(u_int flags __unused)
1176 {
1177 struct pcb *pcb = curthread->td_pcb;
1178
1179 KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0,
1180 ("Only kthread may use fpu_kern_thread"));
1181 KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
1182 ("Mangled pcb_fpusaved"));
1183 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) == 0,
1184 ("Thread already setup for the VFP"));
1185 pcb->pcb_fpflags |= PCB_FP_KERN;
1186 return (0);
1187 }
1188
1189 int
is_fpu_kern_thread(u_int flags __unused)1190 is_fpu_kern_thread(u_int flags __unused)
1191 {
1192 struct pcb *curpcb;
1193
1194 if ((curthread->td_pflags & TDP_KTHREAD) == 0)
1195 return (0);
1196 curpcb = curthread->td_pcb;
1197 return ((curpcb->pcb_fpflags & PCB_FP_KERN) != 0);
1198 }
1199
1200 /*
1201 * FPU save area alloc/free/init utility routines
1202 */
1203 struct vfpstate *
fpu_save_area_alloc(void)1204 fpu_save_area_alloc(void)
1205 {
1206 return (uma_zalloc(fpu_save_area_zone, M_WAITOK));
1207 }
1208
1209 void
fpu_save_area_free(struct vfpstate * fsa)1210 fpu_save_area_free(struct vfpstate *fsa)
1211 {
1212 uma_zfree(fpu_save_area_zone, fsa);
1213 }
1214
1215 void
fpu_save_area_reset(struct vfpstate * fsa)1216 fpu_save_area_reset(struct vfpstate *fsa)
1217 {
1218 memcpy(fsa, fpu_initialstate, sizeof(*fsa));
1219 }
1220 #endif
1221