Botan 2.13.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

cpuid_arm.cpp
Go to the documentation of this file.
1 /*
2 * Runtime CPU detection for ARM
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
10 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
11
12 #if defined(BOTAN_TARGET_OS_IS_IOS)
13 #include <sys/types.h>
14 #include <sys/sysctl.h>
15
16 #else
17 #include <botan/internal/os_utils.h>
18
19 #endif
20
21 #endif
22
23 namespace Botan {
24
25 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
26
27 #if defined(BOTAN_TARGET_OS_IS_IOS)
28
29 namespace {
30
31 uint64_t flags_by_ios_machine_type(const std::string& machine)
32 {
33 /*
34 * This relies on a map of known machine names to features. This
35 * will quickly grow out of date as new products are introduced, but
36 * is apparently the best we can do for iOS.
37 */
38
39 struct version_info {
40 std::string name;
41 size_t min_version_neon;
42 size_t min_version_armv8;
43 };
44
45 static const version_info min_versions[] = {
46 { "iPhone", 2, 6 },
47 { "iPad", 1, 4 },
48 { "iPod", 4, 7 },
49 { "AppleTV", 2, 5 },
50 };
51
52 if(machine.size() < 3)
53 return 0;
54
55 auto comma = machine.find(',');
56
57 // Simulator, or something we don't know about
58 if(comma == std::string::npos)
59 return 0;
60
61 std::string product = machine.substr(0, comma);
62
63 size_t version = 0;
64 size_t place = 1;
65 while(product.size() > 1 && ::isdigit(product.back()))
66 {
67 const size_t digit = product.back() - '0';
68 version += digit * place;
69 place *= 10;
70 product.pop_back();
71 }
72
73 if(version == 0)
74 return 0;
75
76 for(const version_info& info : min_versions)
77 {
78 if(info.name != product)
79 continue;
80
81 if(version >= info.min_version_armv8)
82 {
83 return CPUID::CPUID_ARM_AES_BIT |
84 CPUID::CPUID_ARM_PMULL_BIT |
85 CPUID::CPUID_ARM_SHA1_BIT |
86 CPUID::CPUID_ARM_SHA2_BIT |
87 CPUID::CPUID_ARM_NEON_BIT;
88 }
89
90 if(version >= info.min_version_neon)
91 return CPUID::CPUID_ARM_NEON_BIT;
92 }
93
94 // Some other product we don't know about
95 return 0;
96 }
97
98 }
99
100 #endif
101
102 uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
103 {
104 uint64_t detected_features = 0;
105
106 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
107 /*
108 * On systems with getauxval these bits should normally be defined
109 * in bits/auxv.h but some buggy? glibc installs seem to miss them.
110 * These following values are all fixed, for the Linux ELF format,
111 * so we just hardcode them in ARM_hwcap_bit enum.
112 */
113
114 enum ARM_hwcap_bit {
115 #if defined(BOTAN_TARGET_ARCH_IS_ARM32)
116 NEON_bit = (1 << 12),
117 AES_bit = (1 << 0),
118 PMULL_bit = (1 << 1),
119 SHA1_bit = (1 << 2),
120 SHA2_bit = (1 << 3),
121
122 ARCH_hwcap_neon = 16, // AT_HWCAP
123 ARCH_hwcap_crypto = 26, // AT_HWCAP2
124 #elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
125 NEON_bit = (1 << 1),
126 AES_bit = (1 << 3),
127 PMULL_bit = (1 << 4),
128 SHA1_bit = (1 << 5),
129 SHA2_bit = (1 << 6),
130 SHA3_bit = (1 << 17),
131 SM3_bit = (1 << 18),
132 SM4_bit = (1 << 19),
133 SHA2_512_bit = (1 << 21),
134 SVE_bit = (1 << 22),
135
136 ARCH_hwcap_neon = 16, // AT_HWCAP
137 ARCH_hwcap_crypto = 16, // AT_HWCAP
138 #endif
139 };
140
141 #if defined(AT_DCACHEBSIZE)
142 // Exists only on Linux
143 const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
144
145 // plausibility check
146 if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
147 *cache_line_size = static_cast<size_t>(dcache_line);
148 #else
149 BOTAN_UNUSED(cache_line_size);
150 #endif
151
152 const unsigned long hwcap_neon = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_neon);
153 if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
154 detected_features |= CPUID::CPUID_ARM_NEON_BIT;
155
156 /*
157 On aarch64 this ends up calling getauxval twice with AT_HWCAP
158 It doesn't seem worth optimizing this out, since getauxval is
159 just reading a field in the ELF header.
160 */
161 const unsigned long hwcap_crypto = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_crypto);
162 if(hwcap_crypto & ARM_hwcap_bit::AES_bit)
163 detected_features |= CPUID::CPUID_ARM_AES_BIT;
164 if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit)
165 detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
166 if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit)
167 detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
168 if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
169 detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
170
171 #if defined(BOTAN_TARGET_ARCH_IS_ARM64)
172 if(hwcap_crypto & ARM_hwcap_bit::SHA3_bit)
173 detected_features |= CPUID::CPUID_ARM_SHA3_BIT;
174 if(hwcap_crypto & ARM_hwcap_bit::SM3_bit)
175 detected_features |= CPUID::CPUID_ARM_SM3_BIT;
176 if(hwcap_crypto & ARM_hwcap_bit::SM4_bit)
177 detected_features |= CPUID::CPUID_ARM_SM4_BIT;
178 if(hwcap_crypto & ARM_hwcap_bit::SHA2_512_bit)
179 detected_features |= CPUID::CPUID_ARM_SHA2_512_BIT;
180 if(hwcap_crypto & ARM_hwcap_bit::SVE_bit)
181 detected_features |= CPUID::CPUID_ARM_SVE_BIT;
182 #endif
183
184 #elif defined(BOTAN_TARGET_OS_IS_IOS)
185
186 char machine[64] = { 0 };
187 size_t size = sizeof(machine) - 1;
188 ::sysctlbyname("hw.machine", machine, &size, nullptr, 0);
189
190 detected_features = flags_by_ios_machine_type(machine);
191
192 #elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
193
194 /*
195 No getauxval API available, fall back on probe functions. We only
196 bother with Aarch64 here to simplify the code and because going to
197 extreme contortions to support detect NEON on devices that probably
198 don't support it doesn't seem worthwhile.
199
200 NEON registers v0-v7 are caller saved in Aarch64
201 */
202
203 auto neon_probe = []() noexcept -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; };
204 auto aes_probe = []() noexcept -> int { asm(".word 0x4e284800"); return 1; };
205 auto pmull_probe = []() noexcept -> int { asm(".word 0x0ee0e000"); return 1; };
206 auto sha1_probe = []() noexcept -> int { asm(".word 0x5e280800"); return 1; };
207 auto sha2_probe = []() noexcept -> int { asm(".word 0x5e282800"); return 1; };
208
209 // Only bother running the crypto detection if we found NEON
210
211 if(OS::run_cpu_instruction_probe(neon_probe) == 1)
212 {
213 detected_features |= CPUID::CPUID_ARM_NEON_BIT;
214
215 if(OS::run_cpu_instruction_probe(aes_probe) == 1)
216 detected_features |= CPUID::CPUID_ARM_AES_BIT;
217 if(OS::run_cpu_instruction_probe(pmull_probe) == 1)
218 detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
219 if(OS::run_cpu_instruction_probe(sha1_probe) == 1)
220 detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
221 if(OS::run_cpu_instruction_probe(sha2_probe) == 1)
222 detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
223 }
224
225 #endif
226
227 return detected_features;
228 }
229
230 #endif
231
232 }
Botan::OS::run_cpu_instruction_probe
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:601
name
std::string name
Definition: commoncrypto_hash.cpp:23
Botan
Definition: alg_id.cpp:13
BOTAN_UNUSED
#define BOTAN_UNUSED(...)
Definition: assert.h:142
Botan::OS::get_auxval
unsigned long get_auxval(unsigned long id)
Definition: os_utils.cpp:109