Merge branch 'cpuid' of https://github.com/decltype/ccan
[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 static bool has_feature(int feature, uint32_t ecx, uint32_t edx)
94 {
95         int i;
96
97         for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
98                 if (features[i].feature == feature) {
99                         if (features[i].use_edx)
100                                 return (edx & features[i].mask);
101                         else
102                                 return (ecx & features[i].mask);
103                 }
104         }
105
106         return false;
107 }
108
109 bool cpuid_is_supported(void)
110 {
111         int ret = 0;
112
113         asm volatile(
114                 "pushfl\n\t"
115                 "popl %%eax\n\t"
116                 "movl %%eax, %%ecx\n\t"
117                 "xorl $0x200000, %%eax\n\t"
118                 "pushl %%eax\n\t"
119                 "popfl\n\t"
120
121                 "pushfl\n\t"
122                 "popl %%eax\n\t"
123                 "xorl %%ecx, %%eax\n\t"
124                 "shrl $21, %%eax\n\t"
125                 "andl $1, %%eax\n\t"
126                 "pushl %%ecx\n\t"
127                 "popfl\n\t"
128
129                 : "=a" (ret)
130         );
131
132         return !!ret;
133 }
134
135 bool cpuid_test_feature(cpuid_t feature)
136 {
137         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
138                 return false;
139
140         return (feature <= cpuid_highest_ext_func_supported());
141 }
142
143 bool cpuid_has_feature(int feature, bool extended)
144 {
145         uint32_t eax, ebx, ecx, edx;
146
147         if (!extended)
148                 ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
149         else
150                 ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
151
152         return has_feature(feature, ecx, edx);
153 }
154
155 static const char *const cpuids[] = {
156         "Nooooooooone",
157         "AMDisbetter!",
158         "AuthenticAMD",
159         "CentaurHauls",
160         "CyrixInstead",
161         "GenuineIntel",
162         "TransmetaCPU",
163         "GeniuneTMx86",
164         "Geode by NSC",
165         "NexGenDriven",
166         "RiseRiseRise",
167         "SiS SiS SiS ",
168         "UMC UMC UMC ",
169         "VIA VIA VIA ",
170         "Vortex86 SoC",
171         "KVMKVMKVMKVM"
172 };
173
174 cputype_t cpuid_get_cpu_type(void)
175 {
176         static cputype_t cputype;
177         if (cputype == CT_NONE) {
178                 union {
179                         char buf[12];
180                         uint32_t bufu32[3];
181                 } u;
182                 uint32_t i;
183
184                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
185                 u.buf[12] = '\0';
186
187                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
188                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
189                                 cputype = (cputype_t)i;
190                                 break;
191                         }
192                 }
193         }
194
195         return cputype;
196 }
197
198 const char *cpuid_get_cpu_type_string(const cputype_t cputype)
199 {
200         return cpuids[(int)cputype];
201 }
202
203 uint32_t cpuid_highest_ext_func_supported(void)
204 {
205         static uint32_t highest;
206
207         if (!highest) {
208                 asm volatile(
209                         "cpuid\n\t"
210                         : "=a" (highest)
211                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
212                 );
213         }
214
215         return highest;
216 }
217
218 void cpuid(cpuid_t info, uint32_t *buf)
219 {
220         /* Sanity checks, make sure we're not trying to do something
221          * invalid or we are trying to get information that isn't supported
222          * by the CPU.  */
223         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
224                 && !cpuid_test_feature(info)))
225                 return;
226
227         if (info == CPU_PROC_BRAND_STRING) {
228                 ___cpuid(CPU_PROC_BRAND_STRING,           &buf[0], &buf[1], &buf[2],  &buf[3]);
229                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6],  &buf[7]);
230                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
231                 return;
232         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
233                 *buf = cpuid_highest_ext_func_supported();
234                 return;
235         }
236
237         uint32_t eax, ebx, ecx, edx;
238         ___cpuid(info, &eax, &ebx, &ecx, &edx);
239
240         switch (info) {
241                 case CPU_VENDORID:
242                         buf[0] = ebx;
243                         buf[1] = edx;
244                         buf[2] = ecx;
245                         break;
246                 case CPU_PROCINFO_AND_FEATUREBITS:
247                         buf[0] = eax;   /* The so called "signature" of the CPU.  */
248                         buf[1] = edx;   /* Feature flags #1.  */
249                         buf[2] = ecx;   /* Feature flags #2.  */
250                         buf[3] = ebx;   /* Additional feature information.  */
251                         break;
252                 case CPU_CACHE_AND_TLBD_INFO:
253                         buf[0] = eax;
254                         buf[1] = ebx;
255                         buf[2] = ecx;
256                         buf[3] = edx;
257                         break;
258                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
259                         buf[0] = edx;
260                         buf[1] = ecx;
261                         break;
262                 case CPU_L1_CACHE_AND_TLB_IDS:
263                         buf[0] = eax;
264                         buf[1] = ebx;
265                         buf[2] = ecx;
266                         buf[3] = edx;
267                         break;
268                 case CPU_EXTENDED_L2_CACHE_FEATURES:
269                         *buf = ecx;
270                         break;
271                 case CPU_ADV_POWER_MGT_INFO:
272                         *buf = edx;
273                         break;
274                 case CPU_VIRT_PHYS_ADDR_SIZES:
275                         *buf = eax;
276                         break;
277                 default:
278                         *buf = 0xbaadf00d;
279                         break;
280         }
281 }
282
283 #endif
284