From 94138656d74a5f787ca6c6dd22c08e656ac131f3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 5 Mar 2026 15:56:07 +0000 Subject: [PATCH] [efi] Do not unconditionally raise back to internal TPL Most TPL manipulation is handled by efi_raise_tpl()/efi_restore_tpl() pairs. The exceptions are the places where we need to temporarily drop to a lower TPL in order to allow a timer interrupt to occur. These currently assume that they are called only from code that is already running at the internal TPL (generally TPL_CALLBACK). This assumption is not always correct. In particular, the call from _efi_start() to efi_driver_reconnect_all() takes place after the SNP devices have been released and so will be running at the external TPL. Create an efi_drop_tpl()/efi_undrop_tpl() pair to abstract away the temporary lowering of the TPL, and ensure that the TPL is always raised back to its original level rather than being unconditionally raised to the internal TPL. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi.h | 8 ++++++++ src/interface/efi/efi_connect.c | 10 ++++++---- src/interface/efi/efi_entropy.c | 7 +++++-- src/interface/efi/efi_init.c | 30 +++++++++++++++++++++++++++++- src/interface/efi/efi_timer.c | 6 +++--- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 9554a6ad7..a73ef0959 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -84,6 +84,12 @@ struct efi_saved_tpl { EFI_TPL previous; }; +/** An EFI dropped task priority level */ +struct efi_dropped_tpl { + /** Current TPL */ + EFI_TPL current; +}; + /** An EFI protocol used by iPXE */ struct efi_protocol { /** GUID */ @@ -408,6 +414,8 @@ extern EFI_STATUS efi_init ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ); extern void efi_raise_tpl ( struct efi_saved_tpl *tpl ); extern void efi_restore_tpl ( struct efi_saved_tpl *tpl ); +extern void efi_drop_tpl ( struct efi_dropped_tpl *tpl ); +extern void efi_undrop_tpl ( struct efi_dropped_tpl *tpl ); extern int efi_open_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, void **interface ); extern int efi_open_unsafe_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, diff --git a/src/interface/efi/efi_connect.c b/src/interface/efi/efi_connect.c index f4747cf6b..a4de78b78 100644 --- a/src/interface/efi/efi_connect.c +++ b/src/interface/efi/efi_connect.c @@ -59,6 +59,7 @@ int efi_connect ( EFI_HANDLE device, EFI_HANDLE driver ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE driverlist[2] = { driver, NULL }; EFI_HANDLE *drivers = ( driver ? driverlist : NULL ); + struct efi_dropped_tpl tpl; EFI_STATUS efirc; int rc; @@ -67,9 +68,9 @@ int efi_connect ( EFI_HANDLE device, EFI_HANDLE driver ) { DBGC ( device, "%s driver at %s TPL\n", ( driver ? efi_handle_name ( driver ) : "any" ), efi_tpl_name ( efi_external_tpl ) ); - bs->RestoreTPL ( efi_external_tpl ); + efi_drop_tpl ( &tpl ); efirc = bs->ConnectController ( device, drivers, NULL, TRUE ); - bs->RaiseTPL ( efi_internal_tpl ); + efi_undrop_tpl ( &tpl ); if ( efirc != 0 ) { rc = -EEFI_CONNECT ( efirc ); DBGC ( device, "EFI %s could not connect: %s\n", @@ -89,6 +90,7 @@ int efi_connect ( EFI_HANDLE device, EFI_HANDLE driver ) { */ int efi_disconnect ( EFI_HANDLE device, EFI_HANDLE driver ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_dropped_tpl tpl; EFI_STATUS efirc; int rc; @@ -97,9 +99,9 @@ int efi_disconnect ( EFI_HANDLE device, EFI_HANDLE driver ) { DBGC ( device, "%s driver at %s TPL\n", ( driver ? efi_handle_name ( driver ) : "any" ), efi_tpl_name ( efi_external_tpl ) ); - bs->RestoreTPL ( efi_external_tpl ); + efi_drop_tpl ( &tpl ); efirc = bs->DisconnectController ( device, driver, NULL ); - bs->RaiseTPL ( efi_internal_tpl ); + efi_undrop_tpl ( &tpl ); if ( ( efirc != 0 ) && ( efirc != EFI_NOT_FOUND ) ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not disconnect: %s\n", diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index b6bd12ccc..abd1018f3 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -50,6 +50,9 @@ struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ); /** Event used to wait for timer tick */ static EFI_EVENT tick; +/** Dropped TPL */ +static struct efi_dropped_tpl efi_entropy_tpl; + /** * Enable entropy gathering * @@ -61,7 +64,7 @@ static int efi_entropy_enable ( void ) { int rc; /* Drop to external TPL to allow timer tick event to take place */ - bs->RestoreTPL ( efi_external_tpl ); + efi_drop_tpl ( &efi_entropy_tpl ); /* Create timer tick event */ if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, @@ -92,7 +95,7 @@ static void efi_entropy_disable ( void ) { bs->CloseEvent ( tick ); /* Return to internal TPL */ - bs->RaiseTPL ( efi_internal_tpl ); + efi_undrop_tpl ( &efi_entropy_tpl ); } /** diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index ac62ea747..9fd2d0c6b 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -392,7 +392,7 @@ void efi_raise_tpl ( struct efi_saved_tpl *tpl ) { } /** - * Restore task priority level + * Restore saved task priority level * * @v tpl Saved TPL */ @@ -405,3 +405,31 @@ void efi_restore_tpl ( struct efi_saved_tpl *tpl ) { /* Restore TPL */ bs->RestoreTPL ( tpl->current ); } + +/** + * Drop task priority level temporarily to external level + * + * @v tpl Dropped TPL + */ +void efi_drop_tpl ( struct efi_dropped_tpl *tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Raise TPL temporarily to discover current TPL */ + tpl->current = bs->RaiseTPL ( TPL_HIGH_LEVEL ); + bs->RestoreTPL ( tpl->current ); + + /* Drop to external TPL */ + bs->RestoreTPL ( efi_external_tpl ); +} + +/** + * Restore dropped task priority level + * + * @v tpl Dropped TPL + */ +void efi_undrop_tpl ( struct efi_dropped_tpl *tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Raise back to original TPL */ + bs->RaiseTPL ( tpl->current ); +} diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index ffb899c86..66817f8f7 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -77,7 +77,7 @@ static void efi_udelay ( unsigned long usecs ) { * @ret ticks Current time, in ticks */ static unsigned long efi_currticks ( void ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_dropped_tpl tpl; /* UEFI manages to ingeniously combine the worst aspects of * both polling and interrupt-driven designs. There is no way @@ -137,8 +137,8 @@ static unsigned long efi_currticks ( void ) { if ( efi_shutdown_in_progress ) { efi_jiffies++; } else { - bs->RestoreTPL ( efi_external_tpl ); - bs->RaiseTPL ( efi_internal_tpl ); + efi_drop_tpl ( &tpl ); + efi_undrop_tpl ( &tpl ); } return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) );