/* * Runtime CPU detection * (C) 2009,2010,2013,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_CPUID_H_ #define BOTAN_CPUID_H_ #include #include #include #include BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h) namespace Botan { /** * 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. * * 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 macOS * * - 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_PUBLIC_API(2,1) CPUID final { public: /** * Probe the CPU and see what extensions are supported */ static void initialize(); static bool has_simd_32(); /** * Deprecated equivalent to * o << "CPUID flags: " << CPUID::to_string() << "\n"; */ BOTAN_DEPRECATED("Use CPUID::to_string") static void print(std::ostream& o); /** * Return a possibly empty string containing list of known CPU * extensions. Each name will be seperated by a space, and the ordering * will be arbitrary. This list only contains values that are useful to * Botan (for example FMA instructions are not checked). * * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" */ static std::string to_string(); /** * Return a best guess of the cache line size */ static size_t cache_line_size() { return state().cache_line_size(); } static bool is_little_endian() { #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) return true; #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) return false; #else return state().endian_status() == Endian_Status::Little; #endif } static bool is_big_endian() { #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) return true; #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) return false; #else return state().endian_status() == Endian_Status::Big; #endif } enum CPUID_bits : uint64_t { #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) // 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), CPUID_BMI1_BIT = (1ULL << 13), // 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 = (1ULL << 0), CPUID_POWER_CRYPTO_BIT = (1ULL << 1), CPUID_DARN_BIT = (1ULL << 2), #endif #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) CPUID_ARM_NEON_BIT = (1ULL << 0), CPUID_ARM_SVE_BIT = (1ULL << 1), CPUID_ARM_AES_BIT = (1ULL << 16), CPUID_ARM_PMULL_BIT = (1ULL << 17), CPUID_ARM_SHA1_BIT = (1ULL << 18), CPUID_ARM_SHA2_BIT = (1ULL << 19), CPUID_ARM_SHA3_BIT = (1ULL << 20), CPUID_ARM_SHA2_512_BIT = (1ULL << 21), CPUID_ARM_SM3_BIT = (1ULL << 22), CPUID_ARM_SM4_BIT = (1ULL << 23), #endif CPUID_INITIALIZED_BIT = (1ULL << 63) }; #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) /** * Check if the processor supports AltiVec/VMX */ static bool has_altivec() { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } /** * Check if the processor supports POWER8 crypto extensions */ static bool has_power_crypto() { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } /** * Check if the processor supports POWER9 DARN RNG */ static bool has_darn_rng() { return has_cpuid_bit(CPUID_DARN_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 SVE */ static bool has_arm_sve() { return has_cpuid_bit(CPUID_ARM_SVE_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); } /** * Check if the processor supports ARMv8 SHA-512 */ static bool has_arm_sha2_512() { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } /** * Check if the processor supports ARMv8 SHA-3 */ static bool has_arm_sha3() { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); } /** * Check if the processor supports ARMv8 SM3 */ static bool has_arm_sm3() { return has_cpuid_bit(CPUID_ARM_SM3_BIT); } /** * Check if the processor supports ARMv8 SM4 */ static bool has_arm_sm4() { return has_cpuid_bit(CPUID_ARM_SM4_BIT); } #endif #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) /** * Check if the processor supports RDTSC */ static bool has_rdtsc() { return has_cpuid_bit(CPUID_RDTSC_BIT); } /** * Check if the processor supports SSE2 */ static bool has_sse2() { return has_cpuid_bit(CPUID_SSE2_BIT); } /** * Check if the processor supports SSSE3 */ static bool has_ssse3() { return has_cpuid_bit(CPUID_SSSE3_BIT); } /** * Check if the processor supports SSE4.1 */ static bool has_sse41() { return has_cpuid_bit(CPUID_SSE41_BIT); } /** * Check if the processor supports SSE4.2 */ static bool has_sse42() { return has_cpuid_bit(CPUID_SSE42_BIT); } /** * Check if the processor supports AVX2 */ static bool has_avx2() { return has_cpuid_bit(CPUID_AVX2_BIT); } /** * Check if the processor supports AVX-512F */ static bool has_avx512f() { return has_cpuid_bit(CPUID_AVX512F_BIT); } /** * Check if the processor supports BMI1 */ static bool has_bmi1() { return has_cpuid_bit(CPUID_BMI1_BIT); } /** * Check if the processor supports BMI2 */ static bool has_bmi2() { return has_cpuid_bit(CPUID_BMI2_BIT); } /** * Check if the processor supports AES-NI */ static bool has_aes_ni() { return has_cpuid_bit(CPUID_AESNI_BIT); } /** * Check if the processor supports CLMUL */ static bool has_clmul() { return has_cpuid_bit(CPUID_CLMUL_BIT); } /** * Check if the processor supports Intel SHA extension */ static bool has_intel_sha() { return has_cpuid_bit(CPUID_SHA_BIT); } /** * Check if the processor supports ADX extension */ static bool has_adx() { return has_cpuid_bit(CPUID_ADX_BIT); } /** * Check if the processor supports RDRAND */ static bool has_rdrand() { return has_cpuid_bit(CPUID_RDRAND_BIT); } /** * Check if the processor supports RDSEED */ static bool has_rdseed() { return has_cpuid_bit(CPUID_RDSEED_BIT); } #endif /** * Check if the processor supports byte-level vector permutes * (SSSE3, NEON, Altivec) */ static bool has_vperm() { #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) return has_ssse3(); #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) return has_neon(); #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) return has_altivec(); #else return false; #endif } /** * Check if the processor supports hardware AES instructions */ static bool has_hw_aes() { #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) return has_aes_ni(); #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) return has_arm_aes(); #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) return has_power_crypto(); #else return false; #endif } /** * Check if the processor supports carryless multiply * (CLMUL, PMULL) */ static bool has_carryless_multiply() { #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) return has_clmul(); #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) return has_arm_pmull(); #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) return has_power_crypto(); #else return false; #endif } /* * 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) { state().clear_cpuid_bit(static_cast(bit)); } /* * Don't call this function, use CPUID::has_xxx above * It is only exposed for the tests. */ static bool has_cpuid_bit(CPUID_bits elem) { const uint64_t elem64 = static_cast(elem); return state().has_bit(elem64); } static std::vector bit_from_string(const std::string& tok); private: enum class Endian_Status : uint32_t { Unknown = 0x00000000, Big = 0x01234567, Little = 0x67452301, }; struct CPUID_Data { public: CPUID_Data(); CPUID_Data(const CPUID_Data& other) = default; CPUID_Data& operator=(const CPUID_Data& other) = default; void clear_cpuid_bit(uint64_t bit) { m_processor_features &= ~bit; } bool has_bit(uint64_t bit) const { return (m_processor_features & bit) == bit; } uint64_t processor_features() const { return m_processor_features; } Endian_Status endian_status() const { return m_endian_status; } size_t cache_line_size() const { return m_cache_line_size; } private: static Endian_Status runtime_check_endian(); #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) static uint64_t detect_cpu_features(size_t* cache_line_size); #endif uint64_t m_processor_features; size_t m_cache_line_size; Endian_Status m_endian_status; }; static CPUID_Data& state() { static CPUID::CPUID_Data g_cpuid; return g_cpuid; } }; } #endif