aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/utils/cpuid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/utils/cpuid.cpp')
-rw-r--r--src/lib/utils/cpuid.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/lib/utils/cpuid.cpp b/src/lib/utils/cpuid.cpp
new file mode 100644
index 000000000..6da5673c1
--- /dev/null
+++ b/src/lib/utils/cpuid.cpp
@@ -0,0 +1,236 @@
+/*
+* Runtime CPU detection
+* (C) 2009-2010,2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/cpuid.h>
+#include <botan/types.h>
+#include <botan/get_byte.h>
+#include <botan/mem_ops.h>
+#include <ostream>
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+#if defined(BOTAN_TARGET_OS_IS_DARWIN)
+ #include <sys/sysctl.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ #include <sys/param.h>
+ #include <sys/sysctl.h>
+ #include <machine/cpu.h>
+#endif
+
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+
+#include <intrin.h>
+
+#define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
+#define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
+
+#include <ia32intrin.h>
+
+#define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
+#define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && BOTAN_USE_GCC_INLINE_ASM
+
+#define X86_CPUID(type, out) \
+ asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+ : "0" (type))
+
+#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))
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC)
+
+#include <cpuid.h>
+
+#define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
+
+#define X86_CPUID_SUBLEVEL(type, level, out) \
+ do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
+
+#else
+
+#warning "No way of calling cpuid for this compiler"
+
+#define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
+#define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
+
+#endif
+
+#endif
+
+namespace Botan {
+
+u64bit CPUID::m_x86_processor_flags[2] = { 0, 0 };
+size_t CPUID::m_cache_line_size = 0;
+bool CPUID::m_altivec_capable = false;
+
+namespace {
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+bool altivec_check_sysctl()
+ {
+#if defined(BOTAN_TARGET_OS_IS_DARWIN) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
+
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ int sels[2] = { CTL_MACHDEP, CPU_ALTIVEC };
+#else
+ // From Apple's docs
+ int sels[2] = { CTL_HW, HW_VECTORUNIT };
+#endif
+ int vector_type = 0;
+ size_t length = sizeof(vector_type);
+ int error = sysctl(sels, 2, &vector_type, &length, NULL, 0);
+
+ if(error == 0 && vector_type > 0)
+ return true;
+#endif
+
+ return false;
+ }
+
+bool altivec_check_pvr_emul()
+ {
+ bool altivec_capable = false;
+
+#if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
+
+ /*
+ On PowerPC, MSR 287 is PVR, the Processor Version Number
+ Normally it is only accessible to ring 0, but Linux and NetBSD
+ (others, too, maybe?) will trap and emulate it for us.
+
+ PVR identifiers for various AltiVec enabled CPUs. Taken from
+ PearPC and Linux sources, mostly.
+ */
+
+ const u16bit PVR_G4_7400 = 0x000C;
+ const u16bit PVR_G5_970 = 0x0039;
+ const u16bit PVR_G5_970FX = 0x003C;
+ const u16bit PVR_G5_970MP = 0x0044;
+ const u16bit PVR_G5_970GX = 0x0045;
+ const u16bit PVR_POWER6 = 0x003E;
+ const u16bit PVR_POWER7 = 0x003F;
+ const u16bit PVR_CELL_PPU = 0x0070;
+
+ // Motorola produced G4s with PVR 0x800[0123C] (at least)
+ const u16bit PVR_G4_74xx_24 = 0x800;
+
+ u32bit pvr = 0;
+
+ asm volatile("mfspr %0, 287" : "=r" (pvr));
+
+ // Top 16 bit suffice to identify model
+ pvr >>= 16;
+
+ altivec_capable |= (pvr == PVR_G4_7400);
+ altivec_capable |= ((pvr >> 4) == PVR_G4_74xx_24);
+ altivec_capable |= (pvr == PVR_G5_970);
+ altivec_capable |= (pvr == PVR_G5_970FX);
+ altivec_capable |= (pvr == PVR_G5_970MP);
+ altivec_capable |= (pvr == PVR_G5_970GX);
+ altivec_capable |= (pvr == PVR_POWER6);
+ altivec_capable |= (pvr == PVR_POWER7);
+ altivec_capable |= (pvr == PVR_CELL_PPU);
+#endif
+
+ return altivec_capable;
+ }
+
+#endif
+
+}
+
+void CPUID::print(std::ostream& o)
+ {
+ o << "CPUID flags: ";
+
+#define CPUID_PRINT(flag) do { if(has_##flag()) o << #flag << " "; } while(0)
+ CPUID_PRINT(sse2);
+ CPUID_PRINT(ssse3);
+ CPUID_PRINT(sse41);
+ CPUID_PRINT(sse42);
+ CPUID_PRINT(avx2);
+ CPUID_PRINT(avx512f);
+ CPUID_PRINT(altivec);
+
+ CPUID_PRINT(rdtsc);
+ CPUID_PRINT(bmi2);
+ CPUID_PRINT(clmul);
+ CPUID_PRINT(aes_ni);
+ CPUID_PRINT(rdrand);
+ CPUID_PRINT(rdseed);
+ CPUID_PRINT(intel_sha);
+ CPUID_PRINT(adx);
+#undef CPUID_PRINT
+ o << "\n";
+ }
+
+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 };
+ X86_CPUID(0, cpuid);
+
+ const u32bit max_supported_sublevel = cpuid[0];
+
+ if(max_supported_sublevel == 0)
+ return;
+
+ 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 and RDTSC
+ */
+ if(m_x86_processor_flags[0] == 0)
+ m_x86_processor_flags[0] = (1 << CPUID_SSE2_BIT) | (1 << CPUID_RDTSC_BIT);
+#endif
+ }
+
+}