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 00034 #if defined(__GNUC__) || defined(__INTEL_COMPILER) 00035 # define HAVE_GNU_INLINE_ASM 00036 #endif 00037 00038 typedef void (*kde_sighandler_t) (int); 00039 00040 #ifdef __i386__ 00041 static jmp_buf env; 00042 00043 // Sighandler for the SSE OS support check 00044 static void sighandler( int ) 00045 { 00046 std::longjmp( env, 1 ); 00047 } 00048 #endif 00049 00050 #ifdef __PPC__ 00051 static sigjmp_buf KDE_NO_EXPORT jmpbuf; 00052 static sig_atomic_t KDE_NO_EXPORT canjump = 0; 00053 00054 static void KDE_NO_EXPORT sigill_handler( int sig ) 00055 { 00056 if ( !canjump ) { 00057 signal( sig, SIG_DFL ); 00058 raise( sig ); 00059 } 00060 canjump = 0; 00061 siglongjmp( jmpbuf, 1 ); 00062 } 00063 #endif 00064 00065 static int getCpuFeatures() 00066 { 00067 volatile int features = 0; 00068 00069 #if defined( HAVE_GNU_INLINE_ASM ) 00070 #if defined( __i386__ ) 00071 bool haveCPUID = false; 00072 bool have3DNOW = false; 00073 int result = 0; 00074 00075 // First check if the CPU supports the CPUID instruction 00076 __asm__ __volatile__( 00077 // Try to toggle the CPUID bit in the EFLAGS register 00078 "pushf \n\t" // Push the EFLAGS register onto the stack 00079 "popl %%ecx \n\t" // Pop the value into ECX 00080 "movl %%ecx, %%edx \n\t" // Copy ECX to EDX 00081 "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX 00082 "pushl %%ecx \n\t" // Push the modified value onto the stack 00083 "popf \n\t" // Pop it back into EFLAGS 00084 00085 // Check if the CPUID bit was successfully toggled 00086 "pushf \n\t" // Push EFLAGS back onto the stack 00087 "popl %%ecx \n\t" // Pop the value into ECX 00088 "xorl %%eax, %%eax \n\t" // Zero out the EAX register 00089 "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX 00090 "je .Lno_cpuid_support%= \n\t" // Jump if they're identical 00091 "movl $1, %%eax \n\t" // Set EAX to true 00092 ".Lno_cpuid_support%=: \n\t" 00093 : "=a"(haveCPUID) : : "%ecx", "%edx" ); 00094 00095 // If we don't have CPUID we won't have the other extensions either 00096 if ( ! haveCPUID ) 00097 return 0L; 00098 00099 // Execute CPUID with the feature request bit set 00100 __asm__ __volatile__( 00101 "pushl %%ebx \n\t" // Save EBX 00102 "movl $1, %%eax \n\t" // Set EAX to 1 (features request) 00103 "cpuid \n\t" // Call CPUID 00104 "popl %%ebx \n\t" // Restore EBX 00105 : "=d"(result) : : "%eax", "%ecx" ); 00106 00107 // Test bit 23 (MMX support) 00108 if ( result & 0x00800000 ) 00109 features |= KCPUInfo::IntelMMX; 00110 00111 __asm__ __volatile__( 00112 "pushl %%ebx \n\t" 00113 "movl $0x80000000, %%eax \n\t" 00114 "cpuid \n\t" 00115 "cmpl $0x80000000, %%eax \n\t" 00116 "jbe .Lno_extended%= \n\t" 00117 "movl $0x80000001, %%eax \n\t" 00118 "cpuid \n\t" 00119 "test $0x80000000, %%edx \n\t" 00120 "jz .Lno_extended%= \n\t" 00121 "movl $1, %%eax \n\t" // // Set EAX to true 00122 ".Lno_extended%=: \n\t" 00123 "popl %%ebx \n\t" // Restore EBX 00124 : "=a"(have3DNOW) : ); 00125 00126 if ( have3DNOW ) 00127 features |= KCPUInfo::AMD3DNOW; 00128 00129 #ifdef HAVE_X86_SSE 00130 // Test bit 25 (SSE support) 00131 if ( result & 0x00200000 ) { 00132 features |= KCPUInfo::IntelSSE; 00133 00134 // OS support test for SSE. 00135 // Install our own sighandler for SIGILL. 00136 kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler ); 00137 00138 // Try executing an SSE insn to see if we get a SIGILL 00139 if ( setjmp( env ) ) 00140 features ^= KCPUInfo::IntelSSE; // The OS support test failed 00141 else 00142 __asm__ __volatile__("xorps %xmm0, %xmm0"); 00143 00144 // Restore the default sighandler 00145 std::signal( SIGILL, oldhandler ); 00146 00147 // Test bit 26 (SSE2 support) 00148 if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) ) 00149 features |= KCPUInfo::IntelSSE2; 00150 00151 // Note: The OS requirements for SSE2 are the same as for SSE 00152 // so we don't have to do any additional tests for that. 00153 } 00154 #endif // HAVE_X86_SSE 00155 #elif defined __PPC__ && defined HAVE_PPC_ALTIVEC 00156 signal( SIGILL, sigill_handler ); 00157 if ( sigsetjmp( jmpbuf, 1 ) ) { 00158 signal( SIGILL, SIG_DFL ); 00159 } else { 00160 canjump = 1; 00161 __asm__ __volatile__( "mtspr 256, %0\n\t" 00162 "vand %%v0, %%v0, %%v0" 00163 : /* none */ 00164 : "r" (-1) ); 00165 signal( SIGILL, SIG_DFL ); 00166 features |= KCPUInfo::AltiVec; 00167 } 00168 #endif // __i386__ 00169 #endif //HAVE_GNU_INLINE_ASM 00170 00171 return features; 00172 } 00173 00174 unsigned int KCPUInfo::s_features = getCpuFeatures(); 00175 00176