1 /* 2 * Runtime CPU detection 3 * (C) 2009,2010,2013,2017 Jack Lloyd 4 * 5 * Botan is released under the Simplified BSD License (see license.txt) 6 */ 7 8 #ifndef BOTAN_CPUID_H_ 9 #define BOTAN_CPUID_H_ 10 11 #include <botan/types.h> 12 #include <vector> 13 #include <string> 14 #include <iosfwd> 15 16 BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h) 17 18 namespace Botan { 19 20 /** 21 * A class handling runtime CPU feature detection. It is limited to 22 * just the features necessary to implement CPU specific code in Botan, 23 * rather than being a general purpose utility. 24 * 25 * This class supports: 26 * 27 * - x86 features using CPUID. x86 is also the only processor with 28 * accurate cache line detection currently. 29 * 30 * - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS 31 * 32 * - ARM NEON and crypto extensions detection. On Linux and Android 33 * systems which support getauxval, that is used to access CPU 34 * feature information. Otherwise a relatively portable but 35 * thread-unsafe mechanism involving executing probe functions which 36 * catching SIGILL signal is used. 37 */ 38 class BOTAN_PUBLIC_API(2,1) CPUID final 39 { 40 public: 41 /** 42 * Probe the CPU and see what extensions are supported 43 */ 44 static void initialize(); 45 46 static bool has_simd_32(); 47 48 /** 49 * Deprecated equivalent to 50 * o << "CPUID flags: " << CPUID::to_string() << "\n"; 51 */ 52 BOTAN_DEPRECATED("Use CPUID::to_string") 53 static void print(std::ostream& o); 54 55 /** 56 * Return a possibly empty string containing list of known CPU 57 * extensions. Each name will be seperated by a space, and the ordering 58 * will be arbitrary. This list only contains values that are useful to 59 * Botan (for example FMA instructions are not checked). 60 * 61 * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" 62 */ 63 static std::string to_string(); 64 65 /** 66 * Return a best guess of the cache line size 67 */ cache_line_size()68 static size_t cache_line_size() 69 { 70 return state().cache_line_size(); 71 } 72 is_little_endian()73 static bool is_little_endian() 74 { 75 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) 76 return true; 77 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) 78 return false; 79 #else 80 return state().endian_status() == Endian_Status::Little; 81 #endif 82 } 83 is_big_endian()84 static bool is_big_endian() 85 { 86 #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) 87 return true; 88 #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) 89 return false; 90 #else 91 return state().endian_status() == Endian_Status::Big; 92 #endif 93 } 94 95 enum CPUID_bits : uint64_t { 96 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 97 // These values have no relation to cpuid bitfields 98 99 // SIMD instruction sets 100 CPUID_SSE2_BIT = (1ULL << 0), 101 CPUID_SSSE3_BIT = (1ULL << 1), 102 CPUID_SSE41_BIT = (1ULL << 2), 103 CPUID_SSE42_BIT = (1ULL << 3), 104 CPUID_AVX2_BIT = (1ULL << 4), 105 CPUID_AVX512F_BIT = (1ULL << 5), 106 107 CPUID_AVX512DQ_BIT = (1ULL << 6), 108 CPUID_AVX512BW_BIT = (1ULL << 7), 109 110 // Ice Lake profile: AVX-512 F, DQ, BW, IFMA, VBMI, VBMI2, BITALG 111 CPUID_AVX512_ICL_BIT = (1ULL << 11), 112 113 // Crypto-specific ISAs 114 CPUID_AESNI_BIT = (1ULL << 16), 115 CPUID_CLMUL_BIT = (1ULL << 17), 116 CPUID_RDRAND_BIT = (1ULL << 18), 117 CPUID_RDSEED_BIT = (1ULL << 19), 118 CPUID_SHA_BIT = (1ULL << 20), 119 CPUID_AVX512_AES_BIT = (1ULL << 21), 120 CPUID_AVX512_CLMUL_BIT = (1ULL << 22), 121 122 // Misc useful instructions 123 CPUID_RDTSC_BIT = (1ULL << 48), 124 CPUID_ADX_BIT = (1ULL << 49), 125 CPUID_BMI1_BIT = (1ULL << 50), 126 CPUID_BMI2_BIT = (1ULL << 51), 127 #endif 128 129 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) 130 CPUID_ALTIVEC_BIT = (1ULL << 0), 131 CPUID_POWER_CRYPTO_BIT = (1ULL << 1), 132 CPUID_DARN_BIT = (1ULL << 2), 133 #endif 134 135 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 136 CPUID_ARM_NEON_BIT = (1ULL << 0), 137 CPUID_ARM_SVE_BIT = (1ULL << 1), 138 CPUID_ARM_AES_BIT = (1ULL << 16), 139 CPUID_ARM_PMULL_BIT = (1ULL << 17), 140 CPUID_ARM_SHA1_BIT = (1ULL << 18), 141 CPUID_ARM_SHA2_BIT = (1ULL << 19), 142 CPUID_ARM_SHA3_BIT = (1ULL << 20), 143 CPUID_ARM_SHA2_512_BIT = (1ULL << 21), 144 CPUID_ARM_SM3_BIT = (1ULL << 22), 145 CPUID_ARM_SM4_BIT = (1ULL << 23), 146 #endif 147 148 CPUID_INITIALIZED_BIT = (1ULL << 63) 149 }; 150 151 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) 152 /** 153 * Check if the processor supports AltiVec/VMX 154 */ has_altivec()155 static bool has_altivec() 156 { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } 157 158 /** 159 * Check if the processor supports POWER8 crypto extensions 160 */ has_power_crypto()161 static bool has_power_crypto() 162 { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } 163 164 /** 165 * Check if the processor supports POWER9 DARN RNG 166 */ has_darn_rng()167 static bool has_darn_rng() 168 { return has_cpuid_bit(CPUID_DARN_BIT); } 169 170 #endif 171 172 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 173 /** 174 * Check if the processor supports NEON SIMD 175 */ has_neon()176 static bool has_neon() 177 { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } 178 179 /** 180 * Check if the processor supports ARMv8 SVE 181 */ has_arm_sve()182 static bool has_arm_sve() 183 { return has_cpuid_bit(CPUID_ARM_SVE_BIT); } 184 185 /** 186 * Check if the processor supports ARMv8 SHA1 187 */ has_arm_sha1()188 static bool has_arm_sha1() 189 { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); } 190 191 /** 192 * Check if the processor supports ARMv8 SHA2 193 */ has_arm_sha2()194 static bool has_arm_sha2() 195 { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); } 196 197 /** 198 * Check if the processor supports ARMv8 AES 199 */ has_arm_aes()200 static bool has_arm_aes() 201 { return has_cpuid_bit(CPUID_ARM_AES_BIT); } 202 203 /** 204 * Check if the processor supports ARMv8 PMULL 205 */ has_arm_pmull()206 static bool has_arm_pmull() 207 { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); } 208 209 /** 210 * Check if the processor supports ARMv8 SHA-512 211 */ has_arm_sha2_512()212 static bool has_arm_sha2_512() 213 { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } 214 215 /** 216 * Check if the processor supports ARMv8 SHA-3 217 */ has_arm_sha3()218 static bool has_arm_sha3() 219 { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); } 220 221 /** 222 * Check if the processor supports ARMv8 SM3 223 */ has_arm_sm3()224 static bool has_arm_sm3() 225 { return has_cpuid_bit(CPUID_ARM_SM3_BIT); } 226 227 /** 228 * Check if the processor supports ARMv8 SM4 229 */ has_arm_sm4()230 static bool has_arm_sm4() 231 { return has_cpuid_bit(CPUID_ARM_SM4_BIT); } 232 233 #endif 234 235 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 236 237 /** 238 * Check if the processor supports RDTSC 239 */ has_rdtsc()240 static bool has_rdtsc() 241 { return has_cpuid_bit(CPUID_RDTSC_BIT); } 242 243 /** 244 * Check if the processor supports SSE2 245 */ has_sse2()246 static bool has_sse2() 247 { return has_cpuid_bit(CPUID_SSE2_BIT); } 248 249 /** 250 * Check if the processor supports SSSE3 251 */ has_ssse3()252 static bool has_ssse3() 253 { return has_cpuid_bit(CPUID_SSSE3_BIT); } 254 255 /** 256 * Check if the processor supports SSE4.1 257 */ has_sse41()258 static bool has_sse41() 259 { return has_cpuid_bit(CPUID_SSE41_BIT); } 260 261 /** 262 * Check if the processor supports SSE4.2 263 */ has_sse42()264 static bool has_sse42() 265 { return has_cpuid_bit(CPUID_SSE42_BIT); } 266 267 /** 268 * Check if the processor supports AVX2 269 */ has_avx2()270 static bool has_avx2() 271 { return has_cpuid_bit(CPUID_AVX2_BIT); } 272 273 /** 274 * Check if the processor supports AVX-512F 275 */ has_avx512f()276 static bool has_avx512f() 277 { return has_cpuid_bit(CPUID_AVX512F_BIT); } 278 279 /** 280 * Check if the processor supports AVX-512DQ 281 */ has_avx512dq()282 static bool has_avx512dq() 283 { return has_cpuid_bit(CPUID_AVX512DQ_BIT); } 284 285 /** 286 * Check if the processor supports AVX-512BW 287 */ has_avx512bw()288 static bool has_avx512bw() 289 { return has_cpuid_bit(CPUID_AVX512BW_BIT); } 290 291 /** 292 * Check if the processor supports AVX-512 Ice Lake profile 293 */ has_avx512_icelake()294 static bool has_avx512_icelake() 295 { return has_cpuid_bit(CPUID_AVX512_ICL_BIT); } 296 297 /** 298 * Check if the processor supports AVX-512 AES (VAES) 299 */ has_avx512_aes()300 static bool has_avx512_aes() 301 { return has_cpuid_bit(CPUID_AVX512_AES_BIT); } 302 303 /** 304 * Check if the processor supports AVX-512 VPCLMULQDQ 305 */ has_avx512_clmul()306 static bool has_avx512_clmul() 307 { return has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); } 308 309 /** 310 * Check if the processor supports BMI1 311 */ has_bmi1()312 static bool has_bmi1() 313 { return has_cpuid_bit(CPUID_BMI1_BIT); } 314 315 /** 316 * Check if the processor supports BMI2 317 */ has_bmi2()318 static bool has_bmi2() 319 { return has_cpuid_bit(CPUID_BMI2_BIT); } 320 321 /** 322 * Check if the processor supports AES-NI 323 */ has_aes_ni()324 static bool has_aes_ni() 325 { return has_cpuid_bit(CPUID_AESNI_BIT); } 326 327 /** 328 * Check if the processor supports CLMUL 329 */ has_clmul()330 static bool has_clmul() 331 { return has_cpuid_bit(CPUID_CLMUL_BIT); } 332 333 /** 334 * Check if the processor supports Intel SHA extension 335 */ has_intel_sha()336 static bool has_intel_sha() 337 { return has_cpuid_bit(CPUID_SHA_BIT); } 338 339 /** 340 * Check if the processor supports ADX extension 341 */ has_adx()342 static bool has_adx() 343 { return has_cpuid_bit(CPUID_ADX_BIT); } 344 345 /** 346 * Check if the processor supports RDRAND 347 */ has_rdrand()348 static bool has_rdrand() 349 { return has_cpuid_bit(CPUID_RDRAND_BIT); } 350 351 /** 352 * Check if the processor supports RDSEED 353 */ has_rdseed()354 static bool has_rdseed() 355 { return has_cpuid_bit(CPUID_RDSEED_BIT); } 356 #endif 357 358 /** 359 * Check if the processor supports byte-level vector permutes 360 * (SSSE3, NEON, Altivec) 361 */ has_vperm()362 static bool has_vperm() 363 { 364 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 365 return has_ssse3(); 366 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 367 return has_neon(); 368 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) 369 return has_altivec(); 370 #else 371 return false; 372 #endif 373 } 374 375 /** 376 * Check if the processor supports hardware AES instructions 377 */ has_hw_aes()378 static bool has_hw_aes() 379 { 380 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 381 return has_aes_ni(); 382 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 383 return has_arm_aes(); 384 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) 385 return has_power_crypto(); 386 #else 387 return false; 388 #endif 389 } 390 391 /** 392 * Check if the processor supports carryless multiply 393 * (CLMUL, PMULL) 394 */ has_carryless_multiply()395 static bool has_carryless_multiply() 396 { 397 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 398 return has_clmul(); 399 #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) 400 return has_arm_pmull(); 401 #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) 402 return has_power_crypto(); 403 #else 404 return false; 405 #endif 406 } 407 408 /* 409 * Clear a CPUID bit 410 * Call CPUID::initialize to reset 411 * 412 * This is only exposed for testing, don't use unless you know 413 * what you are doing. 414 */ clear_cpuid_bit(CPUID_bits bit)415 static void clear_cpuid_bit(CPUID_bits bit) 416 { 417 state().clear_cpuid_bit(static_cast<uint64_t>(bit)); 418 } 419 420 /* 421 * Don't call this function, use CPUID::has_xxx above 422 * It is only exposed for the tests. 423 */ has_cpuid_bit(CPUID_bits elem)424 static bool has_cpuid_bit(CPUID_bits elem) 425 { 426 const uint64_t elem64 = static_cast<uint64_t>(elem); 427 return state().has_bit(elem64); 428 } 429 430 static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok); 431 private: 432 enum class Endian_Status : uint32_t { 433 Unknown = 0x00000000, 434 Big = 0x01234567, 435 Little = 0x67452301, 436 }; 437 438 struct CPUID_Data 439 { 440 public: 441 CPUID_Data(); 442 443 CPUID_Data(const CPUID_Data& other) = default; 444 CPUID_Data& operator=(const CPUID_Data& other) = default; 445 clear_cpuid_bitCPUID_Data446 void clear_cpuid_bit(uint64_t bit) 447 { 448 m_processor_features &= ~bit; 449 } 450 has_bitCPUID_Data451 bool has_bit(uint64_t bit) const 452 { 453 return (m_processor_features & bit) == bit; 454 } 455 processor_featuresCPUID_Data456 uint64_t processor_features() const { return m_processor_features; } endian_statusCPUID_Data457 Endian_Status endian_status() const { return m_endian_status; } cache_line_sizeCPUID_Data458 size_t cache_line_size() const { return m_cache_line_size; } 459 460 private: 461 static Endian_Status runtime_check_endian(); 462 463 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \ 464 defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ 465 defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) 466 467 static uint64_t detect_cpu_features(size_t* cache_line_size); 468 469 #endif 470 uint64_t m_processor_features; 471 size_t m_cache_line_size; 472 Endian_Status m_endian_status; 473 }; 474 state()475 static CPUID_Data& state() 476 { 477 static CPUID::CPUID_Data g_cpuid; 478 return g_cpuid; 479 } 480 }; 481 482 } 483 484 #endif 485