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