aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2013-12-14 16:02:31 +0000
committerlloyd <[email protected]>2013-12-14 16:02:31 +0000
commitba33619ef7b9fb1089132a0bfbabde7dc4b4e634 (patch)
treeda70f8ce33736451d853f3ff752e591db94b29ae
parentcbca09dab2616a14d9ce6853a8cdfb62c029eada (diff)
Add cpuid support for new x86 feature flags like AVX2 and RDSEED
-rw-r--r--doc/examples/cpuid.cpp7
-rw-r--r--src/utils/cpuid.cpp146
-rw-r--r--src/utils/cpuid.h53
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;
};
}