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