Do not detect AES-NI support if disabled by configure.
[libgcrypt.git] / src / hwfeatures.c
1 /* hwfeatures.c - Detect hardware features.
2  * Copyright (C) 2007, 2011  Free Software Foundation, Inc.
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26
27 #include "g10lib.h"
28
29 /* A bit vector describing the hardware features currently
30    available. */
31 static unsigned int hw_features;
32
33
34 /* Return a bit vector describing the available hardware features.
35    The HWF_ constants are used to test for them. */
36 unsigned int
37 _gcry_get_hw_features (void)
38 {
39   return hw_features;
40 }
41
42
43 #if defined (__x86_64__) && defined (__GNUC__)
44 static void
45 detect_x86_64_gnuc (void)
46 {
47   /* The code here is only useful for the PadLock engine thus we don't
48      build it if that support has been disabled.  */
49   char vendor_id[12+1];
50
51   asm volatile
52     ("xorl  %%eax, %%eax\n\t"    /* 0 -> EAX.  */
53      "cpuid\n\t"                 /* Get vendor ID.  */
54      "movl  %%ebx, (%0)\n\t"     /* EBX,EDX,ECX -> VENDOR_ID.  */
55      "movl  %%edx, 4(%0)\n\t"
56      "movl  %%ecx, 8(%0)\n\t"
57      :
58      : "S" (&vendor_id[0])
59      : "%eax", "%ebx", "%ecx", "%edx", "cc"
60      );
61   vendor_id[12] = 0;
62
63   if (0)
64     ; /* Just to make "else if" and ifdef macros look pretty.  */
65 #ifdef ENABLE_PADLOCK_SUPPORT
66   else if (!strcmp (vendor_id, "CentaurHauls"))
67     {
68       /* This is a VIA CPU.  Check what PadLock features we have.  */
69       asm volatile
70         ("movl $0xC0000000, %%eax\n\t"  /* Check for extended centaur  */
71          "cpuid\n\t"                    /* feature flags.              */
72          "cmpl $0xC0000001, %%eax\n\t"
73          "jb .Lready%=\n\t"             /* EAX < 0xC0000000 => no padlock.  */
74
75          "movl $0xC0000001, %%eax\n\t"  /* Ask for the extended */
76          "cpuid\n\t"                    /* feature flags.       */
77
78          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
79          "andl $0x0C, %%eax\n\t"        /* Test bits 2 and 3 to see whether */
80          "cmpl $0x0C, %%eax\n\t"        /* the RNG exists and is enabled.   */
81          "jnz .Lno_rng%=\n\t"
82          "orl $1, %0\n"                 /* Set our HWF_PADLOCK_RNG bit.  */
83
84          ".Lno_rng%=:\n\t"
85          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
86          "andl $0xC0, %%eax\n\t"        /* Test bits 6 and 7 to see whether */
87          "cmpl $0xC0, %%eax\n\t"        /* the ACE exists and is enabled.   */
88          "jnz .Lno_ace%=\n\t"
89          "orl $2, %0\n"                 /* Set our HWF_PADLOCK_AES bit.  */
90
91          ".Lno_ace%=:\n\t"
92          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
93          "andl $0xC00, %%eax\n\t"       /* Test bits 10, 11 to see whether  */
94          "cmpl $0xC00, %%eax\n\t"       /* the PHE exists and is enabled.   */
95          "jnz .Lno_phe%=\n\t"
96          "orl $4, %0\n"                 /* Set our HWF_PADLOCK_SHA bit.  */
97
98          ".Lno_phe%=:\n\t"
99          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
100          "andl $0x3000, %%eax\n\t"      /* Test bits 12, 13 to see whether  */
101          "cmpl $0x3000, %%eax\n\t"      /* MONTMUL exists and is enabled.   */
102          "jnz .Lready%=\n\t"
103          "orl $8, %0\n"                 /* Set our HWF_PADLOCK_MMUL bit.  */
104
105          ".Lready%=:\n"
106          : "+r" (hw_features)
107          :
108          : "%eax", "%ebx", "%ecx", "%edx", "cc"
109          );
110     }
111 #endif /*ENABLE_PADLOCK_SUPPORT*/
112   else if (!strcmp (vendor_id, "GenuineIntel"))
113     {
114       /* This is an Intel CPU.  */
115     }
116   else if (!strcmp (vendor_id, "AuthenticAMD"))
117     {
118       /* This is an AMD CPU.  */
119     }
120
121   /* Detect Intel features, that might also be supported by other
122      vendors.  */
123 #ifdef ENABLE_AESNI_SUPPORT
124   asm volatile
125     ("movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
126      "cpuid\n"
127      "testl $0x02000000, %%ecx\n\t" /* Test bit 25.  */
128      "jz .Lno_aes%=\n\t"            /* No AES support.  */
129      "orl $256, %0\n"               /* Set our HWF_INTEL_AES bit.  */
130
131      ".Lno_aes%=:\n"
132      : "+r" (hw_features)
133      :
134      : "%eax", "%ebx", "%ecx", "%edx", "cc"
135      );
136 #endif /*#ifdef ENABLE_AESNI_SUPPORT*/
137
138 }
139 #endif /* __x86_64__ && __GNUC__ */
140
141 #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
142 static void
143 detect_ia32_gnuc (void)
144 {
145   /* The code here is only useful for the PadLock engine thus we don't
146      build it if that support has been disabled.  */
147   int has_cpuid = 0;
148   char vendor_id[12+1];
149
150   /* Detect the CPUID feature by testing some undefined behaviour (16
151      vs 32 bit pushf/popf). */
152   asm volatile
153     ("pushf\n\t"                 /* Copy flags to EAX.  */
154      "popl %%eax\n\t"
155      "movl %%eax, %%ecx\n\t"     /* Save flags into ECX.  */
156      "xorl $0x200000, %%eax\n\t" /* Toggle ID bit and copy it to the flags.  */
157      "pushl %%eax\n\t"
158      "popf\n\t"
159      "pushf\n\t"                 /* Copy changed flags again to EAX.  */
160      "popl %%eax\n\t"
161      "pushl %%ecx\n\t"           /* Restore flags from ECX.  */
162      "popf\n\t"
163      "xorl %%eax, %%ecx\n\t"     /* Compare flags against saved flags.  */
164      "jz .Lno_cpuid%=\n\t"       /* Toggling did not work, thus no CPUID.  */
165      "movl $1, %0\n"             /* Worked. true -> HAS_CPUID.  */
166      ".Lno_cpuid%=:\n\t"
167      : "+r" (has_cpuid)
168      :
169      : "%eax", "%ecx", "cc"
170      );
171
172   if (!has_cpuid)
173     return;  /* No way.  */
174
175   asm volatile
176     ("pushl %%ebx\n\t"           /* Save GOT register.  */
177      "xorl  %%eax, %%eax\n\t"    /* 0 -> EAX.  */
178      "cpuid\n\t"                 /* Get vendor ID.  */
179      "movl  %%ebx, (%0)\n\t"     /* EBX,EDX,ECX -> VENDOR_ID.  */
180      "movl  %%edx, 4(%0)\n\t"
181      "movl  %%ecx, 8(%0)\n\t"
182      "popl  %%ebx\n"
183      :
184      : "S" (&vendor_id[0])
185      : "%eax", "%ecx", "%edx", "cc"
186      );
187   vendor_id[12] = 0;
188
189   if (0)
190     ; /* Just to make "else if" and ifdef macros look pretty.  */
191 #ifdef ENABLE_PADLOCK_SUPPORT
192   else if (!strcmp (vendor_id, "CentaurHauls"))
193     {
194       /* This is a VIA CPU.  Check what PadLock features we have.  */
195       asm volatile
196         ("pushl %%ebx\n\t"              /* Save GOT register.  */
197          "movl $0xC0000000, %%eax\n\t"  /* Check for extended centaur  */
198          "cpuid\n\t"                    /* feature flags.              */
199          "popl %%ebx\n\t"               /* Restore GOT register. */
200          "cmpl $0xC0000001, %%eax\n\t"
201          "jb .Lready%=\n\t"             /* EAX < 0xC0000000 => no padlock.  */
202
203          "pushl %%ebx\n\t"              /* Save GOT register. */
204          "movl $0xC0000001, %%eax\n\t"  /* Ask for the extended */
205          "cpuid\n\t"                    /* feature flags.       */
206          "popl %%ebx\n\t"               /* Restore GOT register. */
207
208          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
209          "andl $0x0C, %%eax\n\t"        /* Test bits 2 and 3 to see whether */
210          "cmpl $0x0C, %%eax\n\t"        /* the RNG exists and is enabled.   */
211          "jnz .Lno_rng%=\n\t"
212          "orl $1, %0\n"                 /* Set our HWF_PADLOCK_RNG bit.  */
213
214          ".Lno_rng%=:\n\t"
215          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
216          "andl $0xC0, %%eax\n\t"        /* Test bits 6 and 7 to see whether */
217          "cmpl $0xC0, %%eax\n\t"        /* the ACE exists and is enabled.   */
218          "jnz .Lno_ace%=\n\t"
219          "orl $2, %0\n"                 /* Set our HWF_PADLOCK_AES bit.  */
220
221          ".Lno_ace%=:\n\t"
222          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
223          "andl $0xC00, %%eax\n\t"       /* Test bits 10, 11 to see whether  */
224          "cmpl $0xC00, %%eax\n\t"       /* the PHE exists and is enabled.   */
225          "jnz .Lno_phe%=\n\t"
226          "orl $4, %0\n"                 /* Set our HWF_PADLOCK_SHA bit.  */
227
228          ".Lno_phe%=:\n\t"
229          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
230          "andl $0x3000, %%eax\n\t"      /* Test bits 12, 13 to see whether  */
231          "cmpl $0x3000, %%eax\n\t"      /* MONTMUL exists and is enabled.   */
232          "jnz .Lready%=\n\t"
233          "orl $8, %0\n"                 /* Set our HWF_PADLOCK_MMUL bit.  */
234
235          ".Lready%=:\n"
236          : "+r" (hw_features)
237          :
238          : "%eax", "%ecx", "%edx", "cc"
239          );
240     }
241 #endif /*ENABLE_PADLOCK_SUPPORT*/
242   else if (!strcmp (vendor_id, "GenuineIntel"))
243     {
244       /* This is an Intel CPU.  */
245     }
246   else if (!strcmp (vendor_id, "AuthenticAMD"))
247     {
248       /* This is an AMD CPU.  */
249
250     }
251
252   /* Detect Intel features, that might also be supported by other
253      vendors.  */
254 #ifdef ENABLE_AESNI_SUPPORT
255   asm volatile
256     ("pushl %%ebx\n\t"          /* Save GOT register.  */
257      "movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
258      "cpuid\n"
259      "popl %%ebx\n\t"           /* Restore GOT register. */
260      "testl $0x02000000, %%ecx\n\t" /* Test bit 25.  */
261      "jz .Lno_aes%=\n\t"            /* No AES support.  */
262      "orl $256, %0\n"               /* Set our HWF_INTEL_AES bit.  */
263
264      ".Lno_aes%=:\n"
265      : "+r" (hw_features)
266      :
267      : "%eax", "%ecx", "%edx", "cc"
268      );
269 #endif /*ENABLE_AESNI_SUPPORT*/
270
271 }
272 #endif /* __i386__ && SIZEOF_UNSIGNED_LONG == 4 && __GNUC__ */
273
274
275 /* Detect the available hardware features.  This function is called
276    once right at startup and we assume that no other threads are
277    running.  */
278 void
279 _gcry_detect_hw_features (unsigned int disabled_features)
280 {
281   hw_features = 0;
282
283   if (fips_mode ())
284     return; /* Hardware support is not to be evaluated.  */
285
286 #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4
287 # ifdef __GNUC__
288   {
289     detect_ia32_gnuc ();
290   }
291 # endif
292 #elif defined (__i386__) && SIZEOF_UNSIGNED_LONG == 8
293 # ifdef __GNUC__
294   {
295   }
296 # endif
297 #elif defined (__x86_64__)
298 # ifdef __GNUC__
299   {
300     detect_x86_64_gnuc ();
301   }
302 # endif
303 #endif
304
305   hw_features &= ~disabled_features;
306 }