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 
flags_by_ios_machine_type(const std::string & machine)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 
detect_cpu_features(size_t * cache_line_size)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 }
238