Botan 1.10.17

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_POWER7p = 0x004A;
161 const u16bit PVR_POWER8 = 0x004D;
162 const u16bit PVR_POWER8E = 0x004B;
163 const u16bit PVR_CELL_PPU = 0x0070;
164
165 // Motorola produced G4s with PVR 0x800[0123C] (at least)
166 const u16bit PVR_G4_74xx_24 = 0x800;
167
168 u32bit pvr = 0;
169
170 asm volatile("mfspr %0, 287" : "=r" (pvr));
171
172 // Top 16 bit suffice to identify model
173 pvr >>= 16;
174
175 altivec_capable |= (pvr == PVR_G4_7400);
176 altivec_capable |= ((pvr >> 4) == PVR_G4_74xx_24);
177 altivec_capable |= (pvr == PVR_G5_970);
178 altivec_capable |= (pvr == PVR_G5_970FX);
179 altivec_capable |= (pvr == PVR_G5_970MP);
180 altivec_capable |= (pvr == PVR_G5_970GX);
181 altivec_capable |= (pvr == PVR_POWER6);
182 altivec_capable |= (pvr == PVR_POWER7);
183 altivec_capable |= (pvr == PVR_POWER7p);
184 altivec_capable |= (pvr == PVR_POWER8);
185 altivec_capable |= (pvr == PVR_POWER8E);
186 altivec_capable |= (pvr == PVR_CELL_PPU);
187 #endif
188
189 return altivec_capable;
190 }
191
192 #endif
193
194 }
195
196 void CPUID::initialize()
197 {
198 u32bit cpuid[4] = { 0 };
199 CALL_CPUID(1, cpuid);
200
201 x86_processor_flags = (static_cast<u64bit>(cpuid[2]) << 32) | cpuid[3];
202
203 #if defined(BOTAN_TARGET_ARCH_IS_X86_64)
204 /*
205 * If we don't have access to CPUID, we can still safely assume that
206 * any x86-64 processor has SSE2.
207 */
208 if(x86_processor_flags == 0)
209 x86_processor_flags |= (1 << CPUID_SSE2_BIT);
210 #endif
211
212 cache_line = get_x86_cache_line_size();
213
214 altivec_capable = false;
215
216 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
217 if(altivec_check_sysctl() || altivec_check_pvr_emul())
218 altivec_capable = true;
219 #endif
220 }
221
222 }
Botan::same_mem
bool same_mem(const T *p1, const T *p2, size_t n)
Definition: mem_ops.h:57
Botan::get_byte
byte get_byte(size_t byte_num, T input)
Definition: get_byte.h:21
Botan::u64bit
unsigned long long u64bit
Definition: types.h:49
Botan::u16bit
unsigned short u16bit
Definition: types.h:27
cpuid.h
Botan
Definition: algo_base.h:14
Botan::CPUID::initialize
static void initialize()
Definition: cpuid.cpp:196
CALL_CPUID
#define CALL_CPUID(type, out)
Definition: cpuid.cpp:81
Botan::u32bit
unsigned int u32bit
Definition: types.h:32