Botan  2.19.1
Crypto and TLS for C++11
cpuid_ppc.cpp
Go to the documentation of this file.
1 /*
2 * Runtime CPU detection for POWER/PowerPC
3 * (C) 2009,2010,2013,2017 Jack Lloyd
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7 
8 #include <botan/cpuid.h>
9 #include <botan/internal/os_utils.h>
10 
11 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
12 
13 /*
14 * On macOS and OpenBSD ppc, use sysctl to detect AltiVec
15 */
16 #if defined(BOTAN_TARGET_OS_IS_MACOS)
17  #include <sys/sysctl.h>
18 #elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
19  #include <sys/param.h>
20  #include <sys/sysctl.h>
21  #include <machine/cpu.h>
22 #endif
23 
24 #endif
25 
26 namespace Botan {
27 
28 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
29 
30 /*
31 * PowerPC specific block: check for AltiVec using either
32 * sysctl or by reading processor version number register.
33 */
34 uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
35  {
36  BOTAN_UNUSED(cache_line_size);
37 
38 #if defined(BOTAN_TARGET_OS_IS_MACOS) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
39  // On macOS and OpenBSD, use sysctl
40 
41  int sels[2] = {
42 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
43  CTL_MACHDEP, CPU_ALTIVEC
44 #else
45  CTL_HW, HW_VECTORUNIT
46 #endif
47  };
48 
49  int vector_type = 0;
50  size_t length = sizeof(vector_type);
51  int error = ::sysctl(sels, 2, &vector_type, &length, NULL, 0);
52 
53  if(error == 0 && vector_type > 0)
54  return CPUID::CPUID_ALTIVEC_BIT;
55 
56 #elif (defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_HAS_ELF_AUX_INFO)) && defined(BOTAN_TARGET_ARCH_IS_PPC64)
57 
58  enum PPC_hwcap_bit {
59  ALTIVEC_bit = (1 << 28),
60  CRYPTO_bit = (1 << 25),
61  DARN_bit = (1 << 21),
62 
63  ARCH_hwcap_altivec = 16, // AT_HWCAP
64  ARCH_hwcap_crypto = 26, // AT_HWCAP2
65  };
66 
67  uint64_t detected_features = 0;
68 
69  const unsigned long hwcap_altivec = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_altivec);
70  if(hwcap_altivec & PPC_hwcap_bit::ALTIVEC_bit)
71  detected_features |= CPUID::CPUID_ALTIVEC_BIT;
72 
73  const unsigned long hwcap_crypto = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_crypto);
74  if(hwcap_crypto & PPC_hwcap_bit::CRYPTO_bit)
75  detected_features |= CPUID::CPUID_POWER_CRYPTO_BIT;
76  if(hwcap_crypto & PPC_hwcap_bit::DARN_bit)
77  detected_features |= CPUID::CPUID_DARN_BIT;
78 
79  return detected_features;
80 
81 #else
82 
83  /*
84  On PowerPC, MSR 287 is PVR, the Processor Version Number
85  Normally it is only accessible to ring 0, but Linux and NetBSD
86  (others, too, maybe?) will trap and emulate it for us.
87  */
88 
89  int pvr = OS::run_cpu_instruction_probe([]() noexcept -> int {
90  uint32_t pvr = 0;
91  asm volatile("mfspr %0, 287" : "=r" (pvr));
92  // Top 16 bits suffice to identify the model
93  return static_cast<int>(pvr >> 16);
94  });
95 
96  if(pvr > 0)
97  {
98  const uint16_t ALTIVEC_PVR[] = {
99  0x003E, // IBM POWER6
100  0x003F, // IBM POWER7
101  0x004A, // IBM POWER7p
102  0x004B, // IBM POWER8E
103  0x004C, // IBM POWER8 NVL
104  0x004D, // IBM POWER8
105  0x004E, // IBM POWER9
106  0x000C, // G4-7400
107  0x0039, // G5 970
108  0x003C, // G5 970FX
109  0x0044, // G5 970MP
110  0x0070, // Cell PPU
111  0, // end
112  };
113 
114  for(size_t i = 0; ALTIVEC_PVR[i]; ++i)
115  {
116  if(pvr == ALTIVEC_PVR[i])
117  return CPUID::CPUID_ALTIVEC_BIT;
118  }
119 
120  return 0;
121  }
122 
123  // TODO try direct instruction probing
124 
125 #endif
126 
127  return 0;
128  }
129 
130 #endif
131 
132 }
int BOTAN_TEST_API run_cpu_instruction_probe(std::function< int()> probe_fn)
Definition: os_utils.cpp:616
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
unsigned long get_auxval(unsigned long id)
Definition: os_utils.cpp:109