Crypto++
cpu.cpp
1 // cpu.cpp - written and placed in the public domain by Wei Dai
2 
3 #include "pch.h"
4 
5 #ifndef CRYPTOPP_IMPORTS
6 
7 #include "cpu.h"
8 #include "misc.h"
9 #include <algorithm>
10 
11 #ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
12 #include <signal.h>
13 #include <setjmp.h>
14 #endif
15 
16 #if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
17 #include <emmintrin.h>
18 #endif
19 
20 NAMESPACE_BEGIN(CryptoPP)
21 
22 #ifdef CRYPTOPP_CPUID_AVAILABLE
23 
24 #if _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64
25 
26 bool CpuId(word32 input, word32 *output)
27 {
28  __cpuid((int *)output, input);
29  return true;
30 }
31 
32 #else
33 
34 #ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
35 extern "C" {
36 typedef void (*SigHandler)(int);
37 
38 static jmp_buf s_jmpNoCPUID;
39 static void SigIllHandlerCPUID(int)
40 {
41  longjmp(s_jmpNoCPUID, 1);
42 }
43 
44 static jmp_buf s_jmpNoSSE2;
45 static void SigIllHandlerSSE2(int)
46 {
47  longjmp(s_jmpNoSSE2, 1);
48 }
49 }
50 #endif
51 
52 bool CpuId(word32 input, word32 *output)
53 {
54 #ifdef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
55  __try
56  {
57  __asm
58  {
59  mov eax, input
60  cpuid
61  mov edi, output
62  mov [edi], eax
63  mov [edi+4], ebx
64  mov [edi+8], ecx
65  mov [edi+12], edx
66  }
67  }
68  __except (1)
69  {
70  return false;
71  }
72  return true;
73 #else
74  SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID);
75  if (oldHandler == SIG_ERR)
76  return false;
77 
78  bool result = true;
79  if (setjmp(s_jmpNoCPUID))
80  result = false;
81  else
82  {
83  asm
84  (
85  // save ebx in case -fPIC is being used
86 #if CRYPTOPP_BOOL_X86
87  "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
88 #else
89  "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx"
90 #endif
91  : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3])
92  : "a" (input)
93  );
94  }
95 
96  signal(SIGILL, oldHandler);
97  return result;
98 #endif
99 }
100 
101 #endif
102 
103 static bool TrySSE2()
104 {
105 #if CRYPTOPP_BOOL_X64
106  return true;
107 #elif defined(CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY)
108  __try
109  {
110 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
111  AS2(por xmm0, xmm0) // executing SSE2 instruction
112 #elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
113  __m128i x = _mm_setzero_si128();
114  return _mm_cvtsi128_si32(x) == 0;
115 #endif
116  }
117  __except (1)
118  {
119  return false;
120  }
121  return true;
122 #else
123  SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2);
124  if (oldHandler == SIG_ERR)
125  return false;
126 
127  bool result = true;
128  if (setjmp(s_jmpNoSSE2))
129  result = false;
130  else
131  {
132 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
133  __asm __volatile ("por %xmm0, %xmm0");
134 #elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE
135  __m128i x = _mm_setzero_si128();
136  result = _mm_cvtsi128_si32(x) == 0;
137 #endif
138  }
139 
140  signal(SIGILL, oldHandler);
141  return result;
142 #endif
143 }
144 
145 bool g_x86DetectionDone = false;
146 bool g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasMMX = false, g_hasAESNI = false, g_hasCLMUL = false, g_isP4 = false;
147 word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE;
148 
149 void DetectX86Features()
150 {
151  word32 cpuid[4], cpuid1[4];
152  if (!CpuId(0, cpuid))
153  return;
154  if (!CpuId(1, cpuid1))
155  return;
156 
157  g_hasMMX = (cpuid1[3] & (1 << 23)) != 0;
158  if ((cpuid1[3] & (1 << 26)) != 0)
159  g_hasSSE2 = TrySSE2();
160  g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9));
161  g_hasAESNI = g_hasSSE2 && (cpuid1[2] & (1<<25));
162  g_hasCLMUL = g_hasSSE2 && (cpuid1[2] & (1<<1));
163 
164  if ((cpuid1[3] & (1 << 25)) != 0)
165  g_hasISSE = true;
166  else
167  {
168  word32 cpuid2[4];
169  CpuId(0x080000000, cpuid2);
170  if (cpuid2[0] >= 0x080000001)
171  {
172  CpuId(0x080000001, cpuid2);
173  g_hasISSE = (cpuid2[3] & (1 << 22)) != 0;
174  }
175  }
176 
177  std::swap(cpuid[2], cpuid[3]);
178  if (memcmp(cpuid+1, "GenuineIntel", 12) == 0)
179  {
180  g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf;
181  g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1);
182  }
183  else if (memcmp(cpuid+1, "AuthenticAMD", 12) == 0)
184  {
185  CpuId(0x80000005, cpuid);
186  g_cacheLineSize = GETBYTE(cpuid[2], 0);
187  }
188 
189  if (!g_cacheLineSize)
190  g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE;
191 
192  g_x86DetectionDone = true;
193 }
194 
195 #endif
196 
197 NAMESPACE_END
198 
199 #endif