]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Do not unconditionally raise back to internal TPL
authorMichael Brown <mcb30@ipxe.org>
Thu, 5 Mar 2026 15:56:07 +0000 (15:56 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 5 Mar 2026 15:56:07 +0000 (15:56 +0000)
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>
src/include/ipxe/efi/efi.h
src/interface/efi/efi_connect.c
src/interface/efi/efi_entropy.c
src/interface/efi/efi_init.c
src/interface/efi/efi_timer.c

index 9554a6ad7b787e19a529206127070e5617858a8d..a73ef0959ed7bb3bd333f00fa3ac2a667005475e 100644 (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,
index f4747cf6bac3e03f1283ad2739e3faf69f6406bd..a4de78b78eded149df69828f3a5421e565a8ea96 100644 (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",
index b6bd12ccce7579c5b6edaa7790fb4baeaaba10ea..abd1018f34367994c0936f4e9c5fbca139e5314c 100644 (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 );
 }
 
 /**
index ac62ea747e0e9b9aca1e2939498d654452446177..9fd2d0c6bac58fb7ff7a960292791256ea003773 100644 (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 );
+}
index ffb899c8650e55cb2b82a6ee515dc7a5a2f8dc67..66817f8f733e65ea43ca29445d3a25b7b0d59f29 100644 (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 ) );