blob: 3238ab12fa370d916afea991ba0fc08a46a289be [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
105 if (have_sse_os_support) {
106 features->ssse3 = IsBitSet(leaf_1.ecx, 9);
107 features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
108 features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
109 }
110
111 if (have_avx_os_support) {
112 features->fma3 = IsBitSet(leaf_1.ecx, 12);
113 features->avx = IsBitSet(leaf_1.ecx, 28);
114 features->avx2 = IsBitSet(leaf_7.ebx, 5);
115 }
116
117 if (have_avx512_os_support) {
118 features->avx512f = IsBitSet(leaf_7.ebx, 16);
119 features->avx512cd = IsBitSet(leaf_7.ebx, 28);
120 features->avx512er = IsBitSet(leaf_7.ebx, 27);
121 features->avx512pf = IsBitSet(leaf_7.ebx, 26);
122 features->avx512bw = IsBitSet(leaf_7.ebx, 30);
123 features->avx512dq = IsBitSet(leaf_7.ebx, 17);
124 features->avx512vl = IsBitSet(leaf_7.ebx, 31);
125 features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
126 features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
127 features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
128 features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
129 features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
130 features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
131 features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
132 features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
133 }
134}
135
136static const X86Info kEmptyX86Info;
137
138X86Info GetX86Info(void) {
139 X86Info info = kEmptyX86Info;
140 const Leaf leaf_0 = CpuId(0);
141 const uint32_t max_cpuid_leaf = leaf_0.eax;
142 SetVendor(leaf_0, info.vendor);
143 if (IsVendor(leaf_0, "GenuineIntel") || IsVendor(leaf_0, "AuthenticAMD")) {
144 ParseCpuId(max_cpuid_leaf, &info);
145 }
146 return info;
147}
148
149#define CPUID(FAMILY, MODEL) (((FAMILY & 0xFF) << 8) | (MODEL & 0xFF))
150
151X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
152 if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
153 switch (CPUID(info->family, info->model)) {
154 case CPUID(0x06, 0x35):
155 case CPUID(0x06, 0x36):
156 // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
157 return INTEL_ATOM_BNL;
158 case CPUID(0x06, 0x37):
159 case CPUID(0x06, 0x4C):
160 // https://en.wikipedia.org/wiki/Silvermont
161 return INTEL_ATOM_SMT;
162 case CPUID(0x06, 0x5C):
163 // https://en.wikipedia.org/wiki/Goldmont
164 return INTEL_ATOM_GMT;
165 case CPUID(0x06, 0x0F):
166 case CPUID(0x06, 0x16):
167 // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
168 return INTEL_CORE;
169 case CPUID(0x06, 0x17):
170 case CPUID(0x06, 0x1D):
171 // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
172 return INTEL_PNR;
173 case CPUID(0x06, 0x1A):
174 case CPUID(0x06, 0x1E):
175 case CPUID(0x06, 0x1F):
176 case CPUID(0x06, 0x2E):
177 // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
178 return INTEL_NHM;
179 case CPUID(0x06, 0x25):
180 case CPUID(0x06, 0x2C):
181 case CPUID(0x06, 0x2F):
182 // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
183 return INTEL_WSM;
184 case CPUID(0x06, 0x2A):
185 case CPUID(0x06, 0x2D):
186 // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
187 return INTEL_SNB;
188 case CPUID(0x06, 0x3A):
189 case CPUID(0x06, 0x3E):
190 // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
191 return INTEL_IVB;
192 case CPUID(0x06, 0x3C):
193 case CPUID(0x06, 0x3F):
194 case CPUID(0x06, 0x45):
195 case CPUID(0x06, 0x46):
196 // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
197 return INTEL_HSW;
198 case CPUID(0x06, 0x3D):
199 case CPUID(0x06, 0x47):
200 case CPUID(0x06, 0x4F):
201 case CPUID(0x06, 0x56):
202 // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
203 return INTEL_BDW;
204 case CPUID(0x06, 0x4E):
205 case CPUID(0x06, 0x55):
206 case CPUID(0x06, 0x5E):
207 // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
208 return INTEL_SKL;
209 case CPUID(0x06, 0x8E):
210 case CPUID(0x06, 0x9E):
211 // https://en.wikipedia.org/wiki/Kaby_Lake
212 return INTEL_KBL;
213 default:
214 return X86_UNKNOWN;
215 }
216 }
217 if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
218 switch (info->family) {
219 // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
220 case 0x0F:
221 return AMD_HAMMER;
222 case 0x10:
223 return AMD_K10;
224 case 0x14:
225 return AMD_BOBCAT;
226 case 0x15:
227 return AMD_BULLDOZER;
228 case 0x16:
229 return AMD_JAGUAR;
230 case 0x17:
231 return AMD_ZEN;
232 default:
233 return X86_UNKNOWN;
234 }
235 }
236 return X86_UNKNOWN;
237}
238
239static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
240 char* buffer) {
241 const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
242 // We allow calling memcpy from SetString which is only called when requesting
243 // X86BrandString.
244 memcpy(buffer, &leaf, sizeof(Leaf));
245}
246
247void FillX86BrandString(char brand_string[49]) {
248 const Leaf leaf_ext_0 = CpuId(0x80000000);
249 const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
250 SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
251 SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
252 SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
253 brand_string[48] = '\0';
254}
255
256////////////////////////////////////////////////////////////////////////////////
257// Introspection functions
258
259int GetX86FeaturesEnumValue(const X86Features* features,
260 X86FeaturesEnum value) {
261 switch (value) {
262 case X86_AES:
263 return features->aes;
264 case X86_ERMS:
265 return features->erms;
266 case X86_F16C:
267 return features->f16c;
268 case X86_FMA3:
269 return features->fma3;
Guillaume Chatelet11e3e202018-02-09 08:55:11 +0100270 case X86_VPCLMULQDQ:
271 return features->vpclmulqdq;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100272 case X86_BMI1:
273 return features->bmi1;
274 case X86_BMI2:
275 return features->bmi2;
276 case X86_SSSE3:
277 return features->ssse3;
278 case X86_SSE4_1:
279 return features->sse4_1;
280 case X86_SSE4_2:
281 return features->sse4_2;
282 case X86_AVX:
283 return features->avx;
284 case X86_AVX2:
285 return features->avx2;
286 case X86_AVX512F:
287 return features->avx512f;
288 case X86_AVX512CD:
289 return features->avx512cd;
290 case X86_AVX512ER:
291 return features->avx512er;
292 case X86_AVX512PF:
293 return features->avx512pf;
294 case X86_AVX512BW:
295 return features->avx512bw;
296 case X86_AVX512DQ:
297 return features->avx512dq;
298 case X86_AVX512VL:
299 return features->avx512vl;
300 case X86_AVX512IFMA:
301 return features->avx512ifma;
302 case X86_AVX512VBMI:
303 return features->avx512vbmi;
304 case X86_AVX512VBMI2:
305 return features->avx512vbmi2;
306 case X86_AVX512VNNI:
307 return features->avx512vnni;
308 case X86_AVX512BITALG:
309 return features->avx512bitalg;
310 case X86_AVX512VPOPCNTDQ:
311 return features->avx512vpopcntdq;
312 case X86_AVX512_4VNNIW:
313 return features->avx512_4vnniw;
314 case X86_AVX512_4VBMI2:
315 return features->avx512_4vbmi2;
316 case X86_LAST_:
317 break;
318 }
319 return false;
320}
321
322const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
323 switch (value) {
324 case X86_AES:
325 return "aes";
326 case X86_ERMS:
327 return "erms";
328 case X86_F16C:
329 return "f16c";
330 case X86_FMA3:
331 return "fma3";
Guillaume Chatelet11e3e202018-02-09 08:55:11 +0100332 case X86_VPCLMULQDQ:
333 return "vpclmulqdq";
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100334 case X86_BMI1:
335 return "bmi1";
336 case X86_BMI2:
337 return "bmi2";
338 case X86_SSSE3:
339 return "ssse3";
340 case X86_SSE4_1:
341 return "sse4_1";
342 case X86_SSE4_2:
343 return "sse4_2";
344 case X86_AVX:
345 return "avx";
346 case X86_AVX2:
347 return "avx2";
348 case X86_AVX512F:
349 return "avx512f";
350 case X86_AVX512CD:
351 return "avx512cd";
352 case X86_AVX512ER:
353 return "avx512er";
354 case X86_AVX512PF:
355 return "avx512pf";
356 case X86_AVX512BW:
357 return "avx512bw";
358 case X86_AVX512DQ:
359 return "avx512dq";
360 case X86_AVX512VL:
361 return "avx512vl";
362 case X86_AVX512IFMA:
363 return "avx512ifma";
364 case X86_AVX512VBMI:
365 return "avx512vbmi";
366 case X86_AVX512VBMI2:
367 return "avx512vbmi2";
368 case X86_AVX512VNNI:
369 return "avx512vnni";
370 case X86_AVX512BITALG:
371 return "avx512bitalg";
372 case X86_AVX512VPOPCNTDQ:
373 return "avx512vpopcntdq";
374 case X86_AVX512_4VNNIW:
375 return "avx512_4vnniw";
376 case X86_AVX512_4VBMI2:
377 return "avx512_4vbmi2";
378 case X86_LAST_:
379 break;
380 }
381 return "unknown_feature";
382}
383
384const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
385 switch (uarch) {
386 case X86_UNKNOWN:
387 return "X86_UNKNOWN";
388 case INTEL_CORE:
389 return "INTEL_CORE";
390 case INTEL_PNR:
391 return "INTEL_PNR";
392 case INTEL_NHM:
393 return "INTEL_NHM";
394 case INTEL_ATOM_BNL:
395 return "INTEL_ATOM_BNL";
396 case INTEL_WSM:
397 return "INTEL_WSM";
398 case INTEL_SNB:
399 return "INTEL_SNB";
400 case INTEL_IVB:
401 return "INTEL_IVB";
402 case INTEL_ATOM_SMT:
403 return "INTEL_ATOM_SMT";
404 case INTEL_HSW:
405 return "INTEL_HSW";
406 case INTEL_BDW:
407 return "INTEL_BDW";
408 case INTEL_SKL:
409 return "INTEL_SKL";
410 case INTEL_ATOM_GMT:
411 return "INTEL_ATOM_GMT";
412 case INTEL_KBL:
413 return "INTEL_KBL";
414 case INTEL_CFL:
415 return "INTEL_CFL";
416 case INTEL_CNL:
417 return "INTEL_CNL";
418 case AMD_HAMMER:
419 return "AMD_HAMMER";
420 case AMD_K10:
421 return "AMD_K10";
422 case AMD_BOBCAT:
423 return "AMD_BOBCAT";
424 case AMD_BULLDOZER:
425 return "AMD_BULLDOZER";
426 case AMD_JAGUAR:
427 return "AMD_JAGUAR";
428 case AMD_ZEN:
429 return "AMD_ZEN";
430 }
431 return "unknown microarchitecture";
432}