Coverage Report

Created: 2022-05-14 06:06

/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