Botan  2.1.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 <string>
13 #include <iosfwd>
14 
15 namespace Botan {
16 
17 /**
18 * A class handling runtime CPU feature detection. It is limited to
19 * just the features necessary to implement CPU specific code in Botan,
20 * rather than being a general purpose utility.
21 *
22 * This class supports:
23 *
24 * - x86 features using CPUID. x86 is also the only processor with
25 * accurate cache line detection currently.
26 *
27 * - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and Darwin
28 *
29 * - ARM NEON and crypto extensions detection. On Linux and Android
30 * systems which support getauxval, that is used to access CPU
31 * feature information. Otherwise a relatively portable but
32 * thread-unsafe mechanism involving executing probe functions which
33 * catching SIGILL signal is used.
34 */
35 class BOTAN_DLL CPUID
36  {
37  public:
38  /**
39  * Probe the CPU and see what extensions are supported
40  */
41  static void initialize();
42 
43  static bool has_simd_32();
44 
45  /**
46  * Deprecated equivalent to
47  * o << "CPUID flags: " << CPUID::to_string() << "\n";
48  */
49  BOTAN_DEPRECATED("Use CPUID::to_string")
50  static void print(std::ostream& o);
51 
52  /**
53  * Return a possibly empty string containing list of known CPU
54  * extensions. Each name will be seperated by a space, and the ordering
55  * will be arbitrary. This list only contains values that are useful to
56  * Botan (for example FMA instructions are not checked).
57  *
58  * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec"
59  */
60  static std::string to_string();
61 
62  /**
63  * Return a best guess of the cache line size
64  */
65  static size_t cache_line_size()
66  {
67  if(g_processor_features == 0)
68  {
69  initialize();
70  }
71  return g_cache_line_size;
72  }
73 
74  static bool is_little_endian()
75  {
76  if(g_processor_features == 0)
77  {
78  initialize();
79  }
80  return g_little_endian;
81  }
82 
83  static bool is_big_endian()
84  {
85  /*
86  * We do not support PDP endian, so the endian is
87  * always either big or little.
88  */
89  return is_little_endian() == false;
90  }
91 
92  enum CPUID_bits : uint64_t {
93 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
94  // These values have no relation to cpuid bitfields
95 
96  // SIMD instruction sets
97  CPUID_SSE2_BIT = (1ULL << 0),
98  CPUID_SSSE3_BIT = (1ULL << 1),
99  CPUID_SSE41_BIT = (1ULL << 2),
100  CPUID_SSE42_BIT = (1ULL << 3),
101  CPUID_AVX2_BIT = (1ULL << 4),
102  CPUID_AVX512F_BIT = (1ULL << 5),
103 
104  // Misc useful instructions
105  CPUID_RDTSC_BIT = (1ULL << 10),
106  CPUID_BMI2_BIT = (1ULL << 11),
107  CPUID_ADX_BIT = (1ULL << 12),
108 
109  // Crypto-specific ISAs
110  CPUID_AESNI_BIT = (1ULL << 16),
111  CPUID_CLMUL_BIT = (1ULL << 17),
112  CPUID_RDRAND_BIT = (1ULL << 18),
113  CPUID_RDSEED_BIT = (1ULL << 19),
114  CPUID_SHA_BIT = (1ULL << 20),
115 #endif
116 
117 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
118  CPUID_ALTIVEC_BIT = (1ULL << 0),
119 #endif
120 
121 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
122  CPUID_ARM_NEON_BIT = (1ULL << 0),
123  CPUID_ARM_AES_BIT = (1ULL << 16),
124  CPUID_ARM_PMULL_BIT = (1ULL << 17),
125  CPUID_ARM_SHA1_BIT = (1ULL << 18),
126  CPUID_ARM_SHA2_BIT = (1ULL << 19),
127 #endif
128 
129  CPUID_INITIALIZED_BIT = (1ULL << 63)
130  };
131 
132 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
133  /**
134  * Check if the processor supports AltiVec/VMX
135  */
136  static bool has_altivec()
137  { return has_cpuid_bit(CPUID_ALTIVEC_BIT); }
138 #endif
139 
140 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
141  /**
142  * Check if the processor supports NEON SIMD
143  */
144  static bool has_neon()
145  { return has_cpuid_bit(CPUID_ARM_NEON_BIT); }
146 
147  /**
148  * Check if the processor supports ARMv8 SHA1
149  */
150  static bool has_arm_sha1()
151  { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); }
152 
153  /**
154  * Check if the processor supports ARMv8 SHA2
155  */
156  static bool has_arm_sha2()
157  { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); }
158 
159  /**
160  * Check if the processor supports ARMv8 AES
161  */
162  static bool has_arm_aes()
163  { return has_cpuid_bit(CPUID_ARM_AES_BIT); }
164 
165  /**
166  * Check if the processor supports ARMv8 PMULL
167  */
168  static bool has_arm_pmull()
169  { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); }
170 #endif
171 
172 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
173 
174  /**
175  * Check if the processor supports RDTSC
176  */
177  static bool has_rdtsc()
178  { return has_cpuid_bit(CPUID_RDTSC_BIT); }
179 
180  /**
181  * Check if the processor supports SSE2
182  */
183  static bool has_sse2()
184  { return has_cpuid_bit(CPUID_SSE2_BIT); }
185 
186  /**
187  * Check if the processor supports SSSE3
188  */
189  static bool has_ssse3()
190  { return has_cpuid_bit(CPUID_SSSE3_BIT); }
191 
192  /**
193  * Check if the processor supports SSE4.1
194  */
195  static bool has_sse41()
196  { return has_cpuid_bit(CPUID_SSE41_BIT); }
197 
198  /**
199  * Check if the processor supports SSE4.2
200  */
201  static bool has_sse42()
202  { return has_cpuid_bit(CPUID_SSE42_BIT); }
203 
204  /**
205  * Check if the processor supports AVX2
206  */
207  static bool has_avx2()
208  { return has_cpuid_bit(CPUID_AVX2_BIT); }
209 
210  /**
211  * Check if the processor supports AVX-512F
212  */
213  static bool has_avx512f()
214  { return has_cpuid_bit(CPUID_AVX512F_BIT); }
215 
216  /**
217  * Check if the processor supports BMI2
218  */
219  static bool has_bmi2()
220  { return has_cpuid_bit(CPUID_BMI2_BIT); }
221 
222  /**
223  * Check if the processor supports AES-NI
224  */
225  static bool has_aes_ni()
226  { return has_cpuid_bit(CPUID_AESNI_BIT); }
227 
228  /**
229  * Check if the processor supports CLMUL
230  */
231  static bool has_clmul()
232  { return has_cpuid_bit(CPUID_CLMUL_BIT); }
233 
234  /**
235  * Check if the processor supports Intel SHA extension
236  */
237  static bool has_intel_sha()
238  { return has_cpuid_bit(CPUID_SHA_BIT); }
239 
240  /**
241  * Check if the processor supports ADX extension
242  */
243  static bool has_adx()
244  { return has_cpuid_bit(CPUID_ADX_BIT); }
245 
246  /**
247  * Check if the processor supports RDRAND
248  */
249  static bool has_rdrand()
250  { return has_cpuid_bit(CPUID_RDRAND_BIT); }
251 
252  /**
253  * Check if the processor supports RDSEED
254  */
255  static bool has_rdseed()
256  { return has_cpuid_bit(CPUID_RDSEED_BIT); }
257 #endif
258 
259  /*
260  * Clear a CPUID bit
261  * Call CPUID::initialize to reset
262  *
263  * This is only exposed for testing, don't use unless you know
264  * what you are doing.
265  */
266  static void clear_cpuid_bit(CPUID_bits bit)
267  {
268  const uint64_t mask = ~(static_cast<uint64_t>(bit));
269  g_processor_features &= mask;
270  }
271 
272  /*
273  * Don't call this function, use CPUID::has_xxx above
274  * It is only exposed for the tests.
275  */
276  static bool has_cpuid_bit(CPUID_bits elem)
277  {
278  if(g_processor_features == 0)
279  initialize();
280  return ((g_processor_features & static_cast<uint64_t>(elem)) != 0);
281  }
282 
283  private:
284  static bool g_little_endian;
285  static size_t g_cache_line_size;
286  static uint64_t g_processor_features;
287  };
288 
289 }
290 
291 #endif
static size_t cache_line_size()
Definition: cpuid.h:65
class BOTAN_DLL BOTAN_DEPRECATED("LibraryInitializer is no longer required") LibraryInitializer
Definition: init.h:22
static bool is_little_endian()
Definition: cpuid.h:74
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:266
static bool has_cpuid_bit(CPUID_bits elem)
Definition: cpuid.h:276
static bool is_big_endian()
Definition: cpuid.h:83