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