Crypto++  5.6.3
Free C++ class library of cryptographic schemes
cpu.cpp
1 // cpu.cpp - written and placed in the public domain by Wei Dai
2 
3 #include "pch.h"
4 #include "config.h"
5 
6 #ifndef EXCEPTION_EXECUTE_HANDLER
7 # define EXCEPTION_EXECUTE_HANDLER 1
8 #endif
9 
10 #ifndef CRYPTOPP_IMPORTS
11 
12 #include "cpu.h"
13 #include "misc.h"
14 #include <algorithm>
15 
16 #ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
17 #include <signal.h>
18 #include <setjmp.h>
19 #endif
20 
21 #if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
22 #include <emmintrin.h>
23 #endif
24 
25 NAMESPACE_BEGIN(CryptoPP)
26 
27 #ifdef CRYPTOPP_CPUID_AVAILABLE
28 
29 #if _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64
30 
31 bool CpuId(word32 input, word32 output[4])
32 {
33  __cpuid((int *)output, input);
34  return true;
35 }
36 
37 #else
38 
39 #ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
40 extern "C" {
41 typedef void (*SigHandler)(int);
42 
43 static jmp_buf s_jmpNoCPUID;
44 static void SigIllHandlerCPUID(int)
45 {
46  longjmp(s_jmpNoCPUID, 1);
47 }
48 
49 static jmp_buf s_jmpNoSSE2;
50 static void SigIllHandlerSSE2(int)
51 {
52  longjmp(s_jmpNoSSE2, 1);
53 }
54 }
55 #endif
56 
57 bool CpuId(word32 input, word32 output[4])
58 {
59 #if defined(CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY)
60  __try
61  {
62  __asm
63  {
64  mov eax, input
65  mov ecx, 0
66  cpuid
67  mov edi, output
68  mov [edi], eax
69  mov [edi+4], ebx
70  mov [edi+8], ecx
71  mov [edi+12], edx
72  }
73  }
74  // GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION
75  __except (EXCEPTION_EXECUTE_HANDLER)
76  {
77  return false;
78  }
79 
80  // function 0 returns the highest basic function understood in EAX
81  if(input == 0)
82  return !!output[0];
83 
84  return true;
85 #else
86  // longjmp and clobber warnings. Volatile is required.
87  // http://github.com/weidai11/cryptopp/issues/24
88  // http://stackoverflow.com/q/7721854
89  volatile bool result = true;
90 
91  SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID);
92  if (oldHandler == SIG_ERR)
93  result = false;
94 
95  if (setjmp(s_jmpNoCPUID))
96  result = false;
97  else
98  {
99  asm volatile
100  (
101  // save ebx in case -fPIC is being used
102  // TODO: this might need an early clobber on EDI.
103 # if CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64
104  "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx"
105 # else
106  "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
107 # endif
108  : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3])
109  : "a" (input), "c" (0)
110  );
111  }
112 
113  signal(SIGILL, oldHandler);
114  return result;
115 #endif
116 }
117 
118 #endif
119 
120 static bool TrySSE2()
121 {
122 #if CRYPTOPP_BOOL_X64
123  return true;
124 #elif defined(CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY)
125  __try
126  {
127 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
128  AS2(por xmm0, xmm0) // executing SSE2 instruction
129 #elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
130  __m128i x = _mm_setzero_si128();
131  return _mm_cvtsi128_si32(x) == 0;
132 #endif
133  }
134  // GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION
135  __except (EXCEPTION_EXECUTE_HANDLER)
136  {
137  return false;
138  }
139  return true;
140 #else
141  // longjmp and clobber warnings. Volatile is required.
142  // http://github.com/weidai11/cryptopp/issues/24
143  // http://stackoverflow.com/q/7721854
144  volatile bool result = true;
145 
146  SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2);
147  if (oldHandler == SIG_ERR)
148  return false;
149 
150  if (setjmp(s_jmpNoSSE2))
151  result = true;
152  else
153  {
154 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
155  __asm __volatile ("por %xmm0, %xmm0");
156 #elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
157  __m128i x = _mm_setzero_si128();
158  result = _mm_cvtsi128_si32(x) == 0;
159 #endif
160  }
161 
162  signal(SIGILL, oldHandler);
163  return result;
164 #endif
165 }
166 
167 bool g_x86DetectionDone = false;
168 bool g_hasMMX = false, g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasAESNI = false, g_hasCLMUL = false, g_isP4 = false, g_hasRDRAND = false, g_hasRDSEED = false;
169 word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE;
170 
171 // MacPorts/GCC does not provide constructor(priority). Apple/GCC and Fink/GCC do provide it.
172 #define HAVE_GCC_CONSTRUCTOR1 (__GNUC__ && (CRYPTOPP_INIT_PRIORITY > 0) && ((CRYPTOPP_GCC_VERSION >= 40300) || (CRYPTOPP_CLANG_VERSION >= 20900) || (_INTEL_COMPILER >= 300)) && !(MACPORTS_GCC_COMPILER > 0))
173 #define HAVE_GCC_CONSTRUCTOR0 (__GNUC__ && (CRYPTOPP_INIT_PRIORITY > 0) && !(MACPORTS_GCC_COMPILER > 0))
174 
175 static inline bool IsIntel(const word32 output[4])
176 {
177  // This is the "GenuineIntel" string
178  return (output[1] /*EBX*/ == 0x756e6547) &&
179  (output[2] /*ECX*/ == 0x6c65746e) &&
180  (output[3] /*EDX*/ == 0x49656e69);
181 }
182 
183 static inline bool IsAMD(const word32 output[4])
184 {
185  // This is the "AuthenticAMD" string
186  return (output[1] /*EBX*/ == 0x68747541) &&
187  (output[2] /*ECX*/ == 0x69746E65) &&
188  (output[3] /*EDX*/ == 0x444D4163);
189 }
190 
191 #if HAVE_GCC_CONSTRUCTOR1
192 void __attribute__ ((constructor (CRYPTOPP_INIT_PRIORITY + 50))) DetectX86Features()
193 #elif HAVE_GCC_CONSTRUCTOR0
194 void __attribute__ ((constructor)) DetectX86Features()
195 #else
196 void DetectX86Features()
197 #endif
198 {
199  word32 cpuid[4], cpuid1[4];
200  if (!CpuId(0, cpuid))
201  return;
202  if (!CpuId(1, cpuid1))
203  return;
204 
205  g_hasMMX = (cpuid1[3] & (1 << 23)) != 0;
206  if ((cpuid1[3] & (1 << 26)) != 0)
207  g_hasSSE2 = TrySSE2();
208  g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9));
209  g_hasAESNI = g_hasSSE2 && (cpuid1[2] & (1<<25));
210  g_hasCLMUL = g_hasSSE2 && (cpuid1[2] & (1<<1));
211 
212  if ((cpuid1[3] & (1 << 25)) != 0)
213  g_hasISSE = true;
214  else
215  {
216  word32 cpuid2[4];
217  CpuId(0x080000000, cpuid2);
218  if (cpuid2[0] >= 0x080000001)
219  {
220  CpuId(0x080000001, cpuid2);
221  g_hasISSE = (cpuid2[3] & (1 << 22)) != 0;
222  }
223  }
224 
225  static const unsigned int RDRAND_FLAG = (1 << 30);
226  static const unsigned int RDSEED_FLAG = (1 << 18);
227  if (IsIntel(cpuid))
228  {
229  g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf;
230  g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1);
231  g_hasRDRAND = !!(cpuid1[2] /*ECX*/ & RDRAND_FLAG);
232 
233  if (cpuid[0] /*EAX*/ >= 7)
234  {
235  word32 cpuid3[4];
236  if (CpuId(7, cpuid3))
237  g_hasRDSEED = !!(cpuid3[1] /*EBX*/ & RDSEED_FLAG);
238  }
239  }
240  else if (IsAMD(cpuid))
241  {
242  CpuId(0x80000005, cpuid);
243  g_cacheLineSize = GETBYTE(cpuid[2], 0);
244  g_hasRDRAND = !!(cpuid[2] /*ECX*/ & RDRAND_FLAG);
245  }
246 
247  if (!g_cacheLineSize)
248  g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE;
249 
250  *((volatile bool*)&g_x86DetectionDone) = true;
251 }
252 
253 #endif
254 
255 NAMESPACE_END
256 
257 #endif
Utility functions for the Crypto++ library.
Library configuration file.
Classes, functions, intrinsics and features for X86, X32 nd X64 assembly.
Crypto++ library namespace.