Coverage Report

Created: 2023-06-07 07:00

/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