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) || defined (USE_SSSE3)
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     X86_SSSE3			= (1 << 5)
44 } cpu_features_t;
45 
46 #ifdef HAVE_GETISAX
47 
48 #include <sys/auxv.h>
49 
50 static cpu_features_t
detect_cpu_features(void)51 detect_cpu_features (void)
52 {
53     cpu_features_t features = 0;
54     unsigned int result = 0;
55 
56     if (getisax (&result, 1))
57     {
58 	if (result & AV_386_CMOV)
59 	    features |= X86_CMOV;
60 	if (result & AV_386_MMX)
61 	    features |= X86_MMX;
62 	if (result & AV_386_AMD_MMX)
63 	    features |= X86_MMX_EXTENSIONS;
64 	if (result & AV_386_SSE)
65 	    features |= X86_SSE;
66 	if (result & AV_386_SSE2)
67 	    features |= X86_SSE2;
68 	if (result & AV_386_SSSE3)
69 	    features |= X86_SSSE3;
70     }
71 
72     return features;
73 }
74 
75 #else
76 
77 #define _PIXMAN_X86_64							\
78     (defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64))
79 
80 static pixman_bool_t
have_cpuid(void)81 have_cpuid (void)
82 {
83 #if _PIXMAN_X86_64 || defined (_MSC_VER)
84 
85     return TRUE;
86 
87 #elif defined (__GNUC__)
88     uint32_t result;
89 
90     __asm__ volatile (
91         "pushf"				"\n\t"
92         "pop %%eax"			"\n\t"
93         "mov %%eax, %%ecx"		"\n\t"
94         "xor $0x00200000, %%eax"	"\n\t"
95         "push %%eax"			"\n\t"
96         "popf"				"\n\t"
97         "pushf"				"\n\t"
98         "pop %%eax"			"\n\t"
99         "xor %%ecx, %%eax"		"\n\t"
100 	"mov %%eax, %0"			"\n\t"
101 	: "=r" (result)
102 	:
103 	: "%eax", "%ecx");
104 
105     return !!result;
106 
107 #else
108 #error "Unknown compiler"
109 #endif
110 }
111 
112 #ifdef _MSC_VER
113 #include <intrin.h> /* for __cpuid */
114 #endif
115 
116 static void
pixman_cpuid(uint32_t feature,uint32_t * a,uint32_t * b,uint32_t * c,uint32_t * d)117 pixman_cpuid (uint32_t feature,
118 	      uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
119 {
120 #if defined (__GNUC__)
121 
122 #if _PIXMAN_X86_64
123     __asm__ volatile (
124         "cpuid"				"\n\t"
125 	: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
126 	: "a" (feature));
127 #else
128     /* On x86-32 we need to be careful about the handling of %ebx
129      * and %esp. We can't declare either one as clobbered
130      * since they are special registers (%ebx is the "PIC
131      * register" holding an offset to global data, %esp the
132      * stack pointer), so we need to make sure that %ebx is
133      * preserved, and that %esp has its original value when
134      * accessing the output operands.
135      */
136     __asm__ volatile (
137 	"xchg %%ebx, %1"		"\n\t"
138 	"cpuid"				"\n\t"
139 	"xchg %%ebx, %1"		"\n\t"
140 	: "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
141 	: "a" (feature));
142 #endif
143 
144 #elif defined (_MSC_VER)
145     int info[4];
146 
147     __cpuid (info, feature);
148 
149     *a = info[0];
150     *b = info[1];
151     *c = info[2];
152     *d = info[3];
153 #else
154 #error Unknown compiler
155 #endif
156 }
157 
158 static cpu_features_t
detect_cpu_features(void)159 detect_cpu_features (void)
160 {
161     uint32_t a, b, c, d;
162     cpu_features_t features = 0;
163 
164     if (!have_cpuid())
165 	return features;
166 
167     /* Get feature bits */
168     pixman_cpuid (0x01, &a, &b, &c, &d);
169     if (d & (1 << 15))
170 	features |= X86_CMOV;
171     if (d & (1 << 23))
172 	features |= X86_MMX;
173     if (d & (1 << 25))
174 	features |= X86_SSE;
175     if (d & (1 << 26))
176 	features |= X86_SSE2;
177     if (c & (1 << 9))
178 	features |= X86_SSSE3;
179 
180     /* Check for AMD specific features */
181     if ((features & X86_MMX) && !(features & X86_SSE))
182     {
183 	char vendor[13];
184 
185 	/* Get vendor string */
186 	memset (vendor, 0, sizeof vendor);
187 
188 	pixman_cpuid (0x00, &a, &b, &c, &d);
189 	memcpy (vendor + 0, &b, 4);
190 	memcpy (vendor + 4, &d, 4);
191 	memcpy (vendor + 8, &c, 4);
192 
193 	if (strcmp (vendor, "AuthenticAMD") == 0 ||
194 	    strcmp (vendor, "HygonGenuine") == 0 ||
195 	    strcmp (vendor, "Geode by NSC") == 0)
196 	{
197 	    pixman_cpuid (0x80000000, &a, &b, &c, &d);
198 	    if (a >= 0x80000001)
199 	    {
200 		pixman_cpuid (0x80000001, &a, &b, &c, &d);
201 
202 		if (d & (1 << 22))
203 		    features |= X86_MMX_EXTENSIONS;
204 	    }
205 	}
206     }
207 
208     return features;
209 }
210 
211 #endif
212 
213 static pixman_bool_t
have_feature(cpu_features_t feature)214 have_feature (cpu_features_t feature)
215 {
216     static pixman_bool_t initialized;
217     static cpu_features_t features;
218 
219     if (!initialized)
220     {
221 	features = detect_cpu_features();
222 	initialized = TRUE;
223     }
224 
225     return (features & feature) == feature;
226 }
227 
228 #endif
229 
230 pixman_implementation_t *
_pixman_x86_get_implementations(pixman_implementation_t * imp)231 _pixman_x86_get_implementations (pixman_implementation_t *imp)
232 {
233 #define MMX_BITS  (X86_MMX | X86_MMX_EXTENSIONS)
234 #define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2)
235 #define SSSE3_BITS (X86_SSE | X86_SSE2 | X86_SSSE3)
236 
237 #ifdef USE_X86_MMX
238     if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS))
239 	imp = _pixman_implementation_create_mmx (imp);
240 #endif
241 
242 #ifdef USE_SSE2
243     if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS))
244 	imp = _pixman_implementation_create_sse2 (imp);
245 #endif
246 
247 #ifdef USE_SSSE3
248     if (!_pixman_disabled ("ssse3") && have_feature (SSSE3_BITS))
249 	imp = _pixman_implementation_create_ssse3 (imp);
250 #endif
251 
252     return imp;
253 }
254