]> git.ozlabs.org Git - ccan/blob - ccan/cpuid/cpuid.c
cpuid: fix build on 64-bit systems
[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 static bool has_feature(int feature, uint32_t ecx, uint32_t edx)
94 {
95         int i;
96
97         for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
98                 if (features[i].feature == feature) {
99                         if (features[i].use_edx)
100                                 return (edx & features[i].mask);
101                         else
102                                 return (ecx & features[i].mask);
103                 }
104         }
105
106         return false;
107 }
108
109 bool cpuid_is_supported(void)
110 {
111         /* The following assembly code uses EAX as the return value,
112          * but we store the value of EAX into ret since GCC uses EAX
113          * as the return register for every C function.  That's a double
114          * operation, but there's no other way to do this unless doing this
115          * function entirely in assembly.  */
116
117         /* This check is to make sure that the compiler is actually compiling
118          * for 64-bit.
119          *
120          * The compiler can be 32-bit and the system 64-bit so the 
121          * following would be true:
122          *      #if defined(__x86_64) ...
123          */
124
125 #if UINTPTR_MAX == 0xffffffffffffffff
126 #define ASM_PUSHF       "pushfq\n\t"
127 #define ASM_POPF        "popfq\n\t"
128 #define ASM_PUSHEAX     "pushq %%rax\n\t"
129 #define ASM_POPEAX      "popq %%rax\n\t"
130 #define ASM_PUSHECX     "popq %%rcx\n\t"
131 #elif UINTPTR_MAX == 0xffffffff
132 #define ASM_PUSHF       "pushfl\n\t"
133 #define ASM_POPF        "popfl\n\t"
134 #define ASM_PUSHEAX     "pushl %%eax\n\t"
135 #define ASM_POPEAX      "popl %%eax\n\t"
136 #define ASM_PUSHECX     "popl %%ecx\n\t"
137 #endif
138
139         int ret = 0;
140         asm volatile(
141                 ASM_PUSHF
142                 ASM_POPEAX
143                 "movl %%eax, %%ecx\n\t"
144                 "xorl $0x200000, %%eax\n\t"
145                 ASM_PUSHEAX
146                 ASM_POPF
147                 ASM_PUSHF
148                 ASM_POPEAX
149                 "xorl %%ecx, %%eax\n\t"
150                 "shrl $21, %%eax\n\t"
151                 "andl $1, %%eax\n\t"
152                 ASM_PUSHECX
153                 ASM_POPF
154                 : "=a" (ret)
155         );
156
157 #undef ASM_PUSHF
158 #undef ASM_POPF
159 #undef ASM_PUSHEAX
160 #undef ASM_POPEAX
161 #undef ASM_PUSHECX
162
163         return !!ret;
164 }
165
166 bool cpuid_test_feature(cpuid_t feature)
167 {
168         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
169                 return false;
170
171         return (feature <= cpuid_highest_ext_func_supported());
172 }
173
174 bool cpuid_has_feature(int feature, bool extended)
175 {
176         uint32_t eax, ebx, ecx, edx;
177
178         if (!extended)
179                 ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
180         else
181                 ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
182
183         return has_feature(feature, ecx, edx);
184 }
185
186 static const char *const cpuids[] = {
187         "Nooooooooone",
188         "AMDisbetter!",
189         "AuthenticAMD",
190         "CentaurHauls",
191         "CyrixInstead",
192         "GenuineIntel",
193         "TransmetaCPU",
194         "GeniuneTMx86",
195         "Geode by NSC",
196         "NexGenDriven",
197         "RiseRiseRise",
198         "SiS SiS SiS ",
199         "UMC UMC UMC ",
200         "VIA VIA VIA ",
201         "Vortex86 SoC",
202         "KVMKVMKVMKVM"
203 };
204
205 cputype_t cpuid_get_cpu_type(void)
206 {
207         static cputype_t cputype;
208         if (cputype == CT_NONE) {
209                 union {
210                         char buf[12];
211                         uint32_t bufu32[3];
212                 } u;
213                 uint32_t i;
214
215                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
216                 u.buf[12] = '\0';
217
218                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
219                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
220                                 cputype = (cputype_t)i;
221                                 break;
222                         }
223                 }
224         }
225
226         return cputype;
227 }
228
229 const char *cpuid_get_cpu_type_string(const cputype_t cputype)
230 {
231         return cpuids[(int)cputype];
232 }
233
234 uint32_t cpuid_highest_ext_func_supported(void)
235 {
236         static uint32_t highest;
237
238         if (!highest) {
239                 asm volatile(
240                         "cpuid\n\t"
241                         : "=a" (highest)
242                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
243                 );
244         }
245
246         return highest;
247 }
248
249 void cpuid(cpuid_t info, uint32_t *buf)
250 {
251         /* Sanity checks, make sure we're not trying to do something
252          * invalid or we are trying to get information that isn't supported
253          * by the CPU.  */
254         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
255                 && !cpuid_test_feature(info)))
256                 return;
257
258         if (info == CPU_PROC_BRAND_STRING) {
259                 ___cpuid(CPU_PROC_BRAND_STRING,           &buf[0], &buf[1], &buf[2],  &buf[3]);
260                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6],  &buf[7]);
261                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
262                 return;
263         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
264                 *buf = cpuid_highest_ext_func_supported();
265                 return;
266         }
267
268         uint32_t eax, ebx, ecx, edx;
269         ___cpuid(info, &eax, &ebx, &ecx, &edx);
270
271         switch (info) {
272                 case CPU_VENDORID:
273                         buf[0] = ebx;
274                         buf[1] = edx;
275                         buf[2] = ecx;
276                         break;
277                 case CPU_PROCINFO_AND_FEATUREBITS:
278                         buf[0] = eax;   /* The so called "signature" of the CPU.  */
279                         buf[1] = edx;   /* Feature flags #1.  */
280                         buf[2] = ecx;   /* Feature flags #2.  */
281                         buf[3] = ebx;   /* Additional feature information.  */
282                         break;
283                 case CPU_CACHE_AND_TLBD_INFO:
284                         buf[0] = eax;
285                         buf[1] = ebx;
286                         buf[2] = ecx;
287                         buf[3] = edx;
288                         break;
289                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
290                         buf[0] = edx;
291                         buf[1] = ecx;
292                         break;
293                 case CPU_L1_CACHE_AND_TLB_IDS:
294                         buf[0] = eax;
295                         buf[1] = ebx;
296                         buf[2] = ecx;
297                         buf[3] = edx;
298                         break;
299                 case CPU_EXTENDED_L2_CACHE_FEATURES:
300                         *buf = ecx;
301                         break;
302                 case CPU_ADV_POWER_MGT_INFO:
303                         *buf = edx;
304                         break;
305                 case CPU_VIRT_PHYS_ADDR_SIZES:
306                         *buf = eax;
307                         break;
308                 default:
309                         *buf = 0xbaadf00d;
310                         break;
311         }
312 }
313
314 #endif
315