1 /* 2 * Mesa 3-D graphics library 3 * Version: 6.5.1 4 * 5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 /** 26 * \file common_x86.c 27 * 28 * Check CPU capabilities & initialize optimized funtions for this particular 29 * processor. 30 * 31 * Changed by Andre Werthmann for using the new SSE functions. 32 * 33 * \author Holger Waechtler <holger (at) akaflieg.extern.tu-berlin.de> 34 * \author Andre Werthmann <wertmann (at) cs.uni-potsdam.de> 35 */ 36 37 /* XXX these includes should probably go into imports.h or glheader.h */ 38 #if defined(USE_SSE_ASM) && defined(__linux__) 39 #include <linux/version.h> 40 #endif 41 #if defined(USE_SSE_ASM) && defined(__FreeBSD__) 42 #include <sys/types.h> 43 #include <sys/sysctl.h> 44 #endif 45 #if defined(USE_SSE_ASM) && defined(__OpenBSD__) 46 #include <sys/param.h> 47 #include <sys/sysctl.h> 48 #include <machine/cpu.h> 49 #endif 50 51 #include "main/imports.h" 52 #include "common_x86_asm.h" 53 54 55 /** Bitmask of X86_FEATURE_x bits */ 56 int _mesa_x86_cpu_features = 0x0; 57 58 static int detection_debug = GL_FALSE; 59 60 /* No reason for this to be public. 61 */ 62 extern GLuint _ASMAPI _mesa_x86_has_cpuid(void); 63 extern void _ASMAPI _mesa_x86_cpuid(GLuint op, GLuint *reg_eax, GLuint *reg_ebx, GLuint *reg_ecx, GLuint *reg_edx); 64 extern GLuint _ASMAPI _mesa_x86_cpuid_eax(GLuint op); 65 extern GLuint _ASMAPI _mesa_x86_cpuid_ebx(GLuint op); 66 extern GLuint _ASMAPI _mesa_x86_cpuid_ecx(GLuint op); 67 extern GLuint _ASMAPI _mesa_x86_cpuid_edx(GLuint op); 68 69 70 #if defined(USE_SSE_ASM) 71 /* 72 * We must verify that the Streaming SIMD Extensions are truly supported 73 * on this processor before we go ahead and hook out the optimized code. 74 * 75 * However, I have been told by Alan Cox that all 2.4 (and later) Linux 76 * kernels provide full SSE support on all processors that expose SSE via 77 * the CPUID mechanism. 78 */ 79 80 /* These are assembly functions: */ 81 extern void _mesa_test_os_sse_support( void ); 82 extern void _mesa_test_os_sse_exception_support( void ); 83 84 85 #if defined(WIN32) 86 #ifndef STATUS_FLOAT_MULTIPLE_TRAPS 87 # define STATUS_FLOAT_MULTIPLE_TRAPS (0xC00002B5L) 88 #endif 89 static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS exp) 90 { 91 PEXCEPTION_RECORD rec = exp->ExceptionRecord; 92 PCONTEXT ctx = exp->ContextRecord; 93 94 if ( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION ) { 95 _mesa_debug(NULL, "EXCEPTION_ILLEGAL_INSTRUCTION\n" ); 96 _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM); 97 } else if ( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS ) { 98 _mesa_debug(NULL, "STATUS_FLOAT_MULTIPLE_TRAPS\n"); 99 /* Windows seems to clear the exception flag itself, we just have to increment Eip */ 100 } else { 101 _mesa_debug(NULL, "UNEXPECTED EXCEPTION (0x%08x), terminating!\n" ); 102 return EXCEPTION_EXECUTE_HANDLER; 103 } 104 105 if ( (ctx->ContextFlags & CONTEXT_CONTROL) != CONTEXT_CONTROL ) { 106 _mesa_debug(NULL, "Context does not contain control registers, terminating!\n"); 107 return EXCEPTION_EXECUTE_HANDLER; 108 } 109 ctx->Eip += 3; 110 111 return EXCEPTION_CONTINUE_EXECUTION; 112 } 113 #endif /* WIN32 */ 114 115 116 /** 117 * Check if SSE is supported. 118 * If not, turn off the X86_FEATURE_XMM flag in _mesa_x86_cpu_features. 119 */ 120 void _mesa_check_os_sse_support( void ) 121 { 122 #if defined(__FreeBSD__) 123 { 124 int ret, enabled; 125 unsigned int len; 126 len = sizeof(enabled); 127 ret = sysctlbyname("hw.instruction_sse", &enabled, &len, NULL, 0); 128 if (ret || !enabled) 129 _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM); 130 } 131 #elif defined (__NetBSD__) 132 { 133 int ret, enabled; 134 size_t len = sizeof(enabled); 135 ret = sysctlbyname("machdep.sse", &enabled, &len, (void *)NULL, 0); 136 if (ret || !enabled) 137 _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM); 138 } 139 #elif defined(__OpenBSD__) 140 { 141 int mib[2]; 142 int ret, enabled; 143 size_t len = sizeof(enabled); 144 145 mib[0] = CTL_MACHDEP; 146 mib[1] = CPU_SSE; 147 148 ret = sysctl(mib, 2, &enabled, &len, NULL, 0); 149 if (ret || !enabled) 150 _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM); 151 } 152 #elif defined(WIN32) 153 LPTOP_LEVEL_EXCEPTION_FILTER oldFilter; 154 155 /* Install our ExceptionFilter */ 156 oldFilter = SetUnhandledExceptionFilter( ExceptionFilter ); 157 158 if ( cpu_has_xmm ) { 159 _mesa_debug(NULL, "Testing OS support for SSE...\n"); 160 161 _mesa_test_os_sse_support(); 162 163 if ( cpu_has_xmm ) { 164 _mesa_debug(NULL, "Yes.\n"); 165 } else { 166 _mesa_debug(NULL, "No!\n"); 167 } 168 } 169 170 if ( cpu_has_xmm ) { 171 _mesa_debug(NULL, "Testing OS support for SSE unmasked exceptions...\n"); 172 173 _mesa_test_os_sse_exception_support(); 174 175 if ( cpu_has_xmm ) { 176 _mesa_debug(NULL, "Yes.\n"); 177 } else { 178 _mesa_debug(NULL, "No!\n"); 179 } 180 } 181 182 /* Restore previous exception filter */ 183 SetUnhandledExceptionFilter( oldFilter ); 184 185 if ( cpu_has_xmm ) { 186 _mesa_debug(NULL, "Tests of OS support for SSE passed.\n"); 187 } else { 188 _mesa_debug(NULL, "Tests of OS support for SSE failed!\n"); 189 } 190 #else 191 /* Do nothing on other platforms for now. 192 */ 193 if (detection_debug) 194 _mesa_debug(NULL, "Not testing OS support for SSE, leaving enabled.\n"); 195 #endif /* __FreeBSD__ */ 196 } 197 198 #endif /* USE_SSE_ASM */ 199 200 201 /** 202 * Initialize the _mesa_x86_cpu_features bitfield. 203 * This is a no-op if called more than once. 204 */ 205 void 206 _mesa_get_x86_features(void) 207 { 208 static int called = 0; 209 210 if (called) 211 return; 212 213 called = 1; 214 215 #ifdef USE_X86_ASM 216 _mesa_x86_cpu_features = 0x0; 217 218 if (_mesa_getenv( "MESA_NO_ASM")) { 219 return; 220 } 221 222 if (!_mesa_x86_has_cpuid()) { 223 _mesa_debug(NULL, "CPUID not detected\n"); 224 } 225 else { 226 GLuint cpu_features; 227 GLuint cpu_ext_features; 228 GLuint cpu_ext_info; 229 char cpu_vendor[13]; 230 GLuint result; 231 232 /* get vendor name */ 233 _mesa_x86_cpuid(0, &result, (GLuint *)(cpu_vendor + 0), (GLuint *)(cpu_vendor + 8), (GLuint *)(cpu_vendor + 4)); 234 cpu_vendor[12] = '\0'; 235 236 if (detection_debug) 237 _mesa_debug(NULL, "CPU vendor: %s\n", cpu_vendor); 238 239 /* get cpu features */ 240 cpu_features = _mesa_x86_cpuid_edx(1); 241 242 if (cpu_features & X86_CPU_FPU) 243 _mesa_x86_cpu_features |= X86_FEATURE_FPU; 244 if (cpu_features & X86_CPU_CMOV) 245 _mesa_x86_cpu_features |= X86_FEATURE_CMOV; 246 247 #ifdef USE_MMX_ASM 248 if (cpu_features & X86_CPU_MMX) 249 _mesa_x86_cpu_features |= X86_FEATURE_MMX; 250 #endif 251 252 #ifdef USE_SSE_ASM 253 if (cpu_features & X86_CPU_XMM) 254 _mesa_x86_cpu_features |= X86_FEATURE_XMM; 255 if (cpu_features & X86_CPU_XMM2) 256 _mesa_x86_cpu_features |= X86_FEATURE_XMM2; 257 #endif 258 259 /* query extended cpu features */ 260 if ((cpu_ext_info = _mesa_x86_cpuid_eax(0x80000000)) > 0x80000000) { 261 if (cpu_ext_info >= 0x80000001) { 262 263 cpu_ext_features = _mesa_x86_cpuid_edx(0x80000001); 264 265 if (cpu_features & X86_CPU_MMX) { 266 267 #ifdef USE_3DNOW_ASM 268 if (cpu_ext_features & X86_CPUEXT_3DNOW) 269 _mesa_x86_cpu_features |= X86_FEATURE_3DNOW; 270 if (cpu_ext_features & X86_CPUEXT_3DNOW_EXT) 271 _mesa_x86_cpu_features |= X86_FEATURE_3DNOWEXT; 272 #endif 273 274 #ifdef USE_MMX_ASM 275 if (cpu_ext_features & X86_CPUEXT_MMX_EXT) 276 _mesa_x86_cpu_features |= X86_FEATURE_MMXEXT; 277 #endif 278 } 279 } 280 281 /* query cpu name */ 282 if (cpu_ext_info >= 0x80000002) { 283 GLuint ofs; 284 char cpu_name[49]; 285 for (ofs = 0; ofs < 3; ofs++) 286 _mesa_x86_cpuid(0x80000002+ofs, (GLuint *)(cpu_name + (16*ofs)+0), (GLuint *)(cpu_name + (16*ofs)+4), (GLuint *)(cpu_name + (16*ofs)+8), (GLuint *)(cpu_name + (16*ofs)+12)); 287 cpu_name[48] = '\0'; /* the name should be NULL terminated, but just to be sure */ 288 289 if (detection_debug) 290 _mesa_debug(NULL, "CPU name: %s\n", cpu_name); 291 } 292 } 293 294 } 295 296 #ifdef USE_MMX_ASM 297 if ( cpu_has_mmx ) { 298 if ( _mesa_getenv( "MESA_NO_MMX" ) == 0 ) { 299 if (detection_debug) 300 _mesa_debug(NULL, "MMX cpu detected.\n"); 301 } else { 302 _mesa_x86_cpu_features &= ~(X86_FEATURE_MMX); 303 } 304 } 305 #endif 306 307 #ifdef USE_3DNOW_ASM 308 if ( cpu_has_3dnow ) { 309 if ( _mesa_getenv( "MESA_NO_3DNOW" ) == 0 ) { 310 if (detection_debug) 311 _mesa_debug(NULL, "3DNow! cpu detected.\n"); 312 } else { 313 _mesa_x86_cpu_features &= ~(X86_FEATURE_3DNOW); 314 } 315 } 316 #endif 317 318 #ifdef USE_SSE_ASM 319 if ( cpu_has_xmm ) { 320 if ( _mesa_getenv( "MESA_NO_SSE" ) == 0 ) { 321 if (detection_debug) 322 _mesa_debug(NULL, "SSE cpu detected.\n"); 323 if ( _mesa_getenv( "MESA_FORCE_SSE" ) == 0 ) { 324 _mesa_check_os_sse_support(); 325 } 326 } else { 327 _mesa_debug(NULL, "SSE cpu detected, but switched off by user.\n"); 328 _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM); 329 } 330 } 331 #endif 332 333 #endif /* USE_X86_ASM */ 334 335 (void) detection_debug; 336 } 337