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