Botan  2.7.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 
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 Darwin
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_PUBLIC_API(2,1) 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  * Deprecated equivalent to
48  * o << "CPUID flags: " << CPUID::to_string() << "\n";
49  */
50  BOTAN_DEPRECATED("Use CPUID::to_string")
51  static void print(std::ostream& o);
52 
53  /**
54  * Return a possibly empty string containing list of known CPU
55  * extensions. Each name will be seperated by a space, and the ordering
56  * will be arbitrary. This list only contains values that are useful to
57  * Botan (for example FMA instructions are not checked).
58  *
59  * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec"
60  */
61  static std::string to_string();
62 
63  /**
64  * Return a best guess of the cache line size
65  */
66  static size_t cache_line_size()
67  {
68  if(g_processor_features == 0)
69  {
70  initialize();
71  }
72  return g_cache_line_size;
73  }
74 
75  static bool is_little_endian()
76  {
77  return endian_status() == ENDIAN_LITTLE;
78  }
79 
80  static bool is_big_endian()
81  {
82  return endian_status() == ENDIAN_BIG;
83  }
84 
85  enum CPUID_bits : uint64_t {
86 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
87  // These values have no relation to cpuid bitfields
88 
89  // SIMD instruction sets
90  CPUID_SSE2_BIT = (1ULL << 0),
91  CPUID_SSSE3_BIT = (1ULL << 1),
92  CPUID_SSE41_BIT = (1ULL << 2),
93  CPUID_SSE42_BIT = (1ULL << 3),
94  CPUID_AVX2_BIT = (1ULL << 4),
95  CPUID_AVX512F_BIT = (1ULL << 5),
96 
97  // Misc useful instructions
98  CPUID_RDTSC_BIT = (1ULL << 10),
99  CPUID_BMI2_BIT = (1ULL << 11),
100  CPUID_ADX_BIT = (1ULL << 12),
101  CPUID_BMI1_BIT = (1ULL << 13),
102 
103  // Crypto-specific ISAs
104  CPUID_AESNI_BIT = (1ULL << 16),
105  CPUID_CLMUL_BIT = (1ULL << 17),
106  CPUID_RDRAND_BIT = (1ULL << 18),
107  CPUID_RDSEED_BIT = (1ULL << 19),
108  CPUID_SHA_BIT = (1ULL << 20),
109 #endif
110 
111 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
112  CPUID_ALTIVEC_BIT = (1ULL << 0),
113  CPUID_PPC_CRYPTO_BIT = (1ULL << 1),
114 #endif
115 
116 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
117  CPUID_ARM_NEON_BIT = (1ULL << 0),
118  CPUID_ARM_AES_BIT = (1ULL << 16),
119  CPUID_ARM_PMULL_BIT = (1ULL << 17),
120  CPUID_ARM_SHA1_BIT = (1ULL << 18),
121  CPUID_ARM_SHA2_BIT = (1ULL << 19),
122 #endif
123 
124  CPUID_INITIALIZED_BIT = (1ULL << 63)
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()
132  { return has_cpuid_bit(CPUID_ALTIVEC_BIT); }
133 
134  /**
135  * Check if the processor supports POWER8 crypto extensions
136  */
137  static bool has_ppc_crypto()
138  { return has_cpuid_bit(CPUID_PPC_CRYPTO_BIT); }
139 
140 #endif
141 
142 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
143  /**
144  * Check if the processor supports NEON SIMD
145  */
146  static bool has_neon()
147  { return has_cpuid_bit(CPUID_ARM_NEON_BIT); }
148 
149  /**
150  * Check if the processor supports ARMv8 SHA1
151  */
152  static bool has_arm_sha1()
153  { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); }
154 
155  /**
156  * Check if the processor supports ARMv8 SHA2
157  */
158  static bool has_arm_sha2()
159  { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); }
160 
161  /**
162  * Check if the processor supports ARMv8 AES
163  */
164  static bool has_arm_aes()
165  { return has_cpuid_bit(CPUID_ARM_AES_BIT); }
166 
167  /**
168  * Check if the processor supports ARMv8 PMULL
169  */
170  static bool has_arm_pmull()
171  { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); }
172 #endif
173 
174 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
175 
176  /**
177  * Check if the processor supports RDTSC
178  */
179  static bool has_rdtsc()
180  { return has_cpuid_bit(CPUID_RDTSC_BIT); }
181 
182  /**
183  * Check if the processor supports SSE2
184  */
185  static bool has_sse2()
186  { return has_cpuid_bit(CPUID_SSE2_BIT); }
187 
188  /**
189  * Check if the processor supports SSSE3
190  */
191  static bool has_ssse3()
192  { return has_cpuid_bit(CPUID_SSSE3_BIT); }
193 
194  /**
195  * Check if the processor supports SSE4.1
196  */
197  static bool has_sse41()
198  { return has_cpuid_bit(CPUID_SSE41_BIT); }
199 
200  /**
201  * Check if the processor supports SSE4.2
202  */
203  static bool has_sse42()
204  { return has_cpuid_bit(CPUID_SSE42_BIT); }
205 
206  /**
207  * Check if the processor supports AVX2
208  */
209  static bool has_avx2()
210  { return has_cpuid_bit(CPUID_AVX2_BIT); }
211 
212  /**
213  * Check if the processor supports AVX-512F
214  */
215  static bool has_avx512f()
216  { return has_cpuid_bit(CPUID_AVX512F_BIT); }
217 
218  /**
219  * Check if the processor supports BMI1
220  */
221  static bool has_bmi1()
222  { return has_cpuid_bit(CPUID_BMI1_BIT); }
223 
224  /**
225  * Check if the processor supports BMI2
226  */
227  static bool has_bmi2()
228  { return has_cpuid_bit(CPUID_BMI2_BIT); }
229 
230  /**
231  * Check if the processor supports AES-NI
232  */
233  static bool has_aes_ni()
234  { return has_cpuid_bit(CPUID_AESNI_BIT); }
235 
236  /**
237  * Check if the processor supports CLMUL
238  */
239  static bool has_clmul()
240  { return has_cpuid_bit(CPUID_CLMUL_BIT); }
241 
242  /**
243  * Check if the processor supports Intel SHA extension
244  */
245  static bool has_intel_sha()
246  { return has_cpuid_bit(CPUID_SHA_BIT); }
247 
248  /**
249  * Check if the processor supports ADX extension
250  */
251  static bool has_adx()
252  { return has_cpuid_bit(CPUID_ADX_BIT); }
253 
254  /**
255  * Check if the processor supports RDRAND
256  */
257  static bool has_rdrand()
258  { return has_cpuid_bit(CPUID_RDRAND_BIT); }
259 
260  /**
261  * Check if the processor supports RDSEED
262  */
263  static bool has_rdseed()
264  { return has_cpuid_bit(CPUID_RDSEED_BIT); }
265 #endif
266 
267  /*
268  * Clear a CPUID bit
269  * Call CPUID::initialize to reset
270  *
271  * This is only exposed for testing, don't use unless you know
272  * what you are doing.
273  */
274  static void clear_cpuid_bit(CPUID_bits bit)
275  {
276  const uint64_t mask = ~(static_cast<uint64_t>(bit));
277  g_processor_features &= mask;
278  }
279 
280  /*
281  * Don't call this function, use CPUID::has_xxx above
282  * It is only exposed for the tests.
283  */
284  static bool has_cpuid_bit(CPUID_bits elem)
285  {
286  if(g_processor_features == 0)
287  initialize();
288 
289  const uint64_t elem64 = static_cast<uint64_t>(elem);
290  return ((g_processor_features & elem64) == elem64);
291  }
292 
293  static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
294  private:
295  enum Endian_status : uint32_t {
296  ENDIAN_UNKNOWN = 0x00000000,
297  ENDIAN_BIG = 0x01234567,
298  ENDIAN_LITTLE = 0x67452301,
299  };
300 
301 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
302  defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
303  defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
304 
305  static uint64_t detect_cpu_features(size_t* cache_line_size);
306 
307 #endif
308 
309  static Endian_status runtime_check_endian();
310 
311  static Endian_status endian_status()
312  {
313  if(g_endian_status == ENDIAN_UNKNOWN)
314  {
315  g_endian_status = runtime_check_endian();
316  }
317  return g_endian_status;
318  }
319 
320  static uint64_t g_processor_features;
321  static size_t g_cache_line_size;
322  static Endian_status g_endian_status;
323  };
324 
325 }
326 
327 #endif
static size_t cache_line_size()
Definition: cpuid.h:66
#define BOTAN_PUBLIC_API(maj, min)
Definition: compiler.h:27
static bool is_little_endian()
Definition: cpuid.h:75
Definition: alg_id.cpp:13
std::string to_string(const secure_vector< uint8_t > &bytes)
Definition: stl_util.h:25
static void clear_cpuid_bit(CPUID_bits bit)
Definition: cpuid.h:274
static bool has_cpuid_bit(CPUID_bits elem)
Definition: cpuid.h:284
static bool is_big_endian()
Definition: cpuid.h:80