1 /* $NetBSD: mips_fpu.c,v 1.17 2021/05/29 12:35:27 simonb Exp $ */
2
3 /*-
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mips_fpu.c,v 1.17 2021/05/29 12:35:27 simonb Exp $");
34
35 #include <sys/param.h>
36 #include <sys/mutex.h>
37 #include <sys/condvar.h>
38 #include <sys/cpu.h>
39 #include <sys/proc.h>
40 #include <sys/lwp.h>
41 #include <sys/pcu.h>
42
43 #include <mips/locore.h>
44 #include <mips/regnum.h>
45 #include <mips/pcb.h>
46
47 static void mips_fpu_state_save(lwp_t *);
48 static void mips_fpu_state_load(lwp_t *, u_int);
49 static void mips_fpu_state_release(lwp_t *);
50
51 const pcu_ops_t mips_fpu_ops = {
52 .pcu_id = PCU_FPU,
53 .pcu_state_save = mips_fpu_state_save,
54 .pcu_state_load = mips_fpu_state_load,
55 .pcu_state_release = mips_fpu_state_release
56 };
57
58 void
fpu_discard(lwp_t * l)59 fpu_discard(lwp_t *l)
60 {
61 pcu_discard(&mips_fpu_ops, l, false);
62 }
63
64 void
fpu_load(void)65 fpu_load(void)
66 {
67 pcu_load(&mips_fpu_ops);
68 }
69
70 void
fpu_save(lwp_t * l)71 fpu_save(lwp_t *l)
72 {
73 pcu_save(&mips_fpu_ops, l);
74 }
75
76 bool
fpu_used_p(const lwp_t * l)77 fpu_used_p(const lwp_t *l)
78 {
79 return pcu_valid_p(&mips_fpu_ops, l);
80 }
81
82 static void
mips_fpu_state_save(lwp_t * l)83 mips_fpu_state_save(lwp_t *l)
84 {
85 struct trapframe * const tf = l->l_md.md_utf;
86 #ifndef __mips_soft_float
87 struct pcb * const pcb = lwp_getpcb(l);
88 mips_fpreg_t * const fp = pcb->pcb_fpregs.r_regs;
89 #endif
90 uint32_t status, fpcsr;
91
92 /*
93 * Don't do anything if the FPU is already off.
94 */
95 if ((tf->tf_regs[_R_SR] & MIPS_SR_COP_1_BIT) == 0)
96 return;
97
98 l->l_cpu->ci_ev_fpu_saves.ev_count++;
99
100 /*
101 * enable COP1 to read FPCSR register.
102 * interrupts remain on.
103 */
104 __asm volatile (
105 ".set noreorder" "\n\t"
106 ".set noat" "\n\t"
107 "mfc0 %0, $%3" "\n\t"
108 "mtc0 %2, $%3" "\n\t"
109 ___STRING(COP0_HAZARD_FPUENABLE)
110 "cfc1 %1, $31" "\n\t"
111 "cfc1 %1, $31" "\n\t"
112 ".set at" "\n\t"
113 ".set reorder" "\n\t"
114 : "=&r" (status), "=r"(fpcsr)
115 : "r"(tf->tf_regs[_R_SR] & (MIPS_SR_COP_1_BIT|MIPS3_SR_FR|MIPS_SR_KX|MIPS_SR_INT_IE)),
116 "n"(MIPS_COP_0_STATUS));
117
118 /*
119 * save FPCSR and FP register values.
120 */
121 #if !defined(__mips_soft_float)
122 #if !defined(__mips_o32)
123 if (tf->tf_regs[_R_SR] & MIPS3_SR_FR) {
124 KASSERT(_MIPS_SIM_NEWABI_P(l->l_proc->p_md.md_abi));
125 fp[32] = fpcsr;
126 __asm volatile (
127 ".set noreorder ;"
128 "sdc1 $f0, (0*%d1)(%0) ;"
129 "sdc1 $f1, (1*%d1)(%0) ;"
130 "sdc1 $f2, (2*%d1)(%0) ;"
131 "sdc1 $f3, (3*%d1)(%0) ;"
132 "sdc1 $f4, (4*%d1)(%0) ;"
133 "sdc1 $f5, (5*%d1)(%0) ;"
134 "sdc1 $f6, (6*%d1)(%0) ;"
135 "sdc1 $f7, (7*%d1)(%0) ;"
136 "sdc1 $f8, (8*%d1)(%0) ;"
137 "sdc1 $f9, (9*%d1)(%0) ;"
138 "sdc1 $f10, (10*%d1)(%0) ;"
139 "sdc1 $f11, (11*%d1)(%0) ;"
140 "sdc1 $f12, (12*%d1)(%0) ;"
141 "sdc1 $f13, (13*%d1)(%0) ;"
142 "sdc1 $f14, (14*%d1)(%0) ;"
143 "sdc1 $f15, (15*%d1)(%0) ;"
144 "sdc1 $f16, (16*%d1)(%0) ;"
145 "sdc1 $f17, (17*%d1)(%0) ;"
146 "sdc1 $f18, (18*%d1)(%0) ;"
147 "sdc1 $f19, (19*%d1)(%0) ;"
148 "sdc1 $f20, (20*%d1)(%0) ;"
149 "sdc1 $f21, (21*%d1)(%0) ;"
150 "sdc1 $f22, (22*%d1)(%0) ;"
151 "sdc1 $f23, (23*%d1)(%0) ;"
152 "sdc1 $f24, (24*%d1)(%0) ;"
153 "sdc1 $f25, (25*%d1)(%0) ;"
154 "sdc1 $f26, (26*%d1)(%0) ;"
155 "sdc1 $f27, (27*%d1)(%0) ;"
156 "sdc1 $f28, (28*%d1)(%0) ;"
157 "sdc1 $f29, (29*%d1)(%0) ;"
158 "sdc1 $f30, (30*%d1)(%0) ;"
159 "sdc1 $f31, (31*%d1)(%0) ;"
160 ".set reorder" :: "r"(fp), "i"(sizeof(fp[0])));
161 } else
162 #endif /* !defined(__mips_o32) */
163 {
164 KASSERT(!_MIPS_SIM_NEWABI_P(l->l_proc->p_md.md_abi));
165 ((int *)fp)[32] = fpcsr;
166 __asm volatile (
167 ".set noreorder ;"
168 "swc1 $f0, (0*%d1)(%0) ;"
169 "swc1 $f1, (1*%d1)(%0) ;"
170 "swc1 $f2, (2*%d1)(%0) ;"
171 "swc1 $f3, (3*%d1)(%0) ;"
172 "swc1 $f4, (4*%d1)(%0) ;"
173 "swc1 $f5, (5*%d1)(%0) ;"
174 "swc1 $f6, (6*%d1)(%0) ;"
175 "swc1 $f7, (7*%d1)(%0) ;"
176 "swc1 $f8, (8*%d1)(%0) ;"
177 "swc1 $f9, (9*%d1)(%0) ;"
178 "swc1 $f10, (10*%d1)(%0) ;"
179 "swc1 $f11, (11*%d1)(%0) ;"
180 "swc1 $f12, (12*%d1)(%0) ;"
181 "swc1 $f13, (13*%d1)(%0) ;"
182 "swc1 $f14, (14*%d1)(%0) ;"
183 "swc1 $f15, (15*%d1)(%0) ;"
184 "swc1 $f16, (16*%d1)(%0) ;"
185 "swc1 $f17, (17*%d1)(%0) ;"
186 "swc1 $f18, (18*%d1)(%0) ;"
187 "swc1 $f19, (19*%d1)(%0) ;"
188 "swc1 $f20, (20*%d1)(%0) ;"
189 "swc1 $f21, (21*%d1)(%0) ;"
190 "swc1 $f22, (22*%d1)(%0) ;"
191 "swc1 $f23, (23*%d1)(%0) ;"
192 "swc1 $f24, (24*%d1)(%0) ;"
193 "swc1 $f25, (25*%d1)(%0) ;"
194 "swc1 $f26, (26*%d1)(%0) ;"
195 "swc1 $f27, (27*%d1)(%0) ;"
196 "swc1 $f28, (28*%d1)(%0) ;"
197 "swc1 $f29, (29*%d1)(%0) ;"
198 "swc1 $f30, (30*%d1)(%0) ;"
199 "swc1 $f31, (31*%d1)(%0) ;"
200 ".set reorder" :: "r"(fp), "i"(4));
201 }
202 #endif
203 /*
204 * stop COP1
205 */
206 __asm volatile ("mtc0 %0, $%1" :: "r"(status), "n"(MIPS_COP_0_STATUS));
207 }
208
209 static void
mips_fpu_state_load(lwp_t * l,u_int flags)210 mips_fpu_state_load(lwp_t *l, u_int flags)
211 {
212 struct trapframe * const tf = l->l_md.md_utf;
213 struct pcb * const pcb = lwp_getpcb(l);
214 #ifndef __mips_soft_float
215 mips_fpreg_t * const fp = pcb->pcb_fpregs.r_regs;
216 #endif
217 uint32_t status;
218 uint32_t fpcsr;
219
220 l->l_cpu->ci_ev_fpu_loads.ev_count++;
221
222 /*
223 * If this is the first time the state is being loaded, zero it first.
224 */
225 if (__predict_false((flags & PCU_VALID) == 0)) {
226 memset(&pcb->pcb_fpregs, 0, sizeof(pcb->pcb_fpregs));
227 }
228
229 /*
230 * Enable the FP when this lwp return to userspace.
231 */
232 tf->tf_regs[_R_SR] |= MIPS_SR_COP_1_BIT;
233
234 /*
235 * enabling COP1 to load FP registers. Interrupts will remain on.
236 */
237 __asm volatile(
238 ".set noreorder" "\n\t"
239 ".set noat" "\n\t"
240 "mfc0 %0, $%2" "\n\t"
241 "mtc0 %1, $%2" "\n\t"
242 ___STRING(COP0_HAZARD_FPUENABLE)
243 ".set at" "\n\t"
244 ".set reorder" "\n\t"
245 : "=&r"(status)
246 : "r"(tf->tf_regs[_R_SR] & (MIPS_SR_COP_1_BIT|MIPS3_SR_FR|MIPS_SR_KX|MIPS_SR_INT_IE)), "n"(MIPS_COP_0_STATUS));
247
248 /*
249 * load FP registers and establish processes' FP context.
250 */
251 #if !defined(__mips_soft_float)
252 #if !defined(__mips_o32)
253 if (tf->tf_regs[_R_SR] & MIPS3_SR_FR) {
254 KASSERT(_MIPS_SIM_NEWABI_P(l->l_proc->p_md.md_abi));
255 __asm volatile (
256 ".set noreorder ;"
257 "ldc1 $f0, (0*%d1)(%0) ;"
258 "ldc1 $f1, (1*%d1)(%0) ;"
259 "ldc1 $f2, (2*%d1)(%0) ;"
260 "ldc1 $f3, (3*%d1)(%0) ;"
261 "ldc1 $f4, (4*%d1)(%0) ;"
262 "ldc1 $f5, (5*%d1)(%0) ;"
263 "ldc1 $f6, (6*%d1)(%0) ;"
264 "ldc1 $f7, (7*%d1)(%0) ;"
265 "ldc1 $f8, (8*%d1)(%0) ;"
266 "ldc1 $f9, (9*%d1)(%0) ;"
267 "ldc1 $f10, (10*%d1)(%0) ;"
268 "ldc1 $f11, (11*%d1)(%0) ;"
269 "ldc1 $f12, (12*%d1)(%0) ;"
270 "ldc1 $f13, (13*%d1)(%0) ;"
271 "ldc1 $f14, (14*%d1)(%0) ;"
272 "ldc1 $f15, (15*%d1)(%0) ;"
273 "ldc1 $f16, (16*%d1)(%0) ;"
274 "ldc1 $f17, (17*%d1)(%0) ;"
275 "ldc1 $f18, (18*%d1)(%0) ;"
276 "ldc1 $f19, (19*%d1)(%0) ;"
277 "ldc1 $f20, (20*%d1)(%0) ;"
278 "ldc1 $f21, (21*%d1)(%0) ;"
279 "ldc1 $f22, (22*%d1)(%0) ;"
280 "ldc1 $f23, (23*%d1)(%0) ;"
281 "ldc1 $f24, (24*%d1)(%0) ;"
282 "ldc1 $f25, (25*%d1)(%0) ;"
283 "ldc1 $f26, (26*%d1)(%0) ;"
284 "ldc1 $f27, (27*%d1)(%0) ;"
285 "ldc1 $f28, (28*%d1)(%0) ;"
286 "ldc1 $f29, (29*%d1)(%0) ;"
287 "ldc1 $f30, (30*%d1)(%0) ;"
288 "ldc1 $f31, (31*%d1)(%0) ;"
289 ".set reorder" :: "r"(fp), "i"(sizeof(fp[0])));
290 fpcsr = fp[32];
291 } else
292 #endif
293 {
294 KASSERT(!_MIPS_SIM_NEWABI_P(l->l_proc->p_md.md_abi));
295 __asm volatile (
296 ".set noreorder ;"
297 "lwc1 $f0, (0*%d1)(%0) ;"
298 "lwc1 $f1, (1*%d1)(%0) ;"
299 "lwc1 $f2, (2*%d1)(%0) ;"
300 "lwc1 $f3, (3*%d1)(%0) ;"
301 "lwc1 $f4, (4*%d1)(%0) ;"
302 "lwc1 $f5, (5*%d1)(%0) ;"
303 "lwc1 $f6, (6*%d1)(%0) ;"
304 "lwc1 $f7, (7*%d1)(%0) ;"
305 "lwc1 $f8, (8*%d1)(%0) ;"
306 "lwc1 $f9, (9*%d1)(%0) ;"
307 "lwc1 $f10, (10*%d1)(%0) ;"
308 "lwc1 $f11, (11*%d1)(%0) ;"
309 "lwc1 $f12, (12*%d1)(%0) ;"
310 "lwc1 $f13, (13*%d1)(%0) ;"
311 "lwc1 $f14, (14*%d1)(%0) ;"
312 "lwc1 $f15, (15*%d1)(%0) ;"
313 "lwc1 $f16, (16*%d1)(%0) ;"
314 "lwc1 $f17, (17*%d1)(%0) ;"
315 "lwc1 $f18, (18*%d1)(%0) ;"
316 "lwc1 $f19, (19*%d1)(%0) ;"
317 "lwc1 $f20, (20*%d1)(%0) ;"
318 "lwc1 $f21, (21*%d1)(%0) ;"
319 "lwc1 $f22, (22*%d1)(%0) ;"
320 "lwc1 $f23, (23*%d1)(%0) ;"
321 "lwc1 $f24, (24*%d1)(%0) ;"
322 "lwc1 $f25, (25*%d1)(%0) ;"
323 "lwc1 $f26, (26*%d1)(%0) ;"
324 "lwc1 $f27, (27*%d1)(%0) ;"
325 "lwc1 $f28, (28*%d1)(%0) ;"
326 "lwc1 $f29, (29*%d1)(%0) ;"
327 "lwc1 $f30, (30*%d1)(%0) ;"
328 "lwc1 $f31, (31*%d1)(%0) ;"
329 ".set reorder"
330 :
331 : "r"(fp), "i"(4));
332 fpcsr = ((int *)fp)[32];
333 }
334 #else
335 fpcsr = 0;
336 #endif
337
338 /*
339 * Mask off the exception bits in the FPCSR, load the FPCSR
340 * and stop COP1 again
341 */
342 fpcsr &= ~MIPS_FCSR_CAUSE;
343 __asm volatile(
344 ".set noreorder" "\n\t"
345 ".set noat" "\n\t"
346 "ctc1 %0, $31" "\n\t"
347 "nop" "\n\t" /* XXX: Hack */
348 "mtc0 %1, $%2" "\n\t"
349 ".set at" "\n\t"
350 ".set reorder" "\n\t"
351 :: "r"(fpcsr), "r"(status),
352 "n"(MIPS_COP_0_STATUS));
353 }
354
355 static void
mips_fpu_state_release(lwp_t * l)356 mips_fpu_state_release(lwp_t *l)
357 {
358 l->l_md.md_utf->tf_regs[_R_SR] &= ~MIPS_SR_COP_1_BIT;
359 }
360