]> git.ozlabs.org Git - ccan/blob - ccan/cpuid/cpuid.c
6a45116d710f08410d9bad56d0afdafd1c912fd2
[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 <stdint.h>
30 #include <string.h>
31
32 #include "cpuid.h"
33
34 enum {
35         CPU_PROC_BRAND_STRING_INTERNAL0                 = 0x80000003,
36         CPU_PROC_BRAND_STRING_INTERNAL1                 = 0x80000004
37 };
38
39 #ifndef _MSC_VER
40 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
41 {
42         __asm__(
43                 "xchg %%ebx, %%edi\n\t"         /* 32bit PIC: Don't clobber ebx.  */
44                 "cpuid\n\t"
45                 "xchg %%ebx, %%edi\n\t"
46                 : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
47                 : "0" (info)
48         );
49 }
50 #else
51 #include <intrin.h>
52
53 static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
54 {
55         uint32_t registers[4];
56         __cpuid(registers, info);
57
58         *eax = registers[0];
59         *ebx = registers[1];
60         *ecx = registers[2];
61         *edx = registers[3];
62 }
63 #endif
64
65 static struct {
66         int feature;
67         unsigned mask;
68         int instruction;        /* 0 = ecx, 1 = edx.  */
69 } features[] = {
70         { CF_MMX,               1 << 23,        1 },
71         { CF_SSE,               1 << 25,        1 },
72         { CF_SSE2,              1 << 26,        1 },
73         { CF_SSE3,              1 << 9,         0 },
74         { CF_FPU,               1 << 0,         1 },
75
76         { CF_TSC,               1 << 4,         1 },
77         { CF_MSR,               1 << 5,         1 },
78
79         { CF_SSSE3,             1 << 9,         0 },
80         { CF_AVX,               1 << 28,        0 },
81
82         /* Extended ones.  */
83         { CEF_x64,              1 << 30,        1 },
84         { CEF_FPU,              1 << 0,         1 },
85         { CEF_DE,               1 << 2,         1 },
86         { CEF_SYSCALLRET,       1 << 11,        1 },
87         { CEF_CMOV,             1 << 15,        1 },
88
89         { CEF_SSE4a,            1 << 6,         0 },
90         { CEF_FMA4,             1 << 16,        0 },
91         { CEF_XOP,              1 << 11,        0 }
92 };
93
94 static int has_feature(int feature, uint32_t ecx, uint32_t edx)
95 {
96         int i;
97
98         for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
99                 if (features[i].feature == feature) {
100                         if (features[i].instruction == 0)
101                                 return (ecx & features[i].mask);
102                         else
103                                 return (edx & features[i].mask);
104                 }
105         }
106
107         return 0;
108 }
109
110 int cpuid_test_feature(cpuid_t feature)
111 {
112         if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
113                 return 0;
114
115         return (feature <= cpuid_highest_ext_func_supported());
116 }
117
118 int cpuid_has_feature(int feature, int extended)
119 {
120         uint32_t eax, ebx, ecx, edx;
121
122         if (extended == 0)
123                 ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
124         else
125                 ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
126
127         return has_feature(feature, ecx, edx);
128 }
129
130 static const char *cpuids[] = {
131         "Nooooooooone",
132         "AMDisbetter!",
133         "AuthenticAMD",
134         "CentaurHauls",
135         "CyrixInstead",
136         "GenuineIntel",
137         "TransmetaCPU",
138         "GeniuneTMx86",
139         "Geode by NSC",
140         "NexGenDriven",
141         "RiseRiseRise",
142         "SiS SiS SiS ",
143         "UMC UMC UMC ",
144         "VIA VIA VIA ",
145         "Vortex86 SoC",
146         "KVMKVMKVMKVM"
147 };
148
149 cputype_t cpuid_get_cpu_type(void)
150 {
151         static cputype_t cputype;
152         if (cputype == CT_NONE) {
153                 union {
154                         char buf[12];
155                         uint32_t bufu32[3];
156                 } u;
157                 uint32_t i;
158
159                 ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
160                 u.buf[12] = '\0';
161
162                 for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
163                         if (strncmp(cpuids[i], u.buf, 12) == 0) {
164                                 cputype = (cputype_t)i;
165                                 break;
166                         }
167                 }
168         }
169
170         return cputype;
171 }
172
173 const char *cpuid_get_cpu_type_string(const cputype_t cputype)
174 {
175         return cpuids[(int)cputype];
176 }
177
178 int cpuid_highest_ext_func_supported(void)
179 {
180         static int highest;
181
182         if (!highest) {
183                 asm volatile(
184                         "cpuid\n\t"
185                         : "=a" (highest)
186                         : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
187                 );
188         }
189
190         return highest;
191 }
192
193 void cpuid(cpuid_t info, void *buf)
194 {
195         /* Sanity checks, make sure we're not trying to do something
196          * invalid or we are trying to get information that isn't supported
197          * by the CPU.  */
198         if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
199                 && !cpuid_test_feature(info)))
200                 return;
201
202         uint32_t *ubuf = buf;
203         if (info == CPU_PROC_BRAND_STRING) {
204                 ___cpuid(CPU_PROC_BRAND_STRING,           &ubuf[0], &ubuf[1], &ubuf[2],  &ubuf[3]);
205                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &ubuf[4], &ubuf[5], &ubuf[6],  &ubuf[7]);
206                 ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &ubuf[8], &ubuf[9], &ubuf[10], &ubuf[11]);
207                 return;
208         } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
209                 *ubuf = cpuid_highest_ext_func_supported();
210                 return;
211         }
212
213         uint32_t eax, ebx, ecx, edx;
214         ___cpuid(info, &eax, &ebx, &ecx, &edx);
215
216         switch (info) {
217                 case CPU_VENDORID:
218                         ubuf[0] = ebx;
219                         ubuf[1] = edx;
220                         ubuf[2] = ecx;
221                         break;
222                 case CPU_PROCINFO_AND_FEATUREBITS:
223                         ubuf[0] = eax;  /* The so called "signature" of the CPU.  */
224                         ubuf[1] = edx;  /* Feature flags #1.  */
225                         ubuf[2] = ecx;  /* Feature flags #2.  */
226                         ubuf[3] = ebx;  /* Additional feature information.  */
227                         break;
228                 case CPU_CACHE_AND_TLBD_INFO:
229                         ubuf[0] = eax;
230                         ubuf[1] = ebx;
231                         ubuf[2] = ecx;
232                         ubuf[3] = edx;
233                         break;
234                 case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
235                         ubuf[0] = edx;
236                         ubuf[1] = ecx;
237                         break;
238                 case CPU_L1_CACHE_AND_TLB_IDS:
239                         break;
240                 case CPU_EXTENDED_L2_CACHE_FEATURES:
241                         *ubuf = ecx;
242                         break;
243                 case CPU_ADV_POWER_MGT_INFO:
244                         *ubuf = edx;
245                         break;
246                 case CPU_VIRT_PHYS_ADDR_SIZES:
247                         *ubuf = eax;
248                         break;
249                 default:
250                         *ubuf = 0xbaadf00d;
251                         break;
252         }
253 }
254
255 #else
256 #warning "Cannot compile this file on a non-x86 machine"
257 #endif
258