Home | History | Annotate | Line # | Download | only in vfp
vfp_init.c revision 1.8
      1 /*      $NetBSD: vfp_init.c,v 1.8 2012/12/05 19:05:46 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 /*
     45  * Use generic co-processor instructions to avoid assembly problems.
     46  */
     47 
     48 /* FMRX <X>, fpsid */
     49 static inline uint32_t
     50 read_fpsid(void)
     51 {
     52 	uint32_t rv;
     53 	__asm __volatile("mrc p10, 7, %0, c0, c0, 0" : "=r" (rv));
     54 	return rv;
     55 }
     56 
     57 /* FMRX <X>, fpexc */
     58 static inline uint32_t
     59 read_fpscr(void)
     60 {
     61 	uint32_t rv;
     62 	__asm __volatile("mrc p10, 7, %0, c1, c0, 0" : "=r" (rv));
     63 	return rv;
     64 }
     65 
     66 /* FMRX <X>, fpexc */
     67 static inline uint32_t
     68 read_fpexc(void)
     69 {
     70 	uint32_t rv;
     71 	__asm __volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (rv));
     72 	return rv;
     73 }
     74 
     75 /* FMRX <X>, fpinst */
     76 static inline uint32_t
     77 read_fpinst(void)
     78 {
     79 	uint32_t rv;
     80 	__asm __volatile("mrc p10, 7, %0, c9, c0, 0" : "=r" (rv));
     81 	return rv;
     82 }
     83 
     84 /* FMRX <X>, fpinst2 */
     85 static inline uint32_t
     86 read_fpinst2(void)
     87 {
     88 	uint32_t rv;
     89 	__asm __volatile("mrc p10, 7, %0, c10, c0, 0" : "=r" (rv));
     90 	return rv;
     91 }
     92 
     93 /* FSTMD <X>, {d0-d15} */
     94 #define save_vfpregs(X)	__asm __volatile("stc p11, c0, [%0], {32}" : \
     95 			    : "r" (X) : "memory")
     96 
     97 /* FMXR <X>, fpscr */
     98 #define write_fpscr(X)	__asm __volatile("mcr p10, 7, %0, c1, c0, 0" : \
     99 			    : "r" (X))
    100 /* FMXR <X>, fpexc */
    101 #define write_fpexc(X)	__asm __volatile("mcr p10, 7, %0, c8, c0, 0" : \
    102 			    : "r" (X))
    103 /* FMXR <X>, fpinst */
    104 #define write_fpinst(X)	__asm __volatile("mcr p10, 7, %0, c9, c0, 0" : \
    105 			    : "r" (X))
    106 /* FMXR <X>, fpinst2 */
    107 #define write_fpinst2(X) __asm __volatile("mcr p10, 7, %0, c10, c0, 0" : \
    108 			    : "r" (X))
    109 /* FLDMD <X>, {d0-d15} */
    110 #define load_vfpregs(X)	__asm __volatile("ldc p11, c0, [%0], {32}" : \
    111 			    : "r" (X) : "memory");
    112 
    113 #ifdef FPU_VFP
    114 
    115 /* The real handler for VFP bounces.  */
    116 static int vfp_handler(u_int, u_int, trapframe_t *, int);
    117 static int vfp_handler(u_int, u_int, trapframe_t *, int);
    118 
    119 static void vfp_state_load(lwp_t *, bool);
    120 static void vfp_state_save(lwp_t *);
    121 static void vfp_state_release(lwp_t *);
    122 
    123 const pcu_ops_t arm_vfp_ops = {
    124 	.pcu_id = PCU_FPU,
    125 	.pcu_state_load = vfp_state_load,
    126 	.pcu_state_save = vfp_state_save,
    127 	.pcu_state_release = vfp_state_release,
    128 };
    129 
    130 struct evcnt vfpevent_use;
    131 struct evcnt vfpevent_reuse;
    132 
    133 /*
    134  * Used to test for a VFP. The following function is installed as a coproc10
    135  * handler on the undefined instruction vector and then we issue a VFP
    136  * instruction. If undefined_test is non zero then the VFP did not handle
    137  * the instruction so must be absent, or disabled.
    138  */
    139 
    140 static int undefined_test;
    141 
    142 static int
    143 vfp_test(u_int address, u_int insn, trapframe_t *frame, int fault_code)
    144 {
    145 
    146 	frame->tf_pc += INSN_SIZE;
    147 	++undefined_test;
    148 	return 0;
    149 }
    150 
    151 #endif /* FPU_VFP */
    152 
    153 struct evcnt vfp_fpscr_ev =
    154     EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "VFP", "FPSCR traps");
    155 EVCNT_ATTACH_STATIC(vfp_fpscr_ev);
    156 
    157 static int
    158 vfp_fpscr_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
    159 {
    160 	struct lwp * const l = curlwp;
    161 	const u_int regno = (insn >> 12) & 0xf;
    162 	/*
    163 	 * Only match move to/from the FPSCR register and we
    164 	 * can't be using the SP,LR,PC as a source.
    165 	 */
    166 	if ((insn & 0xffef0fff) != 0xeee10a10 || regno > 12)
    167 		return 1;
    168 
    169 	struct pcb * const pcb = lwp_getpcb(l);
    170 
    171 #ifdef FPU_VFP
    172 	/*
    173 	 * If FPU is valid somewhere, let's just reenable VFP and
    174 	 * retry the instruction (only safe thing to do since the
    175 	 * pcb has a stale copy).
    176 	 */
    177 	if (pcb->pcb_vfp.vfp_fpexc & VFP_FPEXC_EN)
    178 		return 1;
    179 #endif
    180 
    181 	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
    182 		l->l_md.md_flags |= MDLWP_VFPUSED;
    183 		pcb->pcb_vfp.vfp_fpscr =
    184 		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
    185 	}
    186 
    187 	/*
    188 	 * We know know the pcb has the saved copy.
    189 	 */
    190 	register_t * const regp = &frame->tf_r0 + regno;
    191 	if (insn & 0x00100000) {
    192 		*regp = pcb->pcb_vfp.vfp_fpscr;
    193 	} else {
    194 		pcb->pcb_vfp.vfp_fpscr = *regp;
    195 	}
    196 
    197 	vfp_fpscr_ev.ev_count++;
    198 
    199 	frame->tf_pc += INSN_SIZE;
    200 	return 0;
    201 }
    202 
    203 #ifndef FPU_VFP
    204 /*
    205  * If we don't want VFP support, we still need to handle emulating VFP FPSCR
    206  * instructions.
    207  */
    208 void
    209 vfp_attach(void)
    210 {
    211 	install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
    212 }
    213 
    214 #else
    215 void
    216 vfp_attach(void)
    217 {
    218 	struct cpu_info * const ci = curcpu();
    219 	const char *model = NULL;
    220 	bool vfp_p = false;
    221 
    222 #ifdef FPU_VFP
    223 	if (CPU_ID_ARM11_P(curcpu()->ci_arm_cpuid)
    224 	    || CPU_ID_CORTEX_P(curcpu()->ci_arm_cpuid)) {
    225 		const uint32_t cpacr_vfp = CPACR_CPn(VFP_COPROC);
    226 		const uint32_t cpacr_vfp2 = CPACR_CPn(VFP_COPROC2);
    227 
    228 		/*
    229 		 * We first need to enable access to the coprocessors.
    230 		 */
    231 		uint32_t cpacr = armreg_cpacr_read();
    232 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp);
    233 		cpacr |= __SHIFTIN(CPACR_ALL, cpacr_vfp2);
    234 		armreg_cpacr_write(cpacr);
    235 
    236 		/*
    237 		 * If we could enable them, then they exist.
    238 		 */
    239 		cpacr = armreg_cpacr_read();
    240 		vfp_p = __SHIFTOUT(cpacr, cpacr_vfp2) != CPACR_NOACCESS
    241 		    || __SHIFTOUT(cpacr, cpacr_vfp) != CPACR_NOACCESS;
    242 	}
    243 #endif
    244 
    245 	void *uh = install_coproc_handler(VFP_COPROC, vfp_test);
    246 
    247 	undefined_test = 0;
    248 
    249 	const uint32_t fpsid = read_fpsid();
    250 
    251 	remove_coproc_handler(uh);
    252 
    253 	if (undefined_test != 0) {
    254 		aprint_normal_dev(ci->ci_dev, "No VFP detected\n");
    255 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
    256 		ci->ci_vfp_id = 0;
    257 		return;
    258 	}
    259 
    260 	ci->ci_vfp_id = fpsid;
    261 	switch (fpsid & ~ VFP_FPSID_REV_MSK) {
    262 	case FPU_VFP10_ARM10E:
    263 		model = "VFP10 R1";
    264 		break;
    265 	case FPU_VFP11_ARM11:
    266 		model = "VFP11";
    267 		break;
    268 	case FPU_VFP_CORTEXA5:
    269 	case FPU_VFP_CORTEXA7:
    270 	case FPU_VFP_CORTEXA8:
    271 	case FPU_VFP_CORTEXA9:
    272 		model = "NEON MPE (VFP 3.0+)";
    273 		break;
    274 	default:
    275 		aprint_normal_dev(ci->ci_dev, "unrecognized VFP version %x\n",
    276 		    fpsid);
    277 		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
    278 		return;
    279 	}
    280 
    281 	if (fpsid != 0) {
    282 		aprint_normal("vfp%d at %s: %s\n",
    283 		    device_unit(curcpu()->ci_dev), device_xname(curcpu()->ci_dev),
    284 		    model);
    285 	}
    286 	evcnt_attach_dynamic(&vfpevent_use, EVCNT_TYPE_MISC, NULL,
    287 	    "VFP", "proc use");
    288 	evcnt_attach_dynamic(&vfpevent_reuse, EVCNT_TYPE_MISC, NULL,
    289 	    "VFP", "proc re-use");
    290 	install_coproc_handler(VFP_COPROC, vfp_handler);
    291 	install_coproc_handler(VFP_COPROC2, vfp_handler);
    292 }
    293 
    294 /* The real handler for VFP bounces.  */
    295 static int
    296 vfp_handler(u_int address, u_int insn, trapframe_t *frame,
    297     int fault_code)
    298 {
    299 	struct cpu_info * const ci = curcpu();
    300 
    301 	/* This shouldn't ever happen.  */
    302 	if (fault_code != FAULT_USER)
    303 		panic("VFP fault in non-user mode");
    304 
    305 	if (ci->ci_vfp_id == 0)
    306 		/* No VFP detected, just fault.  */
    307 		return 1;
    308 
    309 	/*
    310 	 * If we are just changing/fetching FPSCR, don't bother loading it.
    311 	 */
    312 	if (!vfp_fpscr_handler(address, insn, frame, fault_code))
    313 		return 0;
    314 
    315 	pcu_load(&arm_vfp_ops);
    316 
    317 	/* Need to restart the faulted instruction.  */
    318 //	frame->tf_pc -= INSN_SIZE;
    319 	return 0;
    320 }
    321 
    322 static void
    323 vfp_state_load(lwp_t *l, bool used)
    324 {
    325 	struct pcb * const pcb = lwp_getpcb(l);
    326 	struct vfpreg * const fregs = &pcb->pcb_vfp;
    327 
    328 	/*
    329 	 * Instrument VFP usage -- if a process has not previously
    330 	 * used the VFP, mark it as having used VFP for the first time,
    331 	 * and count this event.
    332 	 *
    333 	 * If a process has used the VFP, count a "used VFP, and took
    334 	 * a trap to use it again" event.
    335 	 */
    336 	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
    337 		vfpevent_use.ev_count++;
    338 		l->l_md.md_flags |= MDLWP_VFPUSED;
    339 		pcb->pcb_vfp.vfp_fpscr =
    340 		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
    341 	} else {
    342 		vfpevent_reuse.ev_count++;
    343 	}
    344 
    345 	if (fregs->vfp_fpexc & VFP_FPEXC_EN) {
    346 		/*
    347 		 * If we think the VFP is enabled, it must have be disabled by
    348 		 * vfp_state_release for another LWP so we can just restore
    349 		 * FPEXC and return since our VFP state is still loaded.
    350 		 */
    351 		write_fpexc(fregs->vfp_fpexc);
    352 		return;
    353 	}
    354 
    355 	/* Enable the VFP (so that we can write the registers).  */
    356 	uint32_t fpexc = read_fpexc();
    357 	KDASSERT((fpexc & VFP_FPEXC_EX) == 0);
    358 	write_fpexc(fpexc | VFP_FPEXC_EN);
    359 
    360 	load_vfpregs(fregs->vfp_regs);
    361 	write_fpscr(fregs->vfp_fpscr);
    362 
    363 	if (fregs->vfp_fpexc & VFP_FPEXC_EX) {
    364 		struct cpu_info * const ci = curcpu();
    365 		/* Need to restore the exception handling state.  */
    366 		switch (ci->ci_vfp_id) {
    367 		case FPU_VFP10_ARM10E:
    368 		case FPU_VFP11_ARM11:
    369 		case FPU_VFP_CORTEXA5:
    370 		case FPU_VFP_CORTEXA7:
    371 		case FPU_VFP_CORTEXA8:
    372 		case FPU_VFP_CORTEXA9:
    373 			write_fpinst2(fregs->vfp_fpinst2);
    374 			write_fpinst(fregs->vfp_fpinst);
    375 			break;
    376 		default:
    377 			panic("%s: Unsupported VFP %#x",
    378 			    __func__, ci->ci_vfp_id);
    379 		}
    380 	}
    381 
    382 	/* Finally, restore the FPEXC but don't enable the VFP. */
    383 	fregs->vfp_fpexc |= VFP_FPEXC_EN;
    384 	write_fpexc(fregs->vfp_fpexc);
    385 }
    386 
    387 void
    388 vfp_state_save(lwp_t *l)
    389 {
    390 	struct pcb * const pcb = lwp_getpcb(l);
    391 	struct vfpreg * const fregs = &pcb->pcb_vfp;
    392 
    393 	/*
    394 	 * If it's already disabled, then the state has been saved
    395 	 * (or discarded).
    396 	 */
    397 	if ((fregs->vfp_fpexc & VFP_FPEXC_EN) == 0)
    398 		return;
    399 
    400 	/*
    401 	 * Enable the VFP (so we can read the registers).
    402 	 * Make sure the exception bit is cleared so that we can
    403 	 * safely dump the registers.
    404 	 */
    405 	uint32_t fpexc = read_fpexc();
    406 	write_fpexc((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
    407 
    408 	fregs->vfp_fpexc = fpexc;
    409 	if (fpexc & VFP_FPEXC_EX) {
    410 		struct cpu_info * const ci = curcpu();
    411 		/* Need to save the exception handling state */
    412 		switch (ci->ci_vfp_id) {
    413 		case FPU_VFP10_ARM10E:
    414 		case FPU_VFP11_ARM11:
    415 		case FPU_VFP_CORTEXA5:
    416 		case FPU_VFP_CORTEXA7:
    417 		case FPU_VFP_CORTEXA8:
    418 		case FPU_VFP_CORTEXA9:
    419 			fregs->vfp_fpinst = read_fpinst();
    420 			fregs->vfp_fpinst2 = read_fpinst2();
    421 			break;
    422 		default:
    423 			panic("%s: Unsupported VFP %#x",
    424 			    __func__, ci->ci_vfp_id);
    425 		}
    426 	}
    427 	fregs->vfp_fpscr = read_fpscr();
    428 	save_vfpregs(fregs->vfp_regs);
    429 
    430 	/* Disable the VFP.  */
    431 	write_fpexc(fpexc);
    432 }
    433 
    434 void
    435 vfp_state_release(lwp_t *l)
    436 {
    437 	struct pcb * const pcb = lwp_getpcb(l);
    438 
    439 	/*
    440 	 * Now mark the VFP as disabled (and our state has been already
    441 	 * saved or is being discarded).
    442 	 */
    443 	pcb->pcb_vfp.vfp_fpexc &= ~VFP_FPEXC_EN;
    444 
    445 	/*
    446 	 * Turn off the FPU so the next time a VFP instruction is issued
    447 	 * an exception happens.  We don't know if this LWP's state was
    448 	 * loaded but if we turned off the FPU for some other LWP, when
    449 	 * pcu_load invokes vfp_state_load it will see that VFP_FPEXC_EN
    450 	 * is still set so it just restroe fpexc and return since its
    451 	 * contents are still sitting in the VFP.
    452 	 */
    453 	write_fpexc(read_fpexc() & ~VFP_FPEXC_EN);
    454 }
    455 
    456 void
    457 vfp_savecontext(void)
    458 {
    459 	pcu_save(&arm_vfp_ops);
    460 }
    461 
    462 void
    463 vfp_discardcontext(void)
    464 {
    465 	pcu_discard(&arm_vfp_ops);
    466 }
    467 
    468 void
    469 vfp_getcontext(struct lwp *l, mcontext_t *mcp, int *flagsp)
    470 {
    471 	if (l->l_md.md_flags & MDLWP_VFPUSED) {
    472 		const struct pcb * const pcb = lwp_getpcb(l);
    473 		pcu_save(&arm_vfp_ops);
    474 		mcp->__fpu.__vfpregs.__vfp_fpscr = pcb->pcb_vfp.vfp_fpscr;
    475 		memcpy(mcp->__fpu.__vfpregs.__vfp_fstmx, pcb->pcb_vfp.vfp_regs,
    476 		    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
    477 		*flagsp |= _UC_FPU;
    478 	}
    479 }
    480 
    481 void
    482 vfp_setcontext(struct lwp *l, const mcontext_t *mcp)
    483 {
    484 	pcu_discard(&arm_vfp_ops);
    485 	struct pcb * const pcb = lwp_getpcb(l);
    486 	l->l_md.md_flags |= MDLWP_VFPUSED;
    487 	pcb->pcb_vfp.vfp_fpscr = mcp->__fpu.__vfpregs.__vfp_fpscr;
    488 	memcpy(pcb->pcb_vfp.vfp_regs, mcp->__fpu.__vfpregs.__vfp_fstmx,
    489 	    sizeof(mcp->__fpu.__vfpregs.__vfp_fstmx));
    490 }
    491 
    492 #endif /* FPU_VFP */
    493