Coverage Report

Created: 2020-06-30 13:58

/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.70k
         {
70
3.70k
         return state().cache_line_size();
71
3.70k
         }
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
51.0k
         { 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
51.3k
         { 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
880k
         { 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.2k
         { return has_cpuid_bit(CPUID_AESNI_BIT); }
288
289
      /**
290
      * Check if the processor supports CLMUL
291
      */
292
      static bool has_clmul()
293
5.06k
         { 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
724k
         { 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 hardware AES instructions
339
      */
340
      static bool has_hw_aes()
341
22.5k
         {
342
22.5k
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
343
22.5k
         return has_aes_ni();
344
#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
345
         return has_arm_aes();
346
#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
347
         return has_power_crypto();
348
#else
349
         return false;
350
#endif
351
         }
352
353
      /**
354
      * Check if the processor supports carryless multiply
355
      * (CLMUL, PMULL)
356
      */
357
      static bool has_carryless_multiply()
358
5.06k
         {
359
5.06k
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
360
5.06k
         return has_clmul();
361
#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
362
         return has_arm_pmull();
363
#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
364
         return has_power_crypto();
365
#else
366
         return false;
367
#endif
368
         }
369
370
      /*
371
      * Clear a CPUID bit
372
      * Call CPUID::initialize to reset
373
      *
374
      * This is only exposed for testing, don't use unless you know
375
      * what you are doing.
376
      */
377
      static void clear_cpuid_bit(CPUID_bits bit)
378
0
         {
379
0
         state().clear_cpuid_bit(static_cast<uint64_t>(bit));
380
0
         }
381
382
      /*
383
      * Don't call this function, use CPUID::has_xxx above
384
      * It is only exposed for the tests.
385
      */
386
      static bool has_cpuid_bit(CPUID_bits elem)
387
1.73M
         {
388
1.73M
         const uint64_t elem64 = static_cast<uint64_t>(elem);
389
1.73M
         return state().has_bit(elem64);
390
1.73M
         }
391
392
      static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
393
   private:
394
      enum class Endian_Status : uint32_t {
395
         Unknown = 0x00000000,
396
         Big     = 0x01234567,
397
         Little  = 0x67452301,
398
      };
399
400
      struct CPUID_Data
401
         {
402
         public:
403
            CPUID_Data();
404
405
            CPUID_Data(const CPUID_Data& other) = default;
406
            CPUID_Data& operator=(const CPUID_Data& other) = default;
407
408
            void clear_cpuid_bit(uint64_t bit)
409
0
               {
410
0
               m_processor_features &= ~bit;
411
0
               }
412
413
            bool has_bit(uint64_t bit) const
414
1.73M
               {
415
1.73M
               return (m_processor_features & bit) == bit;
416
1.73M
               }
417
418
0
            uint64_t processor_features() const { return m_processor_features; }
419
0
            Endian_Status endian_status() const { return m_endian_status; }
420
3.70k
            size_t cache_line_size() const { return m_cache_line_size; }
421
422
         private:
423
            static Endian_Status runtime_check_endian();
424
425
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
426
    defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
427
    defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
428
429
            static uint64_t detect_cpu_features(size_t* cache_line_size);
430
431
#endif
432
            uint64_t m_processor_features;
433
            size_t m_cache_line_size;
434
            Endian_Status m_endian_status;
435
         };
436
437
      static CPUID_Data& state()
438
1.74M
         {
439
1.74M
         static CPUID::CPUID_Data g_cpuid;
440
1.74M
         return g_cpuid;
441
1.74M
         }
442
   };
443
444
}
445
446
#endif