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