[efi] Avoid dropping below TPL as at entry to iPXE

iPXE will currently drop to TPL_APPLICATION whenever the current
system time is obtained via currticks(), since the system time
mechanism relies on a timer that can fire only when the TPL is below
TPL_CALLBACK.

This can cause unexpected behaviour if the system time is obtained in
the middle of an API call into iPXE by external code.  For example,
MnpDxe sets up a 10ms periodic timer running at TPL_CALLBACK to poll
the underling EFI_SIMPLE_NETWORK_PROTOCOL device for received packets.
If the resulting poll within iPXE happens to hit a code path that
requires obtaining the current system time (e.g. due to reception of
an STP packet, which affects iPXE's blocked link timer), then iPXE
will end up temporarily dropping to TPL_APPLICATION.  This can
potentially result in retriggering the MnpDxe periodic timer, causing
code to be unexpectedly re-entered.

Fix by recording the external TPL at any entry point into iPXE and
dropping only as far as this external TPL, rather than dropping
unconditionally to TPL_APPLICATION.

The side effect of this change is that iPXE's view of the current
system time will be frozen for the duration of any API calls made into
iPXE by external code at TPL_CALLBACK or above.  Since any such
external code is already responsible for allowing execution at
TPL_APPLICATION to occur, then this should not cause a problem in
practice.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2020-11-20 15:15:15 +00:00
parent 062711f1cf
commit e10a40d41f
8 changed files with 110 additions and 71 deletions

View File

@@ -156,13 +156,13 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_driver *efidrv;
struct efi_device *efidev;
struct efi_saved_tpl tpl;
union {
EFI_DEVICE_PATH_PROTOCOL *path;
void *interface;
} path;
EFI_DEVICE_PATH_PROTOCOL *path_end;
size_t path_len;
EFI_TPL saved_tpl;
EFI_STATUS efirc;
int rc;
@@ -181,7 +181,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
}
/* Raise TPL */
saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
efi_raise_tpl ( &tpl );
/* Do nothing if we are currently disconnecting drivers */
if ( efi_driver_disconnecting ) {
@@ -236,7 +236,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
DBGC ( device, "EFIDRV %s using driver \"%s\"\n",
efi_handle_name ( device ),
efidev->driver->name );
bs->RestoreTPL ( saved_tpl );
efi_restore_tpl ( &tpl );
return 0;
}
DBGC ( device, "EFIDRV %s could not start driver \"%s\": %s\n",
@@ -254,7 +254,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
}
err_open_path:
err_disconnecting:
bs->RestoreTPL ( saved_tpl );
efi_restore_tpl ( &tpl );
err_already_started:
return efirc;
}
@@ -273,10 +273,9 @@ static EFI_STATUS EFIAPI
efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
EFI_HANDLE device, UINTN num_children,
EFI_HANDLE *children ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_driver *efidrv;
struct efi_device *efidev;
EFI_TPL saved_tpl;
struct efi_saved_tpl tpl;
UINTN i;
DBGC ( device, "EFIDRV %s DRIVER_STOP", efi_handle_name ( device ) );
@@ -295,7 +294,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
}
/* Raise TPL */
saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
efi_raise_tpl ( &tpl );
/* Stop this device */
efidrv = efidev->driver;
@@ -304,7 +303,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
list_del ( &efidev->dev.siblings );
free ( efidev );
bs->RestoreTPL ( saved_tpl );
efi_restore_tpl ( &tpl );
return 0;
}