Botan  2.8.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_HAS_GETAUXVAL)
13  #include <sys/auxv.h>
14 
15 #elif defined(BOTAN_TARGET_OS_IS_IOS)
16  #include <sys/types.h>
17  #include <sys/sysctl.h>
18 
19 #else
20  #include <botan/internal/os_utils.h>
21 
22 #endif
23 
24 #endif
25 
26 namespace Botan {
27 
28 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
29 
30 #if defined(BOTAN_TARGET_OS_IS_IOS)
31 
32 namespace {
33 
34 uint64_t flags_by_ios_machine_type(const std::string& machine)
35  {
36  /*
37  * This relies on a map of known machine names to features. This
38  * will quickly grow out of date as new products are introduced, but
39  * is apparently the best we can do for iOS.
40  */
41 
42  struct version_info {
43  std::string name;
44  size_t min_version_neon;
45  size_t min_version_armv8;
46  };
47 
48  static const version_info min_versions[] = {
49  { "iPhone", 2, 6 },
50  { "iPad", 1, 4 },
51  { "iPod", 4, 7 },
52  { "AppleTV", 2, 5 },
53  };
54 
55  if(machine.size() < 3)
56  return 0;
57 
58  auto comma = machine.find(',');
59 
60  // Simulator, or something we don't know about
61  if(comma == std::string::npos)
62  return 0;
63 
64  std::string product = machine.substr(0, comma);
65 
66  size_t version = 0;
67  size_t place = 1;
68  while(product.size() > 1 && ::isdigit(product.back()))
69  {
70  const size_t digit = product.back() - '0';
71  version += digit * place;
72  place *= 10;
73  product.pop_back();
74  }
75 
76  if(version == 0)
77  return 0;
78 
79  for(const version_info& info : min_versions)
80  {
81  if(info.name != product)
82  continue;
83 
84  if(version >= info.min_version_armv8)
85  {
86  return CPUID::CPUID_ARM_AES_BIT |
87  CPUID::CPUID_ARM_PMULL_BIT |
88  CPUID::CPUID_ARM_SHA1_BIT |
89  CPUID::CPUID_ARM_SHA2_BIT |
90  CPUID::CPUID_ARM_NEON_BIT;
91  }
92 
93  if(version >= info.min_version_neon)
94  return CPUID::CPUID_ARM_NEON_BIT;
95  }
96 
97  // Some other product we don't know about
98  return 0;
99  }
100 
101 }
102 
103 #endif
104 
105 uint64_t CPUID::detect_cpu_features(size_t* cache_line_size)
106  {
107  uint64_t detected_features = 0;
108 
109 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
110  /*
111  * On systems with getauxval these bits should normally be defined
112  * in bits/auxv.h but some buggy? glibc installs seem to miss them.
113  * These following values are all fixed, for the Linux ELF format,
114  * so we just hardcode them in ARM_hwcap_bit enum.
115  */
116 
117  enum ARM_hwcap_bit {
118 #if defined(BOTAN_TARGET_ARCH_IS_ARM32)
119  NEON_bit = (1 << 12),
120  AES_bit = (1 << 0),
121  PMULL_bit = (1 << 1),
122  SHA1_bit = (1 << 2),
123  SHA2_bit = (1 << 3),
124 
125  ARCH_hwcap_neon = 16, // AT_HWCAP
126  ARCH_hwcap_crypto = 26, // AT_HWCAP2
127 #elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
128  NEON_bit = (1 << 1),
129  AES_bit = (1 << 3),
130  PMULL_bit = (1 << 4),
131  SHA1_bit = (1 << 5),
132  SHA2_bit = (1 << 6),
133  SHA3_bit = (1 << 17),
134  SM3_bit = (1 << 18),
135  SM4_bit = (1 << 19),
136  SHA2_512_bit = (1 << 21),
137  SVE_bit = (1 << 22),
138 
139  ARCH_hwcap_neon = 16, // AT_HWCAP
140  ARCH_hwcap_crypto = 16, // AT_HWCAP
141 #endif
142  };
143 
144 #if defined(AT_DCACHEBSIZE)
145  const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
146 
147  // plausibility check
148  if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
149  *cache_line_size = static_cast<size_t>(dcache_line);
150 #endif
151 
152  const unsigned long hwcap_neon = ::getauxval(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 = ::getauxval(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 = []() -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; };
204  auto aes_probe = []() -> int { asm(".word 0x4e284800"); return 1; };
205  auto pmull_probe = []() -> int { asm(".word 0x0ee0e000"); return 1; };
206  auto sha1_probe = []() -> int { asm(".word 0x5e280800"); return 1; };
207  auto sha2_probe = []() -> 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:408
static size_t cache_line_size()
Definition: cpuid.h:66
std::string name
Definition: alg_id.cpp:13