/src/botan/build/include/botan/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 | | BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h) |
17 | | |
18 | | namespace Botan { |
19 | | |
20 | | /** |
21 | | * A class handling runtime CPU feature detection. It is limited to |
22 | | * just the features necessary to implement CPU specific code in Botan, |
23 | | * rather than being a general purpose utility. |
24 | | * |
25 | | * This class supports: |
26 | | * |
27 | | * - x86 features using CPUID. x86 is also the only processor with |
28 | | * accurate cache line detection currently. |
29 | | * |
30 | | * - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS |
31 | | * |
32 | | * - ARM NEON and crypto extensions detection. On Linux and Android |
33 | | * systems which support getauxval, that is used to access CPU |
34 | | * feature information. Otherwise a relatively portable but |
35 | | * thread-unsafe mechanism involving executing probe functions which |
36 | | * catching SIGILL signal is used. |
37 | | */ |
38 | | class BOTAN_PUBLIC_API(2,1) CPUID final |
39 | | { |
40 | | public: |
41 | | /** |
42 | | * Probe the CPU and see what extensions are supported |
43 | | */ |
44 | | static void initialize(); |
45 | | |
46 | | static bool has_simd_32(); |
47 | | |
48 | | /** |
49 | | * Deprecated equivalent to |
50 | | * o << "CPUID flags: " << CPUID::to_string() << "\n"; |
51 | | */ |
52 | | BOTAN_DEPRECATED("Use CPUID::to_string") |
53 | | static void print(std::ostream& o); |
54 | | |
55 | | /** |
56 | | * Return a possibly empty string containing list of known CPU |
57 | | * extensions. Each name will be seperated by a space, and the ordering |
58 | | * will be arbitrary. This list only contains values that are useful to |
59 | | * Botan (for example FMA instructions are not checked). |
60 | | * |
61 | | * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" |
62 | | */ |
63 | | static std::string to_string(); |
64 | | |
65 | | /** |
66 | | * Return a best guess of the cache line size |
67 | | */ |
68 | | static size_t cache_line_size() |
69 | 3.92k | { |
70 | 3.92k | return state().cache_line_size(); |
71 | 3.92k | } |
72 | | |
73 | | static bool is_little_endian() |
74 | 0 | { |
75 | 0 | #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
76 | 0 | return true; |
77 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
78 | 0 | return false; |
79 | 0 | #else |
80 | 0 | return state().endian_status() == Endian_Status::Little; |
81 | 0 | #endif |
82 | 0 | } |
83 | | |
84 | | static bool is_big_endian() |
85 | 0 | { |
86 | 0 | #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
87 | 0 | return true; |
88 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
89 | 0 | return false; |
90 | 0 | #else |
91 | 0 | return state().endian_status() == Endian_Status::Big; |
92 | 0 | #endif |
93 | 0 | } |
94 | | |
95 | | enum CPUID_bits : uint64_t { |
96 | | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
97 | | // These values have no relation to cpuid bitfields |
98 | | |
99 | | // SIMD instruction sets |
100 | | CPUID_SSE2_BIT = (1ULL << 0), |
101 | | CPUID_SSSE3_BIT = (1ULL << 1), |
102 | | CPUID_SSE41_BIT = (1ULL << 2), |
103 | | CPUID_SSE42_BIT = (1ULL << 3), |
104 | | CPUID_AVX2_BIT = (1ULL << 4), |
105 | | CPUID_AVX512F_BIT = (1ULL << 5), |
106 | | |
107 | | // Misc useful instructions |
108 | | CPUID_RDTSC_BIT = (1ULL << 10), |
109 | | CPUID_BMI2_BIT = (1ULL << 11), |
110 | | CPUID_ADX_BIT = (1ULL << 12), |
111 | | CPUID_BMI1_BIT = (1ULL << 13), |
112 | | |
113 | | // Crypto-specific ISAs |
114 | | CPUID_AESNI_BIT = (1ULL << 16), |
115 | | CPUID_CLMUL_BIT = (1ULL << 17), |
116 | | CPUID_RDRAND_BIT = (1ULL << 18), |
117 | | CPUID_RDSEED_BIT = (1ULL << 19), |
118 | | CPUID_SHA_BIT = (1ULL << 20), |
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 | 49.5k | { 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 | 48.4k | { 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 BMI1 |
273 | | */ |
274 | | static bool has_bmi1() |
275 | 0 | { return has_cpuid_bit(CPUID_BMI1_BIT); } |
276 | | |
277 | | /** |
278 | | * Check if the processor supports BMI2 |
279 | | */ |
280 | | static bool has_bmi2() |
281 | 836k | { return has_cpuid_bit(CPUID_BMI2_BIT); } |
282 | | |
283 | | /** |
284 | | * Check if the processor supports AES-NI |
285 | | */ |
286 | | static bool has_aes_ni() |
287 | 24.9k | { return has_cpuid_bit(CPUID_AESNI_BIT); } |
288 | | |
289 | | /** |
290 | | * Check if the processor supports CLMUL |
291 | | */ |
292 | | static bool has_clmul() |
293 | 4.98k | { return has_cpuid_bit(CPUID_CLMUL_BIT); } |
294 | | |
295 | | /** |
296 | | * Check if the processor supports Intel SHA extension |
297 | | */ |
298 | | static bool has_intel_sha() |
299 | 662k | { return has_cpuid_bit(CPUID_SHA_BIT); } |
300 | | |
301 | | /** |
302 | | * Check if the processor supports ADX extension |
303 | | */ |
304 | | static bool has_adx() |
305 | 0 | { return has_cpuid_bit(CPUID_ADX_BIT); } |
306 | | |
307 | | /** |
308 | | * Check if the processor supports RDRAND |
309 | | */ |
310 | | static bool has_rdrand() |
311 | 0 | { return has_cpuid_bit(CPUID_RDRAND_BIT); } |
312 | | |
313 | | /** |
314 | | * Check if the processor supports RDSEED |
315 | | */ |
316 | | static bool has_rdseed() |
317 | 0 | { return has_cpuid_bit(CPUID_RDSEED_BIT); } |
318 | | #endif |
319 | | |
320 | | /** |
321 | | * Check if the processor supports byte-level vector permutes |
322 | | * (SSSE3, NEON, Altivec) |
323 | | */ |
324 | | static bool has_vperm() |
325 | 0 | { |
326 | 0 | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
327 | 0 | return has_ssse3(); |
328 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
329 | 0 | return has_neon(); |
330 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
331 | 0 | return has_altivec(); |
332 | 0 | #else |
333 | 0 | return false; |
334 | 0 | #endif |
335 | 0 | } |
336 | | |
337 | | /** |
338 | | * Check if the processor supports carryless multiply |
339 | | * (CLMUL, PMULL) |
340 | | */ |
341 | | static bool has_carryless_multiply() |
342 | 4.98k | { |
343 | 4.98k | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
344 | 4.98k | return has_clmul(); |
345 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
346 | | return has_arm_pmull(); |
347 | | #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) |
348 | | return has_power_crypto(); |
349 | | #else |
350 | | return false; |
351 | | #endif |
352 | | } |
353 | | |
354 | | /* |
355 | | * Clear a CPUID bit |
356 | | * Call CPUID::initialize to reset |
357 | | * |
358 | | * This is only exposed for testing, don't use unless you know |
359 | | * what you are doing. |
360 | | */ |
361 | | static void clear_cpuid_bit(CPUID_bits bit) |
362 | 0 | { |
363 | 0 | state().clear_cpuid_bit(static_cast<uint64_t>(bit)); |
364 | 0 | } |
365 | | |
366 | | /* |
367 | | * Don't call this function, use CPUID::has_xxx above |
368 | | * It is only exposed for the tests. |
369 | | */ |
370 | | static bool has_cpuid_bit(CPUID_bits elem) |
371 | 1.62M | { |
372 | 1.62M | const uint64_t elem64 = static_cast<uint64_t>(elem); |
373 | 1.62M | return state().has_bit(elem64); |
374 | 1.62M | } |
375 | | |
376 | | static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok); |
377 | | private: |
378 | | enum class Endian_Status : uint32_t { |
379 | | Unknown = 0x00000000, |
380 | | Big = 0x01234567, |
381 | | Little = 0x67452301, |
382 | | }; |
383 | | |
384 | | struct CPUID_Data |
385 | | { |
386 | | public: |
387 | | CPUID_Data(); |
388 | | |
389 | | CPUID_Data(const CPUID_Data& other) = default; |
390 | | CPUID_Data& operator=(const CPUID_Data& other) = default; |
391 | | |
392 | | void clear_cpuid_bit(uint64_t bit) |
393 | 0 | { |
394 | 0 | m_processor_features &= ~bit; |
395 | 0 | } |
396 | | |
397 | | bool has_bit(uint64_t bit) const |
398 | 1.62M | { |
399 | 1.62M | return (m_processor_features & bit) == bit; |
400 | 1.62M | } |
401 | | |
402 | 0 | uint64_t processor_features() const { return m_processor_features; } |
403 | 0 | Endian_Status endian_status() const { return m_endian_status; } |
404 | 3.92k | size_t cache_line_size() const { return m_cache_line_size; } |
405 | | |
406 | | private: |
407 | | static Endian_Status runtime_check_endian(); |
408 | | |
409 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ |
410 | | defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ |
411 | | defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
412 | | |
413 | | static uint64_t detect_cpu_features(size_t* cache_line_size); |
414 | | |
415 | | #endif |
416 | | uint64_t m_processor_features; |
417 | | size_t m_cache_line_size; |
418 | | Endian_Status m_endian_status; |
419 | | }; |
420 | | |
421 | | static CPUID_Data& state() |
422 | 1.63M | { |
423 | 1.63M | static CPUID::CPUID_Data g_cpuid; |
424 | 1.63M | return g_cpuid; |
425 | 1.63M | } |
426 | | }; |
427 | | |
428 | | } |
429 | | |
430 | | #endif |