Botan  2.19.1
Crypto and TLS for C++11
cpuid.h
Go to the documentation of this file.
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 
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 */
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  {
70  return state().cache_line_size();
71  }
72 
73  static bool is_little_endian()
74  {
75 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
76  return true;
77 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
78  return false;
79 #else
80  return state().endian_status() == Endian_Status::Little;
81 #endif
82  }
83 
84  static bool is_big_endian()
85  {
86 #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
87  return true;
88 #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
89  return false;
90 #else
91  return state().endian_status() == Endian_Status::Big;
92 #endif
93  }
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  CPUID_AVX512DQ_BIT = (1ULL << 6),
108  CPUID_AVX512BW_BIT = (1ULL << 7),
109 
110  // Ice Lake profile: AVX-512 F, DQ, BW, IFMA, VBMI, VBMI2, BITALG
111  CPUID_AVX512_ICL_BIT = (1ULL << 11),
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  CPUID_AVX512_AES_BIT = (1ULL << 21),
120  CPUID_AVX512_CLMUL_BIT = (1ULL << 22),
121 
122  // Misc useful instructions
123  CPUID_RDTSC_BIT = (1ULL << 48),
124  CPUID_ADX_BIT = (1ULL << 49),
125  CPUID_BMI1_BIT = (1ULL << 50),
126  CPUID_BMI2_BIT = (1ULL << 51),
127 #endif
128 
129 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
130  CPUID_ALTIVEC_BIT = (1ULL << 0),
131  CPUID_POWER_CRYPTO_BIT = (1ULL << 1),
132  CPUID_DARN_BIT = (1ULL << 2),
133 #endif
134 
135 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
136  CPUID_ARM_NEON_BIT = (1ULL << 0),
137  CPUID_ARM_SVE_BIT = (1ULL << 1),
138  CPUID_ARM_AES_BIT = (1ULL << 16),
139  CPUID_ARM_PMULL_BIT = (1ULL << 17),
140  CPUID_ARM_SHA1_BIT = (1ULL << 18),
141  CPUID_ARM_SHA2_BIT = (1ULL << 19),
142  CPUID_ARM_SHA3_BIT = (1ULL << 20),
143  CPUID_ARM_SHA2_512_BIT = (1ULL << 21),
144  CPUID_ARM_SM3_BIT = (1ULL << 22),
145  CPUID_ARM_SM4_BIT = (1ULL << 23),
146 #endif
147 
148  CPUID_INITIALIZED_BIT = (1ULL << 63)
149  };
150 
151 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
152  /**
153  * Check if the processor supports AltiVec/VMX
154  */
155  static bool has_altivec()
156  { return has_cpuid_bit(CPUID_ALTIVEC_BIT); }
157 
158  /**
159  * Check if the processor supports POWER8 crypto extensions
160  */
161  static bool has_power_crypto()
162  { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); }
163 
164  /**
165  * Check if the processor supports POWER9 DARN RNG
166  */
167  static bool has_darn_rng()
168  { return has_cpuid_bit(CPUID_DARN_BIT); }
169 
170 #endif
171 
172 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
173  /**
174  * Check if the processor supports NEON SIMD
175  */
176  static bool has_neon()
177  { return has_cpuid_bit(CPUID_ARM_NEON_BIT); }
178 
179  /**
180  * Check if the processor supports ARMv8 SVE
181  */
182  static bool has_arm_sve()
183  { return has_cpuid_bit(CPUID_ARM_SVE_BIT); }
184 
185  /**
186  * Check if the processor supports ARMv8 SHA1
187  */
188  static bool has_arm_sha1()
189  { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); }
190 
191  /**
192  * Check if the processor supports ARMv8 SHA2
193  */
194  static bool has_arm_sha2()
195  { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); }
196 
197  /**
198  * Check if the processor supports ARMv8 AES
199  */
200  static bool has_arm_aes()
201  { return has_cpuid_bit(CPUID_ARM_AES_BIT); }
202 
203  /**
204  * Check if the processor supports ARMv8 PMULL
205  */
206  static bool has_arm_pmull()
207  { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); }
208 
209  /**
210  * Check if the processor supports ARMv8 SHA-512
211  */
212  static bool has_arm_sha2_512()
213  { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); }
214 
215  /**
216  * Check if the processor supports ARMv8 SHA-3
217  */
218  static bool has_arm_sha3()
219  { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); }
220 
221  /**
222  * Check if the processor supports ARMv8 SM3
223  */
224  static bool has_arm_sm3()
225  { return has_cpuid_bit(CPUID_ARM_SM3_BIT); }
226 
227  /**
228  * Check if the processor supports ARMv8 SM4
229  */
230  static bool has_arm_sm4()
231  { return has_cpuid_bit(CPUID_ARM_SM4_BIT); }
232 
233 #endif
234 
235 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
236 
237  /**
238  * Check if the processor supports RDTSC
239  */
240  static bool has_rdtsc()
241  { return has_cpuid_bit(CPUID_RDTSC_BIT); }
242 
243  /**
244  * Check if the processor supports SSE2
245  */
246  static bool has_sse2()
247  { return has_cpuid_bit(CPUID_SSE2_BIT); }
248 
249  /**
250  * Check if the processor supports SSSE3
251  */
252  static bool has_ssse3()
253  { return has_cpuid_bit(CPUID_SSSE3_BIT); }
254 
255  /**
256  * Check if the processor supports SSE4.1
257  */
258  static bool has_sse41()
259  { return has_cpuid_bit(CPUID_SSE41_BIT); }
260 
261  /**
262  * Check if the processor supports SSE4.2
263  */
264  static bool has_sse42()
265  { return has_cpuid_bit(CPUID_SSE42_BIT); }
266 
267  /**
268  * Check if the processor supports AVX2
269  */
270  static bool has_avx2()
271  { return has_cpuid_bit(CPUID_AVX2_BIT); }
272 
273  /**
274  * Check if the processor supports AVX-512F
275  */
276  static bool has_avx512f()
277  { return has_cpuid_bit(CPUID_AVX512F_BIT); }
278 
279  /**
280  * Check if the processor supports AVX-512DQ
281  */
282  static bool has_avx512dq()
283  { return has_cpuid_bit(CPUID_AVX512DQ_BIT); }
284 
285  /**
286  * Check if the processor supports AVX-512BW
287  */
288  static bool has_avx512bw()
289  { return has_cpuid_bit(CPUID_AVX512BW_BIT); }
290 
291  /**
292  * Check if the processor supports AVX-512 Ice Lake profile
293  */
294  static bool has_avx512_icelake()
295  { return has_cpuid_bit(CPUID_AVX512_ICL_BIT); }
296 
297  /**
298  * Check if the processor supports AVX-512 AES (VAES)
299  */
300  static bool has_avx512_aes()
301  { return has_cpuid_bit(CPUID_AVX512_AES_BIT); }
302 
303  /**
304  * Check if the processor supports AVX-512 VPCLMULQDQ
305  */
306  static bool has_avx512_clmul()
307  { return has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); }
308 
309  /**
310  * Check if the processor supports BMI1
311  */
312  static bool has_bmi1()
313  { return has_cpuid_bit(CPUID_BMI1_BIT); }
314 
315  /**
316  * Check if the processor supports BMI2
317  */
318  static bool has_bmi2()
319  { return has_cpuid_bit(CPUID_BMI2_BIT); }
320 
321  /**
322  * Check if the processor supports AES-NI
323  */
324  static bool has_aes_ni()
325  { return has_cpuid_bit(CPUID_AESNI_BIT); }
326 
327  /**
328  * Check if the processor supports CLMUL
329  */
330  static bool has_clmul()
331  { return has_cpuid_bit(CPUID_CLMUL_BIT); }
332 
333  /**
334  * Check if the processor supports Intel SHA extension
335  */
336  static bool has_intel_sha()
337  { return has_cpuid_bit(CPUID_SHA_BIT); }
338 
339  /**
340  * Check if the processor supports ADX extension
341  */
342  static bool has_adx()
343  { return has_cpuid_bit(CPUID_ADX_BIT); }
344 
345  /**
346  * Check if the processor supports RDRAND
347  */
348  static bool has_rdrand()
349  { return has_cpuid_bit(CPUID_RDRAND_BIT); }
350 
351  /**
352  * Check if the processor supports RDSEED
353  */
354  static bool has_rdseed()
355  { return has_cpuid_bit(CPUID_RDSEED_BIT); }
356 #endif
357 
358  /**
359  * Check if the processor supports byte-level vector permutes
360  * (SSSE3, NEON, Altivec)
361  */
362  static bool has_vperm()
363  {
364 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
365  return has_ssse3();
366 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
367  return has_neon();
368 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
369  return has_altivec();
370 #else
371  return false;
372 #endif
373  }
374 
375  /**
376  * Check if the processor supports hardware AES instructions
377  */
378  static bool has_hw_aes()
379  {
380 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
381  return has_aes_ni();
382 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
383  return has_arm_aes();
384 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
385  return has_power_crypto();
386 #else
387  return false;
388 #endif
389  }
390 
391  /**
392  * Check if the processor supports carryless multiply
393  * (CLMUL, PMULL)
394  */
396  {
397 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
398  return has_clmul();
399 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
400  return has_arm_pmull();
401 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
402  return has_power_crypto();
403 #else
404  return false;
405 #endif
406  }
407 
408  /*
409  * Clear a CPUID bit
410  * Call CPUID::initialize to reset
411  *
412  * This is only exposed for testing, don't use unless you know
413  * what you are doing.
414  */
415  static void clear_cpuid_bit(CPUID_bits bit)
416  {
417  state().clear_cpuid_bit(static_cast<uint64_t>(bit));
418  }
419 
420  /*
421  * Don't call this function, use CPUID::has_xxx above
422  * It is only exposed for the tests.
423  */
424  static bool has_cpuid_bit(CPUID_bits elem)
425  {
426  const uint64_t elem64 = static_cast<uint64_t>(elem);
427  return state().has_bit(elem64);
428  }
429 
430  static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
431  private:
432  enum class Endian_Status : uint32_t {
433  Unknown = 0x00000000,
434  Big = 0x01234567,
435  Little = 0x67452301,
436  };
437 
438  struct CPUID_Data
439  {
440  public:
441  CPUID_Data();
442 
443  CPUID_Data(const CPUID_Data& other) = default;
444  CPUID_Data& operator=(const CPUID_Data& other) = default;
445 
446  void clear_cpuid_bit(uint64_t bit)
447  {
448  m_processor_features &= ~bit;
449  }
450 
451  bool has_bit(uint64_t bit) const
452  {
453  return (m_processor_features & bit) == bit;
454  }
455 
456  uint64_t processor_features() const { return m_processor_features; }
457  Endian_Status endian_status() const { return m_endian_status; }
458  size_t cache_line_size() const { return m_cache_line_size; }
459 
460  private:
461  static Endian_Status runtime_check_endian();
462 
463 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
464  defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
465  defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
466 
467  static uint64_t detect_cpu_features(size_t* cache_line_size);
468 
469 #endif
470  uint64_t m_processor_features;
471  size_t m_cache_line_size;
472  Endian_Status m_endian_status;
473  };
474 
475  static CPUID_Data& state()
476  {
477  static CPUID::CPUID_Data g_cpuid;
478  return g_cpuid;
479  }
480  };
481 
482 }
483 
484 #endif
static size_t cache_line_size()
Definition: cpuid.h:68
int(* final)(unsigned char *, CTX *)
#define BOTAN_PUBLIC_API(maj, min)
Definition: compiler.h:31
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
static bool has_carryless_multiply()
Definition: cpuid.h:395
static bool is_little_endian()
Definition: cpuid.h:73
Definition: alg_id.cpp:13
static bool has_vperm()
Definition: cpuid.h:362
static bool has_hw_aes()
Definition: cpuid.h:378
static void clear_cpuid_bit(CPUID_bits bit)
Definition: cpuid.h:415
static bool has_cpuid_bit(CPUID_bits elem)
Definition: cpuid.h:424
static bool is_big_endian()
Definition: cpuid.h:84
#define BOTAN_FUTURE_INTERNAL_HEADER(hdr)
Definition: compiler.h:136