Botan 3.7.1
Crypto and TLS for C&
cpuid_aarch64.cpp
Go to the documentation of this file.
1/*
2* Runtime CPU detection for Aarch64
3* (C) 2009,2010,2013,2017,2020,2024 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 <optional>
12
13#if defined(BOTAN_HAS_OS_UTILS)
14 #include <botan/internal/os_utils.h>
15#endif
16
17#if defined(BOTAN_TARGET_OS_HAS_SYSCTLBYNAME)
18 #include <sys/sysctl.h>
19 #include <sys/types.h>
20#endif
21
22namespace Botan {
23
24#if defined(BOTAN_TARGET_ARCH_IS_ARM64)
25
26namespace {
27
28std::optional<uint32_t> aarch64_feat_via_auxval(uint32_t allowed) {
29 #if defined(BOTAN_HAS_OS_UTILS)
30
31 if(auto auxval = OS::get_auxval_hwcap()) {
32 uint32_t feat = 0;
33
34 /*
35 * On systems with getauxval these bits should normally be defined
36 * in bits/auxv.h but some buggy? glibc installs seem to miss them.
37 * These following values are all fixed, for the Linux ELF format,
38 * so we just hardcode them in ARM_hwcap_bit enum.
39 */
40 enum class ARM_hwcap_bit : uint64_t {
41 NEON_bit = (1 << 1),
42 AES_bit = (1 << 3),
43 PMULL_bit = (1 << 4),
44 SHA1_bit = (1 << 5),
45 SHA2_bit = (1 << 6),
46 SHA3_bit = (1 << 17),
47 SM3_bit = (1 << 18),
48 SM4_bit = (1 << 19),
49 SHA2_512_bit = (1 << 21),
50 SVE_bit = (1 << 22),
51 };
52
53 const auto hwcap = auxval->first;
54
55 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::NEON_bit, CPUID::CPUID_ARM_NEON_BIT, allowed);
56
57 if(feat & CPUID::CPUID_ARM_NEON_BIT) {
58 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::AES_bit, CPUID::CPUID_ARM_AES_BIT, allowed);
59 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::PMULL_bit, CPUID::CPUID_ARM_PMULL_BIT, allowed);
60 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA1_bit, CPUID::CPUID_ARM_SHA1_BIT, allowed);
61 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_bit, CPUID::CPUID_ARM_SHA2_BIT, allowed);
62 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA3_bit, CPUID::CPUID_ARM_SHA3_BIT, allowed);
63 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM3_bit, CPUID::CPUID_ARM_SM3_BIT, allowed);
64 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM4_bit, CPUID::CPUID_ARM_SM4_BIT, allowed);
65 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_512_bit, CPUID::CPUID_ARM_SHA2_512_BIT, allowed);
66 feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SVE_bit, CPUID::CPUID_ARM_SVE_BIT, allowed);
67 }
68
69 return feat;
70 }
71 #else
72 BOTAN_UNUSED(allowed);
73 #endif
74
75 return {};
76}
77
78std::optional<uint32_t> aarch64_feat_using_mac_api(uint32_t allowed) {
79 #if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
80 uint32_t feat = 0;
81
82 auto sysctlbyname_has_feature = [](const char* feature_name) -> bool {
83 unsigned int feature;
84 size_t size = sizeof(feature);
85 ::sysctlbyname(feature_name, &feature, &size, nullptr, 0);
86 return (feature == 1);
87 };
88
89 // All 64-bit Apple ARM chips have NEON, AES, and SHA support
90 feat |= CPUID::CPUID_ARM_NEON_BIT & allowed;
91 if(feat & CPUID::CPUID_ARM_NEON_BIT) {
92 feat |= CPUID::CPUID_ARM_AES_BIT & allowed;
93 feat |= CPUID::CPUID_ARM_PMULL_BIT & allowed;
94 feat |= CPUID::CPUID_ARM_SHA1_BIT & allowed;
95 feat |= CPUID::CPUID_ARM_SHA2_BIT & allowed;
96
97 if(sysctlbyname_has_feature("hw.optional.armv8_2_sha3")) {
98 feat |= CPUID::CPUID_ARM_SHA3_BIT & allowed;
99 }
100 if(sysctlbyname_has_feature("hw.optional.armv8_2_sha512")) {
101 feat |= CPUID::CPUID_ARM_SHA2_512_BIT & allowed;
102 }
103 }
104
105 return feat;
106 #else
107 BOTAN_UNUSED(allowed);
108 return {};
109 #endif
110}
111
112std::optional<uint32_t> aarch64_feat_using_instr_probe(uint32_t allowed) {
113 #if defined(BOTAN_USE_GCC_INLINE_ASM)
114
115 /*
116 No getauxval API available, fall back on probe functions.
117 NEON registers v0-v7 are caller saved in Aarch64
118 */
119
120 auto neon_probe = []() noexcept -> int {
121 asm("and v0.16b, v0.16b, v0.16b");
122 return 1;
123 };
124 auto aes_probe = []() noexcept -> int {
125 asm(".word 0x4e284800");
126 return 1;
127 };
128 auto pmull_probe = []() noexcept -> int {
129 asm(".word 0x0ee0e000");
130 return 1;
131 };
132 auto sha1_probe = []() noexcept -> int {
133 asm(".word 0x5e280800");
134 return 1;
135 };
136 auto sha2_probe = []() noexcept -> int {
137 asm(".word 0x5e282800");
138 return 1;
139 };
140 auto sha512_probe = []() noexcept -> int {
141 asm(".long 0xcec08000");
142 return 1;
143 };
144
145 uint32_t feat = 0;
146 if(allowed & CPUID::CPUID_ARM_NEON_BIT) {
147 if(OS::run_cpu_instruction_probe(neon_probe) == 1) {
148 feat |= CPUID::CPUID_ARM_NEON_BIT;
149 }
150
151 if(feat & CPUID::CPUID_ARM_NEON_BIT) {
152 if(OS::run_cpu_instruction_probe(aes_probe) == 1) {
153 feat |= CPUID::CPUID_ARM_AES_BIT & allowed;
154 }
155 if(OS::run_cpu_instruction_probe(pmull_probe) == 1) {
156 feat |= CPUID::CPUID_ARM_PMULL_BIT & allowed;
157 }
158 if(OS::run_cpu_instruction_probe(sha1_probe) == 1) {
159 feat |= CPUID::CPUID_ARM_SHA1_BIT & allowed;
160 }
161 if(OS::run_cpu_instruction_probe(sha2_probe) == 1) {
162 feat |= CPUID::CPUID_ARM_SHA2_BIT & allowed;
163 }
164 if(OS::run_cpu_instruction_probe(sha512_probe) == 1) {
165 feat |= CPUID::CPUID_ARM_SHA2_512_BIT & allowed;
166 }
167 }
168 }
169
170 return feat;
171 #else
172 BOTAN_UNUSED(allowed);
173 return {};
174 #endif
175}
176
177} // namespace
178
179uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) {
180 if(auto feat_aux = aarch64_feat_via_auxval(allowed)) {
181 return feat_aux.value();
182 } else if(auto feat_mac = aarch64_feat_using_mac_api(allowed)) {
183 return feat_mac.value();
184 } else if(auto feat_instr = aarch64_feat_using_instr_probe(allowed)) {
185 return feat_instr.value();
186 } else {
187 return 0;
188 }
189}
190
191#endif
192
193} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:118
static uint32_t if_set(uint64_t cpuid, T flag, CPUID::CPUID_bits bit, uint32_t allowed)
Definition cpuid.h:402
std::optional< std::pair< unsigned long, unsigned long > > get_auxval_hwcap()
Definition os_utils.cpp:150
int BOTAN_TEST_API run_cpu_instruction_probe(const std::function< int()> &probe_fn)
Definition os_utils.cpp:717