[efi] Add EFI entropy source
[ipxe.git] / src / arch / x86 / interface / efi / efi_entropy.c
1 /*
2 * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <errno.h>
27 #include <ipxe/entropy.h>
28 #include <ipxe/efi/efi.h>
29
30 /** @file
31 *
32 * EFI entropy source
33 *
34 */
35
36 /** Time (in 100ns units) to delay waiting for timer tick
37 *
38 * In theory, UEFI allows us to specify a trigger time of zero to
39 * simply wait for the next timer tick. In practice, specifying zero
40 * seems to often return immediately, which produces almost no
41 * entropy. Specify a delay of 1000ns to try to force an existent
42 * delay.
43 */
44 #define EFI_ENTROPY_TRIGGER_TIME 10
45
46 /** Event used to wait for timer tick */
47 static EFI_EVENT tick;
48
49 /**
50 * Enable entropy gathering
51 *
52 * @ret rc Return status code
53 */
54 static int efi_entropy_enable ( void ) {
55 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
56 EFI_STATUS efirc;
57 int rc;
58
59 /* Create timer tick event */
60 if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL,
61 &tick ) ) != 0 ) {
62 rc = -EEFI ( efirc );
63 DBGC ( &tick, "ENTROPY could not create event: %s\n",
64 strerror ( rc ) );
65 return rc;
66 }
67
68 return 0;
69 }
70
71 /**
72 * Disable entropy gathering
73 *
74 */
75 static void efi_entropy_disable ( void ) {
76 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
77
78 /* Close timer tick event */
79 bs->CloseEvent ( tick );
80 }
81
82 /**
83 * Wait for an RTC tick
84 *
85 * @ret low TSC low-order bits, or negative error
86 */
87 static int efi_entropy_tick ( void ) {
88 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
89 UINTN index;
90 uint16_t low;
91 uint32_t discard_d;
92 EFI_STATUS efirc;
93 int rc;
94
95 /* Wait for next timer tick */
96 if ( ( efirc = bs->SetTimer ( tick, TimerRelative,
97 EFI_ENTROPY_TRIGGER_TIME ) ) != 0 ) {
98 rc = -EEFI ( efirc );
99 DBGC ( &tick, "ENTROPY could not set timer: %s\n",
100 strerror ( rc ) );
101 return rc;
102 }
103 if ( ( efirc = bs->WaitForEvent ( 1, &tick, &index ) ) != 0 ) {
104 rc = -EEFI ( efirc );
105 DBGC ( &tick, "ENTROPY could not wait for timer tick: %s\n",
106 strerror ( rc ) );
107 return rc;
108 }
109
110 /* Get current TSC low-order bits */
111 __asm__ __volatile__ ( "rdtsc" : "=a" ( low ), "=d" ( discard_d ) );
112
113 return low;
114 }
115
116 /**
117 * Get noise sample
118 *
119 * @ret noise Noise sample
120 * @ret rc Return status code
121 */
122 static int efi_get_noise ( noise_sample_t *noise ) {
123 int before;
124 int after;
125 int rc;
126
127 /* Wait for a timer tick */
128 before = efi_entropy_tick();
129 if ( before < 0 ) {
130 rc = before;
131 return rc;
132 }
133
134 /* Wait for another timer tick */
135 after = efi_entropy_tick();
136 if ( after < 0 ) {
137 rc = after;
138 return rc;
139 }
140
141 /* Use TSC delta as noise sample */
142 *noise = ( after - before );
143
144 return 0;
145 }
146
147 PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample );
148 PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable );
149 PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable );
150 PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise );