ReactOS 0.4.17-dev-1005-g171e1de
cpuid.c
Go to the documentation of this file.
1//
2// cpuid.c code for CPU feature detection based on CPUID
3//
4// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
5//
6
7
8#include "precomp.h"
9
10#if (SYMCRYPT_CPU_ARM | SYMCRYPT_CPU_ARM64) & SYMCRYPT_MS_VC
11#include <excpt.h>
12#endif
13
14#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
15
16#ifdef __clang__
17#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function)
18#else
19#pragma GCC push_options
20#pragma GCC target("xsave")
21#endif
22
23//
24// RDRAND availability is signaled by CPUID.1.ecx[30]
25// PCLMULQDQ availability is signaled by CPUID.1.ecx[1]
26// AES_NI availability is signaled by CPUID.1.ecx[25]
27// SSSE3 availability is signaled by CPUID.1.ecx[9]
28// SSE3 availability is signaled by CPUID.1.ecx[0]
29// SSE2 availability is signaled by CPUID.1.edx[26]
30//
31
32#define CPUID_1_ECX_RDRAND_BIT 30
33#define CPUID_1_ECX_PCLMULQDQ_BIT 1
34#define CPUID_1_ECX_AESNI_BIT 25
35#define CPUID_1_ECX_SSSE3_BIT 9
36#define CPUID_1_ECX_SSE3_BIT 0
37#define CPUID_1_EDX_SSE2_BIT 26
38#define CPUID_1_EDX_SSE_BIT 25
39#define CPUID_1_ECX_AVX_BIT 28
40#define CPUID_1_ECX_CMPXCHG16B_BIT 13
41#define CPUID_70_EBX_AVX2_BIT 5
42#define CPUID_70_EBX_RDSEED_BIT 18
43#define CPUID_70_EBX_SHANI_BIT 29
44#define CPUID_70_EBX_ADX_BIT 19
45#define CPUID_70_EBX_BMI2_BIT 8
46#define CPUID_70_EBX_AVX512F_BIT 16
47#define CPUID_70_EBX_AVX512BW_BIT 30
48#define CPUID_70_EBX_AVX512DQ_BIT 17
49#define CPUID_70_EBX_AVX512VL_BIT 31
50#define CPUID_70_ECX_VAES_BIT 9
51#define CPUID_70_ECX_VPCLMULQDQ_BIT 10
52
53
54#define CPUID_1_ECX_OSXSAVE_BIT 27
55
56typedef struct _CPUID_BIT_INFO {
57 BYTE leaf;
58 BYTE word;
59 BYTE bitno;
60 SYMCRYPT_CPU_FEATURES requiredBy;
61} CPUID_BIT_INFO;
62
63#define WORD_EAX 0
64#define WORD_EBX 1
65#define WORD_ECX 2
66#define WORD_EDX 3
67
68int g_SymCryptCpuid1[4]; // We cache the results of CPUID(1) to help diagnose CPU detection errors
69
70const
71CPUID_BIT_INFO cpuidBitInfo[] = {
72 {1, WORD_ECX, CPUID_1_ECX_RDRAND_BIT, SYMCRYPT_CPU_FEATURE_RDRAND },
73 {1, WORD_ECX, CPUID_1_ECX_PCLMULQDQ_BIT, SYMCRYPT_CPU_FEATURE_PCLMULQDQ },
74 {1, WORD_ECX, CPUID_1_ECX_AESNI_BIT, SYMCRYPT_CPU_FEATURE_AESNI },
75 {1, WORD_EDX, CPUID_1_EDX_SSE_BIT, SYMCRYPT_CPU_FEATURE_SSE2 | SYMCRYPT_CPU_FEATURE_SSSE3 },
76 {1, WORD_EDX, CPUID_1_EDX_SSE2_BIT, SYMCRYPT_CPU_FEATURE_SSE2 | SYMCRYPT_CPU_FEATURE_SSSE3 },
77 {1, WORD_ECX, CPUID_1_ECX_SSE3_BIT, SYMCRYPT_CPU_FEATURE_SSSE3 },
78 {1, WORD_ECX, CPUID_1_ECX_SSSE3_BIT, SYMCRYPT_CPU_FEATURE_SSSE3 },
79 {1, WORD_ECX, CPUID_1_ECX_AVX_BIT, SYMCRYPT_CPU_FEATURE_AVX2 },
80 {1, WORD_ECX, CPUID_1_ECX_CMPXCHG16B_BIT, SYMCRYPT_CPU_FEATURE_CMPXCHG16B },
81 {7, WORD_EBX, CPUID_70_EBX_AVX2_BIT, SYMCRYPT_CPU_FEATURE_AVX2 },
82 {7, WORD_EBX, CPUID_70_EBX_RDSEED_BIT, SYMCRYPT_CPU_FEATURE_RDSEED },
83 {7, WORD_EBX, CPUID_70_EBX_SHANI_BIT, SYMCRYPT_CPU_FEATURE_SHANI },
84 {7, WORD_EBX, CPUID_70_EBX_ADX_BIT, SYMCRYPT_CPU_FEATURE_ADX },
85 {7, WORD_EBX, CPUID_70_EBX_BMI2_BIT, SYMCRYPT_CPU_FEATURE_BMI2 },
86 {7, WORD_EBX, CPUID_70_EBX_AVX512F_BIT, SYMCRYPT_CPU_FEATURE_AVX512 },
87 {7, WORD_EBX, CPUID_70_EBX_AVX512VL_BIT, SYMCRYPT_CPU_FEATURE_AVX512 },
88 {7, WORD_EBX, CPUID_70_EBX_AVX512BW_BIT, SYMCRYPT_CPU_FEATURE_AVX512 },
89 {7, WORD_EBX, CPUID_70_EBX_AVX512DQ_BIT, SYMCRYPT_CPU_FEATURE_AVX512 },
90 {7, WORD_ECX, CPUID_70_ECX_VAES_BIT, SYMCRYPT_CPU_FEATURE_VAES },
91 {7, WORD_ECX, CPUID_70_ECX_VPCLMULQDQ_BIT, SYMCRYPT_CPU_FEATURE_VAES },
92};
93
94VOID
97{
99 int CPUInfo[4];
100 int InfoType;
101 int maxInfoType;
102 int i;
103 BOOLEAN allowYmm, allowZmm;
104 INT64 xGetBvResult;
105
106 //
107 // Mark all features as present (the result bits indicate not-present, so set the features we know to 0).
108 //
109 result = ~ (UINT32)(
110 SYMCRYPT_CPU_FEATURE_SSE2 |
111 SYMCRYPT_CPU_FEATURE_SSSE3 |
112 SYMCRYPT_CPU_FEATURE_AESNI |
113 SYMCRYPT_CPU_FEATURE_PCLMULQDQ |
114 SYMCRYPT_CPU_FEATURE_AVX2 |
115 SYMCRYPT_CPU_FEATURE_SHANI |
116 SYMCRYPT_CPU_FEATURE_BMI2 |
117 SYMCRYPT_CPU_FEATURE_ADX |
118 SYMCRYPT_CPU_FEATURE_RDRAND |
119 SYMCRYPT_CPU_FEATURE_RDSEED |
120 SYMCRYPT_CPU_FEATURE_AVX512 |
121 SYMCRYPT_CPU_FEATURE_VAES |
122 SYMCRYPT_CPU_FEATURE_CMPXCHG16B
123 );
124
125 // InfoType holds the function id of previous cpuid
126 // so we don't have to repeatedly invoke cpuid.
127 InfoType = 0;
128 SymCryptCpuidExFunc( CPUInfo, InfoType, 0 );
129 maxInfoType = CPUInfo[WORD_EAX];
130
131 for( i=0; i< sizeof( cpuidBitInfo ) / sizeof( *cpuidBitInfo ); i++ )
132 {
133 if( cpuidBitInfo[i].leaf != InfoType )
134 {
135 InfoType = cpuidBitInfo[i].leaf;
136 SymCryptCpuidExFunc( CPUInfo, InfoType, 0 );
137 }
138 if( cpuidBitInfo[i].leaf > maxInfoType || (CPUInfo[ cpuidBitInfo[i].word ] & (1UL << cpuidBitInfo[i].bitno) ) == 0 )
139 {
140 result |= cpuidBitInfo[i].requiredBy;
141 }
142 }
143
145 {
146 //
147 // Check for OS support of the YMM registers.
148 // This detection is optional in any environment because some environments are single-threaded, and
149 // OS support is not required. (E.g. Boot library.)
150 //
151 // We use the following logic:
152 // Check that the OSXSAVE bit is 1, which means we can use XGETBV
153 // Use XGETBV and check that XCR0[2:1] = '11b' signaling that both XMM and YMM are enabled by OS
154 // Note that we only disable the AVX2 usage; AESNI & XMM registers are used independent of OS support, because
155 // all our (known) OSes have it.
156 //
157 allowYmm = FALSE;
158 allowZmm = FALSE;
159 SymCryptCpuidExFunc( CPUInfo, 1, 0 );
160
161 if( (CPUInfo[WORD_ECX] & (1 << CPUID_1_ECX_OSXSAVE_BIT)) != 0 )
162 {
163 // OSXSAVE bit is set, we can use XGETBV
164 xGetBvResult = _xgetbv( _XCR_XFEATURE_ENABLED_MASK );
165
166 // Check that bits 1 and 2 are set, corresponding to the XMM and YMM register state
167 if( (xGetBvResult & 0x6) == 0x6)
168 {
169 allowYmm = TRUE;
170
171 //
172 // For AVX-512, also check that bits 5, 6, and 7 are set, corresponding to the
173 // opmask, ZMM (0-15), and ZMM (16-31) register states
174 // This follows the recommendation in the Intel 64 and IA-32 Architectures Software
175 // Developer's Manual, Volume 1, 15.3 / 15.4.
176 //
177 // It seems plausible that on some system the OS would not support save/restore of
178 // AVX-512 state, but use of AVX-512VL instructions on Ymm or Xmm registers would be
179 // OK, however Intel explicitly suggests that we should only use AVX512-VL if the
180 // support is indicated by xgetbv, so we use the same logic as for AVX2 (our
181 // SymCrypt feature indicates both CPU support, and OS support for saving/restoring
182 // the extended state)
183 //
184 if( (xGetBvResult & 0xe0) == 0xe0)
185 {
186 allowZmm = TRUE;
187 }
188 }
189 }
190
191 if( !allowYmm )
192 {
193 // Disallow the AVX2-dependent code because we don't have OS YMM support.
194 result |= SYMCRYPT_CPU_FEATURE_AVX2;
195 }
196
197 if( !allowZmm )
198 {
199 // Disallow any AVX512-dependent code because we don't have OS ZMM support.
200 // Note that not all AVX-512 dependent code will need to save/restore ZMM state, but we
201 // do not support AVX-512 instructions (even acting on YMM or XMM registers), unless the
202 // OS indicates support via XCR0
203 result |= SYMCRYPT_CPU_FEATURE_AVX512;
204 }
205 }
206
207
208 if( (result & SYMCRYPT_CPU_FEATURE_AESNI) == 0 ) // thus, if AES-NI is present according to CPUID
209 {
210 //
211 // In Win7 Beta we had an interesting crash bucket.
212 // It only occurred on the AsusTek A6K line of laptops which sometimes
213 // set the cpuid AES-NI bit (but not always). This leads to a crash as
214 // we start using AES instructions that don't exist on those machines.
215 //
216 // I found on-line reviews for the A6K line from december 2005 so it was launched around
217 // that time.
218 //
219 // These laptops all have AMD CPUs, so we fix it by locking out the particular AMD CPUs
220 // families that don't have AES-NI anyway.
221 //
222 // We really shouldn't need this logic, and it only slows things down.
223 // We should be able to remove it at some point in the future.
224 //
225 // At AMD's recommendation, we use the logic below.
226 // The AMD engineers reviewed this code to ensure we don't lock out future CPUs
227 // that will have AES-NI.
228 //
229 SymCryptCpuidExFunc( CPUInfo, 0, 0 );
230 if( CPUInfo[WORD_EBX] == 'htuA'
231 && CPUInfo[WORD_ECX] == 'DMAc'
232 && CPUInfo[WORD_EDX] == 'itne' )
233 {
234 //
235 // We have an AMD cpu, check the family.
236 //
237 UINT32 baseFamily;
238 UINT32 extFamily;
239 UINT32 family;
240
241 //
242 // Extract the base family and extended family values, and combine them to the full
243 // family value.
244 //
245 SymCryptCpuidExFunc( CPUInfo, 1, 0 );
246
247 baseFamily = (CPUInfo[WORD_EAX] >> 8) & 0xf;
248
249 extFamily = (CPUInfo[WORD_EAX] >> 20) & 0xff;
250
251 if( baseFamily < 0xf )
252 {
253 family = baseFamily;
254 } else {
255 family = baseFamily + extFamily;
256 }
257
258 //
259 // AMD will not implement the AES instruction set until family 0x15
260 //
261 if( family < 0x15 )
262 {
263 result |= SYMCRYPT_CPU_FEATURE_AESNI;
264 }
265 }
266 }
267
268 SymCryptCpuidExFunc( g_SymCryptCpuid1, 1, 0 ); // Keep cache of CPUID results for diagnosis
269
271}
272
273#ifdef __clang__
274#pragma clang attribute pop
275#else
276#pragma GCC pop_options
277#endif
278
279#elif SYMCRYPT_CPU_ARM
280
281#define CP15_ISAR5 15, 0, 0, 2, 5 // Instruction Set Attribute Register 5
282
283#define READ_ARM_FEATURE(_FeatureRegister, _Index) \
284 (((ULONG)_MoveFromCoprocessor(_FeatureRegister) >> ((_Index) * 4)) & 0xF)
285
286#define ISAR5_AES 1
287#define ISAR5_AES_AESE 1
288#define ISAR5_AES_PMULL 2
289
290#define ISAR5_SHA2 3
291#define ISAR5_SHA2_SHA256H 1
292
293#define ISAR5_CRC32 4
294#define ISAR5_CRC32_IMP 1
295
296VOID
299{
301
302#if 0 // We currently do not use any neon crypto features on ARM code, so no detection needed.
303
304 //
305 // We start with a result that allows everything.
306 // This makes the code simpler when you have one CPU feature flag that disables multiple feature bits.
307 //
308 result = ~ (UINT32)(
309 SYMCRYPT_CPU_FEATURE_NEON |
310 SYMCRYPT_CPU_FEATURE_NEON_AES |
311 SYMCRYPT_CPU_FEATURE_NEON_PMULL |
312 SYMCRYPT_CPU_FEATURE_NEON_SHA256
313 );
314
315 //
316 // Reading the status registers might fail, so we use a try block.
317 //
318 try {
319
320 if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_AES) < ISAR5_AES_AESE )
321 {
322 result |= SYMCRYPT_CPU_FEATURE_NEON_AES;
323 }
324
325 if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_AES) < ISAR5_AES_PMULL )
326 {
327 result |= SYMCRYPT_CPU_FEATURE_NEON_PMULL;
328 }
329
330 if( READ_ARM_FEATURE(CP15_ISAR5, ISAR5_SHA2) < ISAR5_SHA2_SHA256H )
331 {
332 result |= SYMCRYPT_CPU_FEATURE_NEON_SHA256;
333 }
334
336 //
337 // Something went wrong reading the registers; disable all the crypto extensions leaving only the standard NEON registers available.
338 //
339 result |= SYMCRYPT_CPU_FEATURE_NEON_AES | SYMCRYPT_CPU_FEATURE_NEON_PMULL | SYMCRYPT_CPU_FEATURE_NEON_SHA256;
340 }
341#endif
342 //
343 // For now we ignore the new instructions in ARM until we can get clarity on how to detect Arm32-on-Arm64.
344 //
345 result = ~(UINT32)SYMCRYPT_CPU_FEATURE_NEON;
346
348}
349
350
351#elif SYMCRYPT_CPU_ARM64 && !SYMCRYPT_PLATFORM_WINE
352
353
354#define ARM64_SYSREG(op0, op1, crn, crm, op2) \
355 ( ((op0 & 1) << 14) | \
356 ((op1 & 7) << 11) | \
357 ((crn & 15) << 7) | \
358 ((crm & 15) << 3) | \
359 ((op2 & 7) << 0) )
360
361#define ARM64_ID_AA64ISAR0_EL1 ARM64_SYSREG(3,0, 0, 6,0) // ISA Feature Register 0
362
363#define ISAR0_AES 1
364#define ISAR0_AES_NI 0
365#define ISAR0_AES_INSTRUCTIONS 1
366#define ISAR0_AES_PLUS_PMULL64 2
367
368#define ISAR0_SHA2 3
369#define ISAR0_SHA2_NI 0
370#define ISAR0_SHA2_INSTRUCTIONS 1
371
372#define ISAR0_CRC32 4
373#define ISAR0_CRC32_NI 0
374#define ISAR0_CRC32_INSTRUCTIONS 1
375
376#define READ_ARM64_FEATURE(_FeatureRegister, _Index) \
377 (((ULONG64)_ReadStatusReg(_FeatureRegister) >> ((_Index) * 4)) & 0xF)
378
379VOID
382{
384
385 result = ~ (UINT32)(
386 SYMCRYPT_CPU_FEATURE_NEON |
387 SYMCRYPT_CPU_FEATURE_NEON_AES |
388 SYMCRYPT_CPU_FEATURE_NEON_PMULL |
389 SYMCRYPT_CPU_FEATURE_NEON_SHA256
390 );
391
392#if SYMCRYPT_MS_VC
393 __try {
394
395 if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_AES) < ISAR0_AES_INSTRUCTIONS )
396 {
397 result |= SYMCRYPT_CPU_FEATURE_NEON_AES;
398 }
399
400 if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_AES) < ISAR0_AES_PLUS_PMULL64 )
401 {
402 result |= SYMCRYPT_CPU_FEATURE_NEON_PMULL;
403 }
404
405 if( READ_ARM64_FEATURE(ARM64_ID_AA64ISAR0_EL1, ISAR0_SHA2) < ISAR0_SHA2_INSTRUCTIONS )
406 {
407 result |= SYMCRYPT_CPU_FEATURE_NEON_SHA256;
408 }
409
411
413 ; //NOTHING;
414 }
415#endif
416
417}
418
419#endif // CPU arch selection
unsigned char BOOLEAN
Definition: actypes.h:127
COMPILER_DEPENDENT_INT64 INT64
Definition: actypes.h:132
#define except(x)
Definition: btrfs_drv.h:136
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
GLbitfield flags
Definition: glext.h:7161
GLuint64EXT * result
Definition: glext.h:11304
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
unsigned __int64 __cdecl _xgetbv(unsigned int)
Definition: immintrin.h:343
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:90
const WCHAR * word
Definition: lex.c:36
#define CP15_ISAR5
Definition: ketypes.h:61
#define __try
Definition: pseh2_64.h:211
#define __except
Definition: pseh2_64.h:212
VOID SYMCRYPT_CALL SymCryptCpuidExFunc(int cpuInfo[4], int function_id, int subfunction_id)
VOID SYMCRYPT_CALL SymCryptDetectCpuFeaturesFromRegisters(void)
VOID SYMCRYPT_CALL SymCryptDetectCpuFeaturesByCpuid(UINT32 flags)
#define SYMCRYPT_CPUID_DETECT_FLAG_CHECK_OS_SUPPORT_FOR_YMM
Definition: sc_lib.h:1025
#define SYMCRYPT_CALL
UINT32 SYMCRYPT_CPU_FEATURES
SYMCRYPT_CPU_FEATURES g_SymCryptCpuFeaturesNotPresent
Definition: libmain.c:20
uint32_t UINT32
Definition: typedefs.h:59
unsigned char BYTE
Definition: xxhash.c:193