text: /*
  * Runtime CPU detection
  * (C) 2009,2010,2013,2017 Jack Lloyd
  *
  * Botan is released under the Simplified BSD License (see license.txt)
  */
  
  #include <botan/cpuid.h>
  #include <botan/types.h>
  #include <botan/exceptn.h>
  #include <botan/parsing.h>
  #include <ostream>
  
  namespace Botan {
  
  uint64_t CPUID::g_processor_features = 0;
  size_t CPUID::g_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
  CPUID::Endian_status CPUID::g_endian_status = ENDIAN_UNKNOWN;
  
  bool CPUID::has_simd_32()
     {
  #if defined(BOTAN_TARGET_SUPPORTS_SSE2)
     return CPUID::has_sse2();
  #elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
     return CPUID::has_altivec();
  #elif defined(BOTAN_TARGET_SUPPORTS_NEON)
     return CPUID::has_neon();
  #else
     return true;
  #endif
     }
  
  //static
  std::string CPUID::to_string()
     {
     std::vector<std::string> flags;
  
  #define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0)
  
  #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
     CPUID_PRINT(sse2);
     CPUID_PRINT(ssse3);
     CPUID_PRINT(sse41);
     CPUID_PRINT(sse42);
     CPUID_PRINT(avx2);
     CPUID_PRINT(avx512f);
  
     CPUID_PRINT(rdtsc);
     CPUID_PRINT(bmi2);
     CPUID_PRINT(adx);
  
     CPUID_PRINT(aes_ni);
     CPUID_PRINT(clmul);
     CPUID_PRINT(rdrand);
     CPUID_PRINT(rdseed);
     CPUID_PRINT(intel_sha);
  #endif
  
  #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
     CPUID_PRINT(altivec);
  #endif
  
  #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
     CPUID_PRINT(neon);
     CPUID_PRINT(arm_sha1);
     CPUID_PRINT(arm_sha2);
     CPUID_PRINT(arm_aes);
     CPUID_PRINT(arm_pmull);
  #endif
  
  #undef CPUID_PRINT
  
     return string_join(flags, ' ');
     }
  
  //static
  void CPUID::print(std::ostream& o)
     {
     o << "CPUID flags: " << CPUID::to_string() << "\n";
     }
  
  //static
  void CPUID::initialize()
     {
     g_processor_features = 0;
  
  #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
      defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
      defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
  
     g_processor_features = CPUID::detect_cpu_features(&g_cache_line_size);
  
  #endif
  
     g_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
     }
  
  //static
  CPUID::Endian_status CPUID::runtime_check_endian()
     {
     // Check runtime endian
     const uint32_t endian32 = 0x01234567;
     const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
  
     Endian_status endian = ENDIAN_UNKNOWN;
  
     if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
        {
        endian = ENDIAN_BIG;
        }
     else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
        {
        endian = ENDIAN_LITTLE;
        }
     else
        {
        throw Internal_Error("Unexpected endian at runtime, neither big nor little");
        }
  
     // If we were compiled with a known endian, verify it matches at runtime
  #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
     BOTAN_ASSERT(endian == ENDIAN_LITTLE, "Build and runtime endian match");
  #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
     BOTAN_ASSERT(endian == ENDIAN_BIG, "Build and runtime endian match");
  #endif
  
     return endian;
     }
  
  std::vector<Botan::CPUID::CPUID_bits>
  CPUID::bit_from_string(const std::string& tok)
     {
  #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
     if(tok == "sse2" || tok == "simd")
        return {Botan::CPUID::CPUID_SSE2_BIT};
     if(tok == "ssse3")
        return {Botan::CPUID::CPUID_SSSE3_BIT};
     if(tok == "aesni")
        return {Botan::CPUID::CPUID_AESNI_BIT};
     if(tok == "clmul")
        return {Botan::CPUID::CPUID_CLMUL_BIT};
     if(tok == "avx2")
        return {Botan::CPUID::CPUID_AVX2_BIT};
     if(tok == "sha")
        return {Botan::CPUID::CPUID_SHA_BIT};
  
  #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
     if(tok == "altivec" || tok == "simd")
        return {Botan::CPUID::CPUID_ALTIVEC_BIT};
  
  #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
     if(tok == "neon" || tok == "simd")
        return {Botan::CPUID::CPUID_ARM_NEON_BIT};
     if(tok == "armv8sha1")
        return {Botan::CPUID::CPUID_ARM_SHA1_BIT};
     if(tok == "armv8sha2")
        return {Botan::CPUID::CPUID_ARM_SHA2_BIT};
     if(tok == "armv8aes")
        return {Botan::CPUID::CPUID_ARM_AES_BIT};
     if(tok == "armv8pmull")
        return {Botan::CPUID::CPUID_ARM_PMULL_BIT};
  
  #else
     BOTAN_UNUSED(tok);
  #endif
  
     return {};
     }
  
  }
  