aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-01-17 12:45:17 -0500
committerJack Lloyd <[email protected]>2017-01-17 12:45:17 -0500
commitc3ae43c04ca4a8a3340b8549ab59c817c33f3011 (patch)
tree1e8777e26156482a17ff0a7e456604be15862cc3 /src
parent25474b4c2dca3babaaa078fbef2544f6f0c1b9f0 (diff)
Refactor CPUID implementation, add ARM support
Tested for ARM only under qemu-aarch64, need to check hardware before merging. Changes x86 CPUID bitmasks which does break ABI for 2.1, no API implications.
Diffstat (limited to 'src')
-rw-r--r--src/build-data/os/android.txt1
-rw-r--r--src/build-data/os/linux.txt1
-rw-r--r--src/lib/utils/cpuid.cpp343
-rw-r--r--src/lib/utils/cpuid.h125
4 files changed, 324 insertions, 146 deletions
diff --git a/src/build-data/os/android.txt b/src/build-data/os/android.txt
index 50d97fd8f..db06bab1d 100644
--- a/src/build-data/os/android.txt
+++ b/src/build-data/os/android.txt
@@ -7,6 +7,7 @@ clock_gettime
gettimeofday
posix_mlock
gmtime_r
+getauxval
dlopen
readdir
threads
diff --git a/src/build-data/os/linux.txt b/src/build-data/os/linux.txt
index 6bd81f7f2..59f995fc2 100644
--- a/src/build-data/os/linux.txt
+++ b/src/build-data/os/linux.txt
@@ -8,6 +8,7 @@ gettimeofday
posix_mlock
gmtime_r
dlopen
+getauxval
readdir
timegm
sockets
diff --git a/src/lib/utils/cpuid.cpp b/src/lib/utils/cpuid.cpp
index 428ca2715..bd96da987 100644
--- a/src/lib/utils/cpuid.cpp
+++ b/src/lib/utils/cpuid.cpp
@@ -1,6 +1,6 @@
/*
* Runtime CPU detection
-* (C) 2009-2010,2013 Jack Lloyd
+* (C) 2009,2010,2013,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -14,59 +14,66 @@
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+/*
+* On Darwin and OpenBSD ppc, use sysctl to detect AltiVec
+*/
#if defined(BOTAN_TARGET_OS_IS_DARWIN)
#include <sys/sysctl.h>
-#endif
-
-#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
+#elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#endif
+#elif 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>
#endif
-#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+#include <botan/internal/os_utils.h>
-#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+#elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
-#include <intrin.h>
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
-#define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
-#define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+ #include <intrin.h>
+ #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
-#include <ia32intrin.h>
-
-#define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
-#define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+ #include <ia32intrin.h>
+ #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && defined(BOTAN_USE_GCC_INLINE_ASM)
-#define X86_CPUID(type, out) \
- asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
- : "0" (type))
+ #define X86_CPUID(type, out) \
+ asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+ : "0" (type))
-#define X86_CPUID_SUBLEVEL(type, level, out) \
- asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
- : "0" (type), "2" (level))
+ #define X86_CPUID_SUBLEVEL(type, level, out) \
+ asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+ : "0" (type), "2" (level))
#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
-#include <cpuid.h>
+ #include <cpuid.h>
-#define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
+ #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
-#define X86_CPUID_SUBLEVEL(type, level, out) \
- do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) \
+ do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
#else
-
-#warning "No way of calling cpuid for this compiler"
-
-#define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
-#define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
+ #warning "No way of calling cpuid for this compiler"
+ #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
#endif
@@ -74,18 +81,22 @@
namespace Botan {
-uint64_t CPUID::g_processor_flags[2] = { 0, 0 };
+uint64_t CPUID::g_processor_features = 0;
size_t CPUID::g_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
-bool CPUID::g_initialized = false;
bool CPUID::g_little_endian = false;
namespace {
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
-bool altivec_check_sysctl()
+/*
+* PowerPC specific block: check for AltiVec using either
+* sysctl or by reading processor version number register.
+*/
+uint64_t powerpc_detect_cpu_featutures()
{
#if defined(BOTAN_TARGET_OS_IS_DARWIN) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ // On Darwin/OS X and OpenBSD, use sysctl
#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
int sels[2] = { CTL_MACHDEP, CPU_ALTIVEC };
@@ -98,18 +109,9 @@ bool altivec_check_sysctl()
int error = sysctl(sels, 2, &vector_type, &length, NULL, 0);
if(error == 0 && vector_type > 0)
- return true;
-#endif
-
- return false;
- }
-
-bool altivec_check_pvr_emul()
- {
- bool altivec_capable = false;
-
-#if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
+ return (1ULL << CPUID_ALTIVEC_BIT);
+#elif defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
/*
On PowerPC, MSR 287 is PVR, the Processor Version Number
Normally it is only accessible to ring 0, but Linux and NetBSD
@@ -119,6 +121,14 @@ bool altivec_check_pvr_emul()
PearPC and Linux sources, mostly.
*/
+ uint32_t pvr = 0;
+
+ // TODO: we could run inside SIGILL handler block
+ asm volatile("mfspr %0, 287" : "=r" (pvr));
+
+ // Top 16 bit suffice to identify model
+ pvr >>= 16;
+
const uint16_t PVR_G4_7400 = 0x000C;
const uint16_t PVR_G5_970 = 0x0039;
const uint16_t PVR_G5_970FX = 0x003C;
@@ -129,29 +139,166 @@ bool altivec_check_pvr_emul()
const uint16_t PVR_POWER8 = 0x004B;
const uint16_t PVR_CELL_PPU = 0x0070;
- // Motorola produced G4s with PVR 0x800[0123C] (at least)
- const uint16_t PVR_G4_74xx_24 = 0x800;
+ if(pvr == PVR_G4_7400 ||
+ pvr == PVR_G5_970 || pvr == PVR_G5_970FX ||
+ pvr == PVR_G5_970MP || pvr == PVR_G5_970GX ||
+ pvr == PVR_POWER6 || pvr == PVR_POWER7 || pvr == PVR_POWER8 ||
+ pvr == PVR_CELL_PPU)
+ {
+ return (1ULL << CPUID_ALTIVEC_BIT);
+ }
+#else
+ #warning "No PowerPC feature detection available for this platform"
+#endif
+
+ return 0;
+ }
- uint32_t pvr = 0;
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
- asm volatile("mfspr %0, 287" : "=r" (pvr));
+uint64_t arm_detect_cpu_features(size_t* cache_line_size)
+ {
+ uint64_t detected_features = 0;
+ *cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
- // Top 16 bit suffice to identify model
- pvr >>= 16;
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
+ errno = 0;
+
+#if defined(BOTAN_TARGET_ARCH_IS_ARM32)
+ const unsigned long hwcap = ::getauxval(AT_HWCAP2);
+#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
+ const unsigned long hwcap = ::getauxval(AT_HWCAP);
+#endif
+
+ if(hwcap & HWCAP_ASIMD)
+ detected_features |= CPUID::CPUID_ARM_NEON_BIT;
+ if(hwcap & HWCAP_AES)
+ detected_features |= CPUID::CPUID_ARM_AES_BIT;
+ if(hwcap & HWCAP_PMULL)
+ detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+ if(hwcap & HWCAP_SHA1)
+ detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+ if(hwcap & HWCAP_SHA2)
+ detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+
+ 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
+
+ // TODO: probe functions
+
+ return detected_features;
+ }
+
+#elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+uint64_t x86_detect_cpu_features(size_t* cache_line_size)
+ {
+ uint64_t features_detected = 0;
+ uint32_t cpuid[4] = { 0 };
+
+ // CPUID 0: vendor identification, max sublevel
+ X86_CPUID(0, cpuid);
+
+ const uint32_t max_supported_sublevel = cpuid[0];
+
+ const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
+ const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
+ const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
+ const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
+
+ if(max_supported_sublevel >= 1)
+ {
+ // CPUID 1: feature bits
+ X86_CPUID(1, cpuid);
+ const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
+
+ enum x86_CPUID_1_bits : uint64_t {
+ RDTSC = (1ULL << 4),
+ SSE2 = (1ULL << 26),
+ CLMUL = (1ULL << 33),
+ SSSE3 = (1ULL << 41),
+ SSE41 = (1ULL << 51),
+ SSE42 = (1ULL << 52),
+ AESNI = (1ULL << 57),
+ RDRAND = (1ULL << 62)
+ };
+
+ if(flags0 & x86_CPUID_1_bits::RDTSC)
+ features_detected |= CPUID::CPUID_RDTSC_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE2)
+ features_detected |= CPUID::CPUID_SSE2_BIT;
+ if(flags0 & x86_CPUID_1_bits::CLMUL)
+ features_detected |= CPUID::CPUID_CLMUL_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSSE3)
+ features_detected |= CPUID::CPUID_SSSE3_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE41)
+ features_detected |= CPUID::CPUID_SSE41_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE42)
+ features_detected |= CPUID::CPUID_SSE42_BIT;
+ if(flags0 & x86_CPUID_1_bits::AESNI)
+ features_detected |= CPUID::CPUID_AESNI_BIT;
+ if(flags0 & x86_CPUID_1_bits::RDRAND)
+ features_detected |= CPUID::CPUID_RDRAND_BIT;
+ }
+
+ if(is_intel)
+ {
+ // Intel cache line size is in cpuid(1) output
+ *cache_line_size = 8 * get_byte(2, cpuid[1]);
+ }
+ else if(is_amd)
+ {
+ // AMD puts it in vendor zone
+ X86_CPUID(0x80000005, cpuid);
+ *cache_line_size = get_byte(3, cpuid[2]);
+ }
+
+ if(max_supported_sublevel >= 7)
+ {
+ clear_mem(cpuid, 4);
+ X86_CPUID_SUBLEVEL(7, 0, cpuid);
+
+ enum x86_CPUID_7_bits : uint64_t {
+ AVX2 = (1ULL << 5),
+ BMI2 = (1ULL << 8),
+ AVX512F = (1ULL << 16),
+ RDSEED = (1ULL << 18),
+ ADX = (1ULL << 19),
+ SHA = (1ULL << 29),
+ };
+ uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
+
+ if(flags7 & x86_CPUID_7_bits::AVX2)
+ features_detected |= CPUID::CPUID_AVX2_BIT;
+ if(flags7 & x86_CPUID_7_bits::BMI2)
+ features_detected |= CPUID::CPUID_BMI2_BIT;
+ if(flags7 & x86_CPUID_7_bits::AVX512F)
+ features_detected |= CPUID::CPUID_AVX512F_BIT;
+ if(flags7 & x86_CPUID_7_bits::RDSEED)
+ features_detected |= CPUID::CPUID_RDSEED_BIT;
+ if(flags7 & x86_CPUID_7_bits::ADX)
+ features_detected |= CPUID::CPUID_ADX_BIT;
+ if(flags7 & x86_CPUID_7_bits::SHA)
+ features_detected |= CPUID::CPUID_SHA_BIT;
+ }
- altivec_capable |= (pvr == PVR_G4_7400);
- altivec_capable |= ((pvr >> 4) == PVR_G4_74xx_24);
- altivec_capable |= (pvr == PVR_G5_970);
- altivec_capable |= (pvr == PVR_G5_970FX);
- altivec_capable |= (pvr == PVR_G5_970MP);
- altivec_capable |= (pvr == PVR_G5_970GX);
- altivec_capable |= (pvr == PVR_POWER6);
- altivec_capable |= (pvr == PVR_POWER7);
- altivec_capable |= (pvr == PVR_POWER8);
- altivec_capable |= (pvr == PVR_CELL_PPU);
+ /*
+ * If we don't have access to CPUID, we can still safely assume that
+ * any x86-64 processor has SSE2 and RDTSC
+ */
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
+ if(features_detected == 0)
+ {
+ features_detected |= CPUID::CPUID_SSE2_BIT;
+ features_detected |= CPUID::CPUID_RDTSC_BIT;
+ }
#endif
- return altivec_capable;
+ return features_detected;
}
#endif
@@ -185,79 +332,46 @@ void CPUID::print(std::ostream& o)
CPUID_PRINT(rdtsc);
CPUID_PRINT(bmi2);
- CPUID_PRINT(clmul);
+ CPUID_PRINT(adx);
+
CPUID_PRINT(aes_ni);
+ CPUID_PRINT(clmul);
CPUID_PRINT(rdrand);
CPUID_PRINT(rdseed);
CPUID_PRINT(intel_sha);
- CPUID_PRINT(adx);
#endif
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
CPUID_PRINT(altivec);
#endif
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ CPUID_PRINT(neon);
+ CPUID_PRINT(arm_sha1);
+ CPUID_PRINT(arm_sha2);
+ CPUID_PRINT(arm_aes);
+ CPUID_PRINT(arm_pmull);
+#endif
+
#undef CPUID_PRINT
o << "\n";
}
void CPUID::initialize()
{
- clear_mem(g_processor_flags, 2);
+ g_processor_features = 0;
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
- if(altivec_check_sysctl() || altivec_check_pvr_emul())
- {
- g_processor_flags[0] |= CPUID_ALTIVEC_BIT;
- }
-#endif
-
-#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
- const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
- const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
-
- uint32_t cpuid[4] = { 0 };
- X86_CPUID(0, cpuid);
-
- const uint32_t max_supported_sublevel = cpuid[0];
-
- if(max_supported_sublevel == 0)
- return;
-
- const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
- const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
-
- X86_CPUID(1, cpuid);
-
- g_processor_flags[0] = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
-
- if(is_intel)
- g_cache_line_size = 8 * get_byte(2, cpuid[1]);
-
- if(max_supported_sublevel >= 7)
- {
- clear_mem(cpuid, 4);
- X86_CPUID_SUBLEVEL(7, 0, cpuid);
- g_processor_flags[1] = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
- }
-
- if(is_amd)
- {
- X86_CPUID(0x80000005, cpuid);
- g_cache_line_size = get_byte(3, cpuid[2]);
- }
-
+ g_processor_features = powerpc_detect_cpu_featutures();
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ g_processor_features = arm_detect_cpu_features(&g_cache_line_size);
+#elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ g_processor_features = x86_detect_cpu_features(&g_cache_line_size);
#endif
-#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
- /*
- * If we don't have access to CPUID, we can still safely assume that
- * any x86-64 processor has SSE2 and RDTSC
- */
- if(g_processor_flags[0] == 0)
- g_processor_flags[0] = (1 << CPUID_SSE2_BIT) | (1 << CPUID_RDTSC_BIT);
-#endif
+ g_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
+ // Check runtime endian
const uint32_t endian32 = 0x01234567;
const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
@@ -274,14 +388,13 @@ void CPUID::initialize()
throw Internal_Error("Unexpected endian at runtime, neither big nor little");
}
- // If we were compiled with a known endian, verify if matches at runtime
+ // If we were compiled with a known endian, verify it matches at runtime
#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
- BOTAN_ASSERT(g_little_endian, "Little-endian build but big-endian at runtime");
+ BOTAN_ASSERT(g_little_endian == true, "Build and runtime endian match");
#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
- BOTAN_ASSERT(!g_little_endian, "Big-endian build but little-endian at runtime");
+ BOTAN_ASSERT(g_little_endian == false, "Build and runtime endian match");
#endif
- g_initialized = true;
}
}
diff --git a/src/lib/utils/cpuid.h b/src/lib/utils/cpuid.h
index 634305aa1..2bb5a8301 100644
--- a/src/lib/utils/cpuid.h
+++ b/src/lib/utils/cpuid.h
@@ -1,6 +1,6 @@
/*
* Runtime CPU detection
-* (C) 2009-2010,2013 Jack Lloyd
+* (C) 2009,2010,2013,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -14,9 +14,22 @@
namespace Botan {
/**
-* A class handling runtime CPU feature detection
+* A class handling runtime CPU feature detection. It is limited to
+* just the features necessary to implement CPU specific code in Botan,
+* rather than being a general purpose utility.
*
-* Currently this class supports only x86 (via CPUID) and PowerPC (AltiVec detection)
+* This class supports:
+*
+* - x86 features using CPUID. x86 is also the only processor with
+* accurate cache line detection currently.
+*
+* - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and Darwin
+*
+* - ARM NEON and crypto extensions detection. On Linux and Android
+* systems which support getauxval, that is used to access CPU
+* feature information. Otherwise a relatively portable but
+* thread-unsafe mechanism involving executing probe functions which
+* catching SIGILL signal is used.
*/
class BOTAN_DLL CPUID
{
@@ -35,7 +48,7 @@ class BOTAN_DLL CPUID
*/
static size_t cache_line_size()
{
- if(!g_initialized)
+ if(g_processor_features == 0)
{
initialize();
}
@@ -44,38 +57,51 @@ class BOTAN_DLL CPUID
static bool is_little_endian()
{
- if(!g_initialized)
+ if(g_processor_features == 0)
{
initialize();
}
return g_little_endian;
}
- enum CPUID_bits {
+ enum CPUID_bits : uint64_t {
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
- // This matches the layout of cpuid(1)
- CPUID_RDTSC_BIT = 4,
- CPUID_SSE2_BIT = 26,
- CPUID_CLMUL_BIT = 33,
- CPUID_SSSE3_BIT = 41,
- CPUID_SSE41_BIT = 51,
- CPUID_SSE42_BIT = 52,
- CPUID_AESNI_BIT = 57,
- CPUID_RDRAND_BIT = 62,
-
- CPUID_AVX2_BIT = 64+5,
- CPUID_BMI2_BIT = 64+8,
- CPUID_AVX512F_BIT = 64+16,
- CPUID_RDSEED_BIT = 64+18,
- CPUID_ADX_BIT = 64+19,
- CPUID_SHA_BIT = 64+29,
+ // These values have no relation to cpuid bitfields
+
+ // SIMD instruction sets
+ CPUID_SSE2_BIT = (1ULL << 0),
+ CPUID_SSSE3_BIT = (1ULL << 1),
+ CPUID_SSE41_BIT = (1ULL << 2),
+ CPUID_SSE42_BIT = (1ULL << 3),
+ CPUID_AVX2_BIT = (1ULL << 4),
+ CPUID_AVX512F_BIT = (1ULL << 5),
+
+ // Misc useful instructions
+ CPUID_RDTSC_BIT = (1ULL << 10),
+ CPUID_BMI2_BIT = (1ULL << 11),
+ CPUID_ADX_BIT = (1ULL << 12),
+
+ // Crypto-specific ISAs
+ CPUID_AESNI_BIT = (1ULL << 16),
+ CPUID_CLMUL_BIT = (1ULL << 17),
+ CPUID_RDRAND_BIT = (1ULL << 18),
+ CPUID_RDSEED_BIT = (1ULL << 19),
+ CPUID_SHA_BIT = (1ULL << 20),
#endif
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
- CPUID_ALTIVEC_BIT = 0
+ CPUID_ALTIVEC_BIT = (1ULL << 0),
#endif
- // TODO: ARMv8 feature detection
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ CPUID_ARM_NEON_BIT = (1ULL << 0),
+ CPUID_ARM_AES_BIT = (1ULL << 16),
+ CPUID_ARM_PMULL_BIT = (1ULL << 17),
+ CPUID_ARM_SHA1_BIT = (1ULL << 18),
+ CPUID_ARM_SHA2_BIT = (1ULL << 19),
+#endif
+
+ CPUID_INITIALIZED_BIT = (1ULL << 63)
};
#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
@@ -86,6 +112,38 @@ class BOTAN_DLL CPUID
{ return has_cpuid_bit(CPUID_ALTIVEC_BIT); }
#endif
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ /**
+ * Check if the processor supports NEON SIMD
+ */
+ static bool has_neon()
+ { return has_cpuid_bit(CPUID_ARM_NEON_BIT); }
+
+ /**
+ * Check if the processor supports ARMv8 SHA1
+ */
+ static bool has_arm_sha1()
+ { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); }
+
+ /**
+ * Check if the processor supports ARMv8 SHA2
+ */
+ static bool has_arm_sha2()
+ { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); }
+
+ /**
+ * Check if the processor supports ARMv8 AES
+ */
+ static bool has_arm_aes()
+ { return has_cpuid_bit(CPUID_ARM_AES_BIT); }
+
+ /**
+ * Check if the processor supports ARMv8 PMULL
+ */
+ static bool has_arm_pmull()
+ { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); }
+#endif
+
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
/**
@@ -176,26 +234,31 @@ class BOTAN_DLL CPUID
/*
* Clear a CPUID bit
* Call CPUID::initialize to reset
+ *
+ * This is only exposed for testing, don't use unless you know
+ * what you are doing.
*/
static void clear_cpuid_bit(CPUID_bits bit)
{
- const uint64_t mask = ~(static_cast<uint64_t>(1) << (bit % 64));
- g_processor_flags[bit/64] &= mask;
+ const uint64_t mask = ~(static_cast<uint64_t>(bit));
+ g_processor_features &= mask;
}
+ /*
+ * Don't call this function, use CPUID::has_xxx above
+ * It should have been private.
+ */
static bool has_cpuid_bit(CPUID_bits elem)
{
- if(!g_initialized)
+ if(g_processor_features == 0)
initialize();
- const size_t bit = static_cast<size_t>(elem);
- return ((g_processor_flags[bit/64] >> (bit % 64)) & 1);
+ return ((g_processor_features & static_cast<uint64_t>(elem)) != 0);
}
private:
- static bool g_initialized;
static bool g_little_endian;
static size_t g_cache_line_size;
- static uint64_t g_processor_flags[2];
+ static uint64_t g_processor_features;
};
}