Botan
1.11.15
|
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 }