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 59 fpu_discard(lwp_t *l) 60 { 61 pcu_discard(&mips_fpu_ops, l, false); 62 } 63 64 void 65 fpu_load(void) 66 { 67 pcu_load(&mips_fpu_ops); 68 } 69 70 void 71 fpu_save(lwp_t *l) 72 { 73 pcu_save(&mips_fpu_ops, l); 74 } 75 76 bool 77 fpu_used_p(const lwp_t *l) 78 { 79 return pcu_valid_p(&mips_fpu_ops, l); 80 } 81 82 static void 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 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 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