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/entropy.h>
40 
41 /** Maximum time to wait for an RTC interrupt, in milliseconds */
42 #define RTC_MAX_WAIT_MS 100
43 
44 /** RTC interrupt handler */
45 extern void rtc_isr ( void );
46 
47 /** Previous RTC interrupt handler */
48 static struct segoff rtc_old_handler;
49 
50 /** Flag set by RTC interrupt handler */
51 extern volatile uint8_t __text16 ( rtc_flag );
52 #define rtc_flag __use_text16 ( rtc_flag )
53 
54 /**
55  * Hook RTC interrupt handler
56  *
57  */
58 static void rtc_hook_isr ( void ) {
59 
60  /* RTC interrupt handler */
62  TEXT16_CODE ( "\nrtc_isr:\n\t"
63  /* Preserve registers */
64  "pushw %%ax\n\t"
65  /* Set "interrupt triggered" flag */
66  "movb $0x01, %%cs:rtc_flag\n\t"
67  /* Read RTC status register C to
68  * acknowledge interrupt
69  */
70  "movb %2, %%al\n\t"
71  "outb %%al, %0\n\t"
72  "inb %1\n\t"
73  /* Send EOI */
74  "movb $0x20, %%al\n\t"
75  "outb %%al, $0xa0\n\t"
76  "outb %%al, $0x20\n\t"
77  /* Restore registers and return */
78  "popw %%ax\n\t"
79  "iret\n\t"
80  "\nrtc_flag:\n\t"
81  ".byte 0\n\t" )
82  :
83  : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
84  "i" ( RTC_STATUS_C ) );
85 
87 }
88 
89 /**
90  * Unhook RTC interrupt handler
91  *
92  */
93 static void rtc_unhook_isr ( void ) {
94  int rc;
95 
97  &rtc_old_handler );
98  assert ( rc == 0 ); /* Should always be able to unhook */
99 }
100 
101 /**
102  * Enable RTC interrupts
103  *
104  */
105 static void rtc_enable_int ( void ) {
106  uint8_t status_b;
107 
108  /* Clear any stale pending interrupts via status register C */
110  inb ( CMOS_DATA );
111 
112  /* Set Periodic Interrupt Enable bit in status register B */
114  status_b = inb ( CMOS_DATA );
116  outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
117 
118  /* Re-enable NMI and reset to default address */
120  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
121 }
122 
123 /**
124  * Disable RTC interrupts
125  *
126  */
127 static void rtc_disable_int ( void ) {
128  uint8_t status_b;
129 
130  /* Clear Periodic Interrupt Enable bit in status register B */
132  status_b = inb ( CMOS_DATA );
134  outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
135 
136  /* Re-enable NMI and reset to default address */
138  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
139 }
140 
141 /**
142  * Check that entropy gathering is functional
143  *
144  * @ret rc Return status code
145  */
146 static int rtc_entropy_check ( void ) {
147  unsigned int i;
148 
149  /* Check that RTC interrupts are working */
150  rtc_flag = 0;
151  for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
152 
153  /* Allow interrupts to occur */
154  __asm__ __volatile__ ( "sti\n\t"
155  "nop\n\t"
156  "nop\n\t"
157  "cli\n\t" );
158 
159  /* Check for RTC interrupt flag */
160  if ( rtc_flag )
161  return 0;
162 
163  /* Delay */
164  mdelay ( 1 );
165  }
166 
167  DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
168  return -ETIMEDOUT;
169 }
170 
171 /**
172  * Enable entropy gathering
173  *
174  * @ret rc Return status code
175  */
176 static int rtc_entropy_enable ( void ) {
177  int rc;
178 
179  /* Hook ISR and enable RTC interrupts */
180  rtc_hook_isr();
181  enable_irq ( RTC_IRQ );
182  rtc_enable_int();
183 
184  /* Check that RTC interrupts are working */
185  if ( ( rc = rtc_entropy_check() ) != 0 )
186  goto err_check;
187 
188  return 0;
189 
190  err_check:
191  rtc_disable_int();
192  disable_irq ( RTC_IRQ );
193  rtc_unhook_isr();
194  return rc;
195 }
196 
197 /**
198  * Disable entropy gathering
199  *
200  */
201 static void rtc_entropy_disable ( void ) {
202 
203  /* Disable RTC interrupts and unhook ISR */
204  rtc_disable_int();
205  disable_irq ( RTC_IRQ );
206  rtc_unhook_isr();
207 }
208 
209 /**
210  * Measure a single RTC tick
211  *
212  * @ret delta Length of RTC tick (in TSC units)
213  */
214 uint8_t rtc_sample ( void ) {
215  uint32_t before;
216  uint32_t after;
217  uint32_t temp;
218 
220  REAL_CODE ( /* Enable interrupts */
221  "sti\n\t"
222  /* Wait for RTC interrupt */
223  "movb %b2, %%cs:rtc_flag\n\t"
224  "\n1:\n\t"
225  "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
226  "testb %b2, %b2\n\t"
227  "jz 1b\n\t"
228  /* Read "before" TSC */
229  "rdtsc\n\t"
230  /* Store "before" TSC on stack */
231  "pushl %0\n\t"
232  /* Wait for another RTC interrupt */
233  "xorb %b2, %b2\n\t"
234  "movb %b2, %%cs:rtc_flag\n\t"
235  "\n1:\n\t"
236  "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
237  "testb %b2, %b2\n\t"
238  "jz 1b\n\t"
239  /* Read "after" TSC */
240  "rdtsc\n\t"
241  /* Retrieve "before" TSC on stack */
242  "popl %1\n\t"
243  /* Disable interrupts */
244  "cli\n\t"
245  )
246  : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
247  : "2" ( 0 ) );
248 
249  return ( after - before );
250 }
251 
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
CMOS Real-Time Clock (RTC)
#define RTC_IRQ
RTC IRQ.
Definition: rtc.h:21
void entropy_disable(void)
Disable entropy gathering.
#define CMOS_DEFAULT_ADDRESS
CMOS default address.
Definition: rtc.h:81
Error codes.
min_entropy_t min_entropy_per_sample(void)
min-entropy per sample
#define DBGC(...)
Definition: compiler.h:505
int get_noise(noise_sample_t *noise)
Get noise sample.
#define disable_irq(x)
Definition: pic8259.h:52
#define RTC_STATUS_B
RTC status register B.
Definition: rtc.h:63
unsigned long intptr_t
Definition: stdint.h:21
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.
static struct segoff rtc_old_handler
Previous RTC interrupt handler.
Definition: rtc_entropy.c:48
#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
uint8_t rtc_sample(void)
Measure a single RTC tick.
Definition: rtc_entropy.c:214
static void rtc_enable_int(void)
Enable RTC interrupts.
Definition: rtc_entropy.c:105
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
PROVIDE_ENTROPY(rtc, entropy_enable, rtc_entropy_enable)
static int rtc_entropy_enable(void)
Enable entropy gathering.
Definition: rtc_entropy.c:176
#define rtc_flag
Definition: rtc_entropy.c:52
int entropy_enable(void)
Enable entropy gathering.
static void rtc_hook_isr(void)
Hook RTC interrupt handler.
Definition: rtc_entropy.c:58
unsigned char uint8_t
Definition: stdint.h:10
PROVIDE_ENTROPY_INLINE(rtc, min_entropy_per_sample)
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
static int rtc_entropy_check(void)
Check that entropy gathering is functional.
Definition: rtc_entropy.c:146
__asm__ __volatile__("\n1:\n\t" "movb -1(%2,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %3, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
#define RTC_MAX_WAIT_MS
Maximum time to wait for an RTC interrupt, in milliseconds.
Definition: rtc_entropy.c:42
#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:127
#define RTC_STATUS_C
RTC status register C.
Definition: rtc.h:75
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static void rtc_unhook_isr(void)
Unhook RTC interrupt handler.
Definition: rtc_entropy.c:93
#define CMOS_ADDRESS
CMOS/RTC address (and NMI) register.
Definition: rtc.h:27
#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 void rtc_entropy_disable(void)
Disable entropy gathering.
Definition: rtc_entropy.c:201
#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