[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2026-03-05 15:56:07 +00:00
parent cbeda2012a
commit 94138656d7
5 changed files with 51 additions and 10 deletions

View File

@ -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,

View File

@ -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",

View File

@ -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 );
}
/**

View File

@ -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 );
}

View File

@ -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 ) );