iPXE
rtc_entropy.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 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 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 /** @file
27  *
28  * RTC-based entropy source
29  *
30  */
31 
32 #include <stdint.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <biosint.h>
37 #include <pic8259.h>
38 #include <rtc.h>
39 #include <ipxe/cpuid.h>
40 #include <ipxe/entropy.h>
41 
42 struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL );
43 
44 /** Maximum time to wait for an RTC interrupt, in milliseconds */
45 #define RTC_MAX_WAIT_MS 100
46 
47 /** Number of RTC interrupts to check for */
48 #define RTC_CHECK_COUNT 3
49 
50 /** RTC interrupt handler */
51 extern void rtc_isr ( void );
52 
53 /** Previous RTC interrupt handler */
54 static struct segoff rtc_old_handler;
55 
56 /** Flag set by RTC interrupt handler */
57 extern volatile uint8_t __text16 ( rtc_flag );
58 #define rtc_flag __use_text16 ( rtc_flag )
59 
60 /**
61  * Hook RTC interrupt handler
62  *
63  */
64 static void rtc_hook_isr ( void ) {
65 
66  /* RTC interrupt handler */
68  TEXT16_CODE ( "\nrtc_isr:\n\t"
69  /* Preserve registers */
70  "pushw %%ax\n\t"
71  /* Set "interrupt triggered" flag */
72  "movb $0x01, %%cs:rtc_flag\n\t"
73  /* Read RTC status register C to
74  * acknowledge interrupt
75  */
76  "movb %2, %%al\n\t"
77  "outb %%al, %0\n\t"
78  "inb %1\n\t"
79  /* Send EOI */
80  "movb $0x20, %%al\n\t"
81  "outb %%al, $0xa0\n\t"
82  "outb %%al, $0x20\n\t"
83  /* Restore registers and return */
84  "popw %%ax\n\t"
85  "iret\n\t"
86  "\nrtc_flag:\n\t"
87  ".byte 0\n\t" )
88  :
89  : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
90  "i" ( RTC_STATUS_C ) );
91 
93 }
94 
95 /**
96  * Unhook RTC interrupt handler
97  *
98  */
99 static void rtc_unhook_isr ( void ) {
100  int rc;
101 
103  &rtc_old_handler );
104  assert ( rc == 0 ); /* Should always be able to unhook */
105 }
106 
107 /**
108  * Enable RTC interrupts
109  *
110  */
111 static void rtc_enable_int ( void ) {
112  uint8_t status_b;
113 
114  /* Clear any stale pending interrupts via status register C */
116  inb ( CMOS_DATA );
117 
118  /* Set Periodic Interrupt Enable bit in status register B */
120  status_b = inb ( CMOS_DATA );
122  outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
123 
124  /* Re-enable NMI and reset to default address */
126  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
127 }
128 
129 /**
130  * Disable RTC interrupts
131  *
132  */
133 static void rtc_disable_int ( void ) {
134  uint8_t status_b;
135 
136  /* Clear Periodic Interrupt Enable bit in status register B */
138  status_b = inb ( CMOS_DATA );
140  outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
141 
142  /* Re-enable NMI and reset to default address */
144  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
145 }
146 
147 /**
148  * Check that entropy gathering is functional
149  *
150  * @ret rc Return status code
151  */
152 static int rtc_entropy_check ( void ) {
153  unsigned int count = 0;
154  unsigned int i;
155 
156  /* Check that RTC interrupts are working */
157  rtc_flag = 0;
158  for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
159 
160  /* Allow interrupts to occur */
161  __asm__ __volatile__ ( "sti\n\t"
162  "nop\n\t"
163  "nop\n\t"
164  "cli\n\t" );
165 
166  /* Check for RTC interrupt flag */
167  if ( rtc_flag ) {
168  rtc_flag = 0;
169  if ( ++count >= RTC_CHECK_COUNT )
170  return 0;
171  }
172 
173  /* Delay */
174  mdelay ( 1 );
175  }
176 
177  DBGC ( &rtc_flag, "RTC timed out waiting for interrupt %d/%d\n",
178  ( count + 1 ), RTC_CHECK_COUNT );
179  return -ETIMEDOUT;
180 }
181 
182 /**
183  * Enable entropy gathering
184  *
185  * @ret rc Return status code
186  */
187 static int rtc_entropy_enable ( void ) {
188  struct x86_features features;
189  int rc;
190 
191  /* Check that TSC is supported */
192  x86_features ( &features );
193  if ( ! ( features.intel.edx & CPUID_FEATURES_INTEL_EDX_TSC ) ) {
194  DBGC ( &rtc_flag, "RTC has no TSC\n" );
195  rc = -ENOTSUP;
196  goto err_no_tsc;
197  }
198 
199  /* Hook ISR and enable RTC interrupts */
200  rtc_hook_isr();
201  enable_irq ( RTC_IRQ );
202  rtc_enable_int();
203 
204  /* Check that RTC interrupts are working */
205  if ( ( rc = rtc_entropy_check() ) != 0 )
206  goto err_check;
207 
208  /* The min-entropy has been measured on several platforms
209  * using the entropy_sample test code. Modelling the samples
210  * as independent, and using a confidence level of 99.99%, the
211  * measurements were as follows:
212  *
213  * qemu-kvm : 7.38 bits
214  * VMware : 7.46 bits
215  * Physical hardware : 2.67 bits
216  *
217  * We choose the lowest of these (2.67 bits) and apply a 50%
218  * safety margin to allow for some potential non-independence
219  * of samples.
220  */
221  entropy_init ( &rtc_entropy, MIN_ENTROPY ( 1.3 ) );
222 
223  return 0;
224 
225  err_check:
226  rtc_disable_int();
227  disable_irq ( RTC_IRQ );
228  rtc_unhook_isr();
229  err_no_tsc:
230  return rc;
231 }
232 
233 /**
234  * Disable entropy gathering
235  *
236  */
237 static void rtc_entropy_disable ( void ) {
238 
239  /* Disable RTC interrupts and unhook ISR */
240  rtc_disable_int();
241  disable_irq ( RTC_IRQ );
242  rtc_unhook_isr();
243 }
244 
245 /**
246  * Get noise sample
247  *
248  * @ret noise Noise sample
249  * @ret rc Return status code
250  */
251 static int rtc_get_noise ( noise_sample_t *noise ) {
253  uint32_t after;
254  uint32_t temp;
255 
257  REAL_CODE ( /* Enable interrupts */
258  "sti\n\t"
259  /* Wait for RTC interrupt */
260  "movb %b2, %%cs:rtc_flag\n\t"
261  "\n1:\n\t"
262  "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
263  "testb %b2, %b2\n\t"
264  "jz 1b\n\t"
265  /* Read "before" TSC */
266  "rdtsc\n\t"
267  /* Store "before" TSC on stack */
268  "pushl %0\n\t"
269  /* Wait for another RTC interrupt */
270  "xorb %b2, %b2\n\t"
271  "movb %b2, %%cs:rtc_flag\n\t"
272  "\n1:\n\t"
273  "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
274  "testb %b2, %b2\n\t"
275  "jz 1b\n\t"
276  /* Read "after" TSC */
277  "rdtsc\n\t"
278  /* Retrieve "before" TSC on stack */
279  "popl %1\n\t"
280  /* Disable interrupts */
281  "cli\n\t"
282  )
283  : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
284  : "2" ( 0 ) );
285 
286  *noise = ( after - before );
287  return 0;
288 }
289 
290 /** RTC entropy source */
291 struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ) = {
292  .name = "rtc",
293  .enable = rtc_entropy_enable,
294  .disable = rtc_entropy_disable,
295  .get_noise = rtc_get_noise,
296 };
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
CMOS Real-Time Clock (RTC)
#define RTC_IRQ
RTC IRQ.
Definition: rtc.h:21
#define CMOS_DEFAULT_ADDRESS
CMOS default address.
Definition: rtc.h:81
Error codes.
#define ENTROPY_NORMAL
Normal entropy source.
Definition: entropy.h:180
#define DBGC(...)
Definition: compiler.h:505
int32_t before
Initial microcode version.
Definition: ucode.h:16
void x86_features(struct x86_features *features)
Get x86 CPU features.
Definition: cpuid.c:163
#define disable_irq(x)
Definition: pic8259.h:52
#define RTC_STATUS_B
RTC status register B.
Definition: rtc.h:63
x86 CPU features
Definition: cpuid.h:23
unsigned long intptr_t
Definition: stdint.h:21
An entropy source.
Definition: entropy.h:116
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
void hook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Hook INT vector.
Definition: biosint.c:24
void rtc_isr(void)
RTC interrupt handler.
struct entropy_source rtc_entropy __entropy_source(ENTROPY_NORMAL)
RTC entropy source.
const char * name
Name.
Definition: entropy.h:118
static struct segoff rtc_old_handler
Previous RTC interrupt handler.
Definition: rtc_entropy.c:54
#define CMOS_DATA
CMOS/RTC data register.
Definition: rtc.h:33
int unhook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Unhook INT vector.
Definition: biosint.c:69
static void rtc_enable_int(void)
Enable RTC interrupts.
Definition: rtc_entropy.c:111
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
#define RTC_CHECK_COUNT
Number of RTC interrupts to check for.
Definition: rtc_entropy.c:48
static int rtc_entropy_enable(void)
Enable entropy gathering.
Definition: rtc_entropy.c:187
#define rtc_flag
Definition: rtc_entropy.c:58
x86 CPU feature detection
uint32_t features
Supported features.
Definition: ena.h:16
static void rtc_hook_isr(void)
Hook RTC interrupt handler.
Definition: rtc_entropy.c:64
unsigned char uint8_t
Definition: stdint.h:10
__asm__ __volatile__("\n1:\n\t" "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %4, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c), "+m"(*value) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
uint8_t inb(volatile uint8_t *io_addr)
Read byte from I/O-mapped device.
#define RTC_INT
RTC interrupt vector.
Definition: rtc.h:24
unsigned int uint32_t
Definition: stdint.h:12
#define MIN_ENTROPY(bits)
Construct a min-entropy fixed-point value.
Definition: entropy.h:42
static int rtc_entropy_check(void)
Check that entropy gathering is functional.
Definition: rtc_entropy.c:152
#define RTC_MAX_WAIT_MS
Maximum time to wait for an RTC interrupt, in milliseconds.
Definition: rtc_entropy.c:45
#define enable_irq(x)
Definition: pic8259.h:51
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition: timer.c:78
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
#define outb(data, io_addr)
Definition: io.h:309
static void rtc_disable_int(void)
Disable RTC interrupts.
Definition: rtc_entropy.c:133
int32_t after
Final microcode version.
Definition: ucode.h:18
uint16_t count
Number of entries.
Definition: ena.h:22
#define RTC_STATUS_C
RTC status register C.
Definition: rtc.h:75
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
uint8_t noise_sample_t
A noise sample.
Definition: entropy.h:21
static void rtc_unhook_isr(void)
Unhook RTC interrupt handler.
Definition: rtc_entropy.c:99
#define CMOS_ADDRESS
CMOS/RTC address (and NMI) register.
Definition: rtc.h:27
#define CPUID_FEATURES_INTEL_EDX_TSC
TSC is present.
Definition: cpuid.h:49
static void entropy_init(struct entropy_source *source, min_entropy_t min_entropy_per_sample)
Initialise entropy source.
Definition: entropy.h:489
#define REAL_CODE(asm_code_str)
Definition: libkir.h:226
volatile uint8_t __text16(rtc_flag)
Flag set by RTC interrupt handler.
#define ETIMEDOUT
Connection timed out.
Definition: errno.h:669
String functions.
Entropy source.
static int rtc_get_noise(noise_sample_t *noise)
Get noise sample.
Definition: rtc_entropy.c:251
static void rtc_entropy_disable(void)
Disable entropy gathering.
Definition: rtc_entropy.c:237
#define TEXT16_CODE(asm_code_str)
Definition: libkir.h:217
#define CMOS_DISABLE_NMI
NMI disable bit.
Definition: rtc.h:30
#define RTC_STATUS_B_PIE
RTC Periodic Interrupt Enabled bit.
Definition: rtc.h:72