1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Floating point configuration. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/regset.h> 32 #include <sys/privregs.h> 33 #include <sys/x86_archext.h> 34 #include <sys/archsystm.h> 35 #include <sys/fp.h> 36 #include <sys/cmn_err.h> 37 38 #define XMM_ALIGN 16 39 40 /* 41 * If fpu_exists is non-zero, fpu_probe will attempt to use any 42 * hardware FPU (subject to other constraints, see below). If 43 * fpu_exists is zero, fpu_probe will report that there is no 44 * FPU even if there is one. 45 */ 46 int fpu_exists = 1; 47 48 int fp_kind = FP_387; 49 50 /* 51 * Mechanism to save FPU state. 52 */ 53 #if defined(__amd64) 54 int fp_save_mech = FP_FXSAVE; 55 #elif defined(__i386) 56 int fp_save_mech = FP_FNSAVE; 57 #endif 58 59 /* 60 * The variable fpu_ignored is provided to allow other code to 61 * determine whether emulation is being done because there is 62 * no FPU or because of an override requested via /etc/system. 63 */ 64 int fpu_ignored = 0; 65 66 /* 67 * Used by ppcopy and ppzero to determine whether or not to use the 68 * SSE-based pagecopy and pagezero routines 69 */ 70 int use_sse_pagecopy = 0; 71 int use_sse_pagezero = 0; 72 int use_sse_copy = 0; 73 74 #if defined(__i386) 75 76 /* 77 * The variable fpu_pentium_fdivbug is provided to allow other code to 78 * determine whether the system contains a Pentium with the FDIV problem. 79 */ 80 int fpu_pentium_fdivbug = 0; 81 82 #endif 83 84 #if defined(__xpv) 85 86 /* 87 * Use of SSE or otherwise is forcibly configured for us by the hypervisor. 88 */ 89 90 #define ENABLE_SSE() 91 #define DISABLE_SSE() 92 93 #else /* __xpv */ 94 95 #define ENABLE_SSE() setcr4(CR4_ENABLE_SSE_FLAGS(getcr4())) 96 #define DISABLE_SSE() setcr4(CR4_DISABLE_SSE_FLAGS(getcr4())) 97 98 #endif /* __xpv */ 99 100 /* 101 * Try and figure out what kind of FP capabilities we have, and 102 * set up the control registers accordingly. 103 */ 104 void 105 fpu_probe(void) 106 { 107 do { 108 if (fpu_initial_probe() != 0) 109 continue; 110 111 if (fpu_exists == 0) { 112 fpu_ignored = 1; 113 continue; 114 } 115 116 #if defined(__i386) 117 fpu_pentium_fdivbug = fpu_probe_pentium_fdivbug(); 118 /* 119 * The test does some real floating point operations. 120 * Reset it back to previous state. 121 */ 122 (void) fpu_initial_probe(); 123 124 if (fpu_pentium_fdivbug != 0) { 125 fpu_ignored = 1; 126 continue; 127 } 128 #endif 129 130 #ifndef __xpv 131 /* 132 * Check and see if the fpu is present by looking 133 * at the "extension type" bit. (While this used to 134 * indicate a 387DX coprocessor in days gone by, 135 * it's forced on by modern implementations for 136 * compatibility.) 137 */ 138 if ((getcr0() & CR0_ET) == 0) 139 continue; 140 #endif 141 142 #if defined(__amd64) 143 /* 144 * SSE and SSE2 are required for the 64-bit ABI. 145 * 146 * If they're not present, we can in principal run 147 * 32-bit userland, though 64-bit processes will be hosed. 148 * 149 * (Perhaps we should complain more about this case!) 150 */ 151 if (is_x86_feature(x86_featureset, X86FSET_SSE) && 152 is_x86_feature(x86_featureset, X86FSET_SSE2)) { 153 fp_kind |= __FP_SSE; 154 ENABLE_SSE(); 155 156 if (is_x86_feature(x86_featureset, X86FSET_AVX)) { 157 ASSERT(is_x86_feature(x86_featureset, 158 X86FSET_XSAVE)); 159 fp_kind |= __FP_AVX; 160 } 161 162 if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) { 163 fp_save_mech = FP_XSAVE; 164 fpsave_ctxt = xsave_ctxt; 165 patch_xsave(); 166 } 167 } 168 #elif defined(__i386) 169 /* 170 * SSE and SSE2 are both optional, and we patch kernel 171 * code to exploit it when present. 172 */ 173 if (is_x86_feature(x86_featureset, X86FSET_SSE)) { 174 fp_kind |= __FP_SSE; 175 ENABLE_SSE(); 176 fp_save_mech = FP_FXSAVE; 177 fpsave_ctxt = fpxsave_ctxt; 178 179 if (is_x86_feature(x86_featureset, X86FSET_SSE2)) { 180 patch_sse2(); 181 } 182 183 if (is_x86_feature(x86_featureset, X86FSET_AVX)) { 184 ASSERT(is_x86_feature(x86_featureset, 185 X86FSET_XSAVE)); 186 fp_kind |= __FP_AVX; 187 } 188 189 if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) { 190 fp_save_mech = FP_XSAVE; 191 fpsave_ctxt = xsave_ctxt; 192 patch_xsave(); 193 } else { 194 patch_sse(); /* use fxrstor */ 195 } 196 } else { 197 remove_x86_feature(x86_featureset, X86FSET_SSE2); 198 /* 199 * We will not likely to have a chip with AVX but not 200 * SSE. But to be safe we disable AVX if SSE is not 201 * enabled. 202 */ 203 remove_x86_feature(x86_featureset, X86FSET_AVX); 204 /* 205 * (Just in case the BIOS decided we wanted SSE 206 * enabled when we didn't. See 4965674.) 207 */ 208 DISABLE_SSE(); 209 } 210 #endif 211 if (is_x86_feature(x86_featureset, X86FSET_SSE2)) { 212 use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1; 213 } 214 215 if (fp_kind & __FP_SSE) { 216 struct fxsave_state *fx; 217 uint8_t fxsave_state[sizeof (struct fxsave_state) + 218 XMM_ALIGN]; 219 220 /* 221 * Extract the mxcsr mask from our first fxsave 222 */ 223 fx = (void *)(((uintptr_t)(&fxsave_state[0]) + 224 XMM_ALIGN) & ~(XMM_ALIGN - 1ul)); 225 226 fx->fx_mxcsr_mask = 0; 227 fxsave_insn(fx); 228 if (fx->fx_mxcsr_mask != 0) { 229 /* 230 * Override default mask initialized in fpu.c 231 */ 232 sse_mxcsr_mask = fx->fx_mxcsr_mask; 233 } 234 } 235 236 setcr0(CR0_ENABLE_FPU_FLAGS(getcr0())); 237 return; 238 /*CONSTANTCONDITION*/ 239 } while (0); 240 241 /* 242 * No FPU hardware present 243 */ 244 setcr0(CR0_DISABLE_FPU_FLAGS(getcr0())); 245 DISABLE_SSE(); 246 fp_kind = FP_NO; 247 fpu_exists = 0; 248 } 249