diff options
Diffstat (limited to 'kdefx/kcpuinfo.cpp')
-rw-r--r-- | kdefx/kcpuinfo.cpp | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/kdefx/kcpuinfo.cpp b/kdefx/kcpuinfo.cpp new file mode 100644 index 000000000..97c99f8d9 --- /dev/null +++ b/kdefx/kcpuinfo.cpp @@ -0,0 +1,176 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <csignal> +#include <csetjmp> + +#include <config.h> +#include "kcpuinfo.h" + + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +# define HAVE_GNU_INLINE_ASM +#endif + +typedef void (*kde_sighandler_t) (int); + +#ifdef __i386__ +static jmp_buf env; + +// Sighandler for the SSE OS support check +static void sighandler( int ) +{ + std::longjmp( env, 1 ); +} +#endif + +#ifdef __PPC__ +static sigjmp_buf KDE_NO_EXPORT jmpbuf; +static sig_atomic_t KDE_NO_EXPORT canjump = 0; + +static void KDE_NO_EXPORT sigill_handler( int sig ) +{ + if ( !canjump ) { + signal( sig, SIG_DFL ); + raise( sig ); + } + canjump = 0; + siglongjmp( jmpbuf, 1 ); +} +#endif + +static int getCpuFeatures() +{ + volatile int features = 0; + +#if defined( HAVE_GNU_INLINE_ASM ) +#if defined( __i386__ ) + bool haveCPUID = false; + bool have3DNOW = false; + int result = 0; + + // First check if the CPU supports the CPUID instruction + __asm__ __volatile__( + // Try to toggle the CPUID bit in the EFLAGS register + "pushf \n\t" // Push the EFLAGS register onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "movl %%ecx, %%edx \n\t" // Copy ECX to EDX + "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX + "pushl %%ecx \n\t" // Push the modified value onto the stack + "popf \n\t" // Pop it back into EFLAGS + + // Check if the CPUID bit was successfully toggled + "pushf \n\t" // Push EFLAGS back onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "xorl %%eax, %%eax \n\t" // Zero out the EAX register + "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX + "je .Lno_cpuid_support%= \n\t" // Jump if they're identical + "movl $1, %%eax \n\t" // Set EAX to true + ".Lno_cpuid_support%=: \n\t" + : "=a"(haveCPUID) : : "%ecx", "%edx" ); + + // If we don't have CPUID we won't have the other extensions either + if ( ! haveCPUID ) + return 0L; + + // Execute CPUID with the feature request bit set + __asm__ __volatile__( + "pushl %%ebx \n\t" // Save EBX + "movl $1, %%eax \n\t" // Set EAX to 1 (features request) + "cpuid \n\t" // Call CPUID + "popl %%ebx \n\t" // Restore EBX + : "=d"(result) : : "%eax", "%ecx" ); + + // Test bit 23 (MMX support) + if ( result & 0x00800000 ) + features |= KCPUInfo::IntelMMX; + + __asm__ __volatile__( + "pushl %%ebx \n\t" + "movl $0x80000000, %%eax \n\t" + "cpuid \n\t" + "cmpl $0x80000000, %%eax \n\t" + "jbe .Lno_extended%= \n\t" + "movl $0x80000001, %%eax \n\t" + "cpuid \n\t" + "test $0x80000000, %%edx \n\t" + "jz .Lno_extended%= \n\t" + "movl $1, %%eax \n\t" // // Set EAX to true + ".Lno_extended%=: \n\t" + "popl %%ebx \n\t" // Restore EBX + : "=a"(have3DNOW) : ); + + if ( have3DNOW ) + features |= KCPUInfo::AMD3DNOW; + +#ifdef HAVE_X86_SSE + // Test bit 25 (SSE support) + if ( result & 0x00200000 ) { + features |= KCPUInfo::IntelSSE; + + // OS support test for SSE. + // Install our own sighandler for SIGILL. + kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler ); + + // Try executing an SSE insn to see if we get a SIGILL + if ( setjmp( env ) ) + features ^= KCPUInfo::IntelSSE; // The OS support test failed + else + __asm__ __volatile__("xorps %xmm0, %xmm0"); + + // Restore the default sighandler + std::signal( SIGILL, oldhandler ); + + // Test bit 26 (SSE2 support) + if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) ) + features |= KCPUInfo::IntelSSE2; + + // Note: The OS requirements for SSE2 are the same as for SSE + // so we don't have to do any additional tests for that. + } +#endif // HAVE_X86_SSE +#elif defined __PPC__ && defined HAVE_PPC_ALTIVEC + signal( SIGILL, sigill_handler ); + if ( sigsetjmp( jmpbuf, 1 ) ) { + signal( SIGILL, SIG_DFL ); + } else { + canjump = 1; + __asm__ __volatile__( "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : /* none */ + : "r" (-1) ); + signal( SIGILL, SIG_DFL ); + features |= KCPUInfo::AltiVec; + } +#endif // __i386__ +#endif //HAVE_GNU_INLINE_ASM + + return features; +} + +unsigned int KCPUInfo::s_features = getCpuFeatures(); + + |