diff options
-rw-r--r-- | doc/examples/cpuid.cpp | 7 | ||||
-rw-r--r-- | src/utils/cpuid.cpp | 146 | ||||
-rw-r--r-- | src/utils/cpuid.h | 53 |
3 files changed, 118 insertions, 88 deletions
diff --git a/doc/examples/cpuid.cpp b/doc/examples/cpuid.cpp index ac3f50580..9ffb810a9 100644 --- a/doc/examples/cpuid.cpp +++ b/doc/examples/cpuid.cpp @@ -35,12 +35,15 @@ int main() print_if_feature("SSSE3", CPUID::has_ssse3()); print_if_feature("SSE4.1", CPUID::has_sse41()); print_if_feature("SSE4.2", CPUID::has_sse42()); - print_if_feature("AVX", CPUID::has_avx()); + print_if_feature("AVX2", CPUID::has_avx2()); + print_if_feature("BMI2", CPUID::has_bmi2()); print_if_feature("AltiVec", CPUID::has_altivec()); print_header("Other extensions"); print_if_feature("RDTSC", CPUID::has_rdtsc()); print_if_feature("PCMUL", CPUID::has_pcmuludq()); print_if_feature("AES-NI", CPUID::has_aes_ni()); - print_if_feature("RDRND", CPUID::has_rdrand()); + print_if_feature("RDRAND", CPUID::has_rdrand()); + print_if_feature("RDSEED", CPUID::has_rdseed()); + print_if_feature("SHA", CPUID::has_intel_sha()); } diff --git a/src/utils/cpuid.cpp b/src/utils/cpuid.cpp index f6581f09c..4cb6c826f 100644 --- a/src/utils/cpuid.cpp +++ b/src/utils/cpuid.cpp @@ -1,6 +1,6 @@ /* * Runtime CPU detection -* (C) 2009-2010 Jack Lloyd +* (C) 2009-2010,2013 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -28,90 +28,60 @@ #if defined(BOTAN_BUILD_COMPILER_IS_MSVC) - #include <intrin.h> - #define CALL_CPUID(type, out) do { __cpuid((int*)out, type); } 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 { __cpuid((int*)out, type, level); } while(0) #elif defined(BOTAN_BUILD_COMPILER_IS_INTEL) - #include <ia32intrin.h> - #define CALL_CPUID(type, out) do { __cpuid(out, type); } while(0) +#include <ia32intrin.h> -#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && (BOTAN_GCC_VERSION >= 430) +#define X86_CPUID(type, out) do { __cpuid(out, type); } while(0) - // Only available starting in GCC 4.3 - #include <cpuid.h> +#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && 0 -namespace { +#include <cpuid.h> - /* - * Prevent inlining to work around GCC bug 44174 - */ - void __attribute__((__noinline__)) call_gcc_cpuid(Botan::u32bit type, - Botan::u32bit out[4]) - { - __get_cpuid(type, out, out+1, out+2, out+3); - } +#define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0) - #define CALL_CPUID call_gcc_cpuid +namespace { + +// avoids asm clobber errors in gcc 4.7.3 +void gcc_cpuid(unsigned int type, unsigned int level, unsigned int* eax, unsigned int* ebx, + unsigned int* ecx, unsigned int* edx) + { + __cpuid_count(type, level, eax, ebx, ecx, edx); + } } -#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && \ - (defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_GCC)) +#define X86_CPUID_SUBLEVEL(type, level, out) \ + do { gcc_cpuid(type, level, out, out+1, out+2, out+3); } while(0) - /* - * We can't safely use this on x86-32 as some 32-bit ABIs use ebx as - * a PIC register, and in theory there are some x86-32s still out - * there that don't support cpuid at all; it requires strange - * contortions to detect them. - */ +#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && BOTAN_USE_GCC_INLINE_ASM - #define CALL_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)) -#else - #warning "No method of calling CPUID for this compiler" -#endif +#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)) #endif -#ifndef CALL_CPUID - // In all other cases, just zeroize the supposed cpuid output - #define CALL_CPUID(type, out) \ - do { out[0] = out[1] = out[2] = out[3] = 0; } while(0); #endif namespace Botan { -u64bit CPUID::x86_processor_flags = 0; -size_t CPUID::cache_line = 32; -bool CPUID::altivec_capable = false; +u64bit CPUID::m_x86_processor_flags[2] = { 0, 0 }; +size_t CPUID::m_cache_line_size = 0; +bool CPUID::m_altivec_capable = false; namespace { -u32bit get_x86_cache_line_size() - { - const u32bit INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 }; - const u32bit AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 }; - - u32bit cpuid[4] = { 0 }; - CALL_CPUID(0, cpuid); - - if(same_mem(cpuid + 1, INTEL_CPUID, 3)) - { - CALL_CPUID(1, cpuid); - return 8 * get_byte(2, cpuid[1]); - } - else if(same_mem(cpuid + 1, AMD_CPUID, 3)) - { - CALL_CPUID(0x80000005, cpuid); - return get_byte(3, cpuid[2]); - } - else - return 32; // default cache line guess - } - #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) bool altivec_check_sysctl() @@ -189,27 +159,55 @@ bool altivec_check_pvr_emul() void CPUID::initialize() { +#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + if(altivec_check_sysctl() || altivec_check_pvr_emul()) + altivec_capable = true; +#endif + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + const u32bit INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 }; + const u32bit AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 }; + u32bit cpuid[4] = { 0 }; - CALL_CPUID(1, cpuid); + X86_CPUID(0, cpuid); + + const u32bit max_supported_sublevel = cpuid[0]; + + if(max_supported_sublevel == 0) + return; - x86_processor_flags = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[3]; + 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); + + m_x86_processor_flags[0] = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[3]; + + if(is_intel) + m_cache_line_size = 8 * get_byte(2, cpuid[1]); + + if(max_supported_sublevel >= 7) + { + clear_mem(cpuid, 4); + X86_CPUID_SUBLEVEL(7, 0, cpuid); + m_x86_processor_flags[1] = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[1]; + } + + if(is_amd) + { + X86_CPUID(0x80000005, cpuid); + m_cache_line_size = get_byte(3, cpuid[2]); + } + +#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. */ - if(x86_processor_flags == 0) - x86_processor_flags |= (1 << CPUID_SSE2_BIT); -#endif - - cache_line = get_x86_cache_line_size(); - - altivec_capable = false; - -#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - if(altivec_check_sysctl() || altivec_check_pvr_emul()) - altivec_capable = true; + if(m_x86_processor_flags[0] == 0) + m_x86_processor_flags[0] = (1 << CPUID_SSE2_BIT); #endif } diff --git a/src/utils/cpuid.h b/src/utils/cpuid.h index 14ac6ad39..eaf1c1330 100644 --- a/src/utils/cpuid.h +++ b/src/utils/cpuid.h @@ -1,6 +1,6 @@ /* * Runtime CPU detection -* (C) 2009-2010 Jack Lloyd +* (C) 2009-2010,2013 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -26,7 +26,7 @@ class BOTAN_DLL CPUID /** * Return a best guess of the cache line size */ - static size_t cache_line_size() { return cache_line; } + static size_t cache_line_size() { return m_cache_line_size; } /** * Check if the processor supports RDTSC @@ -59,10 +59,16 @@ class BOTAN_DLL CPUID { return x86_processor_flags_has(CPUID_SSE42_BIT); } /** - * Check if the processor supports extended AVX vector instructions + * Check if the processor supports AVX2 */ - static bool has_avx() - { return x86_processor_flags_has(CPUID_AVX_BIT); } + static bool has_avx2() + { return x86_processor_flags_has(CPUID_AVX2_BIT); } + + /** + * Check if the processor supports BMI2 + */ + static bool has_bmi2() + { return x86_processor_flags_has(CPUID_BMI2_BIT); } /** * Check if the processor supports AES-NI @@ -77,15 +83,33 @@ class BOTAN_DLL CPUID { return x86_processor_flags_has(CPUID_PCMUL_BIT); } /** + * Check if the processor supports Intel SHA extension + */ + static bool has_intel_sha() + { return x86_processor_flags_has(CPUID_SHA_BIT); } + + /** + * Check if the processor supports ADX extension + */ + static bool has_adx() + { return x86_processor_flags_has(CPUID_ADX_BIT); } + + /** * Check if the processor supports RDRAND */ static bool has_rdrand() { return x86_processor_flags_has(CPUID_RDRAND_BIT); } /** + * Check if the processor supports RDSEED + */ + static bool has_rdseed() + { return x86_processor_flags_has(CPUID_RDSEED_BIT); } + + /** * Check if the processor supports AltiVec/VMX */ - static bool has_altivec() { return altivec_capable; } + static bool has_altivec() { return m_altivec_capable; } private: enum CPUID_bits { CPUID_RDTSC_BIT = 4, @@ -95,18 +119,23 @@ class BOTAN_DLL CPUID CPUID_SSE41_BIT = 51, CPUID_SSE42_BIT = 52, CPUID_AESNI_BIT = 57, - CPUID_AVX_BIT = 60, - CPUID_RDRAND_BIT = 62 + CPUID_RDRAND_BIT = 62, + + CPUID_AVX2_BIT = 64+5, + CPUID_BMI2_BIT = 64+8, + CPUID_RDSEED_BIT = 64+18, + CPUID_ADX_BIT = 64+19, + CPUID_SHA_BIT = 64+29, }; static bool x86_processor_flags_has(u64bit bit) { - return ((x86_processor_flags >> bit) & 1); + return ((m_x86_processor_flags[bit/64] >> (bit % 64)) & 1); } - static u64bit x86_processor_flags; - static size_t cache_line; - static bool altivec_capable; + static u64bit m_x86_processor_flags[2]; + static size_t m_cache_line_size; + static bool m_altivec_capable; }; } |