Coverage Report

Created: 2023-06-07 07:00

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