238 lines
5.6 KiB
C
238 lines
5.6 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|