Line data Source code
1 : /* hwf-x86.c - Detect hardware features - x86 part
2 : * Copyright (C) 2007, 2011, 2012 Free Software Foundation, Inc.
3 : * Copyright (C) 2012 Jussi Kivilinna
4 : *
5 : * This file is part of Libgcrypt.
6 : *
7 : * Libgcrypt is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * Libgcrypt is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public
18 : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <stdarg.h>
26 : #include <unistd.h>
27 :
28 : #include "g10lib.h"
29 : #include "hwf-common.h"
30 :
31 : #if !defined (__i386__) && !defined (__x86_64__)
32 : # error Module build for wrong CPU.
33 : #endif
34 :
35 : /* We use the next macro to decide whether we can test for certain
36 : features. */
37 : #undef HAS_X86_CPUID
38 :
39 : #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
40 : # define HAS_X86_CPUID 1
41 :
42 : static int
43 : is_cpuid_available(void)
44 : {
45 : int has_cpuid = 0;
46 :
47 : /* Detect the CPUID feature by testing some undefined behaviour (16
48 : vs 32 bit pushf/popf). */
49 : asm volatile
50 : ("pushf\n\t" /* Copy flags to EAX. */
51 : "popl %%eax\n\t"
52 : "movl %%eax, %%ecx\n\t" /* Save flags into ECX. */
53 : "xorl $0x200000, %%eax\n\t" /* Toggle ID bit and copy it to the flags. */
54 : "pushl %%eax\n\t"
55 : "popf\n\t"
56 : "pushf\n\t" /* Copy changed flags again to EAX. */
57 : "popl %%eax\n\t"
58 : "pushl %%ecx\n\t" /* Restore flags from ECX. */
59 : "popf\n\t"
60 : "xorl %%eax, %%ecx\n\t" /* Compare flags against saved flags. */
61 : "jz .Lno_cpuid%=\n\t" /* Toggling did not work, thus no CPUID. */
62 : "movl $1, %0\n" /* Worked. true -> HAS_CPUID. */
63 : ".Lno_cpuid%=:\n\t"
64 : : "+r" (has_cpuid)
65 : :
66 : : "%eax", "%ecx", "cc"
67 : );
68 :
69 : return has_cpuid;
70 : }
71 :
72 : static void
73 : get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
74 : unsigned int *ecx, unsigned int *edx)
75 : {
76 : unsigned int regs[4];
77 :
78 : asm volatile
79 : ("pushl %%ebx\n\t" /* Save GOT register. */
80 : "movl %1, %%ebx\n\t"
81 : "cpuid\n\t"
82 : "movl %%ebx, %1\n\t"
83 : "popl %%ebx\n\t" /* Restore GOT register. */
84 : : "=a" (regs[0]), "=D" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
85 : : "0" (in), "1" (0), "2" (0), "3" (0)
86 : : "cc"
87 : );
88 :
89 : if (eax)
90 : *eax = regs[0];
91 : if (ebx)
92 : *ebx = regs[1];
93 : if (ecx)
94 : *ecx = regs[2];
95 : if (edx)
96 : *edx = regs[3];
97 : }
98 :
99 : #if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
100 : static unsigned int
101 : get_xgetbv(void)
102 : {
103 : unsigned int t_eax, t_edx;
104 :
105 : asm volatile
106 : ("xgetbv\n\t"
107 : : "=a" (t_eax), "=d" (t_edx)
108 : : "c" (0)
109 : );
110 :
111 : return t_eax;
112 : }
113 : #endif /* ENABLE_AVX_SUPPORT || ENABLE_AVX2_SUPPORT */
114 :
115 : #endif /* i386 && GNUC */
116 :
117 :
118 : #if defined (__x86_64__) && defined (__GNUC__)
119 : # define HAS_X86_CPUID 1
120 :
121 : static int
122 31 : is_cpuid_available(void)
123 : {
124 31 : return 1;
125 : }
126 :
127 : static void
128 93 : get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
129 : unsigned int *ecx, unsigned int *edx)
130 : {
131 : unsigned int regs[4];
132 :
133 93 : asm volatile
134 : ("cpuid\n\t"
135 : : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
136 : : "0" (in), "1" (0), "2" (0), "3" (0)
137 : : "cc"
138 : );
139 :
140 93 : if (eax)
141 62 : *eax = regs[0];
142 93 : if (ebx)
143 62 : *ebx = regs[1];
144 93 : if (ecx)
145 62 : *ecx = regs[2];
146 93 : if (edx)
147 31 : *edx = regs[3];
148 93 : }
149 :
150 : #if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
151 : static unsigned int
152 31 : get_xgetbv(void)
153 : {
154 : unsigned int t_eax, t_edx;
155 :
156 31 : asm volatile
157 : ("xgetbv\n\t"
158 : : "=a" (t_eax), "=d" (t_edx)
159 : : "c" (0)
160 : );
161 :
162 31 : return t_eax;
163 : }
164 : #endif /* ENABLE_AVX_SUPPORT || ENABLE_AVX2_SUPPORT */
165 :
166 : #endif /* x86-64 && GNUC */
167 :
168 :
169 : #ifdef HAS_X86_CPUID
170 : static unsigned int
171 31 : detect_x86_gnuc (void)
172 : {
173 : char vendor_id[12+1];
174 : unsigned int features;
175 31 : unsigned int os_supports_avx_avx2_registers = 0;
176 : unsigned int max_cpuid_level;
177 : unsigned int fms, family, model;
178 31 : unsigned int result = 0;
179 :
180 : (void)os_supports_avx_avx2_registers;
181 :
182 31 : if (!is_cpuid_available())
183 0 : return 0;
184 :
185 31 : get_cpuid(0, &max_cpuid_level,
186 : (unsigned int *)&vendor_id[0],
187 : (unsigned int *)&vendor_id[8],
188 : (unsigned int *)&vendor_id[4]);
189 31 : vendor_id[12] = 0;
190 :
191 : if (0)
192 : ; /* Just to make "else if" and ifdef macros look pretty. */
193 : #ifdef ENABLE_PADLOCK_SUPPORT
194 31 : else if (!strcmp (vendor_id, "CentaurHauls"))
195 : {
196 : /* This is a VIA CPU. Check what PadLock features we have. */
197 :
198 : /* Check for extended centaur (EAX). */
199 0 : get_cpuid(0xC0000000, &features, NULL, NULL, NULL);
200 :
201 : /* Has extended centaur features? */
202 0 : if (features > 0xC0000000)
203 : {
204 : /* Ask for the extended feature flags (EDX). */
205 0 : get_cpuid(0xC0000001, NULL, NULL, NULL, &features);
206 :
207 : /* Test bits 2 and 3 to see whether the RNG exists and is enabled. */
208 0 : if ((features & 0x0C) == 0x0C)
209 0 : result |= HWF_PADLOCK_RNG;
210 :
211 : /* Test bits 6 and 7 to see whether the ACE exists and is enabled. */
212 0 : if ((features & 0xC0) == 0xC0)
213 0 : result |= HWF_PADLOCK_AES;
214 :
215 : /* Test bits 10 and 11 to see whether the PHE exists and is
216 : enabled. */
217 0 : if ((features & 0xC00) == 0xC00)
218 0 : result |= HWF_PADLOCK_SHA;
219 :
220 : /* Test bits 12 and 13 to see whether the MONTMUL exists and is
221 : enabled. */
222 0 : if ((features & 0x3000) == 0x3000)
223 0 : result |= HWF_PADLOCK_MMUL;
224 : }
225 : }
226 : #endif /*ENABLE_PADLOCK_SUPPORT*/
227 31 : else if (!strcmp (vendor_id, "GenuineIntel"))
228 : {
229 : /* This is an Intel CPU. */
230 31 : result |= HWF_INTEL_CPU;
231 : }
232 0 : else if (!strcmp (vendor_id, "AuthenticAMD"))
233 : {
234 : /* This is an AMD CPU. */
235 : }
236 :
237 : /* Detect Intel features, that might also be supported by other
238 : vendors. */
239 :
240 : /* Get CPU family/model/stepping (EAX) and Intel feature flags (ECX). */
241 31 : get_cpuid(1, &fms, NULL, &features, NULL);
242 :
243 31 : family = ((fms & 0xf00) >> 8) + ((fms & 0xff00000) >> 20);
244 31 : model = ((fms & 0xf0) >> 4) + ((fms & 0xf0000) >> 12);
245 :
246 31 : if ((result & HWF_INTEL_CPU) && family == 6)
247 : {
248 : /* These Intel Core processor models have SHLD/SHRD instruction that
249 : * can do integer rotation faster actual ROL/ROR instructions. */
250 31 : switch (model)
251 : {
252 : case 0x2A:
253 : case 0x2D:
254 : case 0x3A:
255 : case 0x3C:
256 : case 0x3F:
257 : case 0x45:
258 : case 0x46:
259 : case 0x3D:
260 : case 0x4F:
261 : case 0x56:
262 : case 0x47:
263 : case 0x4E:
264 : case 0x5E:
265 : case 0x55:
266 : case 0x66:
267 31 : result |= HWF_INTEL_FAST_SHLD;
268 31 : break;
269 : }
270 : }
271 :
272 : #ifdef ENABLE_PCLMUL_SUPPORT
273 : /* Test bit 1 for PCLMUL. */
274 31 : if (features & 0x00000002)
275 31 : result |= HWF_INTEL_PCLMUL;
276 : #endif
277 : /* Test bit 9 for SSSE3. */
278 31 : if (features & 0x00000200)
279 31 : result |= HWF_INTEL_SSSE3;
280 : #ifdef ENABLE_AESNI_SUPPORT
281 : /* Test bit 25 for AES-NI. */
282 31 : if (features & 0x02000000)
283 31 : result |= HWF_INTEL_AESNI;
284 : #endif /*ENABLE_AESNI_SUPPORT*/
285 : #if defined(ENABLE_AVX_SUPPORT) || defined(ENABLE_AVX2_SUPPORT)
286 : /* Test bit 27 for OSXSAVE (required for AVX/AVX2). */
287 31 : if (features & 0x08000000)
288 : {
289 : /* Check that OS has enabled both XMM and YMM state support. */
290 31 : if ((get_xgetbv() & 0x6) == 0x6)
291 31 : os_supports_avx_avx2_registers = 1;
292 : }
293 : #endif
294 : #ifdef ENABLE_AVX_SUPPORT
295 : /* Test bit 28 for AVX. */
296 31 : if (features & 0x10000000)
297 31 : if (os_supports_avx_avx2_registers)
298 31 : result |= HWF_INTEL_AVX;
299 : #endif /*ENABLE_AVX_SUPPORT*/
300 : #ifdef ENABLE_DRNG_SUPPORT
301 : /* Test bit 30 for RDRAND. */
302 31 : if (features & 0x40000000)
303 0 : result |= HWF_INTEL_RDRAND;
304 : #endif /*ENABLE_DRNG_SUPPORT*/
305 :
306 : /* Check additional Intel feature flags. Early Intel P5 processors report
307 : * too high max_cpuid_level, so don't check level 7 if processor does not
308 : * support SSE3 (as cpuid:7 contains only features for newer processors).
309 : * Source: http://www.sandpile.org/x86/cpuid.htm */
310 31 : if (max_cpuid_level >= 7 && (features & 0x00000001))
311 : {
312 : /* Get CPUID:7 contains further Intel feature flags. */
313 31 : get_cpuid(7, NULL, &features, NULL, NULL);
314 :
315 : /* Test bit 8 for BMI2. */
316 31 : if (features & 0x00000100)
317 0 : result |= HWF_INTEL_BMI2;
318 :
319 : #ifdef ENABLE_AVX2_SUPPORT
320 : /* Test bit 5 for AVX2. */
321 31 : if (features & 0x00000020)
322 0 : if (os_supports_avx_avx2_registers)
323 0 : result |= HWF_INTEL_AVX2;
324 : #endif /*ENABLE_AVX_SUPPORT*/
325 : }
326 :
327 31 : return result;
328 : }
329 : #endif /* HAS_X86_CPUID */
330 :
331 :
332 : unsigned int
333 31 : _gcry_hwf_detect_x86 (void)
334 : {
335 : #if defined (HAS_X86_CPUID)
336 31 : return detect_x86_gnuc ();
337 : #else
338 : return 0;
339 : #endif
340 : }
|