Browse Source

[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>
tags/v1.20.1
Michael Brown 7 years ago
parent
commit
5cf5ffea28
3 changed files with 36 additions and 0 deletions
  1. 1
    0
      src/include/ipxe/efi/efi.h
  2. 10
    0
      src/interface/efi/efi_init.c
  3. 25
    0
      src/interface/efi/efi_timer.c

+ 1
- 0
src/include/ipxe/efi/efi.h View File

@@ -214,6 +214,7 @@ extern EFI_HANDLE efi_image_handle;
214 214
 extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
215 215
 extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
216 216
 extern EFI_SYSTEM_TABLE *efi_systab;
217
+extern int efi_shutdown_in_progress;
217 218
 
218 219
 extern const __attribute__ (( pure )) char * efi_guid_ntoa ( EFI_GUID *guid );
219 220
 extern const __attribute__ (( pure )) char *

+ 10
- 0
src/interface/efi/efi_init.c View File

@@ -35,6 +35,9 @@ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
35 35
 /** System table passed to entry point */
36 36
 EFI_SYSTEM_TABLE *efi_systab;
37 37
 
38
+/** EFI shutdown is in progress */
39
+int efi_shutdown_in_progress;
40
+
38 41
 /** Event used to signal shutdown */
39 42
 static EFI_EVENT efi_shutdown_event;
40 43
 
@@ -50,6 +53,13 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
50 53
  */
51 54
 static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
52 55
 				       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 */
53 63
 	shutdown_boot();
54 64
 }
55 65
 

+ 25
- 0
src/interface/efi/efi_timer.c View File

@@ -70,6 +70,31 @@ static void efi_udelay ( unsigned long usecs ) {
70 70
  */
71 71
 static unsigned long efi_currticks ( void ) {
72 72
 
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
+
73 98
 	return efi_jiffies;
74 99
 }
75 100
 

Loading…
Cancel
Save