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 /** Maximum time to wait for an RTC interrupt, in milliseconds */
43 #define RTC_MAX_WAIT_MS 100
44 
45 /** RTC interrupt handler */
46 extern void rtc_isr ( void );
47 
48 /** Previous RTC interrupt handler */
49 static struct segoff rtc_old_handler;
50 
51 /** Flag set by RTC interrupt handler */
52 extern volatile uint8_t __text16 ( rtc_flag );
53 #define rtc_flag __use_text16 ( rtc_flag )
54 
55 /**
56  * Hook RTC interrupt handler
57  *
58  */
59 static void rtc_hook_isr ( void ) {
60 
61  /* RTC interrupt handler */
63  TEXT16_CODE ( "\nrtc_isr:\n\t"
64  /* Preserve registers */
65  "pushw %%ax\n\t"
66  /* Set "interrupt triggered" flag */
67  "movb $0x01, %%cs:rtc_flag\n\t"
68  /* Read RTC status register C to
69  * acknowledge interrupt
70  */
71  "movb %2, %%al\n\t"
72  "outb %%al, %0\n\t"
73  "inb %1\n\t"
74  /* Send EOI */
75  "movb $0x20, %%al\n\t"
76  "outb %%al, $0xa0\n\t"
77  "outb %%al, $0x20\n\t"
78  /* Restore registers and return */
79  "popw %%ax\n\t"
80  "iret\n\t"
81  "\nrtc_flag:\n\t"
82  ".byte 0\n\t" )
83  :
84  : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
85  "i" ( RTC_STATUS_C ) );
86 
88 }
89 
90 /**
91  * Unhook RTC interrupt handler
92  *
93  */
94 static void rtc_unhook_isr ( void ) {
95  int rc;
96 
98  &rtc_old_handler );
99  assert ( rc == 0 ); /* Should always be able to unhook */
100 }
101 
102 /**
103  * Enable RTC interrupts
104  *
105  */
106 static void rtc_enable_int ( void ) {
107  uint8_t status_b;
108 
109  /* Clear any stale pending interrupts via status register C */
111  inb ( CMOS_DATA );
112 
113  /* Set Periodic Interrupt Enable bit in status register B */
115  status_b = inb ( CMOS_DATA );
117  outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
118 
119  /* Re-enable NMI and reset to default address */
121  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
122 }
123 
124 /**
125  * Disable RTC interrupts
126  *
127  */
128 static void rtc_disable_int ( void ) {
129  uint8_t status_b;
130 
131  /* Clear Periodic Interrupt Enable bit in status register B */
133  status_b = inb ( CMOS_DATA );
135  outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
136 
137  /* Re-enable NMI and reset to default address */
139  inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
140 }
141 
142 /**
143  * Check that entropy gathering is functional
144  *
145  * @ret rc Return status code
146  */
147 static int rtc_entropy_check ( void ) {
148  unsigned int i;
149 
150  /* Check that RTC interrupts are working */
151  rtc_flag = 0;
152  for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
153 
154  /* Allow interrupts to occur */
155  __asm__ __volatile__ ( "sti\n\t"
156  "nop\n\t"
157  "nop\n\t"
158  "cli\n\t" );
159 
160  /* Check for RTC interrupt flag */
161  if ( rtc_flag )
162  return 0;
163 
164  /* Delay */
165  mdelay ( 1 );
166  }
167 
168  DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
169  return -ETIMEDOUT;
170 }
171 
172 /**
173  * Enable entropy gathering
174  *
175  * @ret rc Return status code
176  */
177 static int rtc_entropy_enable ( void ) {
178  struct x86_features features;
179  int rc;
180 
181  /* Check that TSC is supported */
182  x86_features ( &features );
183  if ( ! ( features.intel.edx & CPUID_FEATURES_INTEL_EDX_TSC ) ) {
184  DBGC ( &rtc_flag, "RTC has no TSC\n" );
185  rc = -ENOTSUP;
186  goto err_no_tsc;
187  }
188 
189  /* Hook ISR and enable RTC interrupts */
190  rtc_hook_isr();
191  enable_irq ( RTC_IRQ );
192  rtc_enable_int();
193 
194  /* Check that RTC interrupts are working */
195  if ( ( rc = rtc_entropy_check() ) != 0 )
196  goto err_check;
197 
198  return 0;
199 
200  err_check:
201  rtc_disable_int();
202  disable_irq ( RTC_IRQ );
203  rtc_unhook_isr();
204  err_no_tsc:
205  return rc;
206 }
207 
208 /**
209  * Disable entropy gathering
210  *
211  */
212 static void rtc_entropy_disable ( void ) {
213 
214  /* Disable RTC interrupts and unhook ISR */
215  rtc_disable_int();
216  disable_irq ( RTC_IRQ );
217  rtc_unhook_isr();
218 }
219 
220 /**
221  * Measure a single RTC tick
222  *
223  * @ret delta Length of RTC tick (in TSC units)
224  */
225 uint8_t rtc_sample ( void ) {
226  uint32_t before;
227  uint32_t after;
228  uint32_t temp;
229 
231  REAL_CODE ( /* Enable interrupts */
232  "sti\n\t"
233  /* Wait for RTC interrupt */
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 "before" TSC */
240  "rdtsc\n\t"
241  /* Store "before" TSC on stack */
242  "pushl %0\n\t"
243  /* Wait for another RTC interrupt */
244  "xorb %b2, %b2\n\t"
245  "movb %b2, %%cs:rtc_flag\n\t"
246  "\n1:\n\t"
247  "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
248  "testb %b2, %b2\n\t"
249  "jz 1b\n\t"
250  /* Read "after" TSC */
251  "rdtsc\n\t"
252  /* Retrieve "before" TSC on stack */
253  "popl %1\n\t"
254  /* Disable interrupts */
255  "cli\n\t"
256  )
257  : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
258  : "2" ( 0 ) );
259 
260  return ( after - before );
261 }
262 
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.
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
#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.
static struct segoff rtc_old_handler
Previous RTC interrupt handler.
Definition: rtc_entropy.c:49
#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:225
static void rtc_enable_int(void)
Enable RTC interrupts.
Definition: rtc_entropy.c:106
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:177
#define rtc_flag
Definition: rtc_entropy.c:53
x86 CPU feature detection
uint32_t features
Supported features.
Definition: ena.h:16
int entropy_enable(void)
Enable entropy gathering.
static void rtc_hook_isr(void)
Hook RTC interrupt handler.
Definition: rtc_entropy.c:59
unsigned char uint8_t
Definition: stdint.h:10
PROVIDE_ENTROPY_INLINE(rtc, min_entropy_per_sample)
__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
static int rtc_entropy_check(void)
Check that entropy gathering is functional.
Definition: rtc_entropy.c:147
#define RTC_MAX_WAIT_MS
Maximum time to wait for an RTC interrupt, in milliseconds.
Definition: rtc_entropy.c:43
#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:128
#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:94
#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:46
#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:212
#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