8 #include <botan/cpuid.h>
9 #include <botan/types.h>
10 #include <botan/loadstor.h>
11 #include <botan/exceptn.h>
12 #include <botan/mem_ops.h>
13 #include <botan/parsing.h>
16 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
21 #if defined(BOTAN_TARGET_OS_IS_DARWIN)
22 #include <sys/sysctl.h>
23 #elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
24 #include <sys/param.h>
25 #include <sys/sysctl.h>
26 #include <machine/cpu.h>
29 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
35 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
38 #include <botan/internal/os_utils.h>
41 #elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
47 #if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
49 #elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
50 #include <ia32intrin.h>
51 #elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
59 uint64_t CPUID::g_processor_features = 0;
60 size_t CPUID::g_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
61 bool CPUID::g_little_endian =
false;
65 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
71 uint64_t powerpc_detect_cpu_featutures()
73 #if defined(BOTAN_TARGET_OS_IS_DARWIN) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
76 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
77 int sels[2] = { CTL_MACHDEP, CPU_ALTIVEC };
80 int sels[2] = { CTL_HW, HW_VECTORUNIT };
83 size_t length =
sizeof(vector_type);
84 int error = sysctl(sels, 2, &vector_type, &length, NULL, 0);
86 if(error == 0 && vector_type > 0)
87 return (1ULL << CPUID::CPUID_ALTIVEC_BIT);
89 #elif defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
102 asm volatile(
"mfspr %0, 287" :
"=r" (pvr));
107 const uint16_t PVR_G4_7400 = 0x000C;
108 const uint16_t PVR_G5_970 = 0x0039;
109 const uint16_t PVR_G5_970FX = 0x003C;
110 const uint16_t PVR_G5_970MP = 0x0044;
111 const uint16_t PVR_G5_970GX = 0x0045;
112 const uint16_t PVR_POWER6 = 0x003E;
113 const uint16_t PVR_POWER7 = 0x003F;
114 const uint16_t PVR_POWER8 = 0x004B;
115 const uint16_t PVR_CELL_PPU = 0x0070;
117 if(pvr == PVR_G4_7400 ||
118 pvr == PVR_G5_970 || pvr == PVR_G5_970FX ||
119 pvr == PVR_G5_970MP || pvr == PVR_G5_970GX ||
120 pvr == PVR_POWER6 || pvr == PVR_POWER7 || pvr == PVR_POWER8 ||
123 return (1ULL << CPUID::CPUID_ALTIVEC_BIT);
126 #warning "No PowerPC feature detection available for this platform"
132 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
134 uint64_t arm_detect_cpu_features(
size_t* cache_line_size)
136 uint64_t detected_features = 0;
137 *cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
139 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
150 #if defined(BOTAN_TARGET_ARCH_IS_ARM32)
151 NEON_bit = (1 << 12),
153 PMULL_bit = (1 << 1),
157 ARCH_hwcap_neon = 16,
158 ARCH_hwcap_crypto = 26,
159 #elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
162 PMULL_bit = (1 << 4),
166 ARCH_hwcap_neon = 16,
167 ARCH_hwcap_crypto = 16,
171 const unsigned long hwcap_neon = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_neon);
172 if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
173 detected_features |= CPUID::CPUID_ARM_NEON_BIT;
180 const unsigned long hwcap_crypto = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_crypto);
181 if(hwcap_crypto & ARM_hwcap_bit::AES_bit)
182 detected_features |= CPUID::CPUID_ARM_AES_BIT;
183 if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit)
184 detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
185 if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit)
186 detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
187 if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
188 detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
190 #if defined(AT_DCACHEBSIZE)
191 const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE);
194 if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
195 *cache_line_size =
static_cast<size_t>(dcache_line);
205 return detected_features;
208 #elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
210 uint64_t x86_detect_cpu_features(
size_t* cache_line_size)
212 #if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
213 #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
214 #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
216 #elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
217 #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
218 #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
220 #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && defined(BOTAN_USE_GCC_INLINE_ASM)
221 #define X86_CPUID(type, out) \
222 asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
225 #define X86_CPUID_SUBLEVEL(type, level, out) \
226 asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
227 : "0" (type), "2" (level))
229 #elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
230 #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
232 #define X86_CPUID_SUBLEVEL(type, level, out) \
233 do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
235 #warning "No way of calling x86 cpuid instruction for this compiler"
236 #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
237 #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
240 uint64_t features_detected = 0;
241 uint32_t cpuid[4] = { 0 };
246 const uint32_t max_supported_sublevel = cpuid[0];
248 const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
249 const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
250 const bool is_intel =
same_mem(cpuid + 1, INTEL_CPUID, 3);
251 const bool is_amd =
same_mem(cpuid + 1, AMD_CPUID, 3);
253 if(max_supported_sublevel >= 1)
257 const uint64_t flags0 = (
static_cast<uint64_t
>(cpuid[2]) << 32) | cpuid[3];
259 enum x86_CPUID_1_bits : uint64_t {
262 CLMUL = (1ULL << 33),
263 SSSE3 = (1ULL << 41),
264 SSE41 = (1ULL << 51),
265 SSE42 = (1ULL << 52),
266 AESNI = (1ULL << 57),
267 RDRAND = (1ULL << 62)
270 if(flags0 & x86_CPUID_1_bits::RDTSC)
271 features_detected |= CPUID::CPUID_RDTSC_BIT;
272 if(flags0 & x86_CPUID_1_bits::SSE2)
273 features_detected |= CPUID::CPUID_SSE2_BIT;
274 if(flags0 & x86_CPUID_1_bits::CLMUL)
275 features_detected |= CPUID::CPUID_CLMUL_BIT;
276 if(flags0 & x86_CPUID_1_bits::SSSE3)
277 features_detected |= CPUID::CPUID_SSSE3_BIT;
278 if(flags0 & x86_CPUID_1_bits::SSE41)
279 features_detected |= CPUID::CPUID_SSE41_BIT;
280 if(flags0 & x86_CPUID_1_bits::SSE42)
281 features_detected |= CPUID::CPUID_SSE42_BIT;
282 if(flags0 & x86_CPUID_1_bits::AESNI)
283 features_detected |= CPUID::CPUID_AESNI_BIT;
284 if(flags0 & x86_CPUID_1_bits::RDRAND)
285 features_detected |= CPUID::CPUID_RDRAND_BIT;
291 *cache_line_size = 8 *
get_byte(2, cpuid[1]);
296 X86_CPUID(0x80000005, cpuid);
297 *cache_line_size =
get_byte(3, cpuid[2]);
300 if(max_supported_sublevel >= 7)
303 X86_CPUID_SUBLEVEL(7, 0, cpuid);
305 enum x86_CPUID_7_bits : uint64_t {
308 AVX512F = (1ULL << 16),
309 RDSEED = (1ULL << 18),
313 uint64_t flags7 = (
static_cast<uint64_t
>(cpuid[2]) << 32) | cpuid[1];
315 if(flags7 & x86_CPUID_7_bits::AVX2)
316 features_detected |= CPUID::CPUID_AVX2_BIT;
317 if(flags7 & x86_CPUID_7_bits::BMI2)
318 features_detected |= CPUID::CPUID_BMI2_BIT;
319 if(flags7 & x86_CPUID_7_bits::AVX512F)
320 features_detected |= CPUID::CPUID_AVX512F_BIT;
321 if(flags7 & x86_CPUID_7_bits::RDSEED)
322 features_detected |= CPUID::CPUID_RDSEED_BIT;
323 if(flags7 & x86_CPUID_7_bits::ADX)
324 features_detected |= CPUID::CPUID_ADX_BIT;
325 if(flags7 & x86_CPUID_7_bits::SHA)
326 features_detected |= CPUID::CPUID_SHA_BIT;
330 #undef X86_CPUID_SUBLEVEL
336 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
337 if(features_detected == 0)
339 features_detected |= CPUID::CPUID_SSE2_BIT;
340 features_detected |= CPUID::CPUID_RDTSC_BIT;
344 return features_detected;
353 #if defined(BOTAN_TARGET_SUPPORTS_SSE2)
354 return CPUID::has_sse2();
355 #elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
356 return CPUID::has_altivec();
357 #elif defined(BOTAN_TARGET_SUPPORTS_NEON)
358 return CPUID::has_neon();
367 std::vector<std::string>
flags;
369 #define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0)
371 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
390 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
394 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
415 g_processor_features = 0;
417 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
418 g_processor_features = powerpc_detect_cpu_featutures();
419 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
420 g_processor_features = arm_detect_cpu_features(&g_cache_line_size);
421 #elif defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
422 g_processor_features = x86_detect_cpu_features(&g_cache_line_size);
428 const uint32_t endian32 = 0x01234567;
429 const uint8_t* e8 =
reinterpret_cast<const uint8_t*
>(&endian32);
431 if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
433 g_little_endian =
false;
435 else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
437 g_little_endian =
true;
441 throw Internal_Error(
"Unexpected endian at runtime, neither big nor little");
445 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
446 BOTAN_ASSERT(g_little_endian ==
true,
"Build and runtime endian match");
447 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
448 BOTAN_ASSERT(g_little_endian ==
false,
"Build and runtime endian match");
std::string string_join(const std::vector< std::string > &strs, char delim)
bool same_mem(const T *p1, const T *p2, size_t n)
void clear_mem(T *ptr, size_t n)
static bool has_simd_32()
#define BOTAN_ASSERT(expr, assertion_made)
static std::string to_string()
static void print(std::ostream &o)
uint8_t get_byte(size_t byte_num, T input)
#define CPUID_PRINT(flag)