blob: 74602101435452ed210ad5fd39f384626879985e [file] [log] [blame]
Guillaume Chatelet439d3712018-02-01 10:03:09 +01001// Copyright 2017 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "cpuinfo_x86.h"
16#include "internal/bit_utils.h"
17#include "internal/cpuid_x86.h"
18
19#include <stdbool.h>
20#include <string.h>
21
22static const Leaf kEmptyLeaf;
23
24static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
25 if (leaf_id <= max_cpuid_leaf) {
26 return CpuId(leaf_id);
27 } else {
28 return kEmptyLeaf;
29 }
30}
31
32#define MASK_XMM 0x2
33#define MASK_YMM 0x4
34#define MASK_MASKREG 0x20
35#define MASK_ZMM0_15 0x40
36#define MASK_ZMM16_31 0x80
37
38static bool HasMask(uint32_t value, uint32_t mask) {
39 return (value & mask) == mask;
40}
41
42// Checks that operating system saves and restores xmm registers during context
43// switches.
44static bool HasXmmOsXSave(uint32_t xcr0_eax) {
45 return HasMask(xcr0_eax, MASK_XMM);
46}
47
48// Checks that operating system saves and restores ymm registers during context
49// switches.
50static bool HasYmmOsXSave(uint32_t xcr0_eax) {
51 return HasMask(xcr0_eax, MASK_XMM | MASK_YMM);
52}
53
54// Checks that operating system saves and restores zmm registers during context
55// switches.
56static bool HasZmmOsXSave(uint32_t xcr0_eax) {
57 return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
58 MASK_ZMM16_31);
59}
60
61static void SetVendor(const Leaf leaf, char* const vendor) {
62 *(uint32_t*)(vendor) = leaf.ebx;
63 *(uint32_t*)(vendor + 4) = leaf.edx;
64 *(uint32_t*)(vendor + 8) = leaf.ecx;
65 vendor[12] = '\0';
66}
67
68static int IsVendor(const Leaf leaf, const char* const name) {
69 const uint32_t ebx = *(const uint32_t*)(name);
70 const uint32_t edx = *(const uint32_t*)(name + 4);
71 const uint32_t ecx = *(const uint32_t*)(name + 8);
72 return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
73}
74
75// Reference https://en.wikipedia.org/wiki/CPUID.
76static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info) {
77 const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
78 const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
79
80 const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
81 const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
82 const uint32_t xcr0_eax = (have_xsave && have_osxsave) ? GetXCR0Eax() : 0;
83 const bool have_sse_os_support = HasXmmOsXSave(xcr0_eax);
84 const bool have_avx_os_support = HasYmmOsXSave(xcr0_eax);
85 const bool have_avx512_os_support = HasZmmOsXSave(xcr0_eax);
86
87 const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
88 const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
89 const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
90 const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
91
92 X86Features* const features = &info->features;
93
94 info->family = extended_family + family;
95 info->model = (extended_model << 4) + model;
96 info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
97
98 features->aes = IsBitSet(leaf_1.ecx, 25);
99 features->erms = IsBitSet(leaf_7.ebx, 9);
100 features->f16c = IsBitSet(leaf_1.ecx, 29);
101 features->bmi1 = IsBitSet(leaf_7.ebx, 3);
102 features->bmi2 = IsBitSet(leaf_7.ebx, 8);
Guillaume Chatelet11e3e202018-02-09 08:55:11 +0100103 features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100104
Patrik Fiedler3ee0d622018-02-13 11:14:32 +0100105 features->smx = IsBitSet(leaf_1.ecx, 6);
106 features->sgx = IsBitSet(leaf_7.ebx, 2);
107
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100108 if (have_sse_os_support) {
109 features->ssse3 = IsBitSet(leaf_1.ecx, 9);
110 features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
111 features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
112 }
113
114 if (have_avx_os_support) {
115 features->fma3 = IsBitSet(leaf_1.ecx, 12);
116 features->avx = IsBitSet(leaf_1.ecx, 28);
117 features->avx2 = IsBitSet(leaf_7.ebx, 5);
118 }
119
120 if (have_avx512_os_support) {
121 features->avx512f = IsBitSet(leaf_7.ebx, 16);
122 features->avx512cd = IsBitSet(leaf_7.ebx, 28);
123 features->avx512er = IsBitSet(leaf_7.ebx, 27);
124 features->avx512pf = IsBitSet(leaf_7.ebx, 26);
125 features->avx512bw = IsBitSet(leaf_7.ebx, 30);
126 features->avx512dq = IsBitSet(leaf_7.ebx, 17);
127 features->avx512vl = IsBitSet(leaf_7.ebx, 31);
128 features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
129 features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
130 features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
131 features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
132 features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
133 features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
134 features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
135 features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
136 }
137}
138
139static const X86Info kEmptyX86Info;
140
141X86Info GetX86Info(void) {
142 X86Info info = kEmptyX86Info;
143 const Leaf leaf_0 = CpuId(0);
144 const uint32_t max_cpuid_leaf = leaf_0.eax;
145 SetVendor(leaf_0, info.vendor);
146 if (IsVendor(leaf_0, "GenuineIntel") || IsVendor(leaf_0, "AuthenticAMD")) {
147 ParseCpuId(max_cpuid_leaf, &info);
148 }
149 return info;
150}
151
152#define CPUID(FAMILY, MODEL) (((FAMILY & 0xFF) << 8) | (MODEL & 0xFF))
153
154X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
155 if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
156 switch (CPUID(info->family, info->model)) {
157 case CPUID(0x06, 0x35):
158 case CPUID(0x06, 0x36):
159 // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
160 return INTEL_ATOM_BNL;
161 case CPUID(0x06, 0x37):
162 case CPUID(0x06, 0x4C):
163 // https://en.wikipedia.org/wiki/Silvermont
164 return INTEL_ATOM_SMT;
165 case CPUID(0x06, 0x5C):
166 // https://en.wikipedia.org/wiki/Goldmont
167 return INTEL_ATOM_GMT;
168 case CPUID(0x06, 0x0F):
169 case CPUID(0x06, 0x16):
170 // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
171 return INTEL_CORE;
172 case CPUID(0x06, 0x17):
173 case CPUID(0x06, 0x1D):
174 // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
175 return INTEL_PNR;
176 case CPUID(0x06, 0x1A):
177 case CPUID(0x06, 0x1E):
178 case CPUID(0x06, 0x1F):
179 case CPUID(0x06, 0x2E):
180 // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
181 return INTEL_NHM;
182 case CPUID(0x06, 0x25):
183 case CPUID(0x06, 0x2C):
184 case CPUID(0x06, 0x2F):
185 // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
186 return INTEL_WSM;
187 case CPUID(0x06, 0x2A):
188 case CPUID(0x06, 0x2D):
189 // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
190 return INTEL_SNB;
191 case CPUID(0x06, 0x3A):
192 case CPUID(0x06, 0x3E):
193 // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
194 return INTEL_IVB;
195 case CPUID(0x06, 0x3C):
196 case CPUID(0x06, 0x3F):
197 case CPUID(0x06, 0x45):
198 case CPUID(0x06, 0x46):
199 // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
200 return INTEL_HSW;
201 case CPUID(0x06, 0x3D):
202 case CPUID(0x06, 0x47):
203 case CPUID(0x06, 0x4F):
204 case CPUID(0x06, 0x56):
205 // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
206 return INTEL_BDW;
207 case CPUID(0x06, 0x4E):
208 case CPUID(0x06, 0x55):
209 case CPUID(0x06, 0x5E):
210 // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
211 return INTEL_SKL;
212 case CPUID(0x06, 0x8E):
213 case CPUID(0x06, 0x9E):
214 // https://en.wikipedia.org/wiki/Kaby_Lake
215 return INTEL_KBL;
216 default:
217 return X86_UNKNOWN;
218 }
219 }
220 if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
221 switch (info->family) {
222 // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
223 case 0x0F:
224 return AMD_HAMMER;
225 case 0x10:
226 return AMD_K10;
227 case 0x14:
228 return AMD_BOBCAT;
229 case 0x15:
230 return AMD_BULLDOZER;
231 case 0x16:
232 return AMD_JAGUAR;
233 case 0x17:
234 return AMD_ZEN;
235 default:
236 return X86_UNKNOWN;
237 }
238 }
239 return X86_UNKNOWN;
240}
241
242static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
243 char* buffer) {
244 const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
245 // We allow calling memcpy from SetString which is only called when requesting
246 // X86BrandString.
247 memcpy(buffer, &leaf, sizeof(Leaf));
248}
249
250void FillX86BrandString(char brand_string[49]) {
251 const Leaf leaf_ext_0 = CpuId(0x80000000);
252 const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
253 SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
254 SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
255 SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
256 brand_string[48] = '\0';
257}
258
259////////////////////////////////////////////////////////////////////////////////
260// Introspection functions
261
262int GetX86FeaturesEnumValue(const X86Features* features,
263 X86FeaturesEnum value) {
264 switch (value) {
265 case X86_AES:
266 return features->aes;
267 case X86_ERMS:
268 return features->erms;
269 case X86_F16C:
270 return features->f16c;
271 case X86_FMA3:
272 return features->fma3;
Guillaume Chatelet11e3e202018-02-09 08:55:11 +0100273 case X86_VPCLMULQDQ:
274 return features->vpclmulqdq;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100275 case X86_BMI1:
276 return features->bmi1;
277 case X86_BMI2:
278 return features->bmi2;
279 case X86_SSSE3:
280 return features->ssse3;
281 case X86_SSE4_1:
282 return features->sse4_1;
283 case X86_SSE4_2:
284 return features->sse4_2;
285 case X86_AVX:
286 return features->avx;
287 case X86_AVX2:
288 return features->avx2;
289 case X86_AVX512F:
290 return features->avx512f;
291 case X86_AVX512CD:
292 return features->avx512cd;
293 case X86_AVX512ER:
294 return features->avx512er;
295 case X86_AVX512PF:
296 return features->avx512pf;
297 case X86_AVX512BW:
298 return features->avx512bw;
299 case X86_AVX512DQ:
300 return features->avx512dq;
301 case X86_AVX512VL:
302 return features->avx512vl;
303 case X86_AVX512IFMA:
304 return features->avx512ifma;
305 case X86_AVX512VBMI:
306 return features->avx512vbmi;
307 case X86_AVX512VBMI2:
308 return features->avx512vbmi2;
309 case X86_AVX512VNNI:
310 return features->avx512vnni;
311 case X86_AVX512BITALG:
312 return features->avx512bitalg;
313 case X86_AVX512VPOPCNTDQ:
314 return features->avx512vpopcntdq;
315 case X86_AVX512_4VNNIW:
316 return features->avx512_4vnniw;
317 case X86_AVX512_4VBMI2:
318 return features->avx512_4vbmi2;
Patrik Fiedler3ee0d622018-02-13 11:14:32 +0100319 case X86_SMX:
320 return features->smx;
321 case X86_SGX:
322 return features->sgx;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100323 case X86_LAST_:
324 break;
325 }
326 return false;
327}
328
329const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
330 switch (value) {
331 case X86_AES:
332 return "aes";
333 case X86_ERMS:
334 return "erms";
335 case X86_F16C:
336 return "f16c";
337 case X86_FMA3:
338 return "fma3";
Guillaume Chatelet11e3e202018-02-09 08:55:11 +0100339 case X86_VPCLMULQDQ:
340 return "vpclmulqdq";
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100341 case X86_BMI1:
342 return "bmi1";
343 case X86_BMI2:
344 return "bmi2";
345 case X86_SSSE3:
346 return "ssse3";
347 case X86_SSE4_1:
348 return "sse4_1";
349 case X86_SSE4_2:
350 return "sse4_2";
351 case X86_AVX:
352 return "avx";
353 case X86_AVX2:
354 return "avx2";
355 case X86_AVX512F:
356 return "avx512f";
357 case X86_AVX512CD:
358 return "avx512cd";
359 case X86_AVX512ER:
360 return "avx512er";
361 case X86_AVX512PF:
362 return "avx512pf";
363 case X86_AVX512BW:
364 return "avx512bw";
365 case X86_AVX512DQ:
366 return "avx512dq";
367 case X86_AVX512VL:
368 return "avx512vl";
369 case X86_AVX512IFMA:
370 return "avx512ifma";
371 case X86_AVX512VBMI:
372 return "avx512vbmi";
373 case X86_AVX512VBMI2:
374 return "avx512vbmi2";
375 case X86_AVX512VNNI:
376 return "avx512vnni";
377 case X86_AVX512BITALG:
378 return "avx512bitalg";
379 case X86_AVX512VPOPCNTDQ:
380 return "avx512vpopcntdq";
381 case X86_AVX512_4VNNIW:
382 return "avx512_4vnniw";
383 case X86_AVX512_4VBMI2:
384 return "avx512_4vbmi2";
Patrik Fiedler3ee0d622018-02-13 11:14:32 +0100385 case X86_SMX:
386 return "smx";
387 case X86_SGX:
388 return "sgx";
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100389 case X86_LAST_:
390 break;
391 }
392 return "unknown_feature";
393}
394
395const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
396 switch (uarch) {
397 case X86_UNKNOWN:
398 return "X86_UNKNOWN";
399 case INTEL_CORE:
400 return "INTEL_CORE";
401 case INTEL_PNR:
402 return "INTEL_PNR";
403 case INTEL_NHM:
404 return "INTEL_NHM";
405 case INTEL_ATOM_BNL:
406 return "INTEL_ATOM_BNL";
407 case INTEL_WSM:
408 return "INTEL_WSM";
409 case INTEL_SNB:
410 return "INTEL_SNB";
411 case INTEL_IVB:
412 return "INTEL_IVB";
413 case INTEL_ATOM_SMT:
414 return "INTEL_ATOM_SMT";
415 case INTEL_HSW:
416 return "INTEL_HSW";
417 case INTEL_BDW:
418 return "INTEL_BDW";
419 case INTEL_SKL:
420 return "INTEL_SKL";
421 case INTEL_ATOM_GMT:
422 return "INTEL_ATOM_GMT";
423 case INTEL_KBL:
424 return "INTEL_KBL";
425 case INTEL_CFL:
426 return "INTEL_CFL";
427 case INTEL_CNL:
428 return "INTEL_CNL";
429 case AMD_HAMMER:
430 return "AMD_HAMMER";
431 case AMD_K10:
432 return "AMD_K10";
433 case AMD_BOBCAT:
434 return "AMD_BOBCAT";
435 case AMD_BULLDOZER:
436 return "AMD_BULLDOZER";
437 case AMD_JAGUAR:
438 return "AMD_JAGUAR";
439 case AMD_ZEN:
440 return "AMD_ZEN";
441 }
442 return "unknown microarchitecture";
443}