Botan: src/utils/cpuid.cpp Source File

Botan 1.10.16

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
190 void CPUID::initialize()
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 }
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:190
CALL_CPUID
#define CALL_CPUID(type, out)
Definition: cpuid.cpp:81
Botan::u32bit
unsigned int u32bit
Definition: types.h:32