/src/botan/build/include/botan/internal/cpuid.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Runtime CPU detection |
3 | | * (C) 2009,2010,2013,2017 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #ifndef BOTAN_CPUID_H_ |
9 | | #define BOTAN_CPUID_H_ |
10 | | |
11 | | #include <botan/types.h> |
12 | | #include <vector> |
13 | | #include <string> |
14 | | #include <iosfwd> |
15 | | |
16 | | namespace Botan { |
17 | | |
18 | | /** |
19 | | * A class handling runtime CPU feature detection. It is limited to |
20 | | * just the features necessary to implement CPU specific code in Botan, |
21 | | * rather than being a general purpose utility. |
22 | | * |
23 | | * This class supports: |
24 | | * |
25 | | * - x86 features using CPUID. x86 is also the only processor with |
26 | | * accurate cache line detection currently. |
27 | | * |
28 | | * - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS |
29 | | * |
30 | | * - ARM NEON and crypto extensions detection. On Linux and Android |
31 | | * systems which support getauxval, that is used to access CPU |
32 | | * feature information. Otherwise a relatively portable but |
33 | | * thread-unsafe mechanism involving executing probe functions which |
34 | | * catching SIGILL signal is used. |
35 | | */ |
36 | | class BOTAN_TEST_API CPUID final |
37 | | { |
38 | | public: |
39 | | /** |
40 | | * Probe the CPU and see what extensions are supported |
41 | | */ |
42 | | static void initialize(); |
43 | | |
44 | | static bool has_simd_32(); |
45 | | |
46 | | /** |
47 | | * Return a possibly empty string containing list of known CPU |
48 | | * extensions. Each name will be seperated by a space, and the ordering |
49 | | * will be arbitrary. This list only contains values that are useful to |
50 | | * Botan (for example FMA instructions are not checked). |
51 | | * |
52 | | * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" |
53 | | */ |
54 | | static std::string to_string(); |
55 | | |
56 | | /** |
57 | | * Return a best guess of the cache line size |
58 | | */ |
59 | | static size_t cache_line_size() |
60 | 576 | { |
61 | 576 | return state().cache_line_size(); |
62 | 576 | } |
63 | | |
64 | | static bool is_little_endian() |
65 | 0 | { |
66 | 0 | #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
67 | 0 | return true; |
68 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
69 | 0 | return false; |
70 | 0 | #else |
71 | 0 | return state().endian_status() == Endian_Status::Little; |
72 | 0 | #endif |
73 | 0 | } |
74 | | |
75 | | static bool is_big_endian() |
76 | 0 | { |
77 | 0 | #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
78 | 0 | return true; |
79 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
80 | 0 | return false; |
81 | 0 | #else |
82 | 0 | return state().endian_status() == Endian_Status::Big; |
83 | 0 | #endif |
84 | 0 | } |
85 | | |
86 | | enum CPUID_bits : uint64_t { |
87 | | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
88 | | // These values have no relation to cpuid bitfields |
89 | | |
90 | | // SIMD instruction sets |
91 | | CPUID_SSE2_BIT = (1ULL << 0), |
92 | | CPUID_SSSE3_BIT = (1ULL << 1), |
93 | | CPUID_SSE41_BIT = (1ULL << 2), |
94 | | CPUID_SSE42_BIT = (1ULL << 3), |
95 | | CPUID_AVX2_BIT = (1ULL << 4), |
96 | | CPUID_AVX512F_BIT = (1ULL << 5), |
97 | | |
98 | | CPUID_AVX512DQ_BIT = (1ULL << 6), |
99 | | CPUID_AVX512BW_BIT = (1ULL << 7), |
100 | | |
101 | | // Ice Lake profile: AVX-512 F, DQ, BW, VL, IFMA, VBMI, VBMI2, BITALG |
102 | | CPUID_AVX512_ICL_BIT = (1ULL << 11), |
103 | | |
104 | | // Crypto-specific ISAs |
105 | | CPUID_AESNI_BIT = (1ULL << 16), |
106 | | CPUID_CLMUL_BIT = (1ULL << 17), |
107 | | CPUID_RDRAND_BIT = (1ULL << 18), |
108 | | CPUID_RDSEED_BIT = (1ULL << 19), |
109 | | CPUID_SHA_BIT = (1ULL << 20), |
110 | | CPUID_AVX512_AES_BIT = (1ULL << 21), |
111 | | CPUID_AVX512_CLMUL_BIT = (1ULL << 22), |
112 | | |
113 | | // Misc useful instructions |
114 | | CPUID_RDTSC_BIT = (1ULL << 48), |
115 | | CPUID_ADX_BIT = (1ULL << 49), |
116 | | CPUID_BMI1_BIT = (1ULL << 50), |
117 | | CPUID_BMI2_BIT = (1ULL << 51), |
118 | | CPUID_FAST_PDEP_BIT = (1ULL << 52), |
119 | | #endif |
120 | | |
121 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
122 | | CPUID_ALTIVEC_BIT = (1ULL << 0), |
123 | | CPUID_POWER_CRYPTO_BIT = (1ULL << 1), |
124 | | CPUID_DARN_BIT = (1ULL << 2), |
125 | | #endif |
126 | | |
127 | | #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
128 | | CPUID_ARM_NEON_BIT = (1ULL << 0), |
129 | | CPUID_ARM_SVE_BIT = (1ULL << 1), |
130 | | CPUID_ARM_AES_BIT = (1ULL << 16), |
131 | | CPUID_ARM_PMULL_BIT = (1ULL << 17), |
132 | | CPUID_ARM_SHA1_BIT = (1ULL << 18), |
133 | | CPUID_ARM_SHA2_BIT = (1ULL << 19), |
134 | | CPUID_ARM_SHA3_BIT = (1ULL << 20), |
135 | | CPUID_ARM_SHA2_512_BIT = (1ULL << 21), |
136 | | CPUID_ARM_SM3_BIT = (1ULL << 22), |
137 | | CPUID_ARM_SM4_BIT = (1ULL << 23), |
138 | | #endif |
139 | | |
140 | | CPUID_INITIALIZED_BIT = (1ULL << 63) |
141 | | }; |
142 | | |
143 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
144 | | /** |
145 | | * Check if the processor supports AltiVec/VMX |
146 | | */ |
147 | | static bool has_altivec() |
148 | | { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } |
149 | | |
150 | | /** |
151 | | * Check if the processor supports POWER8 crypto extensions |
152 | | */ |
153 | | static bool has_power_crypto() |
154 | | { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } |
155 | | |
156 | | /** |
157 | | * Check if the processor supports POWER9 DARN RNG |
158 | | */ |
159 | | static bool has_darn_rng() |
160 | | { return has_cpuid_bit(CPUID_DARN_BIT); } |
161 | | |
162 | | #endif |
163 | | |
164 | | #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
165 | | /** |
166 | | * Check if the processor supports NEON SIMD |
167 | | */ |
168 | | static bool has_neon() |
169 | | { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } |
170 | | |
171 | | /** |
172 | | * Check if the processor supports ARMv8 SVE |
173 | | */ |
174 | | static bool has_arm_sve() |
175 | | { return has_cpuid_bit(CPUID_ARM_SVE_BIT); } |
176 | | |
177 | | /** |
178 | | * Check if the processor supports ARMv8 SHA1 |
179 | | */ |
180 | | static bool has_arm_sha1() |
181 | | { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); } |
182 | | |
183 | | /** |
184 | | * Check if the processor supports ARMv8 SHA2 |
185 | | */ |
186 | | static bool has_arm_sha2() |
187 | | { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); } |
188 | | |
189 | | /** |
190 | | * Check if the processor supports ARMv8 AES |
191 | | */ |
192 | | static bool has_arm_aes() |
193 | | { return has_cpuid_bit(CPUID_ARM_AES_BIT); } |
194 | | |
195 | | /** |
196 | | * Check if the processor supports ARMv8 PMULL |
197 | | */ |
198 | | static bool has_arm_pmull() |
199 | | { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); } |
200 | | |
201 | | /** |
202 | | * Check if the processor supports ARMv8 SHA-512 |
203 | | */ |
204 | | static bool has_arm_sha2_512() |
205 | | { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } |
206 | | |
207 | | /** |
208 | | * Check if the processor supports ARMv8 SHA-3 |
209 | | */ |
210 | | static bool has_arm_sha3() |
211 | | { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); } |
212 | | |
213 | | /** |
214 | | * Check if the processor supports ARMv8 SM3 |
215 | | */ |
216 | | static bool has_arm_sm3() |
217 | | { return has_cpuid_bit(CPUID_ARM_SM3_BIT); } |
218 | | |
219 | | /** |
220 | | * Check if the processor supports ARMv8 SM4 |
221 | | */ |
222 | | static bool has_arm_sm4() |
223 | | { return has_cpuid_bit(CPUID_ARM_SM4_BIT); } |
224 | | |
225 | | #endif |
226 | | |
227 | | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
228 | | |
229 | | /** |
230 | | * Check if the processor supports RDTSC |
231 | | */ |
232 | | static bool has_rdtsc() |
233 | 0 | { return has_cpuid_bit(CPUID_RDTSC_BIT); } |
234 | | |
235 | | /** |
236 | | * Check if the processor supports SSE2 |
237 | | */ |
238 | | static bool has_sse2() |
239 | 44.8k | { return has_cpuid_bit(CPUID_SSE2_BIT); } |
240 | | |
241 | | /** |
242 | | * Check if the processor supports SSSE3 |
243 | | */ |
244 | | static bool has_ssse3() |
245 | 0 | { return has_cpuid_bit(CPUID_SSSE3_BIT); } |
246 | | |
247 | | /** |
248 | | * Check if the processor supports SSE4.1 |
249 | | */ |
250 | | static bool has_sse41() |
251 | 0 | { return has_cpuid_bit(CPUID_SSE41_BIT); } |
252 | | |
253 | | /** |
254 | | * Check if the processor supports SSE4.2 |
255 | | */ |
256 | | static bool has_sse42() |
257 | 0 | { return has_cpuid_bit(CPUID_SSE42_BIT); } |
258 | | |
259 | | /** |
260 | | * Check if the processor supports AVX2 |
261 | | */ |
262 | | static bool has_avx2() |
263 | 57.8k | { return has_cpuid_bit(CPUID_AVX2_BIT); } |
264 | | |
265 | | /** |
266 | | * Check if the processor supports AVX-512F |
267 | | */ |
268 | | static bool has_avx512f() |
269 | 0 | { return has_cpuid_bit(CPUID_AVX512F_BIT); } |
270 | | |
271 | | /** |
272 | | * Check if the processor supports AVX-512DQ |
273 | | */ |
274 | | static bool has_avx512dq() |
275 | 0 | { return has_cpuid_bit(CPUID_AVX512DQ_BIT); } |
276 | | |
277 | | /** |
278 | | * Check if the processor supports AVX-512BW |
279 | | */ |
280 | | static bool has_avx512bw() |
281 | 0 | { return has_cpuid_bit(CPUID_AVX512BW_BIT); } |
282 | | |
283 | | /** |
284 | | * Check if the processor supports AVX-512 Ice Lake profile |
285 | | */ |
286 | | static bool has_avx512_icelake() |
287 | 0 | { return has_cpuid_bit(CPUID_AVX512_ICL_BIT); } |
288 | | |
289 | | /** |
290 | | * Check if the processor supports AVX-512 AES (VAES) |
291 | | */ |
292 | | static bool has_avx512_aes() |
293 | 0 | { return has_cpuid_bit(CPUID_AVX512_AES_BIT); } |
294 | | |
295 | | /** |
296 | | * Check if the processor supports AVX-512 VPCLMULQDQ |
297 | | */ |
298 | | static bool has_avx512_clmul() |
299 | 0 | { return has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); } |
300 | | |
301 | | /** |
302 | | * Check if the processor supports BMI1 |
303 | | */ |
304 | | static bool has_bmi1() |
305 | 0 | { return has_cpuid_bit(CPUID_BMI1_BIT); } |
306 | | |
307 | | /** |
308 | | * Check if the processor supports BMI2 |
309 | | */ |
310 | | static bool has_bmi2() |
311 | 942k | { return has_cpuid_bit(CPUID_BMI2_BIT); } |
312 | | |
313 | | /** |
314 | | * Check if the processor supports fast PDEP/PEXT from BMI2 |
315 | | */ |
316 | | static bool has_fast_pdep() |
317 | 0 | { return has_cpuid_bit(CPUID_FAST_PDEP_BIT); } |
318 | | |
319 | | /** |
320 | | * Check if the processor supports AES-NI |
321 | | */ |
322 | | static bool has_aes_ni() |
323 | 8.56k | { return has_cpuid_bit(CPUID_AESNI_BIT); } |
324 | | |
325 | | /** |
326 | | * Check if the processor supports CLMUL |
327 | | */ |
328 | | static bool has_clmul() |
329 | 2.00k | { return has_cpuid_bit(CPUID_CLMUL_BIT); } |
330 | | |
331 | | /** |
332 | | * Check if the processor supports Intel SHA extension |
333 | | */ |
334 | | static bool has_intel_sha() |
335 | 824k | { return has_cpuid_bit(CPUID_SHA_BIT); } |
336 | | |
337 | | /** |
338 | | * Check if the processor supports ADX extension |
339 | | */ |
340 | | static bool has_adx() |
341 | 0 | { return has_cpuid_bit(CPUID_ADX_BIT); } |
342 | | |
343 | | /** |
344 | | * Check if the processor supports RDRAND |
345 | | */ |
346 | | static bool has_rdrand() |
347 | 0 | { return has_cpuid_bit(CPUID_RDRAND_BIT); } |
348 | | |
349 | | /** |
350 | | * Check if the processor supports RDSEED |
351 | | */ |
352 | | static bool has_rdseed() |
353 | 0 | { return has_cpuid_bit(CPUID_RDSEED_BIT); } |
354 | | #endif |
355 | | |
356 | | /** |
357 | | * Check if the processor supports byte-level vector permutes |
358 | | * (SSSE3, NEON, Altivec) |
359 | | */ |
360 | | static bool has_vperm() |
361 | 0 | { |
362 | 0 | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
363 | 0 | return has_ssse3(); |
364 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
365 | 0 | return has_neon(); |
366 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
367 | 0 | return has_altivec(); |
368 | 0 | #else |
369 | 0 | return false; |
370 | 0 | #endif |
371 | 0 | } |
372 | | |
373 | | /** |
374 | | * Check if the processor supports hardware AES instructions |
375 | | */ |
376 | | static bool has_hw_aes() |
377 | 7.69k | { |
378 | 7.69k | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
379 | 7.69k | return has_aes_ni(); |
380 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
381 | | return has_arm_aes(); |
382 | | #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
383 | | return has_power_crypto(); |
384 | | #else |
385 | | return false; |
386 | | #endif |
387 | 7.69k | } |
388 | | |
389 | | /** |
390 | | * Check if the processor supports carryless multiply |
391 | | * (CLMUL, PMULL) |
392 | | */ |
393 | | static bool has_carryless_multiply() |
394 | 2.00k | { |
395 | 2.00k | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
396 | 2.00k | return has_clmul(); |
397 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
398 | | return has_arm_pmull(); |
399 | | #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) |
400 | | return has_power_crypto(); |
401 | | #else |
402 | | return false; |
403 | | #endif |
404 | 2.00k | } |
405 | | |
406 | | /* |
407 | | * Clear a CPUID bit |
408 | | * Call CPUID::initialize to reset |
409 | | * |
410 | | * This is only exposed for testing, don't use unless you know |
411 | | * what you are doing. |
412 | | */ |
413 | | static void clear_cpuid_bit(CPUID_bits bit) |
414 | 0 | { |
415 | 0 | state().clear_cpuid_bit(static_cast<uint64_t>(bit)); |
416 | 0 | } |
417 | | |
418 | | /* |
419 | | * Don't call this function, use CPUID::has_xxx above |
420 | | * It is only exposed for the tests. |
421 | | */ |
422 | | static bool has_cpuid_bit(CPUID_bits elem) |
423 | 1.88M | { |
424 | 1.88M | const uint64_t elem64 = static_cast<uint64_t>(elem); |
425 | 1.88M | return state().has_bit(elem64); |
426 | 1.88M | } |
427 | | |
428 | | static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok); |
429 | | private: |
430 | | enum class Endian_Status : uint32_t { |
431 | | Unknown = 0x00000000, |
432 | | Big = 0x01234567, |
433 | | Little = 0x67452301, |
434 | | }; |
435 | | |
436 | | struct CPUID_Data |
437 | | { |
438 | | public: |
439 | | CPUID_Data(); |
440 | | |
441 | | CPUID_Data(const CPUID_Data& other) = default; |
442 | | CPUID_Data& operator=(const CPUID_Data& other) = default; |
443 | | |
444 | | void clear_cpuid_bit(uint64_t bit) |
445 | 0 | { |
446 | 0 | m_processor_features &= ~bit; |
447 | 0 | } |
448 | | |
449 | | bool has_bit(uint64_t bit) const |
450 | 1.88M | { |
451 | 1.88M | return (m_processor_features & bit) == bit; |
452 | 1.88M | } |
453 | | |
454 | 0 | uint64_t processor_features() const { return m_processor_features; } |
455 | 0 | Endian_Status endian_status() const { return m_endian_status; } |
456 | 576 | size_t cache_line_size() const { return m_cache_line_size; } |
457 | | |
458 | | private: |
459 | | static Endian_Status runtime_check_endian(); |
460 | | |
461 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ |
462 | | defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ |
463 | | defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
464 | | |
465 | | static uint64_t detect_cpu_features(size_t* cache_line_size); |
466 | | |
467 | | #endif |
468 | | uint64_t m_processor_features; |
469 | | size_t m_cache_line_size; |
470 | | Endian_Status m_endian_status; |
471 | | }; |
472 | | |
473 | | static CPUID_Data& state() |
474 | 1.88M | { |
475 | 1.88M | static CPUID::CPUID_Data g_cpuid; |
476 | 1.88M | return g_cpuid; |
477 | 1.88M | } |
478 | | }; |
479 | | |
480 | | } |
481 | | |
482 | | #endif |