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