Botan  2.18.1
Crypto and TLS for C++11
cpuid.cpp
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 #include <botan/cpuid.h>
9 #include <botan/types.h>
10 #include <botan/exceptn.h>
11 #include <botan/parsing.h>
12 #include <ostream>
13 
14 namespace Botan {
15 
17  {
18 #if defined(BOTAN_TARGET_SUPPORTS_SSE2)
19  return CPUID::has_sse2();
20 #elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
21  return CPUID::has_altivec();
22 #elif defined(BOTAN_TARGET_SUPPORTS_NEON)
23  return CPUID::has_neon();
24 #else
25  return true;
26 #endif
27  }
28 
29 //static
30 std::string CPUID::to_string()
31  {
32  std::vector<std::string> flags;
33 
34 #define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0)
35 
36 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
37  CPUID_PRINT(sse2);
38  CPUID_PRINT(ssse3);
39  CPUID_PRINT(sse41);
40  CPUID_PRINT(sse42);
41  CPUID_PRINT(avx2);
42  CPUID_PRINT(avx512f);
43  CPUID_PRINT(avx512dq);
44  CPUID_PRINT(avx512bw);
45  CPUID_PRINT(avx512_icelake);
46 
47  CPUID_PRINT(rdtsc);
48  CPUID_PRINT(bmi1);
49  CPUID_PRINT(bmi2);
50  CPUID_PRINT(adx);
51 
52  CPUID_PRINT(aes_ni);
53  CPUID_PRINT(clmul);
54  CPUID_PRINT(rdrand);
55  CPUID_PRINT(rdseed);
56  CPUID_PRINT(intel_sha);
57  CPUID_PRINT(avx512_aes);
58  CPUID_PRINT(avx512_clmul);
59 #endif
60 
61 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
62  CPUID_PRINT(altivec);
63  CPUID_PRINT(power_crypto);
64  CPUID_PRINT(darn_rng);
65 #endif
66 
67 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
68  CPUID_PRINT(neon);
69  CPUID_PRINT(arm_sve);
70 
71  CPUID_PRINT(arm_sha1);
72  CPUID_PRINT(arm_sha2);
73  CPUID_PRINT(arm_aes);
74  CPUID_PRINT(arm_pmull);
75  CPUID_PRINT(arm_sha2_512);
76  CPUID_PRINT(arm_sha3);
77  CPUID_PRINT(arm_sm3);
78  CPUID_PRINT(arm_sm4);
79 #endif
80 
81 #undef CPUID_PRINT
82 
83  return string_join(flags, ' ');
84  }
85 
86 //static
87 void CPUID::print(std::ostream& o)
88  {
89  o << "CPUID flags: " << CPUID::to_string() << "\n";
90  }
91 
92 //static
94  {
95  state() = CPUID_Data();
96  }
97 
98 CPUID::CPUID_Data::CPUID_Data()
99  {
100  m_cache_line_size = 0;
101  m_processor_features = 0;
102 
103 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
104  defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
105  defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
106 
107  m_processor_features = detect_cpu_features(&m_cache_line_size);
108 
109 #endif
110 
111  m_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
112 
113  if(m_cache_line_size == 0)
114  m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
115 
116  m_endian_status = runtime_check_endian();
117  }
118 
119 //static
120 CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian()
121  {
122  // Check runtime endian
123  const uint32_t endian32 = 0x01234567;
124  const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
125 
126  CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown;
127 
128  if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
129  {
130  endian = CPUID::Endian_Status::Big;
131  }
132  else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
133  {
134  endian = CPUID::Endian_Status::Little;
135  }
136  else
137  {
138  throw Internal_Error("Unexpected endian at runtime, neither big nor little");
139  }
140 
141  // If we were compiled with a known endian, verify it matches at runtime
142 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
143  BOTAN_ASSERT(endian == CPUID::Endian_Status::Little, "Build and runtime endian match");
144 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
145  BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match");
146 #endif
147 
148  return endian;
149  }
150 
151 std::vector<Botan::CPUID::CPUID_bits>
152 CPUID::bit_from_string(const std::string& tok)
153  {
154 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
155  if(tok == "sse2" || tok == "simd")
156  return {Botan::CPUID::CPUID_SSE2_BIT};
157  if(tok == "ssse3")
158  return {Botan::CPUID::CPUID_SSSE3_BIT};
159  if(tok == "sse41")
160  return {Botan::CPUID::CPUID_SSE41_BIT};
161  if(tok == "sse42")
162  return {Botan::CPUID::CPUID_SSE42_BIT};
163  // aes_ni is the string printed on the console when running "botan cpuid"
164  if(tok == "aesni" || tok == "aes_ni")
165  return {Botan::CPUID::CPUID_AESNI_BIT};
166  if(tok == "clmul")
167  return {Botan::CPUID::CPUID_CLMUL_BIT};
168  if(tok == "avx2")
169  return {Botan::CPUID::CPUID_AVX2_BIT};
170  if(tok == "avx512f")
171  return {Botan::CPUID::CPUID_AVX512F_BIT};
172  if(tok == "avx512_icelake")
173  return {Botan::CPUID::CPUID_AVX512_ICL_BIT};
174  // there were two if statements testing "sha" and "intel_sha" separately; combined
175  if(tok == "sha" || tok=="intel_sha")
176  return {Botan::CPUID::CPUID_SHA_BIT};
177  if(tok == "rdtsc")
178  return {Botan::CPUID::CPUID_RDTSC_BIT};
179  if(tok == "bmi1")
180  return {Botan::CPUID::CPUID_BMI1_BIT};
181  if(tok == "bmi2")
182  return {Botan::CPUID::CPUID_BMI2_BIT};
183  if(tok == "adx")
184  return {Botan::CPUID::CPUID_ADX_BIT};
185  if(tok == "rdrand")
186  return {Botan::CPUID::CPUID_RDRAND_BIT};
187  if(tok == "rdseed")
188  return {Botan::CPUID::CPUID_RDSEED_BIT};
189  if(tok == "avx512_aes")
190  return {Botan::CPUID::CPUID_AVX512_AES_BIT};
191  if(tok == "avx512_clmul")
192  return {Botan::CPUID::CPUID_AVX512_CLMUL_BIT};
193 
194 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
195  if(tok == "altivec" || tok == "simd")
196  return {Botan::CPUID::CPUID_ALTIVEC_BIT};
197  if(tok == "power_crypto")
198  return {Botan::CPUID::CPUID_POWER_CRYPTO_BIT};
199  if(tok == "darn_rng")
200  return {Botan::CPUID::CPUID_DARN_BIT};
201 
202 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
203  if(tok == "neon" || tok == "simd")
204  return {Botan::CPUID::CPUID_ARM_NEON_BIT};
205  if(tok == "arm_sve")
206  return {Botan::CPUID::CPUID_ARM_SVE_BIT};
207  if(tok == "armv8sha1" || tok == "arm_sha1")
208  return {Botan::CPUID::CPUID_ARM_SHA1_BIT};
209  if(tok == "armv8sha2" || tok == "arm_sha2")
210  return {Botan::CPUID::CPUID_ARM_SHA2_BIT};
211  if(tok == "armv8aes" || tok == "arm_aes")
212  return {Botan::CPUID::CPUID_ARM_AES_BIT};
213  if(tok == "armv8pmull" || tok == "arm_pmull")
214  return {Botan::CPUID::CPUID_ARM_PMULL_BIT};
215  if(tok == "armv8sha3" || tok == "arm_sha3")
216  return {Botan::CPUID::CPUID_ARM_SHA3_BIT};
217  if(tok == "armv8sha2_512" || tok == "arm_sha2_512")
218  return {Botan::CPUID::CPUID_ARM_SHA2_512_BIT};
219  if(tok == "armv8sm3" || tok == "arm_sm3")
220  return {Botan::CPUID::CPUID_ARM_SM3_BIT};
221  if(tok == "armv8sm4" || tok == "arm_sm4")
222  return {Botan::CPUID::CPUID_ARM_SM4_BIT};
223 
224 #else
225  BOTAN_UNUSED(tok);
226 #endif
227 
228  return {};
229  }
230 
231 }
Flags flags(Flag flags)
Definition: p11.h:860
static bool has_simd_32()
Definition: cpuid.cpp:16
#define BOTAN_ASSERT(expr, assertion_made)
Definition: assert.h:55
static std::string to_string()
Definition: cpuid.cpp:30
static std::vector< CPUID::CPUID_bits > bit_from_string(const std::string &tok)
Definition: cpuid.cpp:152
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
static void print(std::ostream &o)
Definition: cpuid.cpp:87
static void initialize()
Definition: cpuid.cpp:93
#define CPUID_PRINT(flag)
std::string string_join(const std::vector< std::string > &strs, char delim)
Definition: parsing.cpp:182