cpuid: better parser for processor info
[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 #include <stdio.h>
33
34 enum {
35         CPU_PROC_BRAND_STRING_INTERNAL0                 = 0x80000003,
36         CPU_PROC_BRAND_STRING_INTERNAL1                 = 0x80000004
37 };
38
39 #ifndef _MSC_VER
40 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
41 {
42         __asm__(
43                 "xchg %%ebx, %%edi\n\t"         /* 32bit PIC: Don't clobber ebx.  */
44                 "cpuid\n\t"
45                 "xchg %%ebx, %%edi\n\t"
46                 : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
47                 : "0" (info)
48         );
49 }
50 #else
51 #include <intrin.h>
52
53 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
54 {
55         uint32_t registers[4];
56         __cpuid(registers, info);
57
58         *eax = registers[0];
59         *ebx = registers[1];
60         *ecx = registers[2];
61         *edx = registers[3];
62 }
63 #endif
64
65 static struct {
66         uint32_t feature;
67         uint32_t mask;
68         bool use_edx;           /* ecx will be used if false.  */
69 } features[] = {
70         { CF_MMX,               1 << 23,        true },
71         { CF_SSE,               1 << 25,        true },
72         { CF_SSE2,              1 << 26,        true },
73         { CF_SSE3,              1 << 9,         false },
74         { CF_FPU,               1 << 0,         true },
75
76         { CF_TSC,               1 << 4,         true },
77         { CF_MSR,               1 << 5,         true },
78
79         { CF_SSSE3,             1 << 9,         false },
80         { CF_AVX,               1 << 28,        false },
81
82         /* Extended ones.  */
83         { CEF_x64,              1 << 30,        true },
84         { CEF_FPU,              1 << 0,         true },
85         { CEF_DE,               1 << 2,         true },
86         { CEF_SYSCALLRET,       1 << 11,        true },
87         { CEF_CMOV,             1 << 15,        true },
88
89         { CEF_SSE4a,            1 << 6,         false },
90         { CEF_FMA4,             1 << 16,        false },
91         { CEF_XOP,              1 << 11,        false }
92 };
93
94 bool cpuid_is_supported(void)
95 {
96         int ret = 0;
97 #if defined(__GNUC__) || defined(__clang__)
98         /* The following assembly code uses EAX as the return value,
99          * but we store the value of EAX into ret since GCC uses EAX
100          * as the return register for every C function.  That's a double
101          * operation, but there's no other way to do this unless doing this
102          * function entirely in assembly.
103          *
104          * The following assembly code has been shamelessly stolen from:
105          *      http://wiki.osdev.org/CPUID
106          * and converted to work with AT&T syntax.
107          *
108          * This check is to make sure that the compiler is actually compiling
109          * for 64-bit.
110          *
111          * The compiler can be 32-bit and the system 64-bit so the 
112          * following would be true:
113          *      #if defined(__x86_64) ...
114          */
115
116 #if UINTPTR_MAX == 0xffffffffffffffff
117 #define ASM_PUSHF       "pushfq\n\t"
118 #define ASM_POPF        "popfq\n\t"
119 #define ASM_PUSHEAX     "pushq %%rax\n\t"
120 #define ASM_POPEAX      "popq %%rax\n\t"
121 #define ASM_PUSHECX     "pushq %%rcx\n\t"
122 #elif UINTPTR_MAX == 0xffffffff
123 #define ASM_PUSHF       "pushfl\n\t"
124 #define ASM_POPF        "popfl\n\t"
125 #define ASM_PUSHEAX     "pushl %%eax\n\t"
126 #define ASM_POPEAX      "popl %%eax\n\t"
127 #define ASM_PUSHECX     "pushl %%ecx\n\t"
128 #endif
129
130         asm volatile(
131                 ASM_PUSHF
132                 ASM_POPEAX
133                 "movl %%eax, %%ecx\n\t"
134                 "xorl $0x200000, %%eax\n\t"
135                 ASM_PUSHEAX
136                 ASM_POPF
137                 ASM_PUSHF
138                 ASM_POPEAX
139                 "xorl %%ecx, %%eax\n\t"
140                 "shrl $21, %%eax\n\t"
141                 "andl $1, %%eax\n\t"
142                 ASM_PUSHECX
143                 ASM_POPF
144                 : "=a" (ret)
145         );
146
147 #undef ASM_PUSHF
148 #undef ASM_POPF
149 #undef ASM_PUSHEAX
150 #undef ASM_POPEAX
151 #undef ASM_PUSHECX
152 #elif defined _MSC_VER
153         __asm {
154                 pushfd
155                 pop eax
156                 mov ecx, eax
157                 xor eax, 0x200000
158                 push eax
159                 popfd
160
161                 pushfd
162                 pop eax
163                 xor eax, ecx
164                 shr eax, 0x21
165                 and eax, 0x1
166                 push ecx
167                 popfd
168
169                 mov eax, ret
170         };
171 #endif
172         return !!ret;
173 }
174
175 bool cpuid_test_feature(cpuid_t feature)
176 {
177         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
178                 return false;
179
180         return (feature <= cpuid_highest_ext_func_supported());
181 }
182
183 bool cpuid_has_feature(int feature, bool extended)
184 {
185         uint32_t eax, ebx, ecx, edx, i;
186
187         if (!extended)
188                 ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
189         else
190                 ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
191
192         for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
193                 if (features[i].feature == feature) {
194                         if (features[i].use_edx)
195                                 return (edx & features[i].mask);
196                         else
197                                 return (ecx & features[i].mask);
198                 }
199         }
200         return false;
201 }
202
203 static const char *const cpuids[] = {
204         "Nooooooooone",
205         "AMDisbetter!",
206         "AuthenticAMD",
207         "CentaurHauls",
208         "CyrixInstead",
209         "GenuineIntel",
210         "TransmetaCPU",
211         "GeniuneTMx86",
212         "Geode by NSC",
213         "NexGenDriven",
214         "RiseRiseRise",
215         "SiS SiS SiS ",
216         "UMC UMC UMC ",
217         "VIA VIA VIA ",
218         "Vortex86 SoC",
219         "KVMKVMKVMKVM"
220 };
221
222 cputype_t cpuid_get_cpu_type(void)
223 {
224         static cputype_t cputype;
225         if (cputype == CT_NONE) {
226                 union {
227                         char buf[12];
228                         uint32_t bufu32[3];
229                 } u;
230                 uint32_t i;
231
232                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
233                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
234                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
235                                 cputype = (cputype_t)i;
236                                 break;
237                         }
238                 }
239         }
240
241         return cputype;
242 }
243
244 bool cpuid_sprintf_cputype(const cputype_t cputype, char *buf)
245 {
246         if (cputype == CT_NONE)
247                 return false;
248
249         memcpy(buf, cpuids[(int)cputype], 12);
250         buf[12] = '\0';
251         return true;
252 }
253
254 uint32_t cpuid_highest_ext_func_supported(void)
255 {
256         static uint32_t highest;
257
258         if (!highest) {
259 #if defined(__GNUC__) || defined(__clang__)
260                 asm volatile(
261                         "cpuid\n\t"
262                         : "=a" (highest)
263                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
264                 );
265 #elif defined _MSC_VER
266                 __asm {
267                         mov eax, CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
268                         cpuid
269                         mov highest, eax
270                 };
271 #endif
272         }
273
274         return highest;
275 }
276
277 void cpuid(cpuid_t info, uint32_t *buf)
278 {
279         /* Sanity checks, make sure we're not trying to do something
280          * invalid or we are trying to get information that isn't supported
281          * by the CPU.  */
282         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
283                 && !cpuid_test_feature(info)))
284                 return;
285
286         if (info == CPU_PROC_BRAND_STRING) {
287                 static char cached[48] = { 0 };
288                 if (cached[0] == '\0') {
289                         ___cpuid(CPU_PROC_BRAND_STRING,           &buf[0], &buf[1], &buf[2],  &buf[3]);
290                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6],  &buf[7]);
291                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
292
293                         memcpy(cached, buf, sizeof cached);
294                 } else
295                         buf = (uint32_t *)cached;
296
297                 return;
298         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
299                 *buf = cpuid_highest_ext_func_supported();
300                 return;
301         }
302
303         uint32_t eax, ebx, ecx, edx;
304         ___cpuid(info, &eax, &ebx, &ecx, &edx);
305
306         switch (info) {
307                 case CPU_VENDORID:
308                         buf[0] = ebx;
309                         buf[1] = edx;
310                         buf[2] = ecx;
311                         break;
312                 case CPU_PROCINFO_AND_FEATUREBITS:
313                         buf[0] = (eax & 0x0F);          /* Stepping  */
314                         buf[1] = (eax >> 4)  & 0x0F;    /* Model  */
315                         buf[2] = (eax >> 8)  & 0x0F;    /* Family  */
316                         buf[3] = (eax >> 16) & 0x0F;    /* Extended Model.  */
317                         buf[4] = (eax >> 24) & 0x0F;    /* Extended Family.  */
318
319                         buf[5] = edx;                   /* Feature flags #1.  */
320                         buf[6] = ecx;                   /* Feature flags #2.  */
321                         buf[7] = ebx;                   /* Additional feature information.  */
322                         break;
323                 case CPU_CACHE_AND_TLBD_INFO:
324                         buf[0] = eax;
325                         buf[1] = ebx;
326                         buf[2] = ecx;
327                         buf[3] = edx;
328                         break;
329                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
330                         buf[0] = edx;
331                         buf[1] = ecx;
332                         break;
333                 case CPU_L1_CACHE_AND_TLB_IDS:
334                         buf[0] = eax;
335                         buf[1] = ebx;
336                         buf[2] = ecx;
337                         buf[3] = edx;
338                         break;
339                 case CPU_EXTENDED_L2_CACHE_FEATURES:
340                         buf[0] = ecx & 0xFF;            /* Line size.  */
341                         buf[1] = (ecx >> 12) & 0xFF;    /* Associativity.  */
342                         buf[2] = ecx >> 16;             /* Cache size.  */
343                         break;
344                 case CPU_ADV_POWER_MGT_INFO:
345                         *buf = edx;
346                         break;
347                 case CPU_VIRT_PHYS_ADDR_SIZES:
348                         buf[0] = eax & 0xFF;            /* physical.  */
349                         buf[1] = (eax >> 8) & 0xFF;     /* virtual.  */
350                         break;
351                 default:
352                         *buf = 0xbaadf00d;
353                         break;
354         }
355 }
356
357 #endif