summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
authorMichael Brown2016-12-07 14:41:06 +0100
committerMichael Brown2016-12-07 14:46:37 +0100
commit5cf5ffea2874434ffdc64c3242f2c53ed7ec1d40 (patch)
tree5347859587c18d490a65de4cd6fa6170557ae211 /src/interface
parent[undi] Try matching UNDI ROMs in BIOS enumeration order (diff)
downloadipxe-5cf5ffea2874434ffdc64c3242f2c53ed7ec1d40.tar.gz
ipxe-5cf5ffea2874434ffdc64c3242f2c53ed7ec1d40.tar.xz
ipxe-5cf5ffea2874434ffdc64c3242f2c53ed7ec1d40.zip
[efi] Work around temporal anomaly encountered during ExitBootServices()
EFI provides no clean way for device drivers to shut down in preparation for handover to a booted operating system. The platform firmware simply doesn't bother to call the drivers' Stop() methods. Instead, drivers must register an EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when ExitBootServices() is called, and clean up without any reference to the EFI driver model. Unfortunately, all timers silently stop working when ExitBootServices() is called. Even more unfortunately, and for no discernible reason, this happens before any EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The net effect of this entertaining design choice is that any timeout loops on the shutdown path (e.g. for gracefully closing outstanding TCP connections) may wait indefinitely. There is no way to report failure from currticks(), since the API lazily assumes that the host system continues to travel through time in the usual direction. Work around EFI's violation of this assumption by falling back to a simple free-running monotonic counter. Debugged-by: Maor Dickman <maord@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/efi/efi_init.c10
-rw-r--r--src/interface/efi/efi_timer.c25
2 files changed, 35 insertions, 0 deletions
diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c
index 93ada21d..cfaff606 100644
--- a/src/interface/efi/efi_init.c
+++ b/src/interface/efi/efi_init.c
@@ -35,6 +35,9 @@ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
/** System table passed to entry point */
EFI_SYSTEM_TABLE *efi_systab;
+/** EFI shutdown is in progress */
+int efi_shutdown_in_progress;
+
/** Event used to signal shutdown */
static EFI_EVENT efi_shutdown_event;
@@ -50,6 +53,13 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
*/
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
void *context __unused ) {
+
+ /* Mark shutdown as being in progress, to indicate that large
+ * parts of the system (e.g. timers) are no longer functional.
+ */
+ efi_shutdown_in_progress = 1;
+
+ /* Shut down iPXE */
shutdown_boot();
}
diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c
index da064120..1fd9971e 100644
--- a/src/interface/efi/efi_timer.c
+++ b/src/interface/efi/efi_timer.c
@@ -70,6 +70,31 @@ static void efi_udelay ( unsigned long usecs ) {
*/
static unsigned long efi_currticks ( void ) {
+ /* EFI provides no clean way for device drivers to shut down
+ * in preparation for handover to a booted operating system.
+ * The platform firmware simply doesn't bother to call the
+ * drivers' Stop() methods. Instead, drivers must register an
+ * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when
+ * ExitBootServices() is called, and clean up without any
+ * reference to the EFI driver model.
+ *
+ * Unfortunately, all timers silently stop working when
+ * ExitBootServices() is called. Even more unfortunately, and
+ * for no discernible reason, this happens before any
+ * EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The
+ * net effect of this entertaining design choice is that any
+ * timeout loops on the shutdown path (e.g. for gracefully
+ * closing outstanding TCP connections) may wait indefinitely.
+ *
+ * There is no way to report failure from currticks(), since
+ * the API lazily assumes that the host system continues to
+ * travel through time in the usual direction. Work around
+ * EFI's violation of this assumption by falling back to a
+ * simple free-running monotonic counter.
+ */
+ if ( efi_shutdown_in_progress )
+ efi_jiffies++;
+
return efi_jiffies;
}