aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/cpuid.cpp
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 /src/utils/cpuid.cpp
parentcbca09dab2616a14d9ce6853a8cdfb62c029eada (diff)
Add cpuid support for new x86 feature flags like AVX2 and RDSEED
Diffstat (limited to 'src/utils/cpuid.cpp')
-rw-r--r--src/utils/cpuid.cpp146
1 files changed, 72 insertions, 74 deletions
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
}