0,0 → 1,237 |
/* |
* Copyright © 2000 SuSE, Inc. |
* Copyright © 2007 Red Hat, Inc. |
* |
* Permission to use, copy, modify, distribute, and sell this software and its |
* documentation for any purpose is hereby granted without fee, provided that |
* the above copyright notice appear in all copies and that both that |
* copyright notice and this permission notice appear in supporting |
* documentation, and that the name of SuSE not be used in advertising or |
* publicity pertaining to distribution of the software without specific, |
* written prior permission. SuSE makes no representations about the |
* suitability of this software for any purpose. It is provided "as is" |
* without express or implied warranty. |
* |
* SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE |
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
*/ |
#ifdef HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#include "pixman-private.h" |
|
#if defined(USE_X86_MMX) || defined (USE_SSE2) |
|
/* The CPU detection code needs to be in a file not compiled with |
* "-mmmx -msse", as gcc would generate CMOV instructions otherwise |
* that would lead to SIGILL instructions on old CPUs that don't have |
* it. |
*/ |
|
typedef enum |
{ |
X86_MMX = (1 << 0), |
X86_MMX_EXTENSIONS = (1 << 1), |
X86_SSE = (1 << 2) | X86_MMX_EXTENSIONS, |
X86_SSE2 = (1 << 3), |
X86_CMOV = (1 << 4) |
} cpu_features_t; |
|
#ifdef HAVE_GETISAX |
|
#include <sys/auxv.h> |
|
static cpu_features_t |
detect_cpu_features (void) |
{ |
cpu_features_t features = 0; |
unsigned int result = 0; |
|
if (getisax (&result, 1)) |
{ |
if (result & AV_386_CMOV) |
features |= X86_CMOV; |
if (result & AV_386_MMX) |
features |= X86_MMX; |
if (result & AV_386_AMD_MMX) |
features |= X86_MMX_EXTENSIONS; |
if (result & AV_386_SSE) |
features |= X86_SSE; |
if (result & AV_386_SSE2) |
features |= X86_SSE2; |
} |
|
return features; |
} |
|
#else |
|
#define _PIXMAN_X86_64 \ |
(defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)) |
|
static pixman_bool_t |
have_cpuid (void) |
{ |
#if _PIXMAN_X86_64 || defined (_MSC_VER) |
|
return TRUE; |
|
#elif defined (__GNUC__) |
uint32_t result; |
|
__asm__ volatile ( |
"pushf" "\n\t" |
"pop %%eax" "\n\t" |
"mov %%eax, %%ecx" "\n\t" |
"xor $0x00200000, %%eax" "\n\t" |
"push %%eax" "\n\t" |
"popf" "\n\t" |
"pushf" "\n\t" |
"pop %%eax" "\n\t" |
"xor %%ecx, %%eax" "\n\t" |
"mov %%eax, %0" "\n\t" |
: "=r" (result) |
: |
: "%eax", "%ecx"); |
|
return !!result; |
|
#else |
#error "Unknown compiler" |
#endif |
} |
|
static void |
pixman_cpuid (uint32_t feature, |
uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) |
{ |
#if defined (__GNUC__) |
|
#if _PIXMAN_X86_64 |
__asm__ volatile ( |
"cpuid" "\n\t" |
: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) |
: "a" (feature)); |
#else |
/* On x86-32 we need to be careful about the handling of %ebx |
* and %esp. We can't declare either one as clobbered |
* since they are special registers (%ebx is the "PIC |
* register" holding an offset to global data, %esp the |
* stack pointer), so we need to make sure that %ebx is |
* preserved, and that %esp has its original value when |
* accessing the output operands. |
*/ |
__asm__ volatile ( |
"xchg %%ebx, %1" "\n\t" |
"cpuid" "\n\t" |
"xchg %%ebx, %1" "\n\t" |
: "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) |
: "a" (feature)); |
#endif |
|
#elif defined (_MSC_VER) |
int info[4]; |
|
__cpuid (info, feature); |
|
*a = info[0]; |
*b = info[1]; |
*c = info[2]; |
*d = info[3]; |
#else |
#error Unknown compiler |
#endif |
} |
|
static cpu_features_t |
detect_cpu_features (void) |
{ |
uint32_t a, b, c, d; |
cpu_features_t features = 0; |
|
if (!have_cpuid()) |
return features; |
|
/* Get feature bits */ |
pixman_cpuid (0x01, &a, &b, &c, &d); |
if (d & (1 << 15)) |
features |= X86_CMOV; |
if (d & (1 << 23)) |
features |= X86_MMX; |
if (d & (1 << 25)) |
features |= X86_SSE; |
if (d & (1 << 26)) |
features |= X86_SSE2; |
|
/* Check for AMD specific features */ |
if ((features & X86_MMX) && !(features & X86_SSE)) |
{ |
char vendor[13]; |
|
/* Get vendor string */ |
memset (vendor, 0, sizeof vendor); |
|
pixman_cpuid (0x00, &a, &b, &c, &d); |
memcpy (vendor + 0, &b, 4); |
memcpy (vendor + 4, &d, 4); |
memcpy (vendor + 8, &c, 4); |
|
if (strcmp (vendor, "AuthenticAMD") == 0 || |
strcmp (vendor, "Geode by NSC") == 0) |
{ |
pixman_cpuid (0x80000000, &a, &b, &c, &d); |
if (a >= 0x80000001) |
{ |
pixman_cpuid (0x80000001, &a, &b, &c, &d); |
|
if (d & (1 << 22)) |
features |= X86_MMX_EXTENSIONS; |
} |
} |
} |
|
return features; |
} |
|
#endif |
|
static pixman_bool_t |
have_feature (cpu_features_t feature) |
{ |
static pixman_bool_t initialized; |
static cpu_features_t features; |
|
if (!initialized) |
{ |
features = detect_cpu_features(); |
initialized = TRUE; |
} |
|
return (features & feature) == feature; |
} |
|
#endif |
|
pixman_implementation_t * |
_pixman_x86_get_implementations (pixman_implementation_t *imp) |
{ |
#define MMX_BITS (X86_MMX | X86_MMX_EXTENSIONS) |
#define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2) |
|
#ifdef USE_X86_MMX |
if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS)) |
imp = _pixman_implementation_create_mmx (imp); |
#endif |
|
#ifdef USE_SSE2 |
if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS)) |
imp = _pixman_implementation_create_sse2 (imp); |
#endif |
|
return imp; |
} |