Botan  1.11.15
src/lib/utils/cpuid.cpp
Go to the documentation of this file.
00001 /*
00002 * Runtime CPU detection
00003 * (C) 2009-2010,2013 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/cpuid.h>
00009 #include <botan/types.h>
00010 #include <botan/get_byte.h>
00011 #include <botan/mem_ops.h>
00012 #include <ostream>
00013 
00014 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
00015 
00016 #if defined(BOTAN_TARGET_OS_IS_DARWIN)
00017   #include <sys/sysctl.h>
00018 #endif
00019 
00020 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
00021   #include <sys/param.h>
00022   #include <sys/sysctl.h>
00023   #include <machine/cpu.h>
00024 #endif
00025 
00026 #endif
00027 
00028 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
00029 
00030 #if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
00031 
00032 #include <intrin.h>
00033 
00034 #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
00035 #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
00036 
00037 #elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
00038 
00039 #include <ia32intrin.h>
00040 
00041 #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
00042 #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
00043 
00044 #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && BOTAN_USE_GCC_INLINE_ASM
00045 
00046 #define X86_CPUID(type, out)                                                    \
00047    asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
00048        : "0" (type))
00049 
00050 #define X86_CPUID_SUBLEVEL(type, level, out)                                    \
00051    asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
00052        : "0" (type), "2" (level))
00053 
00054 #elif defined(BOTAN_BUILD_COMPILER_IS_GCC)
00055 
00056 #include <cpuid.h>
00057 
00058 #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
00059 
00060 #define X86_CPUID_SUBLEVEL(type, level, out) \
00061    do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
00062 
00063 #else
00064 
00065 #warning "No way of calling cpuid for this compiler"
00066 
00067 #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
00068 #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
00069 
00070 #endif
00071 
00072 #endif
00073 
00074 namespace Botan {
00075 
00076 u64bit CPUID::g_x86_processor_flags[2] = { 0, 0 };
00077 size_t CPUID::g_cache_line_size = 0;
00078 bool CPUID::g_altivec_capable = false;
00079 bool CPUID::g_initialized = false;
00080 
00081 namespace {
00082 
00083 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
00084 
00085 bool altivec_check_sysctl()
00086    {
00087 #if defined(BOTAN_TARGET_OS_IS_DARWIN) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
00088 
00089 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
00090    int sels[2] = { CTL_MACHDEP, CPU_ALTIVEC };
00091 #else
00092    // From Apple's docs
00093    int sels[2] = { CTL_HW, HW_VECTORUNIT };
00094 #endif
00095    int vector_type = 0;
00096    size_t length = sizeof(vector_type);
00097    int error = sysctl(sels, 2, &vector_type, &length, NULL, 0);
00098 
00099    if(error == 0 && vector_type > 0)
00100       return true;
00101 #endif
00102 
00103    return false;
00104    }
00105 
00106 bool altivec_check_pvr_emul()
00107    {
00108    bool altivec_capable = false;
00109 
00110 #if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
00111 
00112    /*
00113    On PowerPC, MSR 287 is PVR, the Processor Version Number
00114    Normally it is only accessible to ring 0, but Linux and NetBSD
00115    (others, too, maybe?) will trap and emulate it for us.
00116 
00117    PVR identifiers for various AltiVec enabled CPUs. Taken from
00118    PearPC and Linux sources, mostly.
00119    */
00120 
00121    const u16bit PVR_G4_7400  = 0x000C;
00122    const u16bit PVR_G5_970   = 0x0039;
00123    const u16bit PVR_G5_970FX = 0x003C;
00124    const u16bit PVR_G5_970MP = 0x0044;
00125    const u16bit PVR_G5_970GX = 0x0045;
00126    const u16bit PVR_POWER6   = 0x003E;
00127    const u16bit PVR_POWER7   = 0x003F;
00128    const u16bit PVR_POWER8   = 0x004B;
00129    const u16bit PVR_CELL_PPU = 0x0070;
00130 
00131    // Motorola produced G4s with PVR 0x800[0123C] (at least)
00132    const u16bit PVR_G4_74xx_24  = 0x800;
00133 
00134    u32bit pvr = 0;
00135 
00136    asm volatile("mfspr %0, 287" : "=r" (pvr));
00137 
00138    // Top 16 bit suffice to identify model
00139    pvr >>= 16;
00140 
00141    altivec_capable |= (pvr == PVR_G4_7400);
00142    altivec_capable |= ((pvr >> 4) == PVR_G4_74xx_24);
00143    altivec_capable |= (pvr == PVR_G5_970);
00144    altivec_capable |= (pvr == PVR_G5_970FX);
00145    altivec_capable |= (pvr == PVR_G5_970MP);
00146    altivec_capable |= (pvr == PVR_G5_970GX);
00147    altivec_capable |= (pvr == PVR_POWER6);
00148    altivec_capable |= (pvr == PVR_POWER7);
00149    altivec_capable |= (pvr == PVR_POWER8);
00150    altivec_capable |= (pvr == PVR_CELL_PPU);
00151 #endif
00152 
00153    return altivec_capable;
00154    }
00155 
00156 #endif
00157 
00158 }
00159 
00160 void CPUID::print(std::ostream& o)
00161    {
00162    o << "CPUID flags: ";
00163 
00164 #define CPUID_PRINT(flag) do { if(has_##flag()) o << #flag << " "; } while(0)
00165    CPUID_PRINT(sse2);
00166    CPUID_PRINT(ssse3);
00167    CPUID_PRINT(sse41);
00168    CPUID_PRINT(sse42);
00169    CPUID_PRINT(avx2);
00170    CPUID_PRINT(avx512f);
00171    CPUID_PRINT(altivec);
00172 
00173    CPUID_PRINT(rdtsc);
00174    CPUID_PRINT(bmi2);
00175    CPUID_PRINT(clmul);
00176    CPUID_PRINT(aes_ni);
00177    CPUID_PRINT(rdrand);
00178    CPUID_PRINT(rdseed);
00179    CPUID_PRINT(intel_sha);
00180    CPUID_PRINT(adx);
00181 #undef CPUID_PRINT
00182    o << "\n";
00183    }
00184 
00185 void CPUID::initialize()
00186    {
00187    if(g_initialized)
00188       return;
00189 
00190 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
00191       if(altivec_check_sysctl() || altivec_check_pvr_emul())
00192          g_altivec_capable = true;
00193 #endif
00194 
00195 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
00196    const u32bit INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
00197    const u32bit AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
00198 
00199    u32bit cpuid[4] = { 0 };
00200    X86_CPUID(0, cpuid);
00201 
00202    const u32bit max_supported_sublevel = cpuid[0];
00203 
00204    if(max_supported_sublevel == 0)
00205       return;
00206 
00207    const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
00208    const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
00209 
00210    X86_CPUID(1, cpuid);
00211 
00212    g_x86_processor_flags[0] = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[3];
00213 
00214    if(is_intel)
00215       g_cache_line_size = 8 * get_byte(2, cpuid[1]);
00216 
00217    if(max_supported_sublevel >= 7)
00218       {
00219       clear_mem(cpuid, 4);
00220       X86_CPUID_SUBLEVEL(7, 0, cpuid);
00221       g_x86_processor_flags[1] = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[1];
00222       }
00223 
00224    if(is_amd)
00225       {
00226       X86_CPUID(0x80000005, cpuid);
00227       g_cache_line_size = get_byte(3, cpuid[2]);
00228       }
00229 
00230 #endif
00231 
00232 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
00233    /*
00234    * If we don't have access to CPUID, we can still safely assume that
00235    * any x86-64 processor has SSE2 and RDTSC
00236    */
00237    if(g_x86_processor_flags[0] == 0)
00238       g_x86_processor_flags[0] = (1 << CPUID_SSE2_BIT) | (1 << CPUID_RDTSC_BIT);
00239 #endif
00240 
00241    g_initialized = true;
00242    }
00243 
00244 }