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