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