aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/utils/cpuid/cpuid_arm.cpp
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-09-17 08:12:57 -0400
committerJack Lloyd <[email protected]>2017-09-17 11:25:20 -0400
commiteaa19ec15fc1eeefa2d11f28a11ae85a63c0befa (patch)
tree06db4e25f609c898d96bd03134211335ecf071c2 /src/lib/utils/cpuid/cpuid_arm.cpp
parent01d45593a34d72bd253d1f24151d98c3294bfb3a (diff)
Add ARM feature detection for systems without getauxval
For iOS use sysctl to get the product name and use a static table of minimum versions. For everything else (not Linux/Android or iOS) try probe functions. Only for Aarch64 to keep things simple.
Diffstat (limited to 'src/lib/utils/cpuid/cpuid_arm.cpp')
-rw-r--r--src/lib/utils/cpuid/cpuid_arm.cpp142
1 files changed, 128 insertions, 14 deletions
diff --git a/src/lib/utils/cpuid/cpuid_arm.cpp b/src/lib/utils/cpuid/cpuid_arm.cpp
index c8b78ed09..39b6db652 100644
--- a/src/lib/utils/cpuid/cpuid_arm.cpp
+++ b/src/lib/utils/cpuid/cpuid_arm.cpp
@@ -6,16 +6,19 @@
*/
#include <botan/cpuid.h>
-#include <botan/internal/os_utils.h>
#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
-/*
-* On ARM, use getauxval if available, otherwise fall back to
-* running probe functions with a SIGILL handler.
-*/
#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
#include <sys/auxv.h>
+
+#elif defined(BOTAN_TARGET_OS_IS_IOS)
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+
+#else
+ #include <botan/internal/os_utils.h>
+
#endif
#endif
@@ -24,6 +27,81 @@ namespace Botan {
#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+#if defined(BOTAN_TARGET_OS_IS_IOS)
+
+namespace {
+
+uint64_t flags_by_ios_machine_type(const std::string& machine)
+ {
+ /*
+ * This relies on a map of known machine names to features. This
+ * will quickly grow out of date as new products are introduced, but
+ * is apparently the best we can do for iOS.
+ */
+
+ struct version_info {
+ std::string name;
+ size_t min_version_neon;
+ size_t min_version_armv8;
+ };
+
+ static const version_info min_versions[] = {
+ { "iPhone", 2, 6 },
+ { "iPad", 1, 4 },
+ { "iPod", 4, 7 },
+ { "AppleTV", 2, 5 },
+ };
+
+ if(machine.size() < 3)
+ return 0;
+
+ auto comma = machine.find(',');
+
+ // Simulator, or something we don't know about
+ if(comma == std::string::npos)
+ return 0;
+
+ std::string product = machine.substr(0, comma);
+
+ size_t version = 0;
+ size_t place = 1;
+ while(product.size() > 1 && ::isdigit(product.back()))
+ {
+ const size_t digit = product.back() - '0';
+ version += digit * place;
+ place *= 10;
+ product.pop_back();
+ }
+
+ if(version == 0)
+ return 0;
+
+ for(const version_info& info : min_versions)
+ {
+ if(info.name != product)
+ continue;
+
+ if(version >= info.min_version_armv8)
+ {
+ return CPUID::CPUID_ARM_AES_BIT |
+ CPUID::CPUID_ARM_PMULL_BIT |
+ CPUID::CPUID_ARM_SHA1_BIT |
+ CPUID::CPUID_ARM_SHA2_BIT |
+ CPUID::CPUID_ARM_NEON_BIT;
+ }
+
+ if(version >= info.min_version_neon)
+ return CPUID::CPUID_ARM_NEON_BIT;
+ }
+
+ // Some other product we don't know about
+ return 0;
+ }
+
+}
+
+#endif
+
uint64_t CPUID::detect_cpu_features(size_t* cache_line_size)
{
uint64_t detected_features = 0;
@@ -58,6 +136,14 @@ uint64_t CPUID::detect_cpu_features(size_t* cache_line_size)
#endif
};
+#if defined(AT_DCACHEBSIZE)
+ const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
+
+ // plausibility check
+ if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
+ *cache_line_size = static_cast<size_t>(dcache_line);
+#endif
+
const unsigned long hwcap_neon = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_neon);
if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
detected_features |= CPUID::CPUID_ARM_NEON_BIT;
@@ -77,18 +163,46 @@ uint64_t CPUID::detect_cpu_features(size_t* cache_line_size)
if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
-#if defined(AT_DCACHEBSIZE)
- const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
+#elif defined(BOTAN_TARGET_OS_IS_IOS)
- // plausibility check
- if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
- *cache_line_size = static_cast<size_t>(dcache_line);
-#endif
+ char machine[64] = { 0 };
+ size_t size = sizeof(machine) - 1;
+ ::sysctlbyname("hw.machine", machine, &size, nullptr, 0);
-#else
- // No getauxval API available, fall back on probe functions
+ detected_features = flags_by_ios_machine_type(machine);
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
+
+ /*
+ No getauxval API available, fall back on probe functions. We only
+ bother with Aarch64 here to simplify the code and because going to
+ extreme contortions to support detect NEON on devices that probably
+ don't support it doesn't seem worthwhile.
+
+ NEON registers v0-v7 are caller saved in Aarch64
+ */
+
+ auto neon_probe = []() -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; };
+ auto aes_probe = []() -> int { asm(".word 0x4e284800"); return 1; };
+ auto pmull_probe = []() -> int { asm(".word 0x0ee0e000"); return 1; };
+ auto sha1_probe = []() -> int { asm(".word 0x5e280800"); return 1; };
+ auto sha2_probe = []() -> int { asm(".word 0x5e282800"); return 1; };
+
+ // Only bother running the crypto detection if we found NEON
+
+ if(OS::run_cpu_instruction_probe(neon_probe) == 1)
+ {
+ detected_features |= CPUID::CPUID_ARM_NEON_BIT;
- // TODO: probe functions
+ if(OS::run_cpu_instruction_probe(aes_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_AES_BIT;
+ if(OS::run_cpu_instruction_probe(pmull_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+ if(OS::run_cpu_instruction_probe(sha1_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+ if(OS::run_cpu_instruction_probe(sha2_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+ }
#endif