Botan  2.13.0
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 #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 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:601
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