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