Home | History | Annotate | Line # | Download | only in mips
      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