From 7564f37de249c8d29b254c83a4ff3f4c47ae8057 Mon Sep 17 00:00:00 2001 From: arpi Date: Fri, 19 Oct 2001 00:40:19 +0000 Subject: cpu detect code by Eric Anholt git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@2269 b3059339-0415-0410-9bf9-f77b7e298cf2 --- cpudetect.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 cpudetect.c (limited to 'cpudetect.c') diff --git a/cpudetect.c b/cpudetect.c new file mode 100644 index 0000000000..a0925d618e --- /dev/null +++ b/cpudetect.c @@ -0,0 +1,267 @@ +#include "config.h" +#include "cpudetect.h" + +#ifdef ARCH_X86 + +#include + +#ifdef __FreeBSD__ +#include +#include +#endif + +#ifdef __linux__ +#include +#endif + +#define X86_FXSR_MAGIC +/* Thanks to the FreeBSD project for some of this cpuid code, and + * help understanding how to use it. Thanks to the Mesa + * team for SSE support detection and more cpu detect code. + */ + +/* I believe this code works. However, it has only been used on a PII and PIII */ + +CpuCaps gCpuCaps; +static void check_os_katmai_support( void ); + +#if 0 +static int has_cpuid() +{ + int a, c; + __asm __volatile( + "pushl %%ebx;" + /* Test for the CPUID command. If the ID Flag bit in EFLAGS + * (bit 21) is writable, the CPUID command is present */ + "pushfl;" + "popl %%eax;" + "movl %%ecx, %%eax;" + "xorl %%eax, 0x00200000;" + "push %%eax;" + "popfl;" + "pushfl;" + "popl %%eax;" + "popl %%ebx" + : "=a" (a), "=c" (c) + : + : "cx" + ); + /* FIXME: I have no clue on intel assembly. */ + return (a==c); +} +#endif + +static void +do_cpuid(unsigned int ax, unsigned int *p) +{ + __asm __volatile( + "cpuid;" + : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) + : "0" (ax) + ); +} + + +void GetCpuCaps( CpuCaps *caps) +{ + unsigned int regs[4]; + unsigned int regs2[4]; + + bzero(caps, sizeof(*caps)); + /*if (!has_cpuid()) + return;*/ + do_cpuid(0x00000000, regs); + if (regs[0]>0x00000001) { + do_cpuid(0x00000001, regs2); + switch ((regs2[0] >> 8)&0xf) { + case 3: + caps->cpuType=CPUTYPE_I386; + break; + case 4: + caps->cpuType=CPUTYPE_I486; + break; + case 5: + caps->cpuType=CPUTYPE_I586; + break; + case 6: + caps->cpuType=CPUTYPE_I586; + break; + default: + printf("Unknown cpu type, default to i386\n"); + break; + } + caps->hasMMX = (regs2[3] & (1 << 23 )) >> 23; + caps->hasSSE = (regs2[3] & (1 << 25 )) >> 25; + caps->hasSSE2 = (regs2[3] & (1 << 26 )) >> 26; + /* FIXME: Does SSE2 need more OS support, too? */ +#if defined(__linux__) || defined(__FreeBSD__) + if (caps->hasSSE) + check_os_katmai_support(); + if (!caps->hasSSE) + caps->hasSSE2 = 0; +#else + caps->hasSSE=0; + caps->hasSSE2 = 0; +#endif + /* FIXME: Are MMX2 ops on the same set of processors as SSE? Do they need OS support?*/ + caps->hasMMX2 = caps->hasSSE; + } + if (memcmp(®s[1], "AuthenticAMD", 12)) { + do_cpuid(0x80000000, regs); + if (regs[0]>0x80000001) { + do_cpuid(0x80000001, regs2); + /*caps->hasMMX2 = regs[3] & (1 << 23 );*/ + caps->has3DNow = (regs2[3] & (1 << 31 )) >> 31; + caps->has3DNowExt = (regs2[3] & (1 << 30 )) >> 30; + } + } + printf("cpudetect: MMX=%d MMX2=%d SSE=%d SSE2=%d 3DNow=%d 3DNowExt=%d\n", + gCpuCaps.hasMMX, + gCpuCaps.hasMMX2, + gCpuCaps.hasSSE, + gCpuCaps.hasSSE2, + gCpuCaps.has3DNow, + gCpuCaps.has3DNowExt ); + +} + +#if defined(__linux__) && defined(_POSIX_SOURCE) && defined(X86_FXSR_MAGIC) +static void sigill_handler_sse( int signal, struct sigcontext sc ) +{ + printf( "SIGILL, " ); + + /* Both the "xorps %%xmm0,%%xmm0" and "divps %xmm0,%%xmm1" + * instructions are 3 bytes long. We must increment the instruction + * pointer manually to avoid repeated execution of the offending + * instruction. + * + * If the SIGILL is caused by a divide-by-zero when unmasked + * exceptions aren't supported, the SIMD FPU status and control + * word will be restored at the end of the test, so we don't need + * to worry about doing it here. Besides, we may not be able to... + */ + sc.eip += 3; + + gCpuCaps.hasSSE=0; +} + +static void sigfpe_handler_sse( int signal, struct sigcontext sc ) +{ + printf( "SIGFPE, " ); + + if ( sc.fpstate->magic != 0xffff ) { + /* Our signal context has the extended FPU state, so reset the + * divide-by-zero exception mask and clear the divide-by-zero + * exception bit. + */ + sc.fpstate->mxcsr |= 0x00000200; + sc.fpstate->mxcsr &= 0xfffffffb; + } else { + /* If we ever get here, we're completely hosed. + */ + printf( "\n\n" ); + printf( "SSE enabling test failed badly!" ); + } +} +#endif /* __linux__ && _POSIX_SOURCE && X86_FXSR_MAGIC */ + +/* If we're running on a processor that can do SSE, let's see if we + * are allowed to or not. This will catch 2.4.0 or later kernels that + * haven't been configured for a Pentium III but are running on one, + * and RedHat patched 2.2 kernels that have broken exception handling + * support for user space apps that do SSE. + */ +static void check_os_katmai_support( void ) +{ +#if defined(__FreeBSD__) + int has_sse=0, ret; + size_t len=sizeof(has_sse); + + ret = sysctlbyname("hw.instruction_sse", &has_sse, &len, NULL, 0); + if (ret || !has_sse) + gCpuCaps.hasSSE=0; + +#elif defined(__linux__) +#if defined(_POSIX_SOURCE) && defined(X86_FXSR_MAGIC) + struct sigaction saved_sigill; + struct sigaction saved_sigfpe; + + /* Save the original signal handlers. + */ + sigaction( SIGILL, NULL, &saved_sigill ); + sigaction( SIGFPE, NULL, &saved_sigfpe ); + + signal( SIGILL, (void (*)(int))sigill_handler_sse ); + signal( SIGFPE, (void (*)(int))sigfpe_handler_sse ); + + /* Emulate test for OSFXSR in CR4. The OS will set this bit if it + * supports the extended FPU save and restore required for SSE. If + * we execute an SSE instruction on a PIII and get a SIGILL, the OS + * doesn't support Streaming SIMD Exceptions, even if the processor + * does. + */ + if ( gCpuCaps.hasSSE ) { + printf( "Testing OS support for SSE... " ); + + __asm __volatile ("xorps %%xmm0, %%xmm0"); + + if ( gCpuCaps.hasSSE ) { + printf( "yes.\n" ); + } else { + printf( "no!\n" ); + } + } + + /* Emulate test for OSXMMEXCPT in CR4. The OS will set this bit if + * it supports unmasked SIMD FPU exceptions. If we unmask the + * exceptions, do a SIMD divide-by-zero and get a SIGILL, the OS + * doesn't support unmasked SIMD FPU exceptions. If we get a SIGFPE + * as expected, we're okay but we need to clean up after it. + * + * Are we being too stringent in our requirement that the OS support + * unmasked exceptions? Certain RedHat 2.2 kernels enable SSE by + * setting CR4.OSFXSR but don't support unmasked exceptions. Win98 + * doesn't even support them. We at least know the user-space SSE + * support is good in kernels that do support unmasked exceptions, + * and therefore to be safe I'm going to leave this test in here. + */ + if ( gCpuCaps.hasSSE ) { + printf( "Testing OS support for SSE unmasked exceptions... " ); + + test_os_katmai_exception_support(); + + if ( gCpuCaps.hasSSE ) { + printf( "yes.\n" ); + } else { + printf( "no!\n" ); + } + } + + /* Restore the original signal handlers. + */ + sigaction( SIGILL, &saved_sigill, NULL ); + sigaction( SIGFPE, &saved_sigfpe, NULL ); + + /* If we've gotten to here and the XMM CPUID bit is still set, we're + * safe to go ahead and hook out the SSE code throughout Mesa. + */ + if ( gCpuCaps.hasSSE ) { + printf( "Tests of OS support for SSE passed.\n" ); + } else { + printf( "Tests of OS support for SSE failed!\n" ); + } +#else + /* We can't use POSIX signal handling to test the availability of + * SSE, so we disable it by default. + */ + printf( "Cannot test OS support for SSE, disabling to be safe.\n" ); + gCpuCaps.hasSSE=0; +#endif /* _POSIX_SOURCE && X86_FXSR_MAGIC */ +#else + /* Do nothing on other platforms for now. + */ + message( "Not testing OS support for SSE, leaving disabled.\n" ); + gCpuCaps.hasSSE=0; +#endif /* __linux__ */ +} +#endif /* ARCH_X86 */ \ No newline at end of file -- cgit v1.2.3