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