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