xref: /netbsd-src/sys/arch/arm/vfp/vfp_init.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*      $NetBSD: vfp_init.c,v 1.57 2018/04/08 09:19:27 bouyer Exp $ */
2 
3 /*
4  * Copyright (c) 2008 ARM Ltd
5  * All rights reserved.
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  * 3. The name of the company may not be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/proc.h>
37 #include <sys/cpu.h>
38 
39 #include <arm/locore.h>
40 #include <arm/pcb.h>
41 #include <arm/undefined.h>
42 #include <arm/vfpreg.h>
43 #include <arm/mcontext.h>
44 
45 #include <uvm/uvm_extern.h>		/* for pmap.h */
46 
47 #ifdef FPU_VFP
48 
49 #ifdef CPU_CORTEX
50 #define SETFPU __asm(".fpu\tvfpv4")
51 #else
52 #define SETFPU __asm(".fpu\tvfp")
53 #endif
54 SETFPU;
55 
56 /* FLDMD <X>, {d0-d15} */
57 static inline void
58 load_vfpregs_lo(const uint64_t *p)
59 {
60 	SETFPU;
61 	__asm __volatile("vldmia\t%0, {d0-d15}" :: "r" (p) : "memory");
62 }
63 
64 /* FSTMD <X>, {d0-d15} */
65 static inline void
66 save_vfpregs_lo(uint64_t *p)
67 {
68 	SETFPU;
69 	__asm __volatile("vstmia\t%0, {d0-d15}" :: "r" (p) : "memory");
70 }
71 
72 #ifdef CPU_CORTEX
73 /* FLDMD <X>, {d16-d31} */
74 static inline void
75 load_vfpregs_hi(const uint64_t *p)
76 {
77 	SETFPU;
78 	__asm __volatile("vldmia\t%0, {d16-d31}" :: "r" (&p[16]) : "memory");
79 }
80 
81 /* FLDMD <X>, {d16-d31} */
82 static inline void
83 save_vfpregs_hi(uint64_t *p)
84 {
85 	SETFPU;
86 	__asm __volatile("vstmia\t%0, {d16-d31}" :: "r" (&p[16]) : "memory");
87 }
88 #endif
89 
90 static inline void
91 load_vfpregs(const struct vfpreg *fregs)
92 {
93 	load_vfpregs_lo(fregs->vfp_regs);
94 #ifdef CPU_CORTEX
95 #ifdef CPU_ARM11
96 	switch (curcpu()->ci_vfp_id) {
97 	case FPU_VFP_CORTEXA5:
98 	case FPU_VFP_CORTEXA7:
99 	case FPU_VFP_CORTEXA8:
100 	case FPU_VFP_CORTEXA9:
101 	case FPU_VFP_CORTEXA15:
102 	case FPU_VFP_CORTEXA15_QEMU:
103 	case FPU_VFP_CORTEXA53:
104 	case FPU_VFP_CORTEXA57:
105 #endif
106 		load_vfpregs_hi(fregs->vfp_regs);
107 #ifdef CPU_ARM11
108 		break;
109 	}
110 #endif
111 #endif
112 }
113 
114 static inline void
115 save_vfpregs(struct vfpreg *fregs)
116 {
117 	save_vfpregs_lo(fregs->vfp_regs);
118 #ifdef CPU_CORTEX
119 #ifdef CPU_ARM11
120 	switch (curcpu()->ci_vfp_id) {
121 	case FPU_VFP_CORTEXA5:
122 	case FPU_VFP_CORTEXA7:
123 	case FPU_VFP_CORTEXA8:
124 	case FPU_VFP_CORTEXA9:
125 	case FPU_VFP_CORTEXA15:
126 	case FPU_VFP_CORTEXA15_QEMU:
127 	case FPU_VFP_CORTEXA53:
128 	case FPU_VFP_CORTEXA57:
129 #endif
130 		save_vfpregs_hi(fregs->vfp_regs);
131 #ifdef CPU_ARM11
132 		break;
133 	}
134 #endif
135 #endif
136 }
137 
138 /* The real handler for VFP bounces.  */
139 static int vfp_handler(u_int, u_int, trapframe_t *, int);
140 #ifdef CPU_CORTEX
141 static int neon_handler(u_int, u_int, trapframe_t *, int);
142 #endif
143 
144 static void vfp_state_load(lwp_t *, u_int);
145 static void vfp_state_save(lwp_t *);
146 static void vfp_state_release(lwp_t *);
147 
148 const pcu_ops_t arm_vfp_ops = {
149 	.pcu_id = PCU_FPU,
150 	.pcu_state_save = vfp_state_save,
151 	.pcu_state_load = vfp_state_load,
152 	.pcu_state_release = vfp_state_release,
153 };
154 
155 /* determine what bits can be changed */
156 uint32_t vfp_fpscr_changable = VFP_FPSCR_CSUM;
157 /* default to run fast */
158 uint32_t vfp_fpscr_default = (VFP_FPSCR_DN | VFP_FPSCR_FZ | VFP_FPSCR_RN);
159 
160 /*
161  * Used to test for a VFP. The following function is installed as a coproc10
162  * handler on the undefined instruction vector and then we issue a VFP
163  * instruction. If undefined_test is non zero then the VFP did not handle
164  * the instruction so must be absent, or disabled.
165  */
166 
167 static int undefined_test;
168 
169 static int
170 vfp_test(u_int address, u_int insn, trapframe_t *frame, int fault_code)
171 {
172 
173 	frame->tf_pc += INSN_SIZE;
174 	++undefined_test;
175 	return 0;
176 }
177 
178 #else
179 /* determine what bits can be changed */
180 uint32_t vfp_fpscr_changable = VFP_FPSCR_CSUM|VFP_FPSCR_ESUM|VFP_FPSCR_RMODE;
181 #endif /* FPU_VFP */
182 
183 static int
184 vfp_fpscr_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
185 {
186 	struct lwp * const l = curlwp;
187 	const u_int regno = (insn >> 12) & 0xf;
188 	/*
189 	 * Only match move to/from the FPSCR register and we
190 	 * can't be using the SP,LR,PC as a source.
191 	 */
192 	if ((insn & 0xffef0fff) != 0xeee10a10 || regno > 12)
193 		return 1;
194 
195 	struct pcb * const pcb = lwp_getpcb(l);
196 
197 #ifdef FPU_VFP
198 	/*
199 	 * If FPU is valid somewhere, let's just reenable VFP and
200 	 * retry the instruction (only safe thing to do since the
201 	 * pcb has a stale copy).
202 	 */
203 	if (pcb->pcb_vfp.vfp_fpexc & VFP_FPEXC_EN)
204 		return 1;
205 
206 	if (__predict_false(!vfp_used_p(l))) {
207 		pcb->pcb_vfp.vfp_fpscr = vfp_fpscr_default;
208 	}
209 #endif
210 
211 	/*
212 	 * We now know the pcb has the saved copy.
213 	 */
214 	register_t * const regp = &frame->tf_r0 + regno;
215 	if (insn & 0x00100000) {
216 		*regp = pcb->pcb_vfp.vfp_fpscr;
217 	} else {
218 		pcb->pcb_vfp.vfp_fpscr &= ~vfp_fpscr_changable;
219 		pcb->pcb_vfp.vfp_fpscr |= *regp & vfp_fpscr_changable;
220 	}
221 
222 	curcpu()->ci_vfp_evs[0].ev_count++;
223 
224 	frame->tf_pc += INSN_SIZE;
225 	return 0;
226 }
227 
228 #ifndef FPU_VFP
229 /*
230  * If we don't want VFP support, we still need to handle emulating VFP FPSCR
231  * instructions.
232  */
233 void
234 vfp_attach(struct cpu_info *ci)
235 {
236 	if (CPU_IS_PRIMARY(ci)) {
237 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
238 	}
239 	evcnt_attach_dynamic(&ci->ci_vfp_evs[0], EVCNT_TYPE_TRAP, NULL,
240 	    ci->ci_cpuname, "vfp fpscr traps");
241 }
242 
243 #else
244 void
245 vfp_attach(struct cpu_info *ci)
246 {
247 	const char *model = NULL;
248 
249 	if (CPU_ID_ARM11_P(ci->ci_arm_cpuid)
250 	    || CPU_ID_MV88SV58XX_P(ci->ci_arm_cpuid)
251 	    || CPU_ID_CORTEX_P(ci->ci_arm_cpuid)) {
252 #if 0
253 		const uint32_t nsacr = armreg_nsacr_read();
254 		const uint32_t nsacr_vfp = __BITS(VFP_COPROC,VFP_COPROC2);
255 		if ((nsacr & nsacr_vfp) != nsacr_vfp) {
256 			aprint_normal_dev(ci->ci_dev,
257 			    "VFP access denied (NSACR=%#x)\n", nsacr);
258 			install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
259 			ci->ci_vfp_id = 0;
260 			evcnt_attach_dynamic(&ci->ci_vfp_evs[0],
261 			    EVCNT_TYPE_TRAP, NULL, ci->ci_cpuname,
262 			    "vfp fpscr traps");
263 			return;
264 		}
265 #endif
266 		const uint32_t cpacr_vfp = CPACR_CPn(VFP_COPROC);
267 		const uint32_t cpacr_vfp2 = CPACR_CPn(VFP_COPROC2);
268 
269 		/*
270 		 * We first need to enable access to the coprocessors.
271 		 */
272 		uint32_t cpacr = armreg_cpacr_read();
273 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp);
274 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp2);
275 		armreg_cpacr_write(cpacr);
276 
277 		arm_isb();
278 
279 		/*
280 		 * If we could enable them, then they exist.
281 		 */
282 		cpacr = armreg_cpacr_read();
283 		bool vfp_p = __SHIFTOUT(cpacr, cpacr_vfp2) == CPACR_ALL
284 		    && __SHIFTOUT(cpacr, cpacr_vfp) == CPACR_ALL;
285 		if (!vfp_p) {
286 			aprint_normal_dev(ci->ci_dev,
287 			    "VFP access denied (CPACR=%#x)\n", cpacr);
288 			install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
289 			ci->ci_vfp_id = 0;
290 			evcnt_attach_dynamic(&ci->ci_vfp_evs[0],
291 			    EVCNT_TYPE_TRAP, NULL, ci->ci_cpuname,
292 			    "vfp fpscr traps");
293 			return;
294 		}
295 	}
296 
297 	void *uh = install_coproc_handler(VFP_COPROC, vfp_test);
298 
299 	undefined_test = 0;
300 
301 	const uint32_t fpsid = armreg_fpsid_read();
302 
303 	remove_coproc_handler(uh);
304 
305 	if (undefined_test != 0) {
306 		aprint_normal_dev(ci->ci_dev, "No VFP detected\n");
307 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
308 		ci->ci_vfp_id = 0;
309 		return;
310 	}
311 
312 	ci->ci_vfp_id = fpsid;
313 	switch (fpsid & ~ VFP_FPSID_REV_MSK) {
314 	case FPU_VFP10_ARM10E:
315 		model = "VFP10 R1";
316 		break;
317 	case FPU_VFP11_ARM11:
318 		model = "VFP11";
319 		break;
320 	case FPU_VFP_MV88SV58XX:
321 		model = "VFP3";
322 		break;
323 	case FPU_VFP_CORTEXA5:
324 	case FPU_VFP_CORTEXA7:
325 	case FPU_VFP_CORTEXA8:
326 	case FPU_VFP_CORTEXA9:
327 	case FPU_VFP_CORTEXA15:
328 	case FPU_VFP_CORTEXA15_QEMU:
329 	case FPU_VFP_CORTEXA53:
330 	case FPU_VFP_CORTEXA57:
331 		if (armreg_cpacr_read() & CPACR_V7_ASEDIS) {
332 			model = "VFP 4.0+";
333 		} else {
334 			model = "NEON MPE (VFP 3.0+)";
335 			cpu_neon_present = 1;
336 		}
337 		break;
338 	default:
339 		aprint_normal_dev(ci->ci_dev, "unrecognized VFP version %#x\n",
340 		    fpsid);
341 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
342 		vfp_fpscr_changable = VFP_FPSCR_CSUM|VFP_FPSCR_ESUM
343 		    |VFP_FPSCR_RMODE;
344 		vfp_fpscr_default = 0;
345 		return;
346 	}
347 
348 	cpu_fpu_present = 1;
349 	cpu_media_and_vfp_features[0] = armreg_mvfr0_read();
350 	cpu_media_and_vfp_features[1] = armreg_mvfr1_read();
351 	if (fpsid != 0) {
352 		uint32_t f0 = armreg_mvfr0_read();
353 		uint32_t f1 = armreg_mvfr1_read();
354 		aprint_normal("vfp%d at %s: %s%s%s%s%s\n",
355 		    device_unit(ci->ci_dev),
356 		    device_xname(ci->ci_dev),
357 		    model,
358 		    ((f0 & ARM_MVFR0_ROUNDING_MASK) ? ", rounding" : ""),
359 		    ((f0 & ARM_MVFR0_EXCEPT_MASK) ? ", exceptions" : ""),
360 		    ((f1 & ARM_MVFR1_D_NAN_MASK) ? ", NaN propagation" : ""),
361 		    ((f1 & ARM_MVFR1_FTZ_MASK) ? ", denormals" : ""));
362 		aprint_debug("vfp%d: mvfr: [0]=%#x [1]=%#x\n",
363 		    device_unit(ci->ci_dev), f0, f1);
364 		if (CPU_IS_PRIMARY(ci)) {
365 			if (f0 & ARM_MVFR0_ROUNDING_MASK) {
366 				vfp_fpscr_changable |= VFP_FPSCR_RMODE;
367 			}
368 			if (f1 & ARM_MVFR0_EXCEPT_MASK) {
369 				vfp_fpscr_changable |= VFP_FPSCR_ESUM;
370 			}
371 			// If hardware supports propagation of NaNs, select it.
372 			if (f1 & ARM_MVFR1_D_NAN_MASK) {
373 				vfp_fpscr_default &= ~VFP_FPSCR_DN;
374 				vfp_fpscr_changable |= VFP_FPSCR_DN;
375 			}
376 			// If hardware supports denormalized numbers, use it.
377 			if (cpu_media_and_vfp_features[1] & ARM_MVFR1_FTZ_MASK) {
378 				vfp_fpscr_default &= ~VFP_FPSCR_FZ;
379 				vfp_fpscr_changable |= VFP_FPSCR_FZ;
380 			}
381 		}
382 	}
383 	evcnt_attach_dynamic(&ci->ci_vfp_evs[0], EVCNT_TYPE_MISC, NULL,
384 	    ci->ci_cpuname, "vfp coproc use");
385 	evcnt_attach_dynamic(&ci->ci_vfp_evs[1], EVCNT_TYPE_MISC, NULL,
386 	    ci->ci_cpuname, "vfp coproc re-use");
387 	evcnt_attach_dynamic(&ci->ci_vfp_evs[2], EVCNT_TYPE_TRAP, NULL,
388 	    ci->ci_cpuname, "vfp coproc fault");
389 	install_coproc_handler(VFP_COPROC, vfp_handler);
390 	install_coproc_handler(VFP_COPROC2, vfp_handler);
391 #ifdef CPU_CORTEX
392 	if (cpu_neon_present)
393 		install_coproc_handler(CORE_UNKNOWN_HANDLER, neon_handler);
394 #endif
395 }
396 
397 /* The real handler for VFP bounces.  */
398 static int
399 vfp_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
400 {
401 	struct cpu_info * const ci = curcpu();
402 
403 	/* This shouldn't ever happen.  */
404 	if (fault_code != FAULT_USER)
405 		panic("VFP fault at %#x in non-user mode", frame->tf_pc);
406 
407 	if (ci->ci_vfp_id == 0) {
408 		/* No VFP detected, just fault.  */
409 		return 1;
410 	}
411 
412 	/*
413 	 * If we already own the FPU and it's enabled (and no exception), raise
414 	 * SIGILL.  If there is an exception, drop through to raise a SIGFPE.
415 	 */
416 	if (curcpu()->ci_pcu_curlwp[PCU_FPU] == curlwp
417 	    && (armreg_fpexc_read() & (VFP_FPEXC_EX|VFP_FPEXC_EN)) == VFP_FPEXC_EN)
418 		return 1;
419 
420 	/*
421 	 * Make sure we own the FP.
422 	 */
423 	pcu_load(&arm_vfp_ops);
424 
425 	uint32_t fpexc = armreg_fpexc_read();
426 	if (fpexc & VFP_FPEXC_EX) {
427 		ksiginfo_t ksi;
428 		KASSERT(fpexc & VFP_FPEXC_EN);
429 
430 		curcpu()->ci_vfp_evs[2].ev_count++;
431 
432 		/*
433 		 * Need the clear the exception condition so any signal
434 		 * and future use can proceed.
435 		 */
436 		armreg_fpexc_write(fpexc & ~(VFP_FPEXC_EX|VFP_FPEXC_FSUM));
437 
438 		pcu_save(&arm_vfp_ops, curlwp);
439 
440 		/*
441 		 * XXX Need to emulate bounce instructions here to get correct
442 		 * XXX exception codes, etc.
443 		 */
444 		KSI_INIT_TRAP(&ksi);
445 		ksi.ksi_signo = SIGFPE;
446 		if (fpexc & VFP_FPEXC_IXF)
447 			ksi.ksi_code = FPE_FLTRES;
448 		else if (fpexc & VFP_FPEXC_UFF)
449 			ksi.ksi_code = FPE_FLTUND;
450 		else if (fpexc & VFP_FPEXC_OFF)
451 			ksi.ksi_code = FPE_FLTOVF;
452 		else if (fpexc & VFP_FPEXC_DZF)
453 			ksi.ksi_code = FPE_FLTDIV;
454 		else if (fpexc & VFP_FPEXC_IOF)
455 			ksi.ksi_code = FPE_FLTINV;
456 		ksi.ksi_addr = (uint32_t *)address;
457 		ksi.ksi_trap = 0;
458 		trapsignal(curlwp, &ksi);
459 		return 0;
460 	}
461 
462 	/* Need to restart the faulted instruction.  */
463 //	frame->tf_pc -= INSN_SIZE;
464 	return 0;
465 }
466 
467 #ifdef CPU_CORTEX
468 /* The real handler for NEON bounces.  */
469 static int
470 neon_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
471 {
472 	struct cpu_info * const ci = curcpu();
473 
474 	if (ci->ci_vfp_id == 0)
475 		/* No VFP detected, just fault.  */
476 		return 1;
477 
478 	if ((insn & 0xfe000000) != 0xf2000000
479 	    && (insn & 0xfe000000) != 0xf4000000)
480 		/* Not NEON instruction, just fault.  */
481 		return 1;
482 
483 	/* This shouldn't ever happen.  */
484 	if (fault_code != FAULT_USER)
485 		panic("NEON fault in non-user mode");
486 
487 	/* if we already own the FPU and it's enabled, raise SIGILL */
488 	if (curcpu()->ci_pcu_curlwp[PCU_FPU] == curlwp
489 	    && (armreg_fpexc_read() & VFP_FPEXC_EN) != 0)
490 		return 1;
491 
492 	pcu_load(&arm_vfp_ops);
493 
494 	/* Need to restart the faulted instruction.  */
495 //	frame->tf_pc -= INSN_SIZE;
496 	return 0;
497 }
498 #endif
499 
500 static void
501 vfp_state_load(lwp_t *l, u_int flags)
502 {
503 	struct pcb * const pcb = lwp_getpcb(l);
504 	struct vfpreg * const fregs = &pcb->pcb_vfp;
505 
506 	/*
507 	 * Instrument VFP usage -- if a process has not previously
508 	 * used the VFP, mark it as having used VFP for the first time,
509 	 * and count this event.
510 	 *
511 	 * If a process has used the VFP, count a "used VFP, and took
512 	 * a trap to use it again" event.
513 	 */
514 	if (__predict_false((flags & PCU_VALID) == 0)) {
515 		curcpu()->ci_vfp_evs[0].ev_count++;
516 		pcb->pcb_vfp.vfp_fpscr = vfp_fpscr_default;
517 	} else {
518 		curcpu()->ci_vfp_evs[1].ev_count++;
519 	}
520 
521 	KASSERT((armreg_fpexc_read() & VFP_FPEXC_EN) == 0);
522 	/*
523 	 * If the VFP is already enabled we must be bouncing an instruction.
524 	 */
525 	if (flags & PCU_REENABLE) {
526 		uint32_t fpexc = armreg_fpexc_read();
527 		armreg_fpexc_write(fpexc | VFP_FPEXC_EN);
528 		fregs->vfp_fpexc |= VFP_FPEXC_EN;
529 		return;
530 	}
531 	KASSERT((fregs->vfp_fpexc & VFP_FPEXC_EN) == 0);
532 
533 	/*
534 	 * Load and Enable the VFP (so that we can write the registers).
535 	 */
536 	fregs->vfp_fpexc |= VFP_FPEXC_EN;
537 	armreg_fpexc_write(fregs->vfp_fpexc);
538 	KASSERT(curcpu()->ci_pcu_curlwp[PCU_FPU] == NULL);
539 	KASSERT(l->l_pcu_cpu[PCU_FPU] == NULL);
540 
541 	load_vfpregs(fregs);
542 	armreg_fpscr_write(fregs->vfp_fpscr);
543 
544 	if (fregs->vfp_fpexc & VFP_FPEXC_EX) {
545 		/* Need to restore the exception handling state.  */
546 		armreg_fpinst_write(fregs->vfp_fpinst);
547 		if (fregs->vfp_fpexc & VFP_FPEXC_FP2V)
548 			armreg_fpinst2_write(fregs->vfp_fpinst2);
549 	}
550 }
551 
552 void
553 vfp_state_save(lwp_t *l)
554 {
555 	struct pcb * const pcb = lwp_getpcb(l);
556 	struct vfpreg * const fregs = &pcb->pcb_vfp;
557 	uint32_t fpexc = armreg_fpexc_read();
558 
559 	KASSERT(curcpu()->ci_pcu_curlwp[PCU_FPU] == l);
560 	KASSERT(curcpu() == l->l_pcu_cpu[PCU_FPU]);
561 	KASSERT(curlwp == l || curlwp->l_pcu_cpu[PCU_FPU] != curcpu());
562 	/*
563 	 * Enable the VFP (so we can read the registers).
564 	 * Make sure the exception bit is cleared so that we can
565 	 * safely dump the registers.
566 	 */
567 	armreg_fpexc_write((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
568 
569 	fregs->vfp_fpexc = fpexc;
570 	if (fpexc & VFP_FPEXC_EX) {
571 		/* Need to save the exception handling state */
572 		fregs->vfp_fpinst = armreg_fpinst_read();
573 		if (fpexc & VFP_FPEXC_FP2V)
574 			fregs->vfp_fpinst2 = armreg_fpinst2_read();
575 	}
576 	fregs->vfp_fpscr = armreg_fpscr_read();
577 	save_vfpregs(fregs);
578 
579 	/* Disable the VFP.  */
580 	armreg_fpexc_write(fpexc & ~VFP_FPEXC_EN);
581 }
582 
583 void
584 vfp_state_release(lwp_t *l)
585 {
586 	struct pcb * const pcb = lwp_getpcb(l);
587 
588 	/*
589 	 * Now mark the VFP as disabled (and our state
590 	 * has been already saved or is being discarded).
591 	 */
592 	pcb->pcb_vfp.vfp_fpexc &= ~VFP_FPEXC_EN;
593 
594 	/*
595 	 * Turn off the FPU so the next time a VFP instruction is issued
596 	 * an exception happens.  We don't know if this LWP's state was
597 	 * loaded but if we turned off the FPU for some other LWP, when
598 	 * pcu_load invokes vfp_state_load it will see that VFP_FPEXC_EN
599 	 * is still set so it just restore fpexc and return since its
600 	 * contents are still sitting in the VFP.
601 	 */
602 	armreg_fpexc_write(armreg_fpexc_read() & ~VFP_FPEXC_EN);
603 }
604 
605 void
606 vfp_savecontext(lwp_t *l)
607 {
608 	pcu_save(&arm_vfp_ops, l);
609 }
610 
611 void
612 vfp_discardcontext(lwp_t *l, bool used_p)
613 {
614 	pcu_discard(&arm_vfp_ops, l, used_p);
615 }
616 
617 bool
618 vfp_used_p(const lwp_t *l)
619 {
620 	return pcu_valid_p(&arm_vfp_ops, l);
621 }
622 
623 void
624 vfp_getcontext(struct lwp *l, mcontext_t *mcp, int *flagsp)
625 {
626 	if (vfp_used_p(l)) {
627 		const struct pcb * const pcb = lwp_getpcb(l);
628 
629 		pcu_save(&arm_vfp_ops, l);
630 		mcp->__fpu.__vfpregs.__vfp_fpscr = pcb->pcb_vfp.vfp_fpscr;
631 		memcpy(mcp->__fpu.__vfpregs.__vfp_fstmx, pcb->pcb_vfp.vfp_regs,
632 		    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
633 		*flagsp |= _UC_FPU|_UC_ARM_VFP;
634 	}
635 }
636 
637 void
638 vfp_setcontext(struct lwp *l, const mcontext_t *mcp)
639 {
640 	struct pcb * const pcb = lwp_getpcb(l);
641 
642 	pcu_discard(&arm_vfp_ops, l, true);
643 	pcb->pcb_vfp.vfp_fpscr = mcp->__fpu.__vfpregs.__vfp_fpscr;
644 	memcpy(pcb->pcb_vfp.vfp_regs, mcp->__fpu.__vfpregs.__vfp_fstmx,
645 	    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
646 }
647 
648 #endif /* FPU_VFP */
649