1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (C) 1996 Wolfgang Solfrank. 5 * Copyright (C) 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $ 34 */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include <sys/param.h> 40 #include <sys/proc.h> 41 #include <sys/systm.h> 42 #include <sys/limits.h> 43 44 #include <machine/fpu.h> 45 #include <machine/pcb.h> 46 #include <machine/psl.h> 47 48 static void 49 save_fpu_int(struct thread *td) 50 { 51 register_t msr; 52 struct pcb *pcb; 53 54 pcb = td->td_pcb; 55 56 /* 57 * Temporarily re-enable floating-point during the save 58 */ 59 msr = mfmsr(); 60 if (pcb->pcb_flags & PCB_VSX) 61 mtmsr(msr | PSL_FP | PSL_VSX); 62 else 63 mtmsr(msr | PSL_FP); 64 65 /* 66 * Save the floating-point registers and FPSCR to the PCB 67 */ 68 if (pcb->pcb_flags & PCB_VSX) { 69 #define SFP(n) __asm ("stxvw4x " #n ", 0,%0" \ 70 :: "b"(&pcb->pcb_fpu.fpr[n])); 71 SFP(0); SFP(1); SFP(2); SFP(3); 72 SFP(4); SFP(5); SFP(6); SFP(7); 73 SFP(8); SFP(9); SFP(10); SFP(11); 74 SFP(12); SFP(13); SFP(14); SFP(15); 75 SFP(16); SFP(17); SFP(18); SFP(19); 76 SFP(20); SFP(21); SFP(22); SFP(23); 77 SFP(24); SFP(25); SFP(26); SFP(27); 78 SFP(28); SFP(29); SFP(30); SFP(31); 79 #undef SFP 80 } else { 81 #define SFP(n) __asm ("stfd " #n ", 0(%0)" \ 82 :: "b"(&pcb->pcb_fpu.fpr[n].fpr)); 83 SFP(0); SFP(1); SFP(2); SFP(3); 84 SFP(4); SFP(5); SFP(6); SFP(7); 85 SFP(8); SFP(9); SFP(10); SFP(11); 86 SFP(12); SFP(13); SFP(14); SFP(15); 87 SFP(16); SFP(17); SFP(18); SFP(19); 88 SFP(20); SFP(21); SFP(22); SFP(23); 89 SFP(24); SFP(25); SFP(26); SFP(27); 90 SFP(28); SFP(29); SFP(30); SFP(31); 91 #undef SFP 92 } 93 __asm __volatile ("mffs 0; stfd 0,0(%0)" :: "b"(&pcb->pcb_fpu.fpscr)); 94 95 /* 96 * Disable floating-point again 97 */ 98 isync(); 99 mtmsr(msr); 100 } 101 102 void 103 enable_fpu(struct thread *td) 104 { 105 register_t msr; 106 struct pcb *pcb; 107 struct trapframe *tf; 108 109 pcb = td->td_pcb; 110 tf = trapframe(td); 111 112 /* 113 * Save the thread's FPU CPU number, and set the CPU's current 114 * FPU thread 115 */ 116 td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); 117 PCPU_SET(fputhread, td); 118 119 /* 120 * Enable the FPU for when the thread returns from the exception. 121 * If this is the first time the FPU has been used by the thread, 122 * initialise the FPU registers and FPSCR to 0, and set the flag 123 * to indicate that the FPU is in use. 124 */ 125 pcb->pcb_flags |= PCB_FPU; 126 if (pcb->pcb_flags & PCB_VSX) 127 tf->srr1 |= PSL_FP | PSL_VSX; 128 else 129 tf->srr1 |= PSL_FP; 130 if (!(pcb->pcb_flags & PCB_FPREGS)) { 131 memset(&pcb->pcb_fpu, 0, sizeof pcb->pcb_fpu); 132 pcb->pcb_flags |= PCB_FPREGS; 133 } 134 135 /* 136 * Temporarily enable floating-point so the registers 137 * can be restored. 138 */ 139 msr = mfmsr(); 140 if (pcb->pcb_flags & PCB_VSX) 141 mtmsr(msr | PSL_FP | PSL_VSX); 142 else 143 mtmsr(msr | PSL_FP); 144 145 /* 146 * Load the floating point registers and FPSCR from the PCB. 147 * (A value of 0xff for mtfsf specifies that all 8 4-bit fields 148 * of the saved FPSCR are to be loaded from the FPU reg). 149 */ 150 __asm __volatile ("lfd 0,0(%0); mtfsf 0xff,0" 151 :: "b"(&pcb->pcb_fpu.fpscr)); 152 153 if (pcb->pcb_flags & PCB_VSX) { 154 #define LFP(n) __asm ("lxvw4x " #n ", 0,%0" \ 155 :: "b"(&pcb->pcb_fpu.fpr[n])); 156 LFP(0); LFP(1); LFP(2); LFP(3); 157 LFP(4); LFP(5); LFP(6); LFP(7); 158 LFP(8); LFP(9); LFP(10); LFP(11); 159 LFP(12); LFP(13); LFP(14); LFP(15); 160 LFP(16); LFP(17); LFP(18); LFP(19); 161 LFP(20); LFP(21); LFP(22); LFP(23); 162 LFP(24); LFP(25); LFP(26); LFP(27); 163 LFP(28); LFP(29); LFP(30); LFP(31); 164 #undef LFP 165 } else { 166 #define LFP(n) __asm ("lfd " #n ", 0(%0)" \ 167 :: "b"(&pcb->pcb_fpu.fpr[n].fpr)); 168 LFP(0); LFP(1); LFP(2); LFP(3); 169 LFP(4); LFP(5); LFP(6); LFP(7); 170 LFP(8); LFP(9); LFP(10); LFP(11); 171 LFP(12); LFP(13); LFP(14); LFP(15); 172 LFP(16); LFP(17); LFP(18); LFP(19); 173 LFP(20); LFP(21); LFP(22); LFP(23); 174 LFP(24); LFP(25); LFP(26); LFP(27); 175 LFP(28); LFP(29); LFP(30); LFP(31); 176 #undef LFP 177 } 178 179 isync(); 180 mtmsr(msr); 181 } 182 183 void 184 save_fpu(struct thread *td) 185 { 186 struct pcb *pcb; 187 188 pcb = td->td_pcb; 189 190 save_fpu_int(td); 191 192 /* 193 * Clear the current fp thread and pcb's CPU id 194 * XXX should this be left clear to allow lazy save/restore ? 195 */ 196 pcb->pcb_fpcpu = INT_MAX; 197 PCPU_SET(fputhread, NULL); 198 } 199 200 /* 201 * Save fpu state without dropping ownership. This will only save state if 202 * the current fpu thread is `td'. 203 */ 204 void 205 save_fpu_nodrop(struct thread *td) 206 { 207 208 if (td == PCPU_GET(fputhread)) 209 save_fpu_int(td); 210 } 211 212 /* 213 * Clear Floating-Point Status and Control Register 214 */ 215 void 216 cleanup_fpscr() 217 { 218 register_t msr; 219 220 msr = mfmsr(); 221 mtmsr(msr | PSL_FP); 222 mtfsf(0); 223 224 isync(); 225 mtmsr(msr); 226 } 227 228 /* 229 * Get the current fp exception 230 */ 231 u_int 232 get_fpu_exception(struct thread *td) 233 { 234 register_t msr; 235 u_int ucode; 236 register_t reg; 237 238 critical_enter(); 239 240 msr = mfmsr(); 241 mtmsr(msr | PSL_FP); 242 243 reg = mffs(); 244 245 isync(); 246 mtmsr(msr); 247 248 critical_exit(); 249 250 if (reg & FPSCR_ZX) 251 ucode = FPE_FLTDIV; 252 else if (reg & FPSCR_OX) 253 ucode = FPE_FLTOVF; 254 else if (reg & FPSCR_UX) 255 ucode = FPE_FLTUND; 256 else if (reg & FPSCR_XX) 257 ucode = FPE_FLTRES; 258 else 259 ucode = FPE_FLTINV; 260 261 return ucode; 262 } 263 264