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