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