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