Botan  2.19.1
Crypto and TLS for C++11
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 #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 
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 
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 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:616
std::string name
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
unsigned long get_auxval(unsigned long id)
Definition: os_utils.cpp:109