aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-02-11 22:46:01 +0000
committerMichael Brown <mcb30@ipxe.org>2023-02-12 17:18:10 +0000
commit026c66393d71a4ebe122974712b8180b1157e59e (patch)
tree19bccc496043c07835a38b1a2b4772aecc8e0efd
parent3c83843e111ece30d3dfb5143d5e6aed6164d587 (diff)
downloadipxe-hyperv.zip
ipxe-hyperv.tar.gz
ipxe-hyperv.tar.bz2
[rng] Work around broken RTC periodic interrupts in Hyper-Vhyperv
Some versions of Hyper-V (observed in some Microsoft Azure virtual machines as noted in commit 74222cd ("[rng] Check for functioning RTC interrupt"), and now in Windows Server 2022) fail to properly emulate the RTC periodic interrupt. The typical symptom is that only a single interrupt will be generated: subsequent interrupts will appear to be asserted by the virtual RTC but will be ignored by the virtual PIC. Experiments show that this apparent hypervisor bug can be worked around by disabling and re-enabling the periodic interrupt within the interrupt handler. Apply this workaround only if required, since doing so will substantially increase the overhead of each RTC interrupt. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/arch/x86/interface/pcbios/rtc_entropy.c76
1 files changed, 72 insertions, 4 deletions
diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c
index c400d8a..c9e5e46 100644
--- a/src/arch/x86/interface/pcbios/rtc_entropy.c
+++ b/src/arch/x86/interface/pcbios/rtc_entropy.c
@@ -55,6 +55,10 @@ static struct segoff rtc_old_handler;
extern volatile uint8_t __text16 ( rtc_flag );
#define rtc_flag __use_text16 ( rtc_flag )
+/** RTC interrupt requires rearming each time */
+extern volatile uint8_t __text16 ( rtc_rearm );
+#define rtc_rearm __use_text16 ( rtc_rearm )
+
/**
* Hook RTC interrupt handler
*
@@ -73,19 +77,49 @@ static void rtc_hook_isr ( void ) {
*/
"movb %2, %%al\n\t"
"outb %%al, %0\n\t"
- "inb %1\n\t"
+ "inb %1, %%al\n\t"
+
+ /* Rearm RTC interrupt, if required */
+ "testb $0xff, %%cs:rtc_rearm\n\t"
+ "jz rtc_isr_done\n\t"
+ /* Preserve registers */
+ "pushw %%bx\n\t"
+ /* Read current contents of register B */
+ "movb %3, %%al\n\t"
+ "outb %%al, %0\n\t"
+ "inb %1, %%al\n\t"
+ "movb %%al, %%bl\n\t"
+ /* Toggle periodic interrupt enable in register B */
+ "movb %3, %%al\n\t"
+ "outb %%al, %0\n\t"
+ "movb %%bl, %%al\n\t"
+ "xorb %4, %%al\n\t"
+ "outb %%al, %1\n\t"
+ /* Restore periodic interrupt enable in register B */
+ "movb %3, %%al\n\t"
+ "outb %%al, %0\n\t"
+ "movb %%bl, %%al\n\t"
+ "outb %%al, %1\n\t"
+ /* Restore registers */
+ "popw %%bx\n\t"
+
/* Send EOI */
+ "\nrtc_isr_done:\n\t"
"movb $0x20, %%al\n\t"
"outb %%al, $0xa0\n\t"
"outb %%al, $0x20\n\t"
/* Restore registers and return */
"popw %%ax\n\t"
"iret\n\t"
+
"\nrtc_flag:\n\t"
+ ".byte 0\n\t"
+
+ "\nrtc_rearm:\n\t"
".byte 0\n\t" )
:
- : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
- "i" ( RTC_STATUS_C ) );
+ : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ), "i" ( RTC_STATUS_C ),
+ "i" ( RTC_STATUS_B ), "i" ( RTC_STATUS_B_PIE ) );
hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler );
}
@@ -178,6 +212,38 @@ static int rtc_entropy_check ( void ) {
}
/**
+ * Apply workaround for broken RTC interrupts
+ *
+ * @ret rc Return status code
+ *
+ * Some versions of Hyper-V (observed with Windows Server 2022) fail
+ * to properly emulate the RTC periodic interrupt. The typical
+ * symptom is that only a single interrupt will be generated:
+ * subsequent interrupts will appear to be asserted by the virtual RTC
+ * but will be ignored by the virtual PIC.
+ *
+ * Experiments show that this apparent hypervisor bug can be worked
+ * around by disabling and re-enabling the periodic interrupt within
+ * the interrupt handler.
+ */
+static int rtc_entropy_workaround ( void ) {
+ int rc;
+
+ /* Apply workaround */
+ DBGC ( &rtc_flag, "RTC applying workaround for broken interrupts\n" );
+ rtc_rearm = 1;
+
+ /* Force one interrupt, to trigger the rearming code path */
+ __asm__ __volatile__ ( "int %0" : : "i" ( RTC_INT ) );
+
+ /* Check that RTC interrupts are now working */
+ if ( ( rc = rtc_entropy_check() ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Enable entropy gathering
*
* @ret rc Return status code
@@ -200,8 +266,10 @@ static int rtc_entropy_enable ( void ) {
rtc_enable_int();
/* Check that RTC interrupts are working */
- if ( ( rc = rtc_entropy_check() ) != 0 )
+ if ( ( ( rc = rtc_entropy_check() ) != 0 ) &&
+ ( ( rc = rtc_entropy_workaround() ) != 0 ) ) {
goto err_check;
+ }
return 0;