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