Add support for using DRNG random number generator
[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 #ifdef ENABLE_DRNG_SUPPORT
138   asm volatile
139     ("movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
140      "cpuid\n"
141      "testl $0x40000000, %%ecx\n\t" /* Test bit 30.  */
142      "jz .Lno_rdrand%=\n\t"         /* No RDRAND support.  */
143      "orl $512, %0\n"               /* Set our HWF_INTEL_RDRAND bit.  */
144
145      ".Lno_rdrand%=:\n"
146      : "+r" (hw_features)
147      :
148      : "%eax", "%ebx", "%ecx", "%edx", "cc"
149      );
150 #endif /* #ifdef ENABLE_DRNG_SUPPORT */
151
152 }
153 #endif /* __x86_64__ && __GNUC__ */
154
155 #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
156 static void
157 detect_ia32_gnuc (void)
158 {
159   /* The code here is only useful for the PadLock engine thus we don't
160      build it if that support has been disabled.  */
161   int has_cpuid = 0;
162   char vendor_id[12+1];
163
164   /* Detect the CPUID feature by testing some undefined behaviour (16
165      vs 32 bit pushf/popf). */
166   asm volatile
167     ("pushf\n\t"                 /* Copy flags to EAX.  */
168      "popl %%eax\n\t"
169      "movl %%eax, %%ecx\n\t"     /* Save flags into ECX.  */
170      "xorl $0x200000, %%eax\n\t" /* Toggle ID bit and copy it to the flags.  */
171      "pushl %%eax\n\t"
172      "popf\n\t"
173      "pushf\n\t"                 /* Copy changed flags again to EAX.  */
174      "popl %%eax\n\t"
175      "pushl %%ecx\n\t"           /* Restore flags from ECX.  */
176      "popf\n\t"
177      "xorl %%eax, %%ecx\n\t"     /* Compare flags against saved flags.  */
178      "jz .Lno_cpuid%=\n\t"       /* Toggling did not work, thus no CPUID.  */
179      "movl $1, %0\n"             /* Worked. true -> HAS_CPUID.  */
180      ".Lno_cpuid%=:\n\t"
181      : "+r" (has_cpuid)
182      :
183      : "%eax", "%ecx", "cc"
184      );
185
186   if (!has_cpuid)
187     return;  /* No way.  */
188
189   asm volatile
190     ("pushl %%ebx\n\t"           /* Save GOT register.  */
191      "xorl  %%eax, %%eax\n\t"    /* 0 -> EAX.  */
192      "cpuid\n\t"                 /* Get vendor ID.  */
193      "movl  %%ebx, (%0)\n\t"     /* EBX,EDX,ECX -> VENDOR_ID.  */
194      "movl  %%edx, 4(%0)\n\t"
195      "movl  %%ecx, 8(%0)\n\t"
196      "popl  %%ebx\n"
197      :
198      : "S" (&vendor_id[0])
199      : "%eax", "%ecx", "%edx", "cc"
200      );
201   vendor_id[12] = 0;
202
203   if (0)
204     ; /* Just to make "else if" and ifdef macros look pretty.  */
205 #ifdef ENABLE_PADLOCK_SUPPORT
206   else if (!strcmp (vendor_id, "CentaurHauls"))
207     {
208       /* This is a VIA CPU.  Check what PadLock features we have.  */
209       asm volatile
210         ("pushl %%ebx\n\t"              /* Save GOT register.  */
211          "movl $0xC0000000, %%eax\n\t"  /* Check for extended centaur  */
212          "cpuid\n\t"                    /* feature flags.              */
213          "popl %%ebx\n\t"               /* Restore GOT register. */
214          "cmpl $0xC0000001, %%eax\n\t"
215          "jb .Lready%=\n\t"             /* EAX < 0xC0000000 => no padlock.  */
216
217          "pushl %%ebx\n\t"              /* Save GOT register. */
218          "movl $0xC0000001, %%eax\n\t"  /* Ask for the extended */
219          "cpuid\n\t"                    /* feature flags.       */
220          "popl %%ebx\n\t"               /* Restore GOT register. */
221
222          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
223          "andl $0x0C, %%eax\n\t"        /* Test bits 2 and 3 to see whether */
224          "cmpl $0x0C, %%eax\n\t"        /* the RNG exists and is enabled.   */
225          "jnz .Lno_rng%=\n\t"
226          "orl $1, %0\n"                 /* Set our HWF_PADLOCK_RNG bit.  */
227
228          ".Lno_rng%=:\n\t"
229          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
230          "andl $0xC0, %%eax\n\t"        /* Test bits 6 and 7 to see whether */
231          "cmpl $0xC0, %%eax\n\t"        /* the ACE exists and is enabled.   */
232          "jnz .Lno_ace%=\n\t"
233          "orl $2, %0\n"                 /* Set our HWF_PADLOCK_AES bit.  */
234
235          ".Lno_ace%=:\n\t"
236          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
237          "andl $0xC00, %%eax\n\t"       /* Test bits 10, 11 to see whether  */
238          "cmpl $0xC00, %%eax\n\t"       /* the PHE exists and is enabled.   */
239          "jnz .Lno_phe%=\n\t"
240          "orl $4, %0\n"                 /* Set our HWF_PADLOCK_SHA bit.  */
241
242          ".Lno_phe%=:\n\t"
243          "movl %%edx, %%eax\n\t"        /* Take copy of feature flags.  */
244          "andl $0x3000, %%eax\n\t"      /* Test bits 12, 13 to see whether  */
245          "cmpl $0x3000, %%eax\n\t"      /* MONTMUL exists and is enabled.   */
246          "jnz .Lready%=\n\t"
247          "orl $8, %0\n"                 /* Set our HWF_PADLOCK_MMUL bit.  */
248
249          ".Lready%=:\n"
250          : "+r" (hw_features)
251          :
252          : "%eax", "%ecx", "%edx", "cc"
253          );
254     }
255 #endif /*ENABLE_PADLOCK_SUPPORT*/
256   else if (!strcmp (vendor_id, "GenuineIntel"))
257     {
258       /* This is an Intel CPU.  */
259     }
260   else if (!strcmp (vendor_id, "AuthenticAMD"))
261     {
262       /* This is an AMD CPU.  */
263
264     }
265
266   /* Detect Intel features, that might also be supported by other
267      vendors.  */
268 #ifdef ENABLE_AESNI_SUPPORT
269   asm volatile
270     ("pushl %%ebx\n\t"          /* Save GOT register.  */
271      "movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
272      "cpuid\n"
273      "popl %%ebx\n\t"           /* Restore GOT register. */
274      "testl $0x02000000, %%ecx\n\t" /* Test bit 25.  */
275      "jz .Lno_aes%=\n\t"            /* No AES support.  */
276      "orl $256, %0\n"               /* Set our HWF_INTEL_AES bit.  */
277
278      ".Lno_aes%=:\n"
279      : "+r" (hw_features)
280      :
281      : "%eax", "%ecx", "%edx", "cc"
282      );
283 #endif /*ENABLE_AESNI_SUPPORT*/
284 #ifdef ENABLE_DRNG_SUPPORT
285   asm volatile
286     ("pushl %%ebx\n\t"          /* Save GOT register.  */
287      "movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
288      "cpuid\n"
289      "popl %%ebx\n\t"           /* Restore GOT register. */
290      "testl $0x40000000, %%ecx\n\t" /* Test bit 30.  */
291      "jz .Lno_rdrand%=\n\t"         /* No RDRAND support.  */
292      "orl $512, %0\n"               /* Set our HWF_INTEL_RDRAND bit.  */
293
294      ".Lno_rdrand%=:\n"
295      : "+r" (hw_features)
296      :
297      : "%eax", "%ecx", "%edx", "cc"
298      );
299 #endif /*ENABLE_DRNG_SUPPORT*/
300
301 }
302 #endif /* __i386__ && SIZEOF_UNSIGNED_LONG == 4 && __GNUC__ */
303
304
305 /* Detect the available hardware features.  This function is called
306    once right at startup and we assume that no other threads are
307    running.  */
308 void
309 _gcry_detect_hw_features (unsigned int disabled_features)
310 {
311   hw_features = 0;
312
313   if (fips_mode ())
314     return; /* Hardware support is not to be evaluated.  */
315
316 #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4
317 # ifdef __GNUC__
318   {
319     detect_ia32_gnuc ();
320   }
321 # endif
322 #elif defined (__i386__) && SIZEOF_UNSIGNED_LONG == 8
323 # ifdef __GNUC__
324   {
325   }
326 # endif
327 #elif defined (__x86_64__)
328 # ifdef __GNUC__
329   {
330     detect_x86_64_gnuc ();
331   }
332 # endif
333 #endif
334
335   hw_features &= ~disabled_features;
336 }