Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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 <botan/types.h>
#include <vector>
#include <string>
#include <iosfwd>
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),
CPUID_AVX512DQ_BIT = (1ULL << 6),
CPUID_AVX512BW_BIT = (1ULL << 7),
// Ice Lake profile: AVX-512 F, DQ, BW, IFMA, VBMI, VBMI2, BITALG
CPUID_AVX512_ICL_BIT = (1ULL << 11),
// 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),
CPUID_AVX512_AES_BIT = (1ULL << 21),
CPUID_AVX512_CLMUL_BIT = (1ULL << 22),
// Misc useful instructions
CPUID_RDTSC_BIT = (1ULL << 48),
CPUID_ADX_BIT = (1ULL << 49),
CPUID_BMI1_BIT = (1ULL << 50),
CPUID_BMI2_BIT = (1ULL << 51),
#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 AVX-512DQ
*/
static bool has_avx512dq()
{ return has_cpuid_bit(CPUID_AVX512DQ_BIT); }
/**
* Check if the processor supports AVX-512BW
*/
static bool has_avx512bw()
{ return has_cpuid_bit(CPUID_AVX512BW_BIT); }
/**
* Check if the processor supports AVX-512 Ice Lake profile
*/
static bool has_avx512_icelake()
{ return has_cpuid_bit(CPUID_AVX512_ICL_BIT); }
/**
* Check if the processor supports AVX-512 AES (VAES)
*/
static bool has_avx512_aes()
{ return has_cpuid_bit(CPUID_AVX512_AES_BIT); }
/**
* Check if the processor supports AVX-512 VPCLMULQDQ
*/
static bool has_avx512_clmul()
{ return has_cpuid_bit(CPUID_AVX512_CLMUL_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<uint64_t>(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<uint64_t>(elem);
return state().has_bit(elem64);
}
static std::vector<CPUID::CPUID_bits> 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