Botan  2.7.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 
134  ARCH_hwcap_neon = 16, // AT_HWCAP
135  ARCH_hwcap_crypto = 16, // AT_HWCAP
136 #endif
137  };
138 
139 #if defined(AT_DCACHEBSIZE)
140  const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
141 
142  // plausibility check
143  if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
144  *cache_line_size = static_cast<size_t>(dcache_line);
145 #endif
146 
147  const unsigned long hwcap_neon = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_neon);
148  if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
149  detected_features |= CPUID::CPUID_ARM_NEON_BIT;
150 
151  /*
152  On aarch64 this ends up calling getauxval twice with AT_HWCAP
153  It doesn't seem worth optimizing this out, since getauxval is
154  just reading a field in the ELF header.
155  */
156  const unsigned long hwcap_crypto = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_crypto);
157  if(hwcap_crypto & ARM_hwcap_bit::AES_bit)
158  detected_features |= CPUID::CPUID_ARM_AES_BIT;
159  if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit)
160  detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
161  if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit)
162  detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
163  if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
164  detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
165 
166 #elif defined(BOTAN_TARGET_OS_IS_IOS)
167 
168  char machine[64] = { 0 };
169  size_t size = sizeof(machine) - 1;
170  ::sysctlbyname("hw.machine", machine, &size, nullptr, 0);
171 
172  detected_features = flags_by_ios_machine_type(machine);
173 
174 #elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
175 
176  /*
177  No getauxval API available, fall back on probe functions. We only
178  bother with Aarch64 here to simplify the code and because going to
179  extreme contortions to support detect NEON on devices that probably
180  don't support it doesn't seem worthwhile.
181 
182  NEON registers v0-v7 are caller saved in Aarch64
183  */
184 
185  auto neon_probe = []() -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; };
186  auto aes_probe = []() -> int { asm(".word 0x4e284800"); return 1; };
187  auto pmull_probe = []() -> int { asm(".word 0x0ee0e000"); return 1; };
188  auto sha1_probe = []() -> int { asm(".word 0x5e280800"); return 1; };
189  auto sha2_probe = []() -> int { asm(".word 0x5e282800"); return 1; };
190 
191  // Only bother running the crypto detection if we found NEON
192 
193  if(OS::run_cpu_instruction_probe(neon_probe) == 1)
194  {
195  detected_features |= CPUID::CPUID_ARM_NEON_BIT;
196 
197  if(OS::run_cpu_instruction_probe(aes_probe) == 1)
198  detected_features |= CPUID::CPUID_ARM_AES_BIT;
199  if(OS::run_cpu_instruction_probe(pmull_probe) == 1)
200  detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
201  if(OS::run_cpu_instruction_probe(sha1_probe) == 1)
202  detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
203  if(OS::run_cpu_instruction_probe(sha2_probe) == 1)
204  detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
205  }
206 
207 #endif
208 
209  return detected_features;
210  }
211 
212 #endif
213 
214 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:395
static size_t cache_line_size()
Definition: cpuid.h:66
Definition: alg_id.cpp:13