kcpuinfo.cpp
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org> 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions 00007 * are met: 00008 * 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 00015 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 00016 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00017 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 00018 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 00019 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 00020 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00021 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00022 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00023 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 00024 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00025 */ 00026 00027 #include <csignal> 00028 #include <csetjmp> 00029 00030 #include <config.h> 00031 #include "kcpuinfo.h" 00032 00033 #undef HAVE_PPC_ALTIVEC 00034 00035 #if defined(__GNUC__) || defined(__INTEL_COMPILER) 00036 # define HAVE_GNU_INLINE_ASM 00037 #endif 00038 00039 typedef void (*kde_sighandler_t) (int); 00040 00041 #ifdef __i386__ 00042 static jmp_buf env; 00043 00044 // Sighandler for the SSE OS support check 00045 static void sighandler( int ) 00046 { 00047 std::longjmp( env, 1 ); 00048 } 00049 #endif 00050 00051 #ifdef __PPC__ 00052 static sigjmp_buf KDE_NO_EXPORT jmpbuf; 00053 static sig_atomic_t KDE_NO_EXPORT canjump = 0; 00054 00055 static void KDE_NO_EXPORT sigill_handler( int sig ) 00056 { 00057 if ( !canjump ) { 00058 signal( sig, SIG_DFL ); 00059 raise( sig ); 00060 } 00061 canjump = 0; 00062 siglongjmp( jmpbuf, 1 ); 00063 } 00064 #endif 00065 00066 static int getCpuFeatures() 00067 { 00068 volatile int features = 0; 00069 00070 #if defined( HAVE_GNU_INLINE_ASM ) 00071 #if defined( __i386__ ) 00072 bool haveCPUID = false; 00073 bool have3DNOW = false; 00074 int result = 0; 00075 00076 // First check if the CPU supports the CPUID instruction 00077 __asm__ __volatile__( 00078 // Try to toggle the CPUID bit in the EFLAGS register 00079 "pushf \n\t" // Push the EFLAGS register onto the stack 00080 "popl %%ecx \n\t" // Pop the value into ECX 00081 "movl %%ecx, %%edx \n\t" // Copy ECX to EDX 00082 "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX 00083 "pushl %%ecx \n\t" // Push the modified value onto the stack 00084 "popf \n\t" // Pop it back into EFLAGS 00085 00086 // Check if the CPUID bit was successfully toggled 00087 "pushf \n\t" // Push EFLAGS back onto the stack 00088 "popl %%ecx \n\t" // Pop the value into ECX 00089 "xorl %%eax, %%eax \n\t" // Zero out the EAX register 00090 "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX 00091 "je .Lno_cpuid_support%= \n\t" // Jump if they're identical 00092 "movl $1, %%eax \n\t" // Set EAX to true 00093 ".Lno_cpuid_support%=: \n\t" 00094 : "=a"(haveCPUID) : : "%ecx", "%edx" ); 00095 00096 // If we don't have CPUID we won't have the other extensions either 00097 if ( ! haveCPUID ) 00098 return 0L; 00099 00100 // Execute CPUID with the feature request bit set 00101 __asm__ __volatile__( 00102 "pushl %%ebx \n\t" // Save EBX 00103 "movl $1, %%eax \n\t" // Set EAX to 1 (features request) 00104 "cpuid \n\t" // Call CPUID 00105 "popl %%ebx \n\t" // Restore EBX 00106 : "=d"(result) : : "%eax", "%ecx" ); 00107 00108 // Test bit 23 (MMX support) 00109 if ( result & 0x00800000 ) 00110 features |= KCPUInfo::IntelMMX; 00111 00112 __asm__ __volatile__( 00113 "pushl %%ebx \n\t" 00114 "movl $0x80000000, %%eax \n\t" 00115 "cpuid \n\t" 00116 "cmpl $0x80000000, %%eax \n\t" 00117 "jbe .Lno_extended%= \n\t" 00118 "movl $0x80000001, %%eax \n\t" 00119 "cpuid \n\t" 00120 "test $0x80000000, %%edx \n\t" 00121 "jz .Lno_extended%= \n\t" 00122 "movl $1, %%eax \n\t" // // Set EAX to true 00123 ".Lno_extended%=: \n\t" 00124 "popl %%ebx \n\t" // Restore EBX 00125 : "=a"(have3DNOW) : ); 00126 00127 if ( have3DNOW ) 00128 features |= KCPUInfo::AMD3DNOW; 00129 00130 #ifdef HAVE_X86_SSE 00131 // Test bit 25 (SSE support) 00132 if ( result & 0x00200000 ) { 00133 features |= KCPUInfo::IntelSSE; 00134 00135 // OS support test for SSE. 00136 // Install our own sighandler for SIGILL. 00137 kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler ); 00138 00139 // Try executing an SSE insn to see if we get a SIGILL 00140 if ( setjmp( env ) ) 00141 features ^= KCPUInfo::IntelSSE; // The OS support test failed 00142 else 00143 __asm__ __volatile__("xorps %xmm0, %xmm0"); 00144 00145 // Restore the default sighandler 00146 std::signal( SIGILL, oldhandler ); 00147 00148 // Test bit 26 (SSE2 support) 00149 if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) ) 00150 features |= KCPUInfo::IntelSSE2; 00151 00152 // Note: The OS requirements for SSE2 are the same as for SSE 00153 // so we don't have to do any additional tests for that. 00154 } 00155 #endif // HAVE_X86_SSE 00156 #elif defined __PPC__ && defined HAVE_PPC_ALTIVEC 00157 signal( SIGILL, sigill_handler ); 00158 if ( sigsetjmp( jmpbuf, 1 ) ) { 00159 signal( SIGILL, SIG_DFL ); 00160 } else { 00161 canjump = 1; 00162 __asm__ __volatile__( "mtspr 256, %0\n\t" 00163 "vand %%v0, %%v0, %%v0" 00164 : /* none */ 00165 : "r" (-1) ); 00166 signal( SIGILL, SIG_DFL ); 00167 features |= KCPUInfo::AltiVec; 00168 } 00169 #endif // __i386__ 00170 #endif //HAVE_GNU_INLINE_ASM 00171 00172 return features; 00173 } 00174 00175 unsigned int KCPUInfo::s_features = getCpuFeatures(); 00176 00177