Botan  1.10.9
cpuid.cpp
Go to the documentation of this file.
1 /*
2 * Runtime CPU detection
3 * (C) 2009-2010 Jack Lloyd
4 *
5 * Distributed under the terms of the Botan license
6 */
7 
8 #include <botan/cpuid.h>
9 #include <botan/types.h>
10 #include <botan/get_byte.h>
11 #include <botan/mem_ops.h>
12 
13 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
14 
15 #if defined(BOTAN_TARGET_OS_IS_DARWIN)
16  #include <sys/sysctl.h>
17 #endif
18 
19 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
20  #include <sys/param.h>
21  #include <sys/sysctl.h>
22  #include <machine/cpu.h>
23 #endif
24 
25 #endif
26 
27 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
28 
29 #if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
30 
31  #include <intrin.h>
32  #define CALL_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
33 
34 #elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
35 
36  #include <ia32intrin.h>
37  #define CALL_CPUID(type, out) do { __cpuid(out, type); } while(0)
38 
39 #elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && (BOTAN_GCC_VERSION >= 430)
40 
41  // Only available starting in GCC 4.3
42  #include <cpuid.h>
43 
44 namespace {
45 
46  /*
47  * Prevent inlining to work around GCC bug 44174
48  */
49  void __attribute__((__noinline__)) call_gcc_cpuid(Botan::u32bit type,
50  Botan::u32bit out[4])
51  {
52  __get_cpuid(type, out, out+1, out+2, out+3);
53  }
54 
55  #define CALL_CPUID call_gcc_cpuid
56 
57 }
58 
59 #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && \
60  (defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_GCC))
61 
62  /*
63  * We can't safely use this on x86-32 as some 32-bit ABIs use ebx as
64  * a PIC register, and in theory there are some x86-32s still out
65  * there that don't support cpuid at all; it requires strange
66  * contortions to detect them.
67  */
68 
69  #define CALL_CPUID(type, out) \
70  asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
71  : "0" (type))
72 
73 #else
74  #warning "No method of calling CPUID for this compiler"
75 #endif
76 
77 #endif
78 
79 #ifndef CALL_CPUID
80  // In all other cases, just zeroize the supposed cpuid output
81  #define CALL_CPUID(type, out) \
82  do { out[0] = out[1] = out[2] = out[3] = 0; } while(0);
83 #endif
84 
85 namespace Botan {
86 
87 u64bit CPUID::x86_processor_flags = 0;
88 size_t CPUID::cache_line = 32;
89 bool CPUID::altivec_capable = false;
90 
91 namespace {
92 
93 u32bit get_x86_cache_line_size()
94  {
95  const u32bit INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
96  const u32bit AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
97 
98  u32bit cpuid[4] = { 0 };
99  CALL_CPUID(0, cpuid);
100 
101  if(same_mem(cpuid + 1, INTEL_CPUID, 3))
102  {
103  CALL_CPUID(1, cpuid);
104  return 8 * get_byte(2, cpuid[1]);
105  }
106  else if(same_mem(cpuid + 1, AMD_CPUID, 3))
107  {
108  CALL_CPUID(0x80000005, cpuid);
109  return get_byte(3, cpuid[2]);
110  }
111  else
112  return 32; // default cache line guess
113  }
114 
115 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
116 
117 bool altivec_check_sysctl()
118  {
119 #if defined(BOTAN_TARGET_OS_IS_DARWIN) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
120 
121 #if defined(BOTAN_TARGET_OS_IS_OPENBSD)
122  int sels[2] = { CTL_MACHDEP, CPU_ALTIVEC };
123 #else
124  // From Apple's docs
125  int sels[2] = { CTL_HW, HW_VECTORUNIT };
126 #endif
127  int vector_type = 0;
128  size_t length = sizeof(vector_type);
129  int error = sysctl(sels, 2, &vector_type, &length, NULL, 0);
130 
131  if(error == 0 && vector_type > 0)
132  return true;
133 #endif
134 
135  return false;
136  }
137 
138 bool altivec_check_pvr_emul()
139  {
140  bool altivec_capable = false;
141 
142 #if defined(BOTAN_TARGET_OS_IS_LINUX) || defined(BOTAN_TARGET_OS_IS_NETBSD)
143 
144  /*
145  On PowerPC, MSR 287 is PVR, the Processor Version Number
146  Normally it is only accessible to ring 0, but Linux and NetBSD
147  (others, too, maybe?) will trap and emulate it for us.
148 
149  PVR identifiers for various AltiVec enabled CPUs. Taken from
150  PearPC and Linux sources, mostly.
151  */
152 
153  const u16bit PVR_G4_7400 = 0x000C;
154  const u16bit PVR_G5_970 = 0x0039;
155  const u16bit PVR_G5_970FX = 0x003C;
156  const u16bit PVR_G5_970MP = 0x0044;
157  const u16bit PVR_G5_970GX = 0x0045;
158  const u16bit PVR_POWER6 = 0x003E;
159  const u16bit PVR_POWER7 = 0x003F;
160  const u16bit PVR_CELL_PPU = 0x0070;
161 
162  // Motorola produced G4s with PVR 0x800[0123C] (at least)
163  const u16bit PVR_G4_74xx_24 = 0x800;
164 
165  u32bit pvr = 0;
166 
167  asm volatile("mfspr %0, 287" : "=r" (pvr));
168 
169  // Top 16 bit suffice to identify model
170  pvr >>= 16;
171 
172  altivec_capable |= (pvr == PVR_G4_7400);
173  altivec_capable |= ((pvr >> 4) == PVR_G4_74xx_24);
174  altivec_capable |= (pvr == PVR_G5_970);
175  altivec_capable |= (pvr == PVR_G5_970FX);
176  altivec_capable |= (pvr == PVR_G5_970MP);
177  altivec_capable |= (pvr == PVR_G5_970GX);
178  altivec_capable |= (pvr == PVR_POWER6);
179  altivec_capable |= (pvr == PVR_POWER7);
180  altivec_capable |= (pvr == PVR_CELL_PPU);
181 #endif
182 
183  return altivec_capable;
184  }
185 
186 #endif
187 
188 }
189 
191  {
192  u32bit cpuid[4] = { 0 };
193  CALL_CPUID(1, cpuid);
194 
195  x86_processor_flags = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[3];
196 
197 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
198  /*
199  * If we don't have access to CPUID, we can still safely assume that
200  * any x86-64 processor has SSE2.
201  */
202  if(x86_processor_flags == 0)
203  x86_processor_flags |= (1 << CPUID_SSE2_BIT);
204 #endif
205 
206  cache_line = get_x86_cache_line_size();
207 
208  altivec_capable = false;
209 
210 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
211  if(altivec_check_sysctl() || altivec_check_pvr_emul())
212  altivec_capable = true;
213 #endif
214  }
215 
216 }
bool same_mem(const T *p1, const T *p2, size_t n)
Definition: mem_ops.h:57
byte get_byte(size_t byte_num, T input)
Definition: get_byte.h:21
unsigned long long u64bit
Definition: types.h:49
unsigned short u16bit
Definition: types.h:27
static void initialize()
Definition: cpuid.cpp:190
#define CALL_CPUID(type, out)
Definition: cpuid.cpp:81
unsigned int u32bit
Definition: types.h:32