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
36 typedef 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
49 static cpu_features_t
detect_cpu_features(void)50 detect_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
77 static pixman_bool_t
have_cpuid(void)78 have_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
109 static void
pixman_cpuid(uint32_t feature,uint32_t * a,uint32_t * b,uint32_t * c,uint32_t * d)110 pixman_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
151 static cpu_features_t
detect_cpu_features(void)152 detect_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
203 static pixman_bool_t
have_feature(cpu_features_t feature)204 have_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
220 pixman_implementation_t *
_pixman_x86_get_implementations(pixman_implementation_t * imp)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