62600aa9d6b8cd97dd2c3b7bb73bc3ea71d8d9ca
[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 #undef HAS_X86_CPUID
44
45 #if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
46 #define HAS_X86_CPUID 1
47
48 static int
49 is_cpuid_available(void)
50 {
51   int has_cpuid = 0;
52
53   /* Detect the CPUID feature by testing some undefined behaviour (16
54      vs 32 bit pushf/popf). */
55   asm volatile
56     ("pushf\n\t"                 /* Copy flags to EAX.  */
57      "popl %%eax\n\t"
58      "movl %%eax, %%ecx\n\t"     /* Save flags into ECX.  */
59      "xorl $0x200000, %%eax\n\t" /* Toggle ID bit and copy it to the flags.  */
60      "pushl %%eax\n\t"
61      "popf\n\t"
62      "pushf\n\t"                 /* Copy changed flags again to EAX.  */
63      "popl %%eax\n\t"
64      "pushl %%ecx\n\t"           /* Restore flags from ECX.  */
65      "popf\n\t"
66      "xorl %%eax, %%ecx\n\t"     /* Compare flags against saved flags.  */
67      "jz .Lno_cpuid%=\n\t"       /* Toggling did not work, thus no CPUID.  */
68      "movl $1, %0\n"             /* Worked. true -> HAS_CPUID.  */
69      ".Lno_cpuid%=:\n\t"
70      : "+r" (has_cpuid)
71      :
72      : "%eax", "%ecx", "cc"
73      );
74
75   return has_cpuid;
76 }
77
78 static void
79 get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
80           unsigned int *ecx, unsigned int *edx)
81 {
82   unsigned int regs[4];
83
84   asm volatile
85     ("pushl %%ebx\n\t"           /* Save GOT register.  */
86      "cpuid\n\t"
87      "movl %%ebx, %1\n\t"
88      "popl %%ebx\n\t"            /* Restore GOT register. */
89      : "=a" (regs[0]), "=r" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
90      : "0" (in)
91      : "cc"
92      );
93
94   if (eax)
95     *eax = regs[0];
96   if (ebx)
97     *ebx = regs[1];
98   if (ecx)
99     *ecx = regs[2];
100   if (edx)
101     *edx = regs[3];
102 }
103 #endif /* i386 && GNUC */
104
105
106 #if defined (__x86_64__) && defined (__GNUC__)
107 #define HAS_X86_CPUID 1
108
109 static int
110 is_cpuid_available(void)
111 {
112   return 1;
113 }
114
115 static void
116 get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
117           unsigned int *ecx, unsigned int *edx)
118 {
119   unsigned int regs[4];
120
121   asm volatile
122     ("cpuid\n\t"
123      : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
124      : "0" (in)
125      : "cc"
126      );
127
128   if (eax)
129     *eax = regs[0];
130   if (ebx)
131     *ebx = regs[1];
132   if (ecx)
133     *ecx = regs[2];
134   if (edx)
135     *edx = regs[3];
136 }
137 #endif /* x86-64 && GNUC */
138
139
140 #ifdef HAS_X86_CPUID
141 static void
142 detect_x86_gnuc (void)
143 {
144   char vendor_id[12+1];
145   unsigned int features;
146
147   if (!is_cpuid_available())
148     return;
149
150   get_cpuid(0, NULL,
151             (unsigned int *)&vendor_id[0],
152             (unsigned int *)&vendor_id[8],
153             (unsigned int *)&vendor_id[4]);
154   vendor_id[12] = 0;
155
156   if (0)
157     ; /* Just to make "else if" and ifdef macros look pretty.  */
158 #ifdef ENABLE_PADLOCK_SUPPORT
159   else if (!strcmp (vendor_id, "CentaurHauls"))
160     {
161       /* This is a VIA CPU.  Check what PadLock features we have.  */
162
163       /* Check for extended centaur (EAX).  */
164       get_cpuid(0xC0000000, &features, NULL, NULL, NULL);
165
166       /* Has extended centaur features? */
167       if (features > 0xC0000000)
168         {
169            /* Ask for the extended feature flags (EDX). */
170            get_cpuid(0xC0000001, NULL, NULL, NULL, &features);
171
172            /* Test bits 2 and 3 to see whether the RNG exists and is enabled. */
173            if ((features & 0x0C) == 0x0C)
174              hw_features |= HWF_PADLOCK_RNG;
175
176            /* Test bits 6 and 7 to see whether the ACE exists and is enabled. */
177            if ((features & 0xC0) == 0xC0)
178              hw_features |= HWF_PADLOCK_AES;
179
180            /* Test bits 10 and 11 to see whether the PHE exists and is
181               enabled.  */
182            if ((features & 0xC00) == 0xC00)
183              hw_features |= HWF_PADLOCK_SHA;
184
185            /* Test bits 12 and 13 to see whether the MONTMUL exists and is
186               enabled.  */
187            if ((features & 0x3000) == 0x3000)
188              hw_features |= HWF_PADLOCK_MMUL;
189         }
190     }
191 #endif /*ENABLE_PADLOCK_SUPPORT*/
192   else if (!strcmp (vendor_id, "GenuineIntel"))
193     {
194       /* This is an Intel CPU.  */
195     }
196   else if (!strcmp (vendor_id, "AuthenticAMD"))
197     {
198       /* This is an AMD CPU.  */
199     }
200
201   /* Detect Intel features, that might also be supported by other
202      vendors.  */
203
204   /* Get CPU info and Intel feature flags (ECX).  */
205   get_cpuid(1, NULL, NULL, &features, NULL);
206
207 #ifdef ENABLE_AESNI_SUPPORT
208   /* Test bit 25 for AES-NI.  */
209   if (features & 0x02000000)
210      hw_features |= HWF_INTEL_AESNI;
211 #endif /*ENABLE_AESNI_SUPPORT*/
212 #ifdef ENABLE_DRNG_SUPPORT
213   /* Test bit 30 for RDRAND.  */
214   if (features & 0x40000000)
215      hw_features |= HWF_INTEL_RDRAND;
216 #endif /*ENABLE_DRNG_SUPPORT*/
217
218 }
219 #endif /* HAS_X86_CPUID */
220
221
222 /* Detect the available hardware features.  This function is called
223    once right at startup and we assume that no other threads are
224    running.  */
225 void
226 _gcry_detect_hw_features (unsigned int disabled_features)
227 {
228   hw_features = 0;
229
230   if (fips_mode ())
231     return; /* Hardware support is not to be evaluated.  */
232
233 #if HAS_X86_CPUID
234   {
235     detect_x86_gnuc ();
236   }
237 #endif /* HAS_X86_CPUID */
238
239   hw_features &= ~disabled_features;
240 }