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