2 * Copyright (c) 2015-2016 The FreeBSD Foundation
4 * This software was developed by Andrew Turner under
5 * sponsorship from the FreeBSD Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/limits.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
41 #include <machine/armreg.h>
42 #include <machine/md_var.h>
43 #include <machine/pcb.h>
44 #include <machine/vfp.h>
46 /* Sanity check we can store all the VFP registers */
47 CTASSERT(sizeof(((struct pcb *)0)->pcb_fpustate.vfp_regs) == 16 * 32);
49 static MALLOC_DEFINE(M_FPUKERN_CTX, "fpukern_ctx",
50 "Kernel contexts for VFP state");
53 struct vfpstate *prev;
54 #define FPU_KERN_CTX_DUMMY 0x01 /* avoided save for the kern thread */
55 #define FPU_KERN_CTX_INUSE 0x02
57 struct vfpstate state;
60 static uma_zone_t fpu_save_area_zone;
61 static struct vfpstate *fpu_initialstate;
68 cpacr = READ_SPECIALREG(cpacr_el1);
69 cpacr = (cpacr & ~CPACR_FPEN_MASK) | CPACR_FPEN_TRAP_NONE;
70 WRITE_SPECIALREG(cpacr_el1, cpacr);
79 cpacr = READ_SPECIALREG(cpacr_el1);
80 cpacr = (cpacr & ~CPACR_FPEN_MASK) | CPACR_FPEN_TRAP_ALL1;
81 WRITE_SPECIALREG(cpacr_el1, cpacr);
86 * Called when the thread is dying or when discarding the kernel VFP state.
87 * If the thread was the last to use the VFP unit mark it as unused to tell
88 * the kernel the fp state is unowned. Ensure the VFP unit is off so we get
89 * an exception on the next access.
92 vfp_discard(struct thread *td)
99 if (PCPU_GET(fpcurthread) == td)
100 PCPU_SET(fpcurthread, NULL);
106 vfp_store(struct vfpstate *state)
108 __uint128_t *vfp_state;
111 vfp_state = state->vfp_regs;
115 "stp q0, q1, [%2, #16 * 0]\n"
116 "stp q2, q3, [%2, #16 * 2]\n"
117 "stp q4, q5, [%2, #16 * 4]\n"
118 "stp q6, q7, [%2, #16 * 6]\n"
119 "stp q8, q9, [%2, #16 * 8]\n"
120 "stp q10, q11, [%2, #16 * 10]\n"
121 "stp q12, q13, [%2, #16 * 12]\n"
122 "stp q14, q15, [%2, #16 * 14]\n"
123 "stp q16, q17, [%2, #16 * 16]\n"
124 "stp q18, q19, [%2, #16 * 18]\n"
125 "stp q20, q21, [%2, #16 * 20]\n"
126 "stp q22, q23, [%2, #16 * 22]\n"
127 "stp q24, q25, [%2, #16 * 24]\n"
128 "stp q26, q27, [%2, #16 * 26]\n"
129 "stp q28, q29, [%2, #16 * 28]\n"
130 "stp q30, q31, [%2, #16 * 30]\n"
131 : "=&r"(fpcr), "=&r"(fpsr) : "r"(vfp_state));
133 state->vfp_fpcr = fpcr;
134 state->vfp_fpsr = fpsr;
138 vfp_restore(struct vfpstate *state)
140 __uint128_t *vfp_state;
143 vfp_state = state->vfp_regs;
144 fpcr = state->vfp_fpcr;
145 fpsr = state->vfp_fpsr;
148 "ldp q0, q1, [%2, #16 * 0]\n"
149 "ldp q2, q3, [%2, #16 * 2]\n"
150 "ldp q4, q5, [%2, #16 * 4]\n"
151 "ldp q6, q7, [%2, #16 * 6]\n"
152 "ldp q8, q9, [%2, #16 * 8]\n"
153 "ldp q10, q11, [%2, #16 * 10]\n"
154 "ldp q12, q13, [%2, #16 * 12]\n"
155 "ldp q14, q15, [%2, #16 * 14]\n"
156 "ldp q16, q17, [%2, #16 * 16]\n"
157 "ldp q18, q19, [%2, #16 * 18]\n"
158 "ldp q20, q21, [%2, #16 * 20]\n"
159 "ldp q22, q23, [%2, #16 * 22]\n"
160 "ldp q24, q25, [%2, #16 * 24]\n"
161 "ldp q26, q27, [%2, #16 * 26]\n"
162 "ldp q28, q29, [%2, #16 * 28]\n"
163 "ldp q30, q31, [%2, #16 * 30]\n"
166 : : "r"(fpcr), "r"(fpsr), "r"(vfp_state));
170 vfp_save_state(struct thread *td, struct pcb *pcb)
174 KASSERT(pcb != NULL, ("NULL vfp pcb"));
175 KASSERT(td == NULL || td->td_pcb == pcb, ("Invalid vfp pcb"));
178 * savectx() will be called on panic with dumppcb as an argument,
179 * dumppcb doesn't have pcb_fpusaved set, so set it to save
182 if (pcb->pcb_fpusaved == NULL)
183 pcb->pcb_fpusaved = &pcb->pcb_fpustate;
190 * Only store the registers if the VFP is enabled,
191 * i.e. return if we are trapping on FP access.
193 cpacr = READ_SPECIALREG(cpacr_el1);
194 if ((cpacr & CPACR_FPEN_MASK) == CPACR_FPEN_TRAP_NONE) {
195 KASSERT(PCPU_GET(fpcurthread) == td,
196 ("Storing an invalid VFP state"));
198 vfp_store(pcb->pcb_fpusaved);
206 * Update the VFP state for a forked process or new thread. The PCB will
207 * have been copied from the old thread.
210 vfp_new_thread(struct thread *newtd, struct thread *oldtd, bool fork)
214 newpcb = newtd->td_pcb;
216 /* Kernel threads start with clean VFP */
217 if ((oldtd->td_pflags & TDP_KTHREAD) != 0) {
218 newpcb->pcb_fpflags &=
219 ~(PCB_FP_STARTED | PCB_FP_KERN | PCB_FP_NOSAVE);
221 MPASS((newpcb->pcb_fpflags & (PCB_FP_KERN|PCB_FP_NOSAVE)) == 0);
223 newpcb->pcb_fpflags &= ~PCB_FP_STARTED;
227 newpcb->pcb_fpusaved = &newpcb->pcb_fpustate;
228 newpcb->pcb_vfpcpu = UINT_MAX;
232 * Reset the FP state to avoid leaking state from the parent process across
233 * execve() (and to ensure that we get a consistent floating point environment
234 * in every new process).
237 vfp_reset_state(struct thread *td, struct pcb *pcb)
239 /* Discard the threads VFP state before resetting it */
245 * Clear the thread state. The VFP is disabled and is not the current
246 * VFP thread so we won't change any of these on context switch.
248 bzero(&pcb->pcb_fpustate.vfp_regs, sizeof(pcb->pcb_fpustate.vfp_regs));
249 KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
250 ("pcb_fpusaved should point to pcb_fpustate."));
251 pcb->pcb_fpustate.vfp_fpcr = VFPCR_INIT;
252 pcb->pcb_fpustate.vfp_fpsr = 0;
253 pcb->pcb_vfpcpu = UINT_MAX;
254 pcb->pcb_fpflags = 0;
258 vfp_restore_state(void)
265 cpu = PCPU_GET(cpuid);
266 curpcb = curthread->td_pcb;
267 curpcb->pcb_fpflags |= PCB_FP_STARTED;
272 * If the previous thread on this cpu to use the VFP was not the
273 * current thread, or the current thread last used it on a different
274 * cpu we need to restore the old state.
276 if (PCPU_GET(fpcurthread) != curthread || cpu != curpcb->pcb_vfpcpu) {
277 vfp_restore(curthread->td_pcb->pcb_fpusaved);
278 PCPU_SET(fpcurthread, curthread);
279 curpcb->pcb_vfpcpu = cpu;
286 vfp_init_secondary(void)
290 /* Check if there is a vfp unit present */
291 pfr = READ_SPECIALREG(id_aa64pfr0_el1);
292 if ((pfr & ID_AA64PFR0_FP_MASK) == ID_AA64PFR0_FP_NONE)
295 /* Disable to be enabled when it's used */
300 vfp_init(const void *dummy __unused)
304 /* Check if there is a vfp unit present */
305 pfr = READ_SPECIALREG(id_aa64pfr0_el1);
306 if ((pfr & ID_AA64PFR0_FP_MASK) == ID_AA64PFR0_FP_NONE)
309 fpu_save_area_zone = uma_zcreate("VFP_save_area",
310 sizeof(struct vfpstate), NULL, NULL, NULL, NULL,
311 _Alignof(struct vfpstate) - 1, 0);
312 fpu_initialstate = uma_zalloc(fpu_save_area_zone, M_WAITOK | M_ZERO);
314 /* Ensure the VFP is enabled before accessing it in vfp_store */
316 vfp_store(fpu_initialstate);
318 /* Disable to be enabled when it's used */
321 /* Zero the VFP registers but keep fpcr and fpsr */
322 bzero(fpu_initialstate->vfp_regs, sizeof(fpu_initialstate->vfp_regs));
324 thread0.td_pcb->pcb_fpusaved->vfp_fpcr = VFPCR_INIT;
327 SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, vfp_init, NULL);
329 struct fpu_kern_ctx *
330 fpu_kern_alloc_ctx(u_int flags)
332 struct fpu_kern_ctx *res;
335 sz = sizeof(struct fpu_kern_ctx);
336 res = malloc(sz, M_FPUKERN_CTX, ((flags & FPU_KERN_NOWAIT) ?
337 M_NOWAIT : M_WAITOK) | M_ZERO);
342 fpu_kern_free_ctx(struct fpu_kern_ctx *ctx)
345 KASSERT((ctx->flags & FPU_KERN_CTX_INUSE) == 0, ("free'ing inuse ctx"));
346 /* XXXAndrew clear the memory ? */
347 free(ctx, M_FPUKERN_CTX);
351 fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
356 KASSERT((flags & FPU_KERN_NOCTX) != 0 || ctx != NULL,
357 ("ctx is required when !FPU_KERN_NOCTX"));
358 KASSERT(ctx == NULL || (ctx->flags & FPU_KERN_CTX_INUSE) == 0,
359 ("using inuse ctx"));
360 KASSERT((pcb->pcb_fpflags & PCB_FP_NOSAVE) == 0,
361 ("recursive fpu_kern_enter while in PCB_FP_NOSAVE state"));
363 if ((flags & FPU_KERN_NOCTX) != 0) {
365 if (curthread == PCPU_GET(fpcurthread)) {
366 vfp_save_state(curthread, pcb);
368 PCPU_SET(fpcurthread, NULL);
371 pcb->pcb_fpflags |= PCB_FP_KERN | PCB_FP_NOSAVE |
376 if ((flags & FPU_KERN_KTHR) != 0 && is_fpu_kern_thread(0)) {
377 ctx->flags = FPU_KERN_CTX_DUMMY | FPU_KERN_CTX_INUSE;
381 * Check either we are already using the VFP in the kernel, or
382 * the saved state points to the default user space.
384 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) != 0 ||
385 pcb->pcb_fpusaved == &pcb->pcb_fpustate,
386 ("Mangled pcb_fpusaved %x %p %p", pcb->pcb_fpflags, pcb->pcb_fpusaved, &pcb->pcb_fpustate));
387 ctx->flags = FPU_KERN_CTX_INUSE;
388 vfp_save_state(curthread, pcb);
389 ctx->prev = pcb->pcb_fpusaved;
390 pcb->pcb_fpusaved = &ctx->state;
391 pcb->pcb_fpflags |= PCB_FP_KERN;
392 pcb->pcb_fpflags &= ~PCB_FP_STARTED;
398 fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
404 if ((pcb->pcb_fpflags & PCB_FP_NOSAVE) != 0) {
405 KASSERT(ctx == NULL, ("non-null ctx after FPU_KERN_NOCTX"));
406 KASSERT(PCPU_GET(fpcurthread) == NULL,
407 ("non-NULL fpcurthread for PCB_FP_NOSAVE"));
411 pcb->pcb_fpflags &= ~(PCB_FP_NOSAVE | PCB_FP_STARTED);
414 KASSERT((ctx->flags & FPU_KERN_CTX_INUSE) != 0,
415 ("FPU context not inuse"));
416 ctx->flags &= ~FPU_KERN_CTX_INUSE;
418 if (is_fpu_kern_thread(0) &&
419 (ctx->flags & FPU_KERN_CTX_DUMMY) != 0)
421 KASSERT((ctx->flags & FPU_KERN_CTX_DUMMY) == 0, ("dummy ctx"));
425 pcb->pcb_fpflags &= ~PCB_FP_STARTED;
426 pcb->pcb_fpusaved = ctx->prev;
429 if (pcb->pcb_fpusaved == &pcb->pcb_fpustate) {
430 pcb->pcb_fpflags &= ~PCB_FP_KERN;
432 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) != 0,
433 ("unpaired fpu_kern_leave"));
440 fpu_kern_thread(u_int flags __unused)
442 struct pcb *pcb = curthread->td_pcb;
444 KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0,
445 ("Only kthread may use fpu_kern_thread"));
446 KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
447 ("Mangled pcb_fpusaved"));
448 KASSERT((pcb->pcb_fpflags & PCB_FP_KERN) == 0,
449 ("Thread already setup for the VFP"));
450 pcb->pcb_fpflags |= PCB_FP_KERN;
455 is_fpu_kern_thread(u_int flags __unused)
459 if ((curthread->td_pflags & TDP_KTHREAD) == 0)
461 curpcb = curthread->td_pcb;
462 return ((curpcb->pcb_fpflags & PCB_FP_KERN) != 0);
466 * FPU save area alloc/free/init utility routines
469 fpu_save_area_alloc(void)
471 return (uma_zalloc(fpu_save_area_zone, M_WAITOK));
475 fpu_save_area_free(struct vfpstate *fsa)
477 uma_zfree(fpu_save_area_zone, fsa);
481 fpu_save_area_reset(struct vfpstate *fsa)
483 memcpy(fsa, fpu_initialstate, sizeof(*fsa));