Prepare for hardware feature detection on other platforms.
[libgcrypt.git] / src / hwf-x86.c
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      "cpuid\n\t"
81      "movl %%ebx, %1\n\t"
82      "popl %%ebx\n\t"            /* Restore GOT register. */
83      : "=a" (regs[0]), "=r" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
84      : "0" (in)
85      : "cc"
86      );
87
88   if (eax)
89     *eax = regs[0];
90   if (ebx)
91     *ebx = regs[1];
92   if (ecx)
93     *ecx = regs[2];
94   if (edx)
95     *edx = regs[3];
96 }
97 #endif /* i386 && GNUC */
98
99
100 #if defined (__x86_64__) && defined (__GNUC__)
101 # define HAS_X86_CPUID 1
102
103 static int
104 is_cpuid_available(void)
105 {
106   return 1;
107 }
108
109 static void
110 get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
111           unsigned int *ecx, unsigned int *edx)
112 {
113   unsigned int regs[4];
114
115   asm volatile
116     ("cpuid\n\t"
117      : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
118      : "0" (in)
119      : "cc"
120      );
121
122   if (eax)
123     *eax = regs[0];
124   if (ebx)
125     *ebx = regs[1];
126   if (ecx)
127     *ecx = regs[2];
128   if (edx)
129     *edx = regs[3];
130 }
131 #endif /* x86-64 && GNUC */
132
133
134 #ifdef HAS_X86_CPUID
135 static unsigned int
136 detect_x86_gnuc (void)
137 {
138   char vendor_id[12+1];
139   unsigned int features;
140   unsigned int result = 0;
141
142   if (!is_cpuid_available())
143     return 0;
144
145   get_cpuid(0, NULL,
146             (unsigned int *)&vendor_id[0],
147             (unsigned int *)&vendor_id[8],
148             (unsigned int *)&vendor_id[4]);
149   vendor_id[12] = 0;
150
151   if (0)
152     ; /* Just to make "else if" and ifdef macros look pretty.  */
153 #ifdef ENABLE_PADLOCK_SUPPORT
154   else if (!strcmp (vendor_id, "CentaurHauls"))
155     {
156       /* This is a VIA CPU.  Check what PadLock features we have.  */
157
158       /* Check for extended centaur (EAX).  */
159       get_cpuid(0xC0000000, &features, NULL, NULL, NULL);
160
161       /* Has extended centaur features? */
162       if (features > 0xC0000000)
163         {
164            /* Ask for the extended feature flags (EDX). */
165            get_cpuid(0xC0000001, NULL, NULL, NULL, &features);
166
167            /* Test bits 2 and 3 to see whether the RNG exists and is enabled. */
168            if ((features & 0x0C) == 0x0C)
169              result |= HWF_PADLOCK_RNG;
170
171            /* Test bits 6 and 7 to see whether the ACE exists and is enabled. */
172            if ((features & 0xC0) == 0xC0)
173              result |= HWF_PADLOCK_AES;
174
175            /* Test bits 10 and 11 to see whether the PHE exists and is
176               enabled.  */
177            if ((features & 0xC00) == 0xC00)
178              result |= HWF_PADLOCK_SHA;
179
180            /* Test bits 12 and 13 to see whether the MONTMUL exists and is
181               enabled.  */
182            if ((features & 0x3000) == 0x3000)
183              result |= HWF_PADLOCK_MMUL;
184         }
185     }
186 #endif /*ENABLE_PADLOCK_SUPPORT*/
187   else if (!strcmp (vendor_id, "GenuineIntel"))
188     {
189       /* This is an Intel CPU.  */
190     }
191   else if (!strcmp (vendor_id, "AuthenticAMD"))
192     {
193       /* This is an AMD CPU.  */
194     }
195
196   /* Detect Intel features, that might also be supported by other
197      vendors.  */
198
199   /* Get CPU info and Intel feature flags (ECX).  */
200   get_cpuid(1, NULL, NULL, &features, NULL);
201
202 #ifdef ENABLE_AESNI_SUPPORT
203   /* Test bit 25 for AES-NI.  */
204   if (features & 0x02000000)
205      result |= HWF_INTEL_AESNI;
206 #endif /*ENABLE_AESNI_SUPPORT*/
207 #ifdef ENABLE_DRNG_SUPPORT
208   /* Test bit 30 for RDRAND.  */
209   if (features & 0x40000000)
210      result |= HWF_INTEL_RDRAND;
211 #endif /*ENABLE_DRNG_SUPPORT*/
212
213   return result;
214 }
215 #endif /* HAS_X86_CPUID */
216
217
218 unsigned int
219 _gcry_hwf_detect_x86 (void)
220 {
221 #if defined (HAS_X86_CPUID)
222   return detect_x86_gnuc ();
223 #else
224   return 0;
225 #endif
226 }