]> git.ozlabs.org Git - ccan/blob - ccan/cpuid/cpuid.c
cpuid: apply the 32-bit fix
[ccan] / ccan / cpuid / cpuid.c
1 /*
2  * Copyright (c) 2013 Ahmed Samy  <f.fallen45@gmail.com>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  *
22  * This file has been written with some help from wikipedia:
23  *      http://en.wikipedia.org/wiki/CPUID
24  */
25
26 /* Only compile this file if we're on a x86 machine.  */
27 #if defined(__i386__) || defined(__i386) || defined(__x86_64) \
28         || defined(_M_AMD64) || defined(__M_X64)
29 #include "cpuid.h"
30
31 #include <string.h>
32
33 enum {
34         CPU_PROC_BRAND_STRING_INTERNAL0                 = 0x80000003,
35         CPU_PROC_BRAND_STRING_INTERNAL1                 = 0x80000004
36 };
37
38 #ifndef _MSC_VER
39 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
40 {
41         __asm__(
42                 "xchg %%ebx, %%edi\n\t"         /* 32bit PIC: Don't clobber ebx.  */
43                 "cpuid\n\t"
44                 "xchg %%ebx, %%edi\n\t"
45                 : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
46                 : "0" (info)
47         );
48 }
49 #else
50 #include <intrin.h>
51
52 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
53 {
54         uint32_t registers[4];
55         __cpuid(registers, info);
56
57         *eax = registers[0];
58         *ebx = registers[1];
59         *ecx = registers[2];
60         *edx = registers[3];
61 }
62 #endif
63
64 static struct {
65         uint32_t feature;
66         uint32_t mask;
67         bool use_edx;           /* ecx will be used if false.  */
68 } features[] = {
69         { CF_MMX,               1 << 23,        true },
70         { CF_SSE,               1 << 25,        true },
71         { CF_SSE2,              1 << 26,        true },
72         { CF_SSE3,              1 << 9,         false },
73         { CF_FPU,               1 << 0,         true },
74
75         { CF_TSC,               1 << 4,         true },
76         { CF_MSR,               1 << 5,         true },
77
78         { CF_SSSE3,             1 << 9,         false },
79         { CF_AVX,               1 << 28,        false },
80
81         /* Extended ones.  */
82         { CEF_x64,              1 << 30,        true },
83         { CEF_FPU,              1 << 0,         true },
84         { CEF_DE,               1 << 2,         true },
85         { CEF_SYSCALLRET,       1 << 11,        true },
86         { CEF_CMOV,             1 << 15,        true },
87
88         { CEF_SSE4a,            1 << 6,         false },
89         { CEF_FMA4,             1 << 16,        false },
90         { CEF_XOP,              1 << 11,        false }
91 };
92
93 bool cpuid_is_supported(void)
94 {
95         /* The following assembly code uses EAX as the return value,
96          * but we store the value of EAX into ret since GCC uses EAX
97          * as the return register for every C function.  That's a double
98          * operation, but there's no other way to do this unless doing this
99          * function entirely in assembly.
100          *
101          * The following assembly code has been shamelessly stolen from:
102          *      http://wiki.osdev.org/CPUID
103          * and converted to work with AT&T syntax.
104          *
105          * This check is to make sure that the compiler is actually compiling
106          * for 64-bit.
107          *
108          * The compiler can be 32-bit and the system 64-bit so the 
109          * following would be true:
110          *      #if defined(__x86_64) ...
111          */
112
113 #if UINTPTR_MAX == 0xffffffffffffffff
114 #define ASM_PUSHF       "pushfq\n\t"
115 #define ASM_POPF        "popfq\n\t"
116 #define ASM_PUSHEAX     "pushq %%rax\n\t"
117 #define ASM_POPEAX      "popq %%rax\n\t"
118 #define ASM_PUSHECX     "pushq %%rcx\n\t"
119 #elif UINTPTR_MAX == 0xffffffff
120 #define ASM_PUSHF       "pushfl\n\t"
121 #define ASM_POPF        "popfl\n\t"
122 #define ASM_PUSHEAX     "pushl %%eax\n\t"
123 #define ASM_POPEAX      "popl %%eax\n\t"
124 #define ASM_PUSHECX     "pushl %%ecx\n\t"
125 #endif
126
127         int ret = 0;
128         asm volatile(
129                 ASM_PUSHF
130                 ASM_POPEAX
131                 "movl %%eax, %%ecx\n\t"
132                 "xorl $0x200000, %%eax\n\t"
133                 ASM_PUSHEAX
134                 ASM_POPF
135                 ASM_PUSHF
136                 ASM_POPEAX
137                 "xorl %%ecx, %%eax\n\t"
138                 "shrl $21, %%eax\n\t"
139                 "andl $1, %%eax\n\t"
140                 ASM_PUSHECX
141                 ASM_POPF
142                 : "=a" (ret)
143         );
144
145 #undef ASM_PUSHF
146 #undef ASM_POPF
147 #undef ASM_PUSHEAX
148 #undef ASM_POPEAX
149 #undef ASM_PUSHECX
150
151         return !!ret;
152 }
153
154 bool cpuid_test_feature(cpuid_t feature)
155 {
156         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
157                 return false;
158
159         return (feature <= cpuid_highest_ext_func_supported());
160 }
161
162 bool cpuid_has_feature(int feature, bool extended)
163 {
164         uint32_t eax, ebx, ecx, edx, i;
165
166         if (!extended)
167                 ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
168         else
169                 ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
170
171         for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
172                 if (features[i].feature == feature) {
173                         if (features[i].use_edx)
174                                 return (edx & features[i].mask);
175                         else
176                                 return (ecx & features[i].mask);
177                 }
178         }
179         return false;
180 }
181
182 static const char *const cpuids[] = {
183         "Nooooooooone",
184         "AMDisbetter!",
185         "AuthenticAMD",
186         "CentaurHauls",
187         "CyrixInstead",
188         "GenuineIntel",
189         "TransmetaCPU",
190         "GeniuneTMx86",
191         "Geode by NSC",
192         "NexGenDriven",
193         "RiseRiseRise",
194         "SiS SiS SiS ",
195         "UMC UMC UMC ",
196         "VIA VIA VIA ",
197         "Vortex86 SoC",
198         "KVMKVMKVMKVM"
199 };
200
201 cputype_t cpuid_get_cpu_type(void)
202 {
203         static cputype_t cputype;
204         if (cputype == CT_NONE) {
205                 union {
206                         char buf[12];
207                         uint32_t bufu32[3];
208                 } u;
209                 uint32_t i;
210
211                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
212                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
213                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
214                                 cputype = (cputype_t)i;
215                                 break;
216                         }
217                 }
218         }
219
220         return cputype;
221 }
222
223 const char *cpuid_get_cpu_type_string(const cputype_t cputype)
224 {
225         return cpuids[(int)cputype];
226 }
227
228 uint32_t cpuid_highest_ext_func_supported(void)
229 {
230         static uint32_t highest;
231
232         if (!highest) {
233                 asm volatile(
234                         "cpuid\n\t"
235                         : "=a" (highest)
236                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
237                 );
238         }
239
240         return highest;
241 }
242
243 void cpuid(cpuid_t info, uint32_t *buf)
244 {
245         /* Sanity checks, make sure we're not trying to do something
246          * invalid or we are trying to get information that isn't supported
247          * by the CPU.  */
248         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
249                 && !cpuid_test_feature(info)))
250                 return;
251
252         if (info == CPU_PROC_BRAND_STRING) {
253                 static char cached[48] = { 0 };
254                 if (cached[0] == '\0') {
255                         ___cpuid(CPU_PROC_BRAND_STRING,           &buf[0], &buf[1], &buf[2],  &buf[3]);
256                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6],  &buf[7]);
257                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
258
259                         memcpy(cached, buf, sizeof cached);
260                 } else
261                         buf = (uint32_t *)cached;
262
263                 return;
264         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
265                 *buf = cpuid_highest_ext_func_supported();
266                 return;
267         }
268
269         uint32_t eax, ebx, ecx, edx;
270         ___cpuid(info, &eax, &ebx, &ecx, &edx);
271
272         switch (info) {
273                 case CPU_VENDORID:
274                         buf[0] = ebx;
275                         buf[1] = edx;
276                         buf[2] = ecx;
277                         break;
278                 case CPU_PROCINFO_AND_FEATUREBITS:
279                         buf[0] = eax;   /* The so called "signature" of the CPU.  */
280                         buf[1] = edx;   /* Feature flags #1.  */
281                         buf[2] = ecx;   /* Feature flags #2.  */
282                         buf[3] = ebx;   /* Additional feature information.  */
283                         break;
284                 case CPU_CACHE_AND_TLBD_INFO:
285                         buf[0] = eax;
286                         buf[1] = ebx;
287                         buf[2] = ecx;
288                         buf[3] = edx;
289                         break;
290                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
291                         buf[0] = edx;
292                         buf[1] = ecx;
293                         break;
294                 case CPU_L1_CACHE_AND_TLB_IDS:
295                         buf[0] = eax;
296                         buf[1] = ebx;
297                         buf[2] = ecx;
298                         buf[3] = edx;
299                         break;
300                 case CPU_EXTENDED_L2_CACHE_FEATURES:
301                         *buf = ecx;
302                         break;
303                 case CPU_ADV_POWER_MGT_INFO:
304                         *buf = edx;
305                         break;
306                 case CPU_VIRT_PHYS_ADDR_SIZES:
307                         *buf = eax;
308                         break;
309                 default:
310                         *buf = 0xbaadf00d;
311                         break;
312         }
313 }
314
315 #endif