xref: /netbsd-src/sys/arch/arm/vfp/vfp_init.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /*      $NetBSD: vfp_init.c,v 1.19 2013/02/05 23:23:34 matt 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/pcb.h>
40 #include <arm/undefined.h>
41 #include <arm/vfpreg.h>
42 #include <arm/mcontext.h>
43 
44 #include <uvm/uvm_extern.h>		/* for pmap.h */
45 
46 extern int cpu_media_and_vfp_features[];
47 extern int cpu_neon_present;
48 
49 /*
50  * Use generic co-processor instructions to avoid assembly problems.
51  */
52 
53 /* FMRX <X>, fpsid */
54 static inline uint32_t
55 read_fpsid(void)
56 {
57 	uint32_t rv;
58 	__asm __volatile("mrc p10, 7, %0, c0, c0, 0" : "=r" (rv));
59 	return rv;
60 }
61 
62 /* FMRX <X>, fpexc */
63 static inline uint32_t
64 read_fpscr(void)
65 {
66 	uint32_t rv;
67 	__asm __volatile("mrc p10, 7, %0, c1, c0, 0" : "=r" (rv));
68 	return rv;
69 }
70 
71 /* FMRX <X>, fpexc */
72 static inline uint32_t
73 read_fpexc(void)
74 {
75 	uint32_t rv;
76 	__asm __volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (rv));
77 	return rv;
78 }
79 
80 /* FMRX <X>, fpinst */
81 static inline uint32_t
82 read_fpinst(void)
83 {
84 	uint32_t rv;
85 	__asm __volatile("mrc p10, 7, %0, c9, c0, 0" : "=r" (rv));
86 	return rv;
87 }
88 
89 /* FMRX <X>, fpinst2 */
90 static inline uint32_t
91 read_fpinst2(void)
92 {
93 	uint32_t rv;
94 	__asm __volatile("mrc p10, 7, %0, c10, c0, 0" : "=r" (rv));
95 	return rv;
96 }
97 
98 /* FMXR <X>, fpscr */
99 #define write_fpscr(X)	__asm __volatile("mcr p10, 7, %0, c1, c0, 0" : \
100 			    : "r" (X))
101 /* FMXR <X>, fpexc */
102 #define write_fpexc(X)	__asm __volatile("mcr p10, 7, %0, c8, c0, 0" : \
103 			    : "r" (X))
104 /* FMXR <X>, fpinst */
105 #define write_fpinst(X)	__asm __volatile("mcr p10, 7, %0, c9, c0, 0" : \
106 			    : "r" (X))
107 /* FMXR <X>, fpinst2 */
108 #define write_fpinst2(X) __asm __volatile("mcr p10, 7, %0, c10, c0, 0" : \
109 			    : "r" (X))
110 
111 #ifdef FPU_VFP
112 
113 /* FLDMD <X>, {d0-d15} */
114 static inline void
115 load_vfpregs_lo(const uint64_t *p)
116 {
117 	/* vldmia rN, {d0-d15} */
118 	__asm __volatile("ldc\tp11, c0, [%0], {32}" :: "r" (p) : "memory");
119 }
120 
121 /* FSTMD <X>, {d0-d15} */
122 static inline void
123 save_vfpregs_lo(uint64_t *p)
124 {
125 	__asm __volatile("stc\tp11, c0, [%0], {32}" :: "r" (p) : "memory");
126 }
127 
128 #ifdef CPU_CORTEX
129 /* FLDMD <X>, {d16-d31} */
130 static inline void
131 load_vfpregs_hi(const uint64_t *p)
132 {
133 	__asm __volatile("ldcl\tp11, c0, [%0], {32}" :: "r" (&p[16]) : "memory");
134 }
135 
136 /* FLDMD <X>, {d16-d31} */
137 static inline void
138 save_vfpregs_hi(uint64_t *p)
139 {
140 	__asm __volatile("stcl\tp11, c0, [%0], {32}" :: "r" (&p[16]) : "memory");
141 }
142 #endif
143 
144 static inline void
145 load_vfpregs(const struct vfpreg *fregs)
146 {
147 	load_vfpregs_lo(fregs->vfp_regs);
148 #ifdef CPU_CORTEX
149 #ifdef CPU_ARM11
150 	switch (curcpu()->ci_vfp_id) {
151 	case FPU_VFP_CORTEXA5:
152 	case FPU_VFP_CORTEXA7:
153 	case FPU_VFP_CORTEXA8:
154 	case FPU_VFP_CORTEXA9:
155 #endif
156 		load_vfpregs_hi(fregs->vfp_regs);
157 #ifdef CPU_ARM11
158 		break;
159 	}
160 #endif
161 #endif
162 }
163 
164 static inline void
165 save_vfpregs(struct vfpreg *fregs)
166 {
167 	save_vfpregs_lo(fregs->vfp_regs);
168 #ifdef CPU_CORTEX
169 #ifdef CPU_ARM11
170 	switch (curcpu()->ci_vfp_id) {
171 	case FPU_VFP_CORTEXA5:
172 	case FPU_VFP_CORTEXA7:
173 	case FPU_VFP_CORTEXA8:
174 	case FPU_VFP_CORTEXA9:
175 #endif
176 		save_vfpregs_hi(fregs->vfp_regs);
177 #ifdef CPU_ARM11
178 		break;
179 	}
180 #endif
181 #endif
182 }
183 
184 /* The real handler for VFP bounces.  */
185 static int vfp_handler(u_int, u_int, trapframe_t *, int);
186 #ifdef CPU_CORTEX
187 static int neon_handler(u_int, u_int, trapframe_t *, int);
188 #endif
189 
190 static void vfp_state_load(lwp_t *, u_int);
191 static void vfp_state_save(lwp_t *, u_int);
192 static void vfp_state_release(lwp_t *, u_int);
193 
194 const pcu_ops_t arm_vfp_ops = {
195 	.pcu_id = PCU_FPU,
196 	.pcu_state_save = vfp_state_save,
197 	.pcu_state_load = vfp_state_load,
198 	.pcu_state_release = vfp_state_release,
199 };
200 
201 struct evcnt vfpevent_use;
202 struct evcnt vfpevent_reuse;
203 
204 /*
205  * Used to test for a VFP. The following function is installed as a coproc10
206  * handler on the undefined instruction vector and then we issue a VFP
207  * instruction. If undefined_test is non zero then the VFP did not handle
208  * the instruction so must be absent, or disabled.
209  */
210 
211 static int undefined_test;
212 
213 static int
214 vfp_test(u_int address, u_int insn, trapframe_t *frame, int fault_code)
215 {
216 
217 	frame->tf_pc += INSN_SIZE;
218 	++undefined_test;
219 	return 0;
220 }
221 
222 #endif /* FPU_VFP */
223 
224 struct evcnt vfp_fpscr_ev =
225     EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "VFP", "FPSCR traps");
226 EVCNT_ATTACH_STATIC(vfp_fpscr_ev);
227 
228 static int
229 vfp_fpscr_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
230 {
231 	struct lwp * const l = curlwp;
232 	const u_int regno = (insn >> 12) & 0xf;
233 	/*
234 	 * Only match move to/from the FPSCR register and we
235 	 * can't be using the SP,LR,PC as a source.
236 	 */
237 	if ((insn & 0xffef0fff) != 0xeee10a10 || regno > 12)
238 		return 1;
239 
240 	struct pcb * const pcb = lwp_getpcb(l);
241 
242 #ifdef FPU_VFP
243 	/*
244 	 * If FPU is valid somewhere, let's just reenable VFP and
245 	 * retry the instruction (only safe thing to do since the
246 	 * pcb has a stale copy).
247 	 */
248 	if (pcb->pcb_vfp.vfp_fpexc & VFP_FPEXC_EN)
249 		return 1;
250 #endif
251 
252 	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
253 		l->l_md.md_flags |= MDLWP_VFPUSED;
254 		pcb->pcb_vfp.vfp_fpscr =
255 		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
256 	}
257 
258 	/*
259 	 * We know know the pcb has the saved copy.
260 	 */
261 	register_t * const regp = &frame->tf_r0 + regno;
262 	if (insn & 0x00100000) {
263 		*regp = pcb->pcb_vfp.vfp_fpscr;
264 	} else {
265 		pcb->pcb_vfp.vfp_fpscr = *regp;
266 	}
267 
268 	vfp_fpscr_ev.ev_count++;
269 
270 	frame->tf_pc += INSN_SIZE;
271 	return 0;
272 }
273 
274 #ifndef FPU_VFP
275 /*
276  * If we don't want VFP support, we still need to handle emulating VFP FPSCR
277  * instructions.
278  */
279 void
280 vfp_attach(void)
281 {
282 	install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
283 }
284 
285 #else
286 #if 0
287 static bool
288 vfp_patch_branch(uintptr_t code, uintptr_t func, uintptr_t newfunc)
289 {
290 	for (;; code += sizeof(uint32_t)) {
291 		uint32_t insn = *(uint32_t *)code;
292 		if ((insn & 0xffd08000) == 0xe8908000)	/* ldm ... { pc } */
293 			return false;
294 		if ((insn & 0xfffffff0) == 0xe12fff10)	/* bx rN */
295 			return false;
296 		if ((insn & 0xf1a0f000) == 0xe1a0f000)	/* mov pc, ... */
297 			return false;
298 		if ((insn >> 25) != 0x75)		/* not b/bl insn */
299 			continue;
300 		intptr_t imm26 = ((int32_t)insn << 8) >> 6;
301 		if (code + imm26 + 8 == func) {
302 			int32_t imm24 = (newfunc - (code + 8)) >> 2;
303 			uint32_t new_insn = (insn & 0xff000000)
304 			   | (imm24 & 0xffffff);
305 			KASSERTMSG((uint32_t)((imm24 >> 24) + 1) <= 1, "%x",
306 			    ((imm24 >> 24) + 1));
307 			*(uint32_t *)code = new_insn;
308 			cpu_idcache_wbinv_range(code, sizeof(uint32_t));
309 			return true;
310 		}
311 	}
312 }
313 #endif
314 
315 void
316 vfp_attach(void)
317 {
318 	struct cpu_info * const ci = curcpu();
319 	const char *model = NULL;
320 	bool vfp_p = false;
321 
322 	if (CPU_ID_ARM11_P(curcpu()->ci_arm_cpuid)
323 	    || CPU_ID_CORTEX_P(curcpu()->ci_arm_cpuid)) {
324 		const uint32_t cpacr_vfp = CPACR_CPn(VFP_COPROC);
325 		const uint32_t cpacr_vfp2 = CPACR_CPn(VFP_COPROC2);
326 
327 		/*
328 		 * We first need to enable access to the coprocessors.
329 		 */
330 		uint32_t cpacr = armreg_cpacr_read();
331 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp);
332 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp2);
333 #if 0
334 		if (CPU_ID_CORTEX_P(curcpu()->ci_arm_cpuid)) {
335 			/*
336 			 * Disable access to the upper 16 FP registers and NEON.
337 			 */
338 			cpacr |= CPACR_V7_D32DIS;
339 			cpacr |= CPACR_V7_ASEDIS;
340 		}
341 #endif
342 		armreg_cpacr_write(cpacr);
343 
344 		/*
345 		 * If we could enable them, then they exist.
346 		 */
347 		cpacr = armreg_cpacr_read();
348 		vfp_p = __SHIFTOUT(cpacr, cpacr_vfp2) != CPACR_NOACCESS
349 		    || __SHIFTOUT(cpacr, cpacr_vfp) != CPACR_NOACCESS;
350 	}
351 
352 	void *uh = install_coproc_handler(VFP_COPROC, vfp_test);
353 
354 	undefined_test = 0;
355 
356 	const uint32_t fpsid = read_fpsid();
357 
358 	remove_coproc_handler(uh);
359 
360 	if (undefined_test != 0) {
361 		aprint_normal_dev(ci->ci_dev, "No VFP detected\n");
362 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
363 		ci->ci_vfp_id = 0;
364 		return;
365 	}
366 
367 	ci->ci_vfp_id = fpsid;
368 	switch (fpsid & ~ VFP_FPSID_REV_MSK) {
369 	case FPU_VFP10_ARM10E:
370 		model = "VFP10 R1";
371 		break;
372 	case FPU_VFP11_ARM11:
373 		model = "VFP11";
374 		break;
375 	case FPU_VFP_CORTEXA5:
376 	case FPU_VFP_CORTEXA7:
377 	case FPU_VFP_CORTEXA8:
378 	case FPU_VFP_CORTEXA9:
379 		model = "NEON MPE (VFP 3.0+)";
380 		cpu_neon_present = 1;
381 		break;
382 	default:
383 		aprint_normal_dev(ci->ci_dev, "unrecognized VFP version %x\n",
384 		    fpsid);
385 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
386 		return;
387 	}
388 
389 	cpu_fpu_present = 1;
390 	__asm("mrc p10,7,%0,c7,c0,0" : "=r"(cpu_media_and_vfp_features[0]));
391 	__asm("mrc p10,7,%0,c6,c0,0" : "=r"(cpu_media_and_vfp_features[1]));
392 	if (fpsid != 0) {
393 		aprint_normal("vfp%d at %s: %s\n",
394 		    device_unit(curcpu()->ci_dev), device_xname(curcpu()->ci_dev),
395 		    model);
396 	}
397 	evcnt_attach_dynamic(&vfpevent_use, EVCNT_TYPE_MISC, NULL,
398 	    "VFP", "coproc use");
399 	evcnt_attach_dynamic(&vfpevent_reuse, EVCNT_TYPE_MISC, NULL,
400 	    "VFP", "coproc re-use");
401 	install_coproc_handler(VFP_COPROC, vfp_handler);
402 	install_coproc_handler(VFP_COPROC2, vfp_handler);
403 #ifdef CPU_CORTEX
404 	install_coproc_handler(CORE_UNKNOWN_HANDLER, neon_handler);
405 #endif
406 
407 #if 0
408 	vfp_patch_branch((uintptr_t)pmap_copy_page_generic,
409 	   (uintptr_t)bcopy_page, (uintptr_t)bcopy_page_vfp);
410 	vfp_patch_branch((uintptr_t)pmap_zero_page_generic,
411 	   (uintptr_t)bzero_page, (uintptr_t)bzero_page_vfp);
412 #endif
413 }
414 
415 /* The real handler for VFP bounces.  */
416 static int
417 vfp_handler(u_int address, u_int insn, trapframe_t *frame,
418     int fault_code)
419 {
420 	struct cpu_info * const ci = curcpu();
421 
422 	/* This shouldn't ever happen.  */
423 	if (fault_code != FAULT_USER)
424 		panic("VFP fault at %#x in non-user mode", frame->tf_pc);
425 
426 	if (ci->ci_vfp_id == 0)
427 		/* No VFP detected, just fault.  */
428 		return 1;
429 
430 	/*
431 	 * If we are just changing/fetching FPSCR, don't bother loading it.
432 	 */
433 	if (!vfp_fpscr_handler(address, insn, frame, fault_code))
434 		return 0;
435 
436 	pcu_load(&arm_vfp_ops);
437 
438 	/* Need to restart the faulted instruction.  */
439 //	frame->tf_pc -= INSN_SIZE;
440 	return 0;
441 }
442 
443 #ifdef CPU_CORTEX
444 /* The real handler for NEON bounces.  */
445 static int
446 neon_handler(u_int address, u_int insn, trapframe_t *frame,
447     int fault_code)
448 {
449 	struct cpu_info * const ci = curcpu();
450 
451 	if (ci->ci_vfp_id == 0)
452 		/* No VFP detected, just fault.  */
453 		return 1;
454 
455 	if ((insn & 0xfe000000) != 0xf2000000
456 	    && (insn & 0xfe000000) != 0xf4000000)
457 		/* Not NEON instruction, just fault.  */
458 		return 1;
459 
460 	/* This shouldn't ever happen.  */
461 	if (fault_code != FAULT_USER)
462 		panic("NEON fault in non-user mode");
463 
464 	pcu_load(&arm_vfp_ops);
465 
466 	/* Need to restart the faulted instruction.  */
467 //	frame->tf_pc -= INSN_SIZE;
468 	return 0;
469 }
470 #endif
471 
472 static void
473 vfp_state_load(lwp_t *l, u_int flags)
474 {
475 	struct pcb * const pcb = lwp_getpcb(l);
476 
477 	KASSERT(flags & PCU_ENABLE);
478 
479 	if (flags & PCU_KERNEL) {
480 		if ((flags & PCU_LOADED) == 0) {
481 			pcb->pcb_kernel_vfp.vfp_fpexc = pcb->pcb_vfp.vfp_fpexc;
482 		}
483 		pcb->pcb_vfp.vfp_fpexc = VFP_FPEXC_EN;
484 		write_fpexc(pcb->pcb_vfp.vfp_fpexc);
485 		/*
486 		 * Load the kernel registers (just the first 16) if they've
487 		 * been used..
488 		 */
489 		if (flags & PCU_LOADED) {
490 			load_vfpregs_lo(pcb->pcb_kernel_vfp.vfp_regs);
491 		}
492 		return;
493 	}
494 	struct vfpreg * const fregs = &pcb->pcb_vfp;
495 
496 	/*
497 	 * Instrument VFP usage -- if a process has not previously
498 	 * used the VFP, mark it as having used VFP for the first time,
499 	 * and count this event.
500 	 *
501 	 * If a process has used the VFP, count a "used VFP, and took
502 	 * a trap to use it again" event.
503 	 */
504 	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
505 		vfpevent_use.ev_count++;
506 		l->l_md.md_flags |= MDLWP_VFPUSED;
507 		pcb->pcb_vfp.vfp_fpscr =
508 		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
509 	} else {
510 		vfpevent_reuse.ev_count++;
511 	}
512 
513 	if (fregs->vfp_fpexc & VFP_FPEXC_EN) {
514 		/*
515 		 * If we think the VFP is enabled, it must have be disabled by
516 		 * vfp_state_release for another LWP so we can just restore
517 		 * FPEXC and return since our VFP state is still loaded.
518 		 */
519 		write_fpexc(fregs->vfp_fpexc);
520 		return;
521 	}
522 
523 	/* Load and Enable the VFP (so that we can write the registers).  */
524 	if (flags & PCU_RELOAD) {
525 		uint32_t fpexc = read_fpexc();
526 		KDASSERT((fpexc & VFP_FPEXC_EX) == 0);
527 		write_fpexc(fpexc | VFP_FPEXC_EN);
528 
529 		load_vfpregs(fregs);
530 		write_fpscr(fregs->vfp_fpscr);
531 
532 		if (fregs->vfp_fpexc & VFP_FPEXC_EX) {
533 			struct cpu_info * const ci = curcpu();
534 			/* Need to restore the exception handling state.  */
535 			switch (ci->ci_vfp_id) {
536 			case FPU_VFP10_ARM10E:
537 			case FPU_VFP11_ARM11:
538 			case FPU_VFP_CORTEXA5:
539 			case FPU_VFP_CORTEXA7:
540 			case FPU_VFP_CORTEXA8:
541 			case FPU_VFP_CORTEXA9:
542 				write_fpinst2(fregs->vfp_fpinst2);
543 				write_fpinst(fregs->vfp_fpinst);
544 				break;
545 			default:
546 				panic("%s: Unsupported VFP %#x",
547 				    __func__, ci->ci_vfp_id);
548 			}
549 		}
550 	}
551 
552 	/* Finally, restore the FPEXC but don't enable the VFP. */
553 	fregs->vfp_fpexc |= VFP_FPEXC_EN;
554 	write_fpexc(fregs->vfp_fpexc);
555 }
556 
557 void
558 vfp_state_save(lwp_t *l, u_int flags)
559 {
560 	struct pcb * const pcb = lwp_getpcb(l);
561 	uint32_t fpexc = read_fpexc();
562 	write_fpexc((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
563 
564 	if (flags & PCU_KERNEL) {
565 		/*
566 		 * Save the kernel set of VFP registers.
567 		 * (just the first 16).
568 		 */
569 		save_vfpregs_lo(pcb->pcb_kernel_vfp.vfp_regs);
570 		return;
571 	}
572 
573 	struct vfpreg * const fregs = &pcb->pcb_vfp;
574 
575 	/*
576 	 * Enable the VFP (so we can read the registers).
577 	 * Make sure the exception bit is cleared so that we can
578 	 * safely dump the registers.
579 	 */
580 	fregs->vfp_fpexc = fpexc;
581 	if (fpexc & VFP_FPEXC_EX) {
582 		struct cpu_info * const ci = curcpu();
583 		/* Need to save the exception handling state */
584 		switch (ci->ci_vfp_id) {
585 		case FPU_VFP10_ARM10E:
586 		case FPU_VFP11_ARM11:
587 		case FPU_VFP_CORTEXA5:
588 		case FPU_VFP_CORTEXA7:
589 		case FPU_VFP_CORTEXA8:
590 		case FPU_VFP_CORTEXA9:
591 			fregs->vfp_fpinst = read_fpinst();
592 			fregs->vfp_fpinst2 = read_fpinst2();
593 			break;
594 		default:
595 			panic("%s: Unsupported VFP %#x",
596 			    __func__, ci->ci_vfp_id);
597 		}
598 	}
599 	fregs->vfp_fpscr = read_fpscr();
600 	save_vfpregs(fregs);
601 
602 	/* Disable the VFP.  */
603 	write_fpexc(fpexc);
604 }
605 
606 void
607 vfp_state_release(lwp_t *l, u_int flags)
608 {
609 	struct pcb * const pcb = lwp_getpcb(l);
610 
611 	if (flags & PCU_KERNEL) {
612 		/*
613 		 * Restore the FPEXC since we borrowed that field.
614 		 */
615 		pcb->pcb_vfp.vfp_fpexc = pcb->pcb_kernel_vfp.vfp_fpexc;
616 	} else {
617 		/*
618 		 * Now mark the VFP as disabled (and our state
619 		 * has been already saved or is being discarded).
620 		 */
621 		pcb->pcb_vfp.vfp_fpexc &= ~VFP_FPEXC_EN;
622 	}
623 
624 	/*
625 	 * Turn off the FPU so the next time a VFP instruction is issued
626 	 * an exception happens.  We don't know if this LWP's state was
627 	 * loaded but if we turned off the FPU for some other LWP, when
628 	 * pcu_load invokes vfp_state_load it will see that VFP_FPEXC_EN
629 	 * is still set so it just restore fpexc and return since its
630 	 * contents are still sitting in the VFP.
631 	 */
632 	write_fpexc(read_fpexc() & ~VFP_FPEXC_EN);
633 }
634 
635 void
636 vfp_savecontext(void)
637 {
638 	pcu_save(&arm_vfp_ops);
639 }
640 
641 void
642 vfp_discardcontext(void)
643 {
644 	pcu_discard(&arm_vfp_ops);
645 }
646 
647 void
648 vfp_kernel_acquire(void)
649 {
650 	if (__predict_false(cpu_intr_p())) {
651 		write_fpexc(VFP_FPEXC_EN);
652 		if (curcpu()->ci_data.cpu_pcu_curlwp[PCU_FPU] != NULL) {
653 			lwp_t * const l = curlwp;
654 			struct pcb * const pcb = lwp_getpcb(l);
655 			KASSERT((l->l_md.md_flags & MDLWP_VFPINTR) == 0);
656 			l->l_md.md_flags |= MDLWP_VFPINTR;
657 			save_vfpregs_lo(&pcb->pcb_kernel_vfp.vfp_regs[16]);
658 		}
659 	} else {
660 		pcu_kernel_acquire(&arm_vfp_ops);
661 	}
662 }
663 
664 void
665 vfp_kernel_release(void)
666 {
667 	if (__predict_false(cpu_intr_p())) {
668 		uint32_t fpexc = 0;
669 		if (curcpu()->ci_data.cpu_pcu_curlwp[PCU_FPU] != NULL) {
670 			lwp_t * const l = curlwp;
671 			struct pcb * const pcb = lwp_getpcb(l);
672 			KASSERT(l->l_md.md_flags & MDLWP_VFPINTR);
673 			load_vfpregs_lo(&pcb->pcb_kernel_vfp.vfp_regs[16]);
674 			l->l_md.md_flags &= ~MDLWP_VFPINTR;
675 			fpexc = pcb->pcb_vfp.vfp_fpexc;
676 		}
677 		write_fpexc(fpexc);
678 	} else {
679 		pcu_kernel_release(&arm_vfp_ops);
680 	}
681 }
682 
683 void
684 vfp_getcontext(struct lwp *l, mcontext_t *mcp, int *flagsp)
685 {
686 	if (l->l_md.md_flags & MDLWP_VFPUSED) {
687 		const struct pcb * const pcb = lwp_getpcb(l);
688 		pcu_save(&arm_vfp_ops);
689 		mcp->__fpu.__vfpregs.__vfp_fpscr = pcb->pcb_vfp.vfp_fpscr;
690 		memcpy(mcp->__fpu.__vfpregs.__vfp_fstmx, pcb->pcb_vfp.vfp_regs,
691 		    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
692 		*flagsp |= _UC_FPU|_UC_ARM_VFP;
693 	}
694 }
695 
696 void
697 vfp_setcontext(struct lwp *l, const mcontext_t *mcp)
698 {
699 	pcu_discard(&arm_vfp_ops);
700 	struct pcb * const pcb = lwp_getpcb(l);
701 	l->l_md.md_flags |= MDLWP_VFPUSED;
702 	pcb->pcb_vfp.vfp_fpscr = mcp->__fpu.__vfpregs.__vfp_fpscr;
703 	memcpy(pcb->pcb_vfp.vfp_regs, mcp->__fpu.__vfpregs.__vfp_fstmx,
704 	    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
705 }
706 
707 #endif /* FPU_VFP */
708