1/*
2 * Copyright © 2000 SuSE, Inc.
3 * Copyright © 2007 Red Hat, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of SuSE not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  SuSE makes no representations about the
12 * suitability of this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 *
15 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include "pixman-private.h"
27
28#if defined(USE_X86_MMX) || defined (USE_SSE2)
29
30/* The CPU detection code needs to be in a file not compiled with
31 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
32 * that would lead to SIGILL instructions on old CPUs that don't have
33 * it.
34 */
35
36typedef enum
37{
38    X86_MMX			= (1 << 0),
39    X86_MMX_EXTENSIONS		= (1 << 1),
40    X86_SSE			= (1 << 2) | X86_MMX_EXTENSIONS,
41    X86_SSE2			= (1 << 3),
42    X86_CMOV			= (1 << 4)
43} cpu_features_t;
44
45#ifdef HAVE_GETISAX
46
47#include <sys/auxv.h>
48
49static cpu_features_t
50detect_cpu_features (void)
51{
52    cpu_features_t features = 0;
53    unsigned int result = 0;
54
55    if (getisax (&result, 1))
56    {
57	if (result & AV_386_CMOV)
58	    features |= X86_CMOV;
59	if (result & AV_386_MMX)
60	    features |= X86_MMX;
61	if (result & AV_386_AMD_MMX)
62	    features |= X86_MMX_EXTENSIONS;
63	if (result & AV_386_SSE)
64	    features |= X86_SSE;
65	if (result & AV_386_SSE2)
66	    features |= X86_SSE2;
67    }
68
69    return features;
70}
71
72#else
73
74#define _PIXMAN_X86_64							\
75    (defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64))
76
77static pixman_bool_t
78have_cpuid (void)
79{
80#if _PIXMAN_X86_64 || defined (_MSC_VER)
81
82    return TRUE;
83
84#elif defined (__GNUC__)
85    uint32_t result;
86
87    __asm__ volatile (
88        "pushf"				"\n\t"
89        "pop %%eax"			"\n\t"
90        "mov %%eax, %%ecx"		"\n\t"
91        "xor $0x00200000, %%eax"	"\n\t"
92        "push %%eax"			"\n\t"
93        "popf"				"\n\t"
94        "pushf"				"\n\t"
95        "pop %%eax"			"\n\t"
96        "xor %%ecx, %%eax"		"\n\t"
97	"mov %%eax, %0"			"\n\t"
98	: "=r" (result)
99	:
100	: "%eax", "%ecx");
101
102    return !!result;
103
104#else
105#error "Unknown compiler"
106#endif
107}
108
109static void
110pixman_cpuid (uint32_t feature,
111	      uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
112{
113#if defined (__GNUC__)
114
115#if _PIXMAN_X86_64
116    __asm__ volatile (
117        "cpuid"				"\n\t"
118	: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
119	: "a" (feature));
120#else
121    /* On x86-32 we need to be careful about the handling of %ebx
122     * and %esp. We can't declare either one as clobbered
123     * since they are special registers (%ebx is the "PIC
124     * register" holding an offset to global data, %esp the
125     * stack pointer), so we need to make sure that %ebx is
126     * preserved, and that %esp has its original value when
127     * accessing the output operands.
128     */
129    __asm__ volatile (
130	"xchg %%ebx, %1"		"\n\t"
131	"cpuid"				"\n\t"
132	"xchg %%ebx, %1"		"\n\t"
133	: "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
134	: "a" (feature));
135#endif
136
137#elif defined (_MSC_VER)
138    int info[4];
139
140    __cpuid (info, feature);
141
142    *a = info[0];
143    *b = info[1];
144    *c = info[2];
145    *d = info[3];
146#else
147#error Unknown compiler
148#endif
149}
150
151static cpu_features_t
152detect_cpu_features (void)
153{
154    uint32_t a, b, c, d;
155    cpu_features_t features = 0;
156
157    if (!have_cpuid())
158	return features;
159
160    /* Get feature bits */
161    pixman_cpuid (0x01, &a, &b, &c, &d);
162    if (d & (1 << 15))
163	features |= X86_CMOV;
164    if (d & (1 << 23))
165	features |= X86_MMX;
166    if (d & (1 << 25))
167	features |= X86_SSE;
168    if (d & (1 << 26))
169	features |= X86_SSE2;
170
171    /* Check for AMD specific features */
172    if ((features & X86_MMX) && !(features & X86_SSE))
173    {
174	char vendor[13];
175
176	/* Get vendor string */
177	memset (vendor, 0, sizeof vendor);
178
179	pixman_cpuid (0x00, &a, &b, &c, &d);
180	memcpy (vendor + 0, &b, 4);
181	memcpy (vendor + 4, &d, 4);
182	memcpy (vendor + 8, &c, 4);
183
184	if (strcmp (vendor, "AuthenticAMD") == 0 ||
185	    strcmp (vendor, "Geode by NSC") == 0)
186	{
187	    pixman_cpuid (0x80000000, &a, &b, &c, &d);
188	    if (a >= 0x80000001)
189	    {
190		pixman_cpuid (0x80000001, &a, &b, &c, &d);
191
192		if (d & (1 << 22))
193		    features |= X86_MMX_EXTENSIONS;
194	    }
195	}
196    }
197
198    return features;
199}
200
201#endif
202
203static pixman_bool_t
204have_feature (cpu_features_t feature)
205{
206    static pixman_bool_t initialized;
207    static cpu_features_t features;
208
209    if (!initialized)
210    {
211	features = detect_cpu_features();
212	initialized = TRUE;
213    }
214
215    return (features & feature) == feature;
216}
217
218#endif
219
220pixman_implementation_t *
221_pixman_x86_get_implementations (pixman_implementation_t *imp)
222{
223#define MMX_BITS  (X86_MMX | X86_MMX_EXTENSIONS)
224#define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2)
225
226#ifdef USE_X86_MMX
227    if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS))
228	imp = _pixman_implementation_create_mmx (imp);
229#endif
230
231#ifdef USE_SSE2
232    if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS))
233	imp = _pixman_implementation_create_sse2 (imp);
234#endif
235
236    return imp;
237}
238