|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 * |
|
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 } |