Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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>
  • Loading branch information
mcb30 committed Jul 10, 2016
1 parent d681794 commit 74222cd
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/arch/x86/include/bits/errfile.h
Expand Up @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 )
#define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 )
#define ERRFILE_gdbmach ( ERRFILE_ARCH | ERRFILE_CORE | 0x000e0000 )
#define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 )

#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
Expand Down
56 changes: 56 additions & 0 deletions src/arch/x86/interface/pcbios/rtc_entropy.c
Expand Up @@ -31,17 +31,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <biosint.h>
#include <pic8259.h>
#include <rtc.h>
#include <ipxe/entropy.h>

/** Maximum time to wait for an RTC interrupt, in milliseconds */
#define RTC_MAX_WAIT_MS 100

/** RTC interrupt handler */
extern void rtc_isr ( void );

/** Previous RTC interrupt handler */
static struct segoff rtc_old_handler;

/** Flag set by RTC interrupt handler */
extern volatile uint8_t __text16 ( rtc_flag );
#define rtc_flag __use_text16 ( rtc_flag )

/**
* Hook RTC interrupt handler
*
Expand Down Expand Up @@ -96,6 +105,10 @@ static void rtc_unhook_isr ( void ) {
static void rtc_enable_int ( void ) {
uint8_t status_b;

/* Clear any stale pending interrupts via status register C */
outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
inb ( CMOS_DATA );

/* Set Periodic Interrupt Enable bit in status register B */
outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
status_b = inb ( CMOS_DATA );
Expand Down Expand Up @@ -125,18 +138,60 @@ static void rtc_disable_int ( void ) {
inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
}

/**
* Check that entropy gathering is functional
*
* @ret rc Return status code
*/
static int rtc_entropy_check ( void ) {
unsigned int i;

/* Check that RTC interrupts are working */
rtc_flag = 0;
for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {

/* Allow interrupts to occur */
__asm__ __volatile__ ( "sti\n\t"
"nop\n\t"
"nop\n\t"
"cli\n\t" );

/* Check for RTC interrupt flag */
if ( rtc_flag )
return 0;

/* Delay */
mdelay ( 1 );
}

DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
return -ETIMEDOUT;
}

/**
* Enable entropy gathering
*
* @ret rc Return status code
*/
static int rtc_entropy_enable ( void ) {
int rc;

/* Hook ISR and enable RTC interrupts */
rtc_hook_isr();
enable_irq ( RTC_IRQ );
rtc_enable_int();

/* Check that RTC interrupts are working */
if ( ( rc = rtc_entropy_check() ) != 0 )
goto err_check;

return 0;

err_check:
rtc_disable_int();
disable_irq ( RTC_IRQ );
rtc_unhook_isr();
return rc;
}

/**
Expand All @@ -145,6 +200,7 @@ static int rtc_entropy_enable ( void ) {
*/
static void rtc_entropy_disable ( void ) {

/* Disable RTC interrupts and unhook ISR */
rtc_disable_int();
disable_irq ( RTC_IRQ );
rtc_unhook_isr();
Expand Down

0 comments on commit 74222cd

Please sign in to comment.