Skip to content

Commit 5cf5ffe

Browse files
committedDec 7, 2016
[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>
1 parent e09331a commit 5cf5ffe

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed
 

‎src/include/ipxe/efi/efi.h

+1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ extern EFI_HANDLE efi_image_handle;
214214
extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
215215
extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
216216
extern EFI_SYSTEM_TABLE *efi_systab;
217+
extern int efi_shutdown_in_progress;
217218

218219
extern const __attribute__ (( pure )) char * efi_guid_ntoa ( EFI_GUID *guid );
219220
extern const __attribute__ (( pure )) char *

‎src/interface/efi/efi_init.c

+10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
3535
/** System table passed to entry point */
3636
EFI_SYSTEM_TABLE *efi_systab;
3737

38+
/** EFI shutdown is in progress */
39+
int efi_shutdown_in_progress;
40+
3841
/** Event used to signal shutdown */
3942
static EFI_EVENT efi_shutdown_event;
4043

@@ -50,6 +53,13 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
5053
*/
5154
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
5255
void *context __unused ) {
56+
57+
/* Mark shutdown as being in progress, to indicate that large
58+
* parts of the system (e.g. timers) are no longer functional.
59+
*/
60+
efi_shutdown_in_progress = 1;
61+
62+
/* Shut down iPXE */
5363
shutdown_boot();
5464
}
5565

‎src/interface/efi/efi_timer.c

+25
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,31 @@ static void efi_udelay ( unsigned long usecs ) {
7070
*/
7171
static unsigned long efi_currticks ( void ) {
7272

73+
/* EFI provides no clean way for device drivers to shut down
74+
* in preparation for handover to a booted operating system.
75+
* The platform firmware simply doesn't bother to call the
76+
* drivers' Stop() methods. Instead, drivers must register an
77+
* EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when
78+
* ExitBootServices() is called, and clean up without any
79+
* reference to the EFI driver model.
80+
*
81+
* Unfortunately, all timers silently stop working when
82+
* ExitBootServices() is called. Even more unfortunately, and
83+
* for no discernible reason, this happens before any
84+
* EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled. The
85+
* net effect of this entertaining design choice is that any
86+
* timeout loops on the shutdown path (e.g. for gracefully
87+
* closing outstanding TCP connections) may wait indefinitely.
88+
*
89+
* There is no way to report failure from currticks(), since
90+
* the API lazily assumes that the host system continues to
91+
* travel through time in the usual direction. Work around
92+
* EFI's violation of this assumption by falling back to a
93+
* simple free-running monotonic counter.
94+
*/
95+
if ( efi_shutdown_in_progress )
96+
efi_jiffies++;
97+
7398
return efi_jiffies;
7499
}
75100

0 commit comments

Comments
 (0)
Please sign in to comment.