cpuid.hpp
Go to the documentation of this file.
1 #ifndef CRYPTO3_CPUID_HPP
2 #define CRYPTO3_CPUID_HPP
3 
4 #include <vector>
5 #include <string>
6 #include <iosfwd>
7 
8 /*
9  * If no way of dynamically determining the cache line size for the
10  * system exists, this value is used as the default. Used by the side
11  * channel countermeasures rather than for alignment purposes, so it is
12  * better to be on the smaller side if the exact value cannot be
13  * determined. Typically 32 or 64 bytes on modern CPUs.
14  */
15 #if !defined(CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE)
16 #define CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE 32
17 #endif
18 
19 namespace nil {
20  namespace crypto3 {
39  class cpuid final {
40  public:
44  static void initialize() {
45  g_processor_features = 0;
46 
47 #if defined(BOOST_ARCH_PPC) || defined(BOOST_ARCH_ARM) || defined(BOOST_ARCH_X86)
48 
49  g_processor_features = cpuid::detect_cpu_features(&g_cache_line_size);
50 
51 #endif
52 
53  g_endian_status = runtime_check_endian();
54  g_processor_features |= cpuid::CPUID_INITIALIZED_BIT;
55  }
56 
57  static bool has_simd_32() {
58 #if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE2_VERSION
59  return cpuid::has_sse2();
60 #elif BOOST_HW_SIMD_ARM >= BOOST_HW_SIMD_ARM_NEON_VERSION
61  return cpuid::has_altivec();
62 #elif BOOST_HW_SIMD_PPC >= BOOST_HW_SIMD_PPC_VMX_VERSION
63  return cpuid::has_neon();
64 #else
65  return true;
66 #endif
67  }
68 
77  static std::string to_string() {
78  std::vector<std::string> flags;
79 
80 #define CPUID_PRINT(flag) \
81  do { \
82  if (has_##flag()) { \
83  flags.push_back(#flag); \
84  } \
85  } while (0)
86 
87 #if defined(BOOST_ARCH_X86)
88  CPUID_PRINT(sse2);
89  CPUID_PRINT(ssse3);
90  CPUID_PRINT(sse41);
91  CPUID_PRINT(sse42);
92  CPUID_PRINT(avx2);
93  CPUID_PRINT(avx512f);
94 
95  CPUID_PRINT(rdtsc);
96  CPUID_PRINT(bmi2);
97  CPUID_PRINT(adx);
98 
99  CPUID_PRINT(aes_ni);
100  CPUID_PRINT(clmul);
101  CPUID_PRINT(rdrand);
102  CPUID_PRINT(rdseed);
103  CPUID_PRINT(intel_sha);
104 #endif
105 
106 #if defined(BOOST_ARCH_PPC)
107  CPUID_PRINT(altivec);
108  CPUID_PRINT(ppc_crypto);
109 #endif
110 
111 #if defined(BOOST_ARCH_ARM)
112  CPUID_PRINT(neon);
113  CPUID_PRINT(arm_sha1);
114  CPUID_PRINT(arm_sha2);
115  CPUID_PRINT(arm_aes);
116  CPUID_PRINT(arm_pmull);
117 #endif
118 
119 #undef CPUID_PRINT
120 
121  std::string out;
122 
123  for (const std::string &c : flags) {
124  out.push_back(' ');
125  out.insert(out.end(), c.begin(), c.end());
126  }
127 
128  return out;
129  }
130 
134  static size_t cache_line_size() {
135  if (g_processor_features == 0) {
136  initialize();
137  }
138  return g_cache_line_size;
139  }
140 
141  static bool is_little_endian() {
142  return get_endian_status() == ENDIAN_LITTLE;
143  }
144 
145  static bool is_big_endian() {
146  return get_endian_status() == ENDIAN_BIG;
147  }
148 
149  enum CPUID_bits : uint64_t {
150 #if defined(BOOST_ARCH_X86)
151  // These values have no relation to cpuid bitfields
152 
153  // SIMD instruction sets
154  CPUID_SSE2_BIT = (1ULL << 0),
155  CPUID_SSSE3_BIT = (1ULL << 1),
156  CPUID_SSE41_BIT = (1ULL << 2),
157  CPUID_SSE42_BIT = (1ULL << 3),
158  CPUID_AVX2_BIT = (1ULL << 4),
159  CPUID_AVX512F_BIT = (1ULL << 5),
160 
161  // Misc useful instructions
162  CPUID_RDTSC_BIT = (1ULL << 10),
163  CPUID_BMI2_BIT = (1ULL << 11),
164  CPUID_ADX_BIT = (1ULL << 12),
165  CPUID_BMI1_BIT = (1ULL << 13),
166 
167  // Crypto-specific ISAs
168  CPUID_AESNI_BIT = (1ULL << 16),
169  CPUID_CLMUL_BIT = (1ULL << 17),
170  CPUID_RDRAND_BIT = (1ULL << 18),
171  CPUID_RDSEED_BIT = (1ULL << 19),
172  CPUID_SHA_BIT = (1ULL << 20),
173 #endif
174 
175 #if defined(BOOST_ARCH_PPC)
176  CPUID_ALTIVEC_BIT = (1ULL << 0),
177  CPUID_PPC_CRYPTO3_BIT = (1ULL << 1),
178 #endif
179 
180 #if defined(BOOST_ARCH_ARM)
181  CPUID_ARM_NEON_BIT = (1ULL << 0),
182  CPUID_ARM_RIJNDAEL_BIT = (1ULL << 16),
183  CPUID_ARM_PMULL_BIT = (1ULL << 17),
184  CPUID_ARM_SHA1_BIT = (1ULL << 18),
185  CPUID_ARM_SHA2_BIT = (1ULL << 19),
186 #endif
187 
188  CPUID_INITIALIZED_BIT = (1ULL << 63)
189  };
190 
191 #if defined(BOOST_ARCH_PPC)
195  static bool has_altivec() {
196  return has_cpuid_bit(CPUID_ALTIVEC_BIT);
197  }
198 
202  static bool has_ppc_crypto() {
203  return has_cpuid_bit(CPUID_PPC_CRYPTO3_BIT);
204  }
205 
206 #endif
207 
208 #if defined(BOOST_ARCH_ARM)
212  static bool has_neon() {
213  return has_cpuid_bit(CPUID_ARM_NEON_BIT);
214  }
215 
219  static bool has_arm_sha1() {
220  return has_cpuid_bit(CPUID_ARM_SHA1_BIT);
221  }
222 
226  static bool has_arm_sha2() {
227  return has_cpuid_bit(CPUID_ARM_SHA2_BIT);
228  }
229 
233  static bool has_arm_aes() {
234  return has_cpuid_bit(CPUID_ARM_RIJNDAEL_BIT);
235  }
236 
240  static bool has_arm_pmull() {
241  return has_cpuid_bit(CPUID_ARM_PMULL_BIT);
242  }
243 #endif
244 
245 #if defined(BOOST_ARCH_X86)
246 
250  static bool has_rdtsc() {
251  return has_cpuid_bit(CPUID_RDTSC_BIT);
252  }
253 
257  static bool has_sse2() {
258  return has_cpuid_bit(CPUID_SSE2_BIT);
259  }
260 
264  static bool has_ssse3() {
265  return has_cpuid_bit(CPUID_SSSE3_BIT);
266  }
267 
271  static bool has_sse41() {
272  return has_cpuid_bit(CPUID_SSE41_BIT);
273  }
274 
278  static bool has_sse42() {
279  return has_cpuid_bit(CPUID_SSE42_BIT);
280  }
281 
285  static bool has_avx2() {
286  return has_cpuid_bit(CPUID_AVX2_BIT);
287  }
288 
292  static bool has_avx512f() {
293  return has_cpuid_bit(CPUID_AVX512F_BIT);
294  }
295 
299  static bool has_bmi1() {
300  return has_cpuid_bit(CPUID_BMI1_BIT);
301  }
302 
306  static bool has_bmi2() {
307  return has_cpuid_bit(CPUID_BMI2_BIT);
308  }
309 
313  static bool has_aes_ni() {
314  return has_cpuid_bit(CPUID_AESNI_BIT);
315  }
316 
320  static bool has_clmul() {
321  return has_cpuid_bit(CPUID_CLMUL_BIT);
322  }
323 
327  static bool has_intel_sha() {
328  return has_cpuid_bit(CPUID_SHA_BIT);
329  }
330 
334  static bool has_adx() {
335  return has_cpuid_bit(CPUID_ADX_BIT);
336  }
337 
341  static bool has_rdrand() {
342  return has_cpuid_bit(CPUID_RDRAND_BIT);
343  }
344 
348  static bool has_rdseed() {
349  return has_cpuid_bit(CPUID_RDSEED_BIT);
350  }
351 
352 #endif
353 
354  /*
355  * Clear a cpuid bit
356  * Call cpuid::initialize to reset
357  *
358  * This is only exposed for testing, don't use unless you know
359  * what you are doing.
360  */
361  static void clear_cpuid_bit(CPUID_bits bit) {
362  const uint64_t mask = ~(static_cast<uint64_t>(bit));
363  g_processor_features &= mask;
364  }
365 
366  /*
367  * Don't call this function, use cpuid::has_xxx above
368  * It is only exposed for the tests.
369  */
370  static bool has_cpuid_bit(CPUID_bits elem) {
371  if (g_processor_features == 0) {
372  initialize();
373  }
374 
375  const uint64_t elem64 = static_cast<uint64_t>(elem);
376  return ((g_processor_features & elem64) == elem64);
377  }
378 
379  static std::vector<cpuid::CPUID_bits> bit_from_string(const std::string &tok) {
380 #if defined(BOOST_ARCH_X86)
381  if (tok == "sse2" || tok == "simd") {
382  return {nil::crypto3::cpuid::CPUID_SSE2_BIT};
383  }
384  if (tok == "ssse3") {
385  return {nil::crypto3::cpuid::CPUID_SSSE3_BIT};
386  }
387  if (tok == "aesni") {
388  return {nil::crypto3::cpuid::CPUID_AESNI_BIT};
389  }
390  if (tok == "clmul") {
391  return {nil::crypto3::cpuid::CPUID_CLMUL_BIT};
392  }
393  if (tok == "avx2") {
394  return {nil::crypto3::cpuid::CPUID_AVX2_BIT};
395  }
396  if (tok == "sha") {
397  return {nil::crypto3::cpuid::CPUID_SHA_BIT};
398  }
399 
400 #elif defined(BOOST_ARCH_PPC)
401  if (tok == "altivec" || tok == "simd")
402  return {nil::crypto3::cpuid::CPUID_ALTIVEC_BIT};
403 
404 #elif defined(BOOST_ARCH_ARM)
405  if (tok == "neon" || tok == "simd")
406  return {nil::crypto3::cpuid::CPUID_ARM_NEON_BIT};
407  if (tok == "armv8sha1")
408  return {nil::crypto3::cpuid::CPUID_ARM_SHA1_BIT};
409  if (tok == "armv8sha2")
410  return {nil::crypto3::cpuid::CPUID_ARM_SHA2_BIT};
411  if (tok == "armv8aes")
412  return {nil::crypto3::cpuid::CPUID_ARM_RIJNDAEL_BIT};
413  if (tok == "armv8pmull")
414  return {nil::crypto3::cpuid::CPUID_ARM_PMULL_BIT};
415 
416 #else
417  CRYPTO3_UNUSED(tok);
418 #endif
419 
420  return {};
421  }
422 
423  private:
424  enum endian_status : uint32_t {
425  ENDIAN_UNKNOWN = 0x00000000,
426  ENDIAN_BIG = 0x01234567,
427  ENDIAN_LITTLE = 0x67452301,
428  };
429 
430 #if defined(BOOST_ARCH_PPC) || defined(BOOST_ARCH_ARM) || defined(BOOST_ARCH_X86)
431 
432  static uint64_t detect_cpu_features(size_t *cache_line_size);
433 
434 #endif
435 
436  static endian_status runtime_check_endian() {
437  // Check runtime endian
438  const uint32_t endian32 = 0x01234567;
439  const uint8_t *e8 = reinterpret_cast<const uint8_t *>(&endian32);
440 
441  endian_status endian = ENDIAN_UNKNOWN;
442 
443  if (e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) {
444  endian = ENDIAN_BIG;
445  } else if (e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) {
446  endian = ENDIAN_LITTLE;
447  } else {
448  throw std::exception();
449  }
450 
451  // If we were compiled with a known endian, verify it matches at runtime
452 #if defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE)
453  BOOST_ASSERT_MSG(endian == ENDIAN_LITTLE, "Build and runtime endian match");
454 #elif defined(BOOST_ENDIAN_BIG_BYTE_AVAILABLE)
455  BOOST_ASSERT_MSG(endian == ENDIAN_BIG, "Build and runtime endian match");
456 #endif
457 
458  return endian;
459  }
460 
461  static endian_status get_endian_status() {
462  if (g_endian_status == ENDIAN_UNKNOWN) {
463  g_endian_status = runtime_check_endian();
464  }
465  return g_endian_status;
466  }
467 
468  static uint64_t g_processor_features;
469  static size_t g_cache_line_size;
470  static enum endian_status g_endian_status;
471  };
472 
473  uint64_t cpuid::g_processor_features = 0;
474  size_t cpuid::g_cache_line_size = CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
475  cpuid::endian_status cpuid::g_endian_status = ENDIAN_UNKNOWN;
476  } // namespace crypto3
477 } // namespace nil
478 
479 #endif
Definition: cpuid.hpp:39
static bool has_cpuid_bit(CPUID_bits elem)
Definition: cpuid.hpp:370
static std::string to_string()
Definition: cpuid.hpp:77
static bool is_big_endian()
Definition: cpuid.hpp:145
static size_t cache_line_size()
Definition: cpuid.hpp:134
static bool is_little_endian()
Definition: cpuid.hpp:141
static std::vector< cpuid::CPUID_bits > bit_from_string(const std::string &tok)
Definition: cpuid.hpp:379
static bool has_simd_32()
Definition: cpuid.hpp:57
static void clear_cpuid_bit(CPUID_bits bit)
Definition: cpuid.hpp:361
CPUID_bits
Definition: cpuid.hpp:149
@ CPUID_INITIALIZED_BIT
Definition: cpuid.hpp:188
static void initialize()
Definition: cpuid.hpp:44
#define CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE
Definition: cpuid.hpp:16
#define CPUID_PRINT(flag)
Definition: pair.hpp:31