diff options
author | Jack Lloyd <[email protected]> | 2019-01-28 15:13:59 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2019-01-30 19:28:39 -0500 |
commit | d9a5ffe01f33d509afac68563dbb26a9dc8b9ef6 (patch) | |
tree | af3605c57ef457a2987b842908f702966082ec97 /src/lib/utils/cpuid | |
parent | 8c835b3b1238083c4b4bb4a90e4d9e9b38dffb11 (diff) |
Refactor CPUID to make it thread safe
Needed for #1819 and unfortunately Windows does not allow thread local
data to be stored as a member of a DLL exported class. So hide it
behind an accessor function instead.
This slows down CPUID test somewhat and I would like to address that
but it seems hard without breaking the CPUID API, which is for better
or worse public.
Diffstat (limited to 'src/lib/utils/cpuid')
-rw-r--r-- | src/lib/utils/cpuid/cpuid.cpp | 37 | ||||
-rw-r--r-- | src/lib/utils/cpuid/cpuid.h | 72 | ||||
-rw-r--r-- | src/lib/utils/cpuid/cpuid_arm.cpp | 2 | ||||
-rw-r--r-- | src/lib/utils/cpuid/cpuid_ppc.cpp | 2 | ||||
-rw-r--r-- | src/lib/utils/cpuid/cpuid_x86.cpp | 2 |
5 files changed, 67 insertions, 48 deletions
diff --git a/src/lib/utils/cpuid/cpuid.cpp b/src/lib/utils/cpuid/cpuid.cpp index d2d08dd43..e890af061 100644 --- a/src/lib/utils/cpuid/cpuid.cpp +++ b/src/lib/utils/cpuid/cpuid.cpp @@ -13,9 +13,12 @@ namespace Botan { -uint64_t CPUID::g_processor_features = 0; -size_t CPUID::g_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE; -CPUID::Endian_status CPUID::g_endian_status = ENDIAN_UNKNOWN; +//static +CPUID::CPUID_Data& CPUID::state() + { + static BOTAN_THREAD_LOCAL CPUID::CPUID_Data g_cpuid; + return g_cpuid; + } bool CPUID::has_simd_32() { @@ -90,36 +93,44 @@ void CPUID::print(std::ostream& o) //static void CPUID::initialize() { - g_processor_features = 0; + state() = CPUID_Data(); + } +CPUID::CPUID_Data::CPUID_Data() + { #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - g_processor_features = CPUID::detect_cpu_features(&g_cache_line_size); + m_cache_line_size = 0; + m_processor_features = detect_cpu_features(&m_cache_line_size); #endif - g_endian_status = runtime_check_endian(); - g_processor_features |= CPUID::CPUID_INITIALIZED_BIT; + m_processor_features |= CPUID::CPUID_INITIALIZED_BIT; + + if(m_cache_line_size == 0) + m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE; + + m_endian_status = runtime_check_endian(); } //static -CPUID::Endian_status CPUID::runtime_check_endian() +CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian() { // Check runtime endian const uint32_t endian32 = 0x01234567; const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32); - Endian_status endian = ENDIAN_UNKNOWN; + CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown; if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) { - endian = ENDIAN_BIG; + endian = CPUID::Endian_Status::Big; } else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) { - endian = ENDIAN_LITTLE; + endian = CPUID::Endian_Status::Little; } else { @@ -128,9 +139,9 @@ CPUID::Endian_status CPUID::runtime_check_endian() // If we were compiled with a known endian, verify it matches at runtime #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - BOTAN_ASSERT(endian == ENDIAN_LITTLE, "Build and runtime endian match"); + BOTAN_ASSERT(endian == CPUID::Endian_Status::Little, "Build and runtime endian match"); #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - BOTAN_ASSERT(endian == ENDIAN_BIG, "Build and runtime endian match"); + BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match"); #endif return endian; diff --git a/src/lib/utils/cpuid/cpuid.h b/src/lib/utils/cpuid/cpuid.h index 95f6d687b..46eef2095 100644 --- a/src/lib/utils/cpuid/cpuid.h +++ b/src/lib/utils/cpuid/cpuid.h @@ -65,21 +65,17 @@ class BOTAN_PUBLIC_API(2,1) CPUID final */ static size_t cache_line_size() { - if(g_processor_features == 0) - { - initialize(); - } - return g_cache_line_size; + return state().cache_line_size(); } static bool is_little_endian() { - return endian_status() == ENDIAN_LITTLE; + return state().endian_status() == Endian_Status::Little; } static bool is_big_endian() { - return endian_status() == ENDIAN_BIG; + return state().endian_status() == Endian_Status::Big; } enum CPUID_bits : uint64_t { @@ -309,8 +305,7 @@ class BOTAN_PUBLIC_API(2,1) CPUID final */ static void clear_cpuid_bit(CPUID_bits bit) { - const uint64_t mask = ~(static_cast<uint64_t>(bit)); - g_processor_features &= mask; + state().clear_cpuid_bit(static_cast<uint64_t>(bit)); } /* @@ -319,43 +314,56 @@ class BOTAN_PUBLIC_API(2,1) CPUID final */ static bool has_cpuid_bit(CPUID_bits elem) { - if(g_processor_features == 0) - initialize(); - const uint64_t elem64 = static_cast<uint64_t>(elem); - return ((g_processor_features & elem64) == elem64); + return state().has_bit(elem64); } static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok); private: - enum Endian_status : uint32_t { - ENDIAN_UNKNOWN = 0x00000000, - ENDIAN_BIG = 0x01234567, - ENDIAN_LITTLE = 0x67452301, + 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); + 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 Endian_status runtime_check_endian(); - - static Endian_status endian_status() - { - if(g_endian_status == ENDIAN_UNKNOWN) - { - g_endian_status = runtime_check_endian(); - } - return g_endian_status; - } - - static uint64_t g_processor_features; - static size_t g_cache_line_size; - static Endian_status g_endian_status; + static CPUID_Data& state(); }; } diff --git a/src/lib/utils/cpuid/cpuid_arm.cpp b/src/lib/utils/cpuid/cpuid_arm.cpp index beb80f7d0..b5626f96b 100644 --- a/src/lib/utils/cpuid/cpuid_arm.cpp +++ b/src/lib/utils/cpuid/cpuid_arm.cpp @@ -102,7 +102,7 @@ uint64_t flags_by_ios_machine_type(const std::string& machine) #endif -uint64_t CPUID::detect_cpu_features(size_t* cache_line_size) +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) { uint64_t detected_features = 0; diff --git a/src/lib/utils/cpuid/cpuid_ppc.cpp b/src/lib/utils/cpuid/cpuid_ppc.cpp index 80a6241df..3afca539c 100644 --- a/src/lib/utils/cpuid/cpuid_ppc.cpp +++ b/src/lib/utils/cpuid/cpuid_ppc.cpp @@ -33,7 +33,7 @@ namespace Botan { * PowerPC specific block: check for AltiVec using either * sysctl or by reading processor version number register. */ -uint64_t CPUID::detect_cpu_features(size_t* cache_line_size) +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) { BOTAN_UNUSED(cache_line_size); diff --git a/src/lib/utils/cpuid/cpuid_x86.cpp b/src/lib/utils/cpuid/cpuid_x86.cpp index 5387a801e..97c17da86 100644 --- a/src/lib/utils/cpuid/cpuid_x86.cpp +++ b/src/lib/utils/cpuid/cpuid_x86.cpp @@ -25,7 +25,7 @@ namespace Botan { #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) -uint64_t CPUID::detect_cpu_features(size_t* cache_line_size) +uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size) { #if defined(BOTAN_BUILD_COMPILER_IS_MSVC) #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0) |