Botan  2.13.0
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  // 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  { return has_cpuid_bit(CPUID_RDTSC_BIT); }
234 
235  /**
236  * Check if the processor supports SSE2
237  */
238  static bool has_sse2()
239  { return has_cpuid_bit(CPUID_SSE2_BIT); }
240 
241  /**
242  * Check if the processor supports SSSE3
243  */
244  static bool has_ssse3()
245  { return has_cpuid_bit(CPUID_SSSE3_BIT); }
246 
247  /**
248  * Check if the processor supports SSE4.1
249  */
250  static bool has_sse41()
251  { return has_cpuid_bit(CPUID_SSE41_BIT); }
252 
253  /**
254  * Check if the processor supports SSE4.2
255  */
256  static bool has_sse42()
257  { return has_cpuid_bit(CPUID_SSE42_BIT); }
258 
259  /**
260  * Check if the processor supports AVX2
261  */
262  static bool has_avx2()
263  { return has_cpuid_bit(CPUID_AVX2_BIT); }
264 
265  /**
266  * Check if the processor supports AVX-512F
267  */
268  static bool has_avx512f()
269  { return has_cpuid_bit(CPUID_AVX512F_BIT); }
270 
271  /**
272  * Check if the processor supports BMI1
273  */
274  static bool has_bmi1()
275  { return has_cpuid_bit(CPUID_BMI1_BIT); }
276 
277  /**
278  * Check if the processor supports BMI2
279  */
280  static bool has_bmi2()
281  { 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  { return has_cpuid_bit(CPUID_AESNI_BIT); }
288 
289  /**
290  * Check if the processor supports CLMUL
291  */
292  static bool has_clmul()
293  { 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  { return has_cpuid_bit(CPUID_SHA_BIT); }
300 
301  /**
302  * Check if the processor supports ADX extension
303  */
304  static bool has_adx()
305  { return has_cpuid_bit(CPUID_ADX_BIT); }
306 
307  /**
308  * Check if the processor supports RDRAND
309  */
310  static bool has_rdrand()
311  { return has_cpuid_bit(CPUID_RDRAND_BIT); }
312 
313  /**
314  * Check if the processor supports RDSEED
315  */
316  static bool has_rdseed()
317  { 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  {
326 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
327  return has_ssse3();
328 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
329  return has_neon();
330 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
331  return has_altivec();
332 #else
333  return false;
334 #endif
335  }
336 
337  /**
338  * Check if the processor supports carryless multiply
339  * (CLMUL, PMULL)
340  */
342  {
343 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
344  return has_clmul();
345 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
346  return has_arm_pmull();
347 #else
348  return false;
349 #endif
350  }
351 
352  /*
353  * Clear a CPUID bit
354  * Call CPUID::initialize to reset
355  *
356  * This is only exposed for testing, don't use unless you know
357  * what you are doing.
358  */
359  static void clear_cpuid_bit(CPUID_bits bit)
360  {
361  state().clear_cpuid_bit(static_cast<uint64_t>(bit));
362  }
363 
364  /*
365  * Don't call this function, use CPUID::has_xxx above
366  * It is only exposed for the tests.
367  */
368  static bool has_cpuid_bit(CPUID_bits elem)
369  {
370  const uint64_t elem64 = static_cast<uint64_t>(elem);
371  return state().has_bit(elem64);
372  }
373 
374  static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
375  private:
376  enum class Endian_Status : uint32_t {
377  Unknown = 0x00000000,
378  Big = 0x01234567,
379  Little = 0x67452301,
380  };
381 
382  struct CPUID_Data
383  {
384  public:
385  CPUID_Data();
386 
387  CPUID_Data(const CPUID_Data& other) = default;
388  CPUID_Data& operator=(const CPUID_Data& other) = default;
389 
390  void clear_cpuid_bit(uint64_t bit)
391  {
392  m_processor_features &= ~bit;
393  }
394 
395  bool has_bit(uint64_t bit) const
396  {
397  return (m_processor_features & bit) == bit;
398  }
399 
400  uint64_t processor_features() const { return m_processor_features; }
401  Endian_Status endian_status() const { return m_endian_status; }
402  size_t cache_line_size() const { return m_cache_line_size; }
403 
404  private:
405  static Endian_Status runtime_check_endian();
406 
407 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
408  defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
409  defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
410 
411  static uint64_t detect_cpu_features(size_t* cache_line_size);
412 
413 #endif
414  uint64_t m_processor_features;
415  size_t m_cache_line_size;
416  Endian_Status m_endian_status;
417  };
418 
419  static CPUID_Data& state()
420  {
421  static CPUID::CPUID_Data g_cpuid;
422  return g_cpuid;
423  }
424  };
425 
426 }
427 
428 #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:341
static bool is_little_endian()
Definition: cpuid.h:73
Definition: alg_id.cpp:13
static bool has_vperm()
Definition: cpuid.h:324
static void clear_cpuid_bit(CPUID_bits bit)
Definition: cpuid.h:359
static bool has_cpuid_bit(CPUID_bits elem)
Definition: cpuid.h:368
static bool is_big_endian()
Definition: cpuid.h:84
#define BOTAN_FUTURE_INTERNAL_HEADER(hdr)
Definition: compiler.h:139