cpuid: use a sprintf-like function to get cputype as a string
[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 bool cpuid_sprintf_cputype(const cputype_t cputype, char *buf)
224 {
225         if (cputype == CT_NONE)
226                 return false;
227
228         memcpy(buf, cpuids[(int)cputype], 12);
229         buf[12] = '\0';
230         return true;
231 }
232
233 uint32_t cpuid_highest_ext_func_supported(void)
234 {
235         static uint32_t highest;
236
237         if (!highest) {
238                 asm volatile(
239                         "cpuid\n\t"
240                         : "=a" (highest)
241                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
242                 );
243         }
244
245         return highest;
246 }
247
248 void cpuid(cpuid_t info, uint32_t *buf)
249 {
250         /* Sanity checks, make sure we're not trying to do something
251          * invalid or we are trying to get information that isn't supported
252          * by the CPU.  */
253         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
254                 && !cpuid_test_feature(info)))
255                 return;
256
257         if (info == CPU_PROC_BRAND_STRING) {
258                 static char cached[48] = { 0 };
259                 if (cached[0] == '\0') {
260                         ___cpuid(CPU_PROC_BRAND_STRING,           &buf[0], &buf[1], &buf[2],  &buf[3]);
261                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6],  &buf[7]);
262                         ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
263
264                         memcpy(cached, buf, sizeof cached);
265                 } else
266                         buf = (uint32_t *)cached;
267
268                 return;
269         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
270                 *buf = cpuid_highest_ext_func_supported();
271                 return;
272         }
273
274         uint32_t eax, ebx, ecx, edx;
275         ___cpuid(info, &eax, &ebx, &ecx, &edx);
276
277         switch (info) {
278                 case CPU_VENDORID:
279                         buf[0] = ebx;
280                         buf[1] = edx;
281                         buf[2] = ecx;
282                         break;
283                 case CPU_PROCINFO_AND_FEATUREBITS:
284                         buf[0] = eax;   /* The so called "signature" of the CPU.  */
285                         buf[1] = edx;   /* Feature flags #1.  */
286                         buf[2] = ecx;   /* Feature flags #2.  */
287                         buf[3] = ebx;   /* Additional feature information.  */
288                         break;
289                 case CPU_CACHE_AND_TLBD_INFO:
290                         buf[0] = eax;
291                         buf[1] = ebx;
292                         buf[2] = ecx;
293                         buf[3] = edx;
294                         break;
295                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
296                         buf[0] = edx;
297                         buf[1] = ecx;
298                         break;
299                 case CPU_L1_CACHE_AND_TLB_IDS:
300                         buf[0] = eax;
301                         buf[1] = ebx;
302                         buf[2] = ecx;
303                         buf[3] = edx;
304                         break;
305                 case CPU_EXTENDED_L2_CACHE_FEATURES:
306                         buf[0] = ecx & 0xFF;            /* Line size.  */
307                         buf[1] = (ecx >> 12) & 0xFF;    /* Associativity.  */
308                         buf[2] = ecx >> 16;             /* Cache size.  */
309                         break;
310                 case CPU_ADV_POWER_MGT_INFO:
311                         *buf = edx;
312                         break;
313                 case CPU_VIRT_PHYS_ADDR_SIZES:
314                         buf[0] = eax & 0xFF;            /* physical.  */
315                         buf[1] = (eax >> 8) & 0xFF;     /* virtual.  */
316                         break;
317                 default:
318                         *buf = 0xbaadf00d;
319                         break;
320         }
321 }
322
323 #endif