xref: /netbsd/sys/arch/mips/mips/mips_fpu.c (revision 0360f0a4)
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