2 * Copyright (c) 2012 Mark Tinguely
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
35 #include <sys/kernel.h>
37 #include <machine/frame.h>
38 #include <machine/fp.h>
39 #include <machine/pcb.h>
40 #include <machine/undefined.h>
41 #include <machine/vfp.h>
43 /* function prototypes */
44 unsigned int get_coprocessorACR(void);
45 int vfp_bounce(u_int, u_int, struct trapframe *, int);
46 void vfp_discard(void);
47 void vfp_enable(void);
48 void vfp_restore(struct vfp_state *);
49 void vfp_store(struct vfp_state *);
50 void set_coprocessorACR(u_int);
52 extern int vfp_exists;
53 static struct undefined_handler vfp10_uh, vfp11_uh;
54 /* If true the VFP unit has 32 double registers, otherwise it has 16 */
57 /* The VFMXR command using coprocessor commands */
58 #define fmxr(reg, val) \
59 __asm __volatile("mcr p10, 7, %0, " __STRING(reg) " , c0, 0" :: "r"(val));
61 /* The VFMRX command using coprocessor commands */
64 __asm __volatile("mrc p10, 7, %0, " __STRING(reg) " , c0, 0" : "=r"(val));\
69 get_coprocessorACR(void)
72 __asm __volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val) : : "cc");
77 set_coprocessorACR(u_int val)
79 __asm __volatile("mcr p15, 0, %0, c1, c0, 2\n\t"
80 : : "r" (val) : "cc");
85 /* called for each cpu */
89 u_int fpsid, fpexc, tmp;
90 u_int coproc, vfp_arch;
92 coproc = get_coprocessorACR();
93 coproc |= COPROC10 | COPROC11;
94 set_coprocessorACR(coproc);
96 fpsid = fmrx(VFPSID); /* read the vfp system id */
97 fpexc = fmrx(VFPEXC); /* read the vfp exception reg */
99 if (!(fpsid & VFPSID_HARDSOFT_IMP)) {
102 PCPU_SET(vfpsid, fpsid); /* save the VFPSID */
105 (fpsid & VFPSID_SUBVERSION2_MASK) >> VFPSID_SUBVERSION_OFF;
107 if (vfp_arch >= VFP_ARCH3) {
109 PCPU_SET(vfpmvfr0, tmp);
111 if ((tmp & VMVFR0_RB_MASK) == 2)
115 PCPU_SET(vfpmvfr1, tmp);
118 /* initialize the coprocess 10 and 11 calls
119 * These are called to restore the registers and enable
122 if (vfp10_uh.uh_handler == NULL) {
123 vfp10_uh.uh_handler = vfp_bounce;
124 vfp11_uh.uh_handler = vfp_bounce;
125 install_coproc_handler_static(10, &vfp10_uh);
126 install_coproc_handler_static(11, &vfp11_uh);
131 SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, vfp_init, NULL);
134 /* start VFP unit, restore the vfp registers from the PCB and retry
138 vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
142 struct thread *vfptd;
145 return 1; /* vfp does not exist */
146 fpexc = fmrx(VFPEXC); /* read the vfp exception reg */
147 if (fpexc & VFPEXC_EN) {
148 vfptd = PCPU_GET(vfpcthread);
149 /* did the kernel call the vfp or exception that expect us
150 * to emulate the command. Newer hardware does not require
151 * emulation, so we don't emulate yet.
154 /* don't save if newer registers are on another processor */
155 if (vfptd /* && (vfptd == curthread) */ &&
156 (vfptd->td_pcb->pcb_vfpcpu == PCPU_GET(cpu)))
158 /* someone did not save their registers, */
159 if (vfptd /* && (vfptd == curthread) */)
161 vfp_store(&vfptd->td_pcb->pcb_vfpstate);
164 fmxr(VFPEXC, fpexc); /* turn vfp hardware off */
165 if (vfptd == curthread) {
166 /* kill the process - we do not handle emulation */
167 killproc(curthread->td_proc, "vfp emulation");
170 /* should not happen. someone did not save their context */
171 printf("vfp_bounce: vfpcthread: %p curthread: %p\n",
175 fmxr(VFPEXC, fpexc); /* enable the vfp and repeat command */
176 curpcb = PCPU_GET(curpcb);
177 /* If we were the last process to use the VFP, the process did not
178 * use a VFP on another processor, then the registers in the VFP
179 * will still be ours and are current. Eventually, we will make the
182 vfp_restore(&curpcb->pcb_vfpstate);
184 curpcb->pcb_vfpcpu = PCPU_GET(cpu);
186 PCPU_SET(vfpcthread, PCPU_GET(curthread));
190 /* vfs_store is called from from a VFP command to restore the registers and
191 * turn on the VFP hardware.
192 * Eventually we will use the information that this process was the last
193 * to use the VFP hardware and bypass the restore, just turn on the hardware.
196 vfp_restore(struct vfp_state *vfpsave)
201 * Work around an issue with GCC where the asm it generates is
202 * not unified syntax and fails to assemble because it expects
203 * the ldcleq instruction in the form ldc<c>l, not in the UAL
204 * form ldcl<c>, and similar for stcleq.
207 #define ldclne "ldclne"
208 #define stclne "stclne"
210 #define ldclne "ldcnel"
211 #define stclne "stcnel"
214 __asm __volatile("ldc p10, c0, [%1], #128\n" /* d0-d15 */
215 "cmp %2, #0\n" /* -D16 or -D32? */
216 ldclne" p11, c0, [%1], #128\n" /* d16-d31 */
217 "addeq %1, %1, #128\n" /* skip missing regs */
218 "ldr %0, [%1]\n" /* set old vfpscr */
219 "mcr p10, 7, %0, cr1, c0, 0\n"
220 : "=&r" (vfpscr) : "r" (vfpsave), "r" (is_d32) : "cc");
221 PCPU_SET(vfpcthread, PCPU_GET(curthread));
225 /* vfs_store is called from switch to save the vfp hardware registers
226 * into the pcb before switching to another process.
227 * we already know that the new process is different from this old
228 * process and that this process last used the VFP registers.
229 * Below we check to see if the VFP has been enabled since the last
231 * This routine will exit with the VFP turned off. The next VFP user
232 * will trap to restore its registers and turn on the VFP hardware.
235 vfp_store(struct vfp_state *vfpsave)
237 u_int tmp, vfpscr = 0;
239 tmp = fmrx(VFPEXC); /* Is the vfp enabled? */
240 if (vfpsave && tmp & VFPEXC_EN) {
241 __asm __volatile("stc p11, c0, [%1], #128\n" /* d0-d15 */
242 "cmp %2, #0\n" /* -D16 or -D32? */
243 stclne" p11, c0, [%1], #128\n" /* d16-d31 */
244 "addeq %1, %1, #128\n" /* skip missing regs */
245 "mrc p10, 7, %0, cr1, c0, 0\n" /* fmxr(VFPSCR) */
246 "str %0, [%1]\n" /* save vfpscr */
247 : "=&r" (vfpscr) : "r" (vfpsave), "r" (is_d32) : "cc");
253 /* eventually we will use this information for UP also */
254 PCPU_SET(vfpcthread, 0);
256 tmp &= ~VFPEXC_EN; /* disable the vfp hardware */
260 /* discard the registers at cpu_thread_free() when fpcurthread == td.
261 * Turn off the VFP hardware.
268 PCPU_SET(vfpcthread, 0); /* permanent forget about reg */
270 tmp &= ~VFPEXC_EN; /* turn off VFP hardware */
274 /* Enable the VFP hardware without restoring registers.
275 * Called when the registers are still in the VFP unit