Procházet zdrojové kódy

[rng] Check for functioning RTC interrupt

On some platforms (observed in a small subset of Microsoft Azure
(Hyper-V) virtual machines), the RTC appears to be incapable of
generating an interrupt via the legacy PIC.  The RTC status registers
show that a periodic interrupt has been asserted, but the PIC IRR
shows that IRQ8 remains inactive.

On such systems, iPXE will currently freeze during the "iPXE
initialising devices..." message.

Work around this problem by checking that RTC interrupts are being
raised before returning from rtc_entropy_enable().  If no interrupt is
seen within 100ms, then we assume that the RTC interrupt mechanism is
broken.  In these circumstances, iPXE will continue to initialise but
any subsequent attempt to generate entropy will fail.  In particular,
HTTPS connections will fail with an error indicating that no entropy
is available.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown před 8 roky
rodič
revize
74222cd2c1

+ 1
- 0
src/arch/x86/include/bits/errfile.h Zobrazit soubor

@@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
23 23
 #define ERRFILE_vesafb		( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 )
24 24
 #define ERRFILE_int13con	( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 )
25 25
 #define ERRFILE_gdbmach		( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 )
26
+#define ERRFILE_rtc_entropy	( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 )
26 27
 
27 28
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
28 29
 #define ERRFILE_bzimage	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )

+ 56
- 0
src/arch/x86/interface/pcbios/rtc_entropy.c Zobrazit soubor

@@ -31,17 +31,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
31 31
 
32 32
 #include <stdint.h>
33 33
 #include <string.h>
34
+#include <errno.h>
35
+#include <unistd.h>
34 36
 #include <biosint.h>
35 37
 #include <pic8259.h>
36 38
 #include <rtc.h>
37 39
 #include <ipxe/entropy.h>
38 40
 
41
+/** Maximum time to wait for an RTC interrupt, in milliseconds */
42
+#define RTC_MAX_WAIT_MS 100
43
+
39 44
 /** RTC interrupt handler */
40 45
 extern void rtc_isr ( void );
41 46
 
42 47
 /** Previous RTC interrupt handler */
43 48
 static struct segoff rtc_old_handler;
44 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
+
45 54
 /**
46 55
  * Hook RTC interrupt handler
47 56
  *
@@ -96,6 +105,10 @@ static void rtc_unhook_isr ( void ) {
96 105
 static void rtc_enable_int ( void ) {
97 106
 	uint8_t status_b;
98 107
 
108
+	/* Clear any stale pending interrupts via status register C */
109
+	outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
110
+	inb ( CMOS_DATA );
111
+
99 112
 	/* Set Periodic Interrupt Enable bit in status register B */
100 113
 	outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
101 114
 	status_b = inb ( CMOS_DATA );
@@ -125,18 +138,60 @@ static void rtc_disable_int ( void ) {
125 138
 	inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
126 139
 }
127 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
+
128 171
 /**
129 172
  * Enable entropy gathering
130 173
  *
131 174
  * @ret rc		Return status code
132 175
  */
133 176
 static int rtc_entropy_enable ( void ) {
177
+	int rc;
134 178
 
179
+	/* Hook ISR and enable RTC interrupts */
135 180
 	rtc_hook_isr();
136 181
 	enable_irq ( RTC_IRQ );
137 182
 	rtc_enable_int();
138 183
 
184
+	/* Check that RTC interrupts are working */
185
+	if ( ( rc = rtc_entropy_check() ) != 0 )
186
+		goto err_check;
187
+
139 188
 	return 0;
189
+
190
+ err_check:
191
+	rtc_disable_int();
192
+	disable_irq ( RTC_IRQ );
193
+	rtc_unhook_isr();
194
+	return rc;
140 195
 }
141 196
 
142 197
 /**
@@ -145,6 +200,7 @@ static int rtc_entropy_enable ( void ) {
145 200
  */
146 201
 static void rtc_entropy_disable ( void ) {
147 202
 
203
+	/* Disable RTC interrupts and unhook ISR */
148 204
 	rtc_disable_int();
149 205
 	disable_irq ( RTC_IRQ );
150 206
 	rtc_unhook_isr();

Načítá se…
Zrušit
Uložit