/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,2023 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 <iosfwd> |
13 | | #include <string> |
14 | | #include <vector> |
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 | | public: |
38 | | /** |
39 | | * Probe the CPU and see what extensions are supported |
40 | | */ |
41 | | static void initialize(); |
42 | | |
43 | | /** |
44 | | * Return true if a 4x32 SIMD instruction set is available |
45 | | * (SSE2, NEON, or Altivec/VMX) |
46 | | */ |
47 | | static bool has_simd_32(); |
48 | | |
49 | | /** |
50 | | * Return a possibly empty string containing list of known CPU |
51 | | * extensions. Each name will be seperated by a space, and the ordering |
52 | | * will be arbitrary. This list only contains values that are useful to |
53 | | * Botan (for example FMA instructions are not checked). |
54 | | * |
55 | | * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" |
56 | | */ |
57 | | static std::string to_string(); |
58 | | |
59 | 0 | static bool is_little_endian() { |
60 | 0 | #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
61 | 0 | return true; |
62 | | #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
63 | | return false; |
64 | | #else |
65 | | return !has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT); |
66 | | #endif |
67 | 0 | } |
68 | | |
69 | 0 | static bool is_big_endian() { |
70 | 0 | #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) |
71 | 0 | return true; |
72 | 0 | #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) |
73 | 0 | return false; |
74 | 0 | #else |
75 | 0 | return has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT); |
76 | 0 | #endif |
77 | 0 | } |
78 | | |
79 | | enum CPUID_bits : uint32_t { |
80 | | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
81 | | // These values have no relation to cpuid bitfields |
82 | | |
83 | | // SIMD instruction sets |
84 | | CPUID_SSE2_BIT = (1U << 0), |
85 | | CPUID_SSSE3_BIT = (1U << 1), |
86 | | CPUID_AVX2_BIT = (1U << 2), |
87 | | CPUID_AVX512_BIT = (1U << 3), |
88 | | |
89 | | // Misc useful instructions |
90 | | CPUID_RDTSC_BIT = (1U << 10), |
91 | | CPUID_ADX_BIT = (1U << 11), |
92 | | CPUID_BMI_BIT = (1U << 12), |
93 | | |
94 | | // Crypto-specific ISAs |
95 | | CPUID_AESNI_BIT = (1U << 16), |
96 | | CPUID_CLMUL_BIT = (1U << 17), |
97 | | CPUID_RDRAND_BIT = (1U << 18), |
98 | | CPUID_RDSEED_BIT = (1U << 19), |
99 | | CPUID_SHA_BIT = (1U << 20), |
100 | | CPUID_AVX512_AES_BIT = (1U << 21), |
101 | | CPUID_AVX512_CLMUL_BIT = (1U << 22), |
102 | | #endif |
103 | | |
104 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
105 | | CPUID_ALTIVEC_BIT = (1U << 0), |
106 | | CPUID_POWER_CRYPTO_BIT = (1U << 1), |
107 | | CPUID_DARN_BIT = (1U << 2), |
108 | | #endif |
109 | | |
110 | | #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
111 | | CPUID_ARM_NEON_BIT = (1U << 0), |
112 | | CPUID_ARM_SVE_BIT = (1U << 1), |
113 | | CPUID_ARM_AES_BIT = (1U << 16), |
114 | | CPUID_ARM_PMULL_BIT = (1U << 17), |
115 | | CPUID_ARM_SHA1_BIT = (1U << 18), |
116 | | CPUID_ARM_SHA2_BIT = (1U << 19), |
117 | | CPUID_ARM_SHA3_BIT = (1U << 20), |
118 | | CPUID_ARM_SHA2_512_BIT = (1U << 21), |
119 | | CPUID_ARM_SM3_BIT = (1U << 22), |
120 | | CPUID_ARM_SM4_BIT = (1U << 23), |
121 | | #endif |
122 | | |
123 | | CPUID_IS_BIG_ENDIAN_BIT = (1U << 30), |
124 | | CPUID_INITIALIZED_BIT = (1U << 31) |
125 | | }; |
126 | | |
127 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
128 | | /** |
129 | | * Check if the processor supports AltiVec/VMX |
130 | | */ |
131 | | static bool has_altivec() { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } |
132 | | |
133 | | /** |
134 | | * Check if the processor supports POWER8 crypto extensions |
135 | | */ |
136 | | static bool has_power_crypto() { return has_altivec() && has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } |
137 | | |
138 | | /** |
139 | | * Check if the processor supports POWER9 DARN RNG |
140 | | */ |
141 | | static bool has_darn_rng() { return has_cpuid_bit(CPUID_DARN_BIT); } |
142 | | |
143 | | #endif |
144 | | |
145 | | #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
146 | | /** |
147 | | * Check if the processor supports NEON SIMD |
148 | | */ |
149 | | static bool has_neon() { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } |
150 | | |
151 | | /** |
152 | | * Check if the processor supports ARMv8 SVE |
153 | | */ |
154 | | static bool has_arm_sve() { return has_cpuid_bit(CPUID_ARM_SVE_BIT); } |
155 | | |
156 | | /** |
157 | | * Check if the processor supports ARMv8 SHA1 |
158 | | */ |
159 | | static bool has_arm_sha1() { return has_neon() && has_cpuid_bit(CPUID_ARM_SHA1_BIT); } |
160 | | |
161 | | /** |
162 | | * Check if the processor supports ARMv8 SHA2 |
163 | | */ |
164 | | static bool has_arm_sha2() { return has_neon() && has_cpuid_bit(CPUID_ARM_SHA2_BIT); } |
165 | | |
166 | | /** |
167 | | * Check if the processor supports ARMv8 AES |
168 | | */ |
169 | | static bool has_arm_aes() { return has_neon() && has_cpuid_bit(CPUID_ARM_AES_BIT); } |
170 | | |
171 | | /** |
172 | | * Check if the processor supports ARMv8 PMULL |
173 | | */ |
174 | | static bool has_arm_pmull() { return has_neon() && has_cpuid_bit(CPUID_ARM_PMULL_BIT); } |
175 | | |
176 | | /** |
177 | | * Check if the processor supports ARMv8 SHA-512 |
178 | | */ |
179 | | static bool has_arm_sha2_512() { return has_neon() && has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } |
180 | | |
181 | | /** |
182 | | * Check if the processor supports ARMv8 SHA-3 |
183 | | */ |
184 | | static bool has_arm_sha3() { return has_neon() && has_cpuid_bit(CPUID_ARM_SHA3_BIT); } |
185 | | |
186 | | /** |
187 | | * Check if the processor supports ARMv8 SM3 |
188 | | */ |
189 | | static bool has_arm_sm3() { return has_neon() && has_cpuid_bit(CPUID_ARM_SM3_BIT); } |
190 | | |
191 | | /** |
192 | | * Check if the processor supports ARMv8 SM4 |
193 | | */ |
194 | | static bool has_arm_sm4() { return has_neon() && has_cpuid_bit(CPUID_ARM_SM4_BIT); } |
195 | | |
196 | | #endif |
197 | | |
198 | | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
199 | | |
200 | | /** |
201 | | * Check if the processor supports RDTSC |
202 | | */ |
203 | 0 | static bool has_rdtsc() { return has_cpuid_bit(CPUID_RDTSC_BIT); } |
204 | | |
205 | | /** |
206 | | * Check if the processor supports SSE2 |
207 | | */ |
208 | 8.28k | static bool has_sse2() { return has_cpuid_bit(CPUID_SSE2_BIT); } |
209 | | |
210 | | /** |
211 | | * Check if the processor supports SSSE3 |
212 | | */ |
213 | 0 | static bool has_ssse3() { return has_sse2() && has_cpuid_bit(CPUID_SSSE3_BIT); } |
214 | | |
215 | | /** |
216 | | * Check if the processor supports AVX2 |
217 | | */ |
218 | 8.27k | static bool has_avx2() { return has_cpuid_bit(CPUID_AVX2_BIT); } |
219 | | |
220 | | /** |
221 | | * Check if the processor supports our AVX-512 minimum profile |
222 | | * |
223 | | * Namely AVX-512 F, DQ, BW, VL, IFMA, VBMI, VBMI2, BITALG |
224 | | */ |
225 | 8.27k | static bool has_avx512() { return has_cpuid_bit(CPUID_AVX512_BIT); } |
226 | | |
227 | | /** |
228 | | * Check if the processor supports AVX-512 AES (VAES) |
229 | | */ |
230 | 0 | static bool has_avx512_aes() { return has_avx512() && has_cpuid_bit(CPUID_AVX512_AES_BIT); } |
231 | | |
232 | | /** |
233 | | * Check if the processor supports AVX-512 VPCLMULQDQ |
234 | | */ |
235 | 0 | static bool has_avx512_clmul() { return has_avx512() && has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); } |
236 | | |
237 | | /** |
238 | | * Check if the processor supports BMI2 (and BMI1) |
239 | | */ |
240 | 10 | static bool has_bmi2() { return has_cpuid_bit(CPUID_BMI_BIT); } |
241 | | |
242 | | /** |
243 | | * Check if the processor supports AES-NI |
244 | | */ |
245 | 0 | static bool has_aes_ni() { return has_ssse3() && has_cpuid_bit(CPUID_AESNI_BIT); } |
246 | | |
247 | | /** |
248 | | * Check if the processor supports CLMUL |
249 | | */ |
250 | 0 | static bool has_clmul() { return has_ssse3() && has_cpuid_bit(CPUID_CLMUL_BIT); } |
251 | | |
252 | | /** |
253 | | * Check if the processor supports Intel SHA extension |
254 | | */ |
255 | 10 | static bool has_intel_sha() { return has_sse2() && has_cpuid_bit(CPUID_SHA_BIT); } |
256 | | |
257 | | /** |
258 | | * Check if the processor supports ADX extension |
259 | | */ |
260 | 0 | static bool has_adx() { return has_cpuid_bit(CPUID_ADX_BIT); } |
261 | | |
262 | | /** |
263 | | * Check if the processor supports RDRAND |
264 | | */ |
265 | 0 | static bool has_rdrand() { return has_cpuid_bit(CPUID_RDRAND_BIT); } |
266 | | |
267 | | /** |
268 | | * Check if the processor supports RDSEED |
269 | | */ |
270 | 0 | static bool has_rdseed() { return has_cpuid_bit(CPUID_RDSEED_BIT); } |
271 | | #endif |
272 | | |
273 | | /** |
274 | | * Check if the processor supports byte-level vector permutes |
275 | | * (SSSE3, NEON, Altivec) |
276 | | */ |
277 | 0 | static bool has_vperm() { |
278 | 0 | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
279 | 0 | return has_ssse3(); |
280 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
281 | | return has_neon(); |
282 | | #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
283 | | return has_altivec(); |
284 | | #else |
285 | | return false; |
286 | | #endif |
287 | 0 | } |
288 | | |
289 | | /** |
290 | | * Check if the processor supports hardware AES instructions |
291 | | */ |
292 | 0 | static bool has_hw_aes() { |
293 | 0 | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
294 | 0 | return has_aes_ni(); |
295 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
296 | | return has_arm_aes(); |
297 | | #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) |
298 | | return has_power_crypto(); |
299 | | #else |
300 | | return false; |
301 | | #endif |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * Check if the processor supports carryless multiply |
306 | | * (CLMUL, PMULL) |
307 | | */ |
308 | 0 | static bool has_carryless_multiply() { |
309 | 0 | #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
310 | 0 | return has_clmul(); |
311 | | #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) |
312 | | return has_arm_pmull(); |
313 | | #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) |
314 | | return has_power_crypto(); |
315 | | #else |
316 | | return false; |
317 | | #endif |
318 | 0 | } |
319 | | |
320 | | /* |
321 | | * Clear a CPUID bit |
322 | | * Call CPUID::initialize to reset |
323 | | * |
324 | | * This is only exposed for testing, don't use unless you know |
325 | | * what you are doing. |
326 | | */ |
327 | 0 | static void clear_cpuid_bit(CPUID_bits bit) { state().clear_cpuid_bit(static_cast<uint32_t>(bit)); } |
328 | | |
329 | | /* |
330 | | * Don't call this function, use CPUID::has_xxx above |
331 | | * It is only exposed for the tests. |
332 | | */ |
333 | 24.8k | static bool has_cpuid_bit(CPUID_bits elem) { |
334 | 24.8k | const uint32_t elem32 = static_cast<uint32_t>(elem); |
335 | 24.8k | return state().has_bit(elem32); |
336 | 24.8k | } |
337 | | |
338 | | static std::vector<CPUID::CPUID_bits> bit_from_string(std::string_view tok); |
339 | | |
340 | | private: |
341 | | struct CPUID_Data { |
342 | | public: |
343 | | CPUID_Data(); |
344 | | |
345 | | CPUID_Data(const CPUID_Data& other) = default; |
346 | | CPUID_Data& operator=(const CPUID_Data& other) = default; |
347 | | |
348 | 0 | void clear_cpuid_bit(uint32_t bit) { m_processor_features &= ~bit; } |
349 | | |
350 | 24.8k | bool has_bit(uint32_t bit) const { return (m_processor_features & bit) == bit; } |
351 | | |
352 | | private: |
353 | | #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ |
354 | | defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) |
355 | | |
356 | | static uint32_t detect_cpu_features(); |
357 | | |
358 | | #endif |
359 | | uint32_t m_processor_features; |
360 | | }; |
361 | | |
362 | 24.8k | static CPUID_Data& state() { |
363 | 24.8k | static CPUID::CPUID_Data g_cpuid; |
364 | 24.8k | return g_cpuid; |
365 | 24.8k | } |
366 | | }; |
367 | | |
368 | | } // namespace Botan |
369 | | |
370 | | #endif |