diff options
author | Jack Lloyd <[email protected]> | 2017-09-17 08:12:57 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-09-17 11:25:20 -0400 |
commit | eaa19ec15fc1eeefa2d11f28a11ae85a63c0befa (patch) | |
tree | 06db4e25f609c898d96bd03134211335ecf071c2 /src/lib/utils/cpuid/cpuid_arm.cpp | |
parent | 01d45593a34d72bd253d1f24151d98c3294bfb3a (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.cpp | 142 |
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 |