]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Allow wrapping the global boot services table in situ
authorMichael Brown <mcb30@ipxe.org>
Thu, 20 Mar 2025 12:25:26 +0000 (12:25 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 20 Mar 2025 12:35:42 +0000 (12:35 +0000)
When DEBUG=efi_wrap is enabled, we construct a patched copy of the
boot services table and patch the global system table to point to this
copy.  This ensures that any subsequently loaded EFI binaries will
call our wrappers.

Previously loaded EFI binaries will typically have cached the boot
services table pointer (in the gBS variable used by EDK2 code), and
therefore will not pick up the updated pointer and so will not call
our wrappers.  In most cases, this is what we want to happen: we are
interested in tracing the calls issued by the newly loaded binary and
we do not want to be distracted by the high volume of boot services
calls issued by existing UEFI drivers.

In some circumstances (such as when a badly behaved OEM driver is
causing the system to lock up during the ExitBootServices() call), it
can be very useful to be able to patch the global boot services table
in situ, so that we can trace calls issued by existing drivers.

Restructure the wrapping code to allow wrapping to be enabled or
disabled at any time, and to allow for patching the global boot
services table in situ.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/image/efi_image.c
src/include/ipxe/efi/efi_wrap.h
src/interface/efi/efi_wrap.c

index 104753a85eccb9713cc1c72f837066983608c36c..d2171e7de0fa26bf823ab5bb097683a3780ed612 100644 (file)
@@ -268,7 +268,7 @@ static int efi_image_exec ( struct image *image ) {
        efi_snp_release();
 
        /* Wrap calls made by the loaded image (for debugging) */
-       efi_wrap ( handle );
+       efi_wrap_image ( handle );
 
        /* Reset console since image will probably use it */
        console_reset();
@@ -291,6 +291,7 @@ static int efi_image_exec ( struct image *image ) {
        rc = 0;
 
  err_start_image:
+       efi_unwrap();
        efi_snp_claim();
  err_open_protocol:
        /* If there was no error, then the image must have been
index 2747a9e3330c1c32f8ac5a830428e834100d058d..d1e970bde481064fc09f51486faf8e39ddb995cf 100644 (file)
@@ -10,7 +10,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <ipxe/efi/efi.h>
 
-extern EFI_BOOT_SERVICES * efi_wrap_bs ( void );
-extern void efi_wrap ( EFI_HANDLE handle );
+extern void efi_wrap_bs ( EFI_BOOT_SERVICES *wrapped );
+extern void efi_wrap_systab ( int global );
+extern void efi_unwrap ( void );
+
+extern void efi_wrap_image ( EFI_HANDLE handle );
 
 #endif /* _IPXE_EFI_WRAP_H */
index 252bc26246c57ec308946b394a5d77c175ea67b4..8891f464bbead488dc1b15b6efd1e71d3b5656ef 100644 (file)
@@ -43,6 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** Number of lines to prescroll when needed */
 #define EFI_WRAP_PRESCROLL 16
 
+/** Public EFI system table pointer */
+static EFI_SYSTEM_TABLE *efi_systab_pub;
+
+/** Private EFI system table used while wrapping is active */
+static EFI_SYSTEM_TABLE efi_systab_priv;
+
+/** Original EFI boot services table pointer */
+static EFI_BOOT_SERVICES *efi_bs_orig;
+
+/** Backup of original EFI boot services table */
+static EFI_BOOT_SERVICES efi_bs_copy;
+
 /**
  * Convert EFI status code to text
  *
@@ -1217,93 +1229,141 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl,
 }
 
 /**
- * Build boot services table wrapper
+ * Wrap a boot services table
  *
- * @ret bs             Wrapped boot services table
+ * @v wrapper          Boot services table to wrap
  */
-EFI_BOOT_SERVICES * efi_wrap_bs ( void ) {
-       static EFI_BOOT_SERVICES efi_bs_wrapper;
+void efi_wrap_bs ( EFI_BOOT_SERVICES *wrapper ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 
+       /* Do nothing unless debugging is enabled */
+       if ( ! DBG_LOG )
+               return;
+
        /* Build boot services table wrapper */
-       memcpy ( &efi_bs_wrapper, bs, sizeof ( efi_bs_wrapper ) );
-       efi_bs_wrapper.RaiseTPL         = efi_raise_tpl_wrapper;
-       efi_bs_wrapper.RestoreTPL       = efi_restore_tpl_wrapper;
-       efi_bs_wrapper.AllocatePages    = efi_allocate_pages_wrapper;
-       efi_bs_wrapper.FreePages        = efi_free_pages_wrapper;
-       efi_bs_wrapper.GetMemoryMap     = efi_get_memory_map_wrapper;
-       efi_bs_wrapper.AllocatePool     = efi_allocate_pool_wrapper;
-       efi_bs_wrapper.FreePool         = efi_free_pool_wrapper;
-       efi_bs_wrapper.CreateEvent      = efi_create_event_wrapper;
-       efi_bs_wrapper.SetTimer         = efi_set_timer_wrapper;
-       efi_bs_wrapper.WaitForEvent     = efi_wait_for_event_wrapper;
-       efi_bs_wrapper.SignalEvent      = efi_signal_event_wrapper;
-       efi_bs_wrapper.CloseEvent       = efi_close_event_wrapper;
-       efi_bs_wrapper.CheckEvent       = efi_check_event_wrapper;
-       efi_bs_wrapper.InstallProtocolInterface
+       memcpy ( wrapper, bs, sizeof ( *wrapper ) );
+       wrapper->RaiseTPL               = efi_raise_tpl_wrapper;
+       wrapper->RestoreTPL             = efi_restore_tpl_wrapper;
+       wrapper->AllocatePages          = efi_allocate_pages_wrapper;
+       wrapper->FreePages              = efi_free_pages_wrapper;
+       wrapper->GetMemoryMap           = efi_get_memory_map_wrapper;
+       wrapper->AllocatePool           = efi_allocate_pool_wrapper;
+       wrapper->FreePool               = efi_free_pool_wrapper;
+       wrapper->CreateEvent            = efi_create_event_wrapper;
+       wrapper->SetTimer               = efi_set_timer_wrapper;
+       wrapper->WaitForEvent           = efi_wait_for_event_wrapper;
+       wrapper->SignalEvent            = efi_signal_event_wrapper;
+       wrapper->CloseEvent             = efi_close_event_wrapper;
+       wrapper->CheckEvent             = efi_check_event_wrapper;
+       wrapper->InstallProtocolInterface
                = efi_install_protocol_interface_wrapper;
-       efi_bs_wrapper.ReinstallProtocolInterface
+       wrapper->ReinstallProtocolInterface
                = efi_reinstall_protocol_interface_wrapper;
-       efi_bs_wrapper.UninstallProtocolInterface
+       wrapper->UninstallProtocolInterface
                = efi_uninstall_protocol_interface_wrapper;
-       efi_bs_wrapper.HandleProtocol   = efi_handle_protocol_wrapper;
-       efi_bs_wrapper.RegisterProtocolNotify
-               = efi_register_protocol_notify_wrapper;
-       efi_bs_wrapper.LocateHandle     = efi_locate_handle_wrapper;
-       efi_bs_wrapper.LocateDevicePath = efi_locate_device_path_wrapper;
-       efi_bs_wrapper.InstallConfigurationTable
+       wrapper->HandleProtocol         = efi_handle_protocol_wrapper;
+       wrapper->RegisterProtocolNotify = efi_register_protocol_notify_wrapper;
+       wrapper->LocateHandle           = efi_locate_handle_wrapper;
+       wrapper->LocateDevicePath       = efi_locate_device_path_wrapper;
+       wrapper->InstallConfigurationTable
                = efi_install_configuration_table_wrapper;
-       efi_bs_wrapper.LoadImage        = efi_load_image_wrapper;
-       efi_bs_wrapper.StartImage       = efi_start_image_wrapper;
-       efi_bs_wrapper.Exit             = efi_exit_wrapper;
-       efi_bs_wrapper.UnloadImage      = efi_unload_image_wrapper;
-       efi_bs_wrapper.ExitBootServices = efi_exit_boot_services_wrapper;
-       efi_bs_wrapper.GetNextMonotonicCount
-               = efi_get_next_monotonic_count_wrapper;
-       efi_bs_wrapper.Stall            = efi_stall_wrapper;
-       efi_bs_wrapper.SetWatchdogTimer = efi_set_watchdog_timer_wrapper;
-       efi_bs_wrapper.ConnectController
-               = efi_connect_controller_wrapper;
-       efi_bs_wrapper.DisconnectController
-               = efi_disconnect_controller_wrapper;
-       efi_bs_wrapper.OpenProtocol     = efi_open_protocol_wrapper;
-       efi_bs_wrapper.CloseProtocol    = efi_close_protocol_wrapper;
-       efi_bs_wrapper.OpenProtocolInformation
+       wrapper->LoadImage              = efi_load_image_wrapper;
+       wrapper->StartImage             = efi_start_image_wrapper;
+       wrapper->Exit                   = efi_exit_wrapper;
+       wrapper->UnloadImage            = efi_unload_image_wrapper;
+       wrapper->ExitBootServices       = efi_exit_boot_services_wrapper;
+       wrapper->GetNextMonotonicCount  = efi_get_next_monotonic_count_wrapper;
+       wrapper->Stall                  = efi_stall_wrapper;
+       wrapper->SetWatchdogTimer       = efi_set_watchdog_timer_wrapper;
+       wrapper->ConnectController      = efi_connect_controller_wrapper;
+       wrapper->DisconnectController   = efi_disconnect_controller_wrapper;
+       wrapper->OpenProtocol           = efi_open_protocol_wrapper;
+       wrapper->CloseProtocol          = efi_close_protocol_wrapper;
+       wrapper->OpenProtocolInformation
                = efi_open_protocol_information_wrapper;
-       efi_bs_wrapper.ProtocolsPerHandle
-               = efi_protocols_per_handle_wrapper;
-       efi_bs_wrapper.LocateHandleBuffer
-               = efi_locate_handle_buffer_wrapper;
-       efi_bs_wrapper.LocateProtocol   = efi_locate_protocol_wrapper;
-       efi_bs_wrapper.InstallMultipleProtocolInterfaces
+       wrapper->ProtocolsPerHandle     = efi_protocols_per_handle_wrapper;
+       wrapper->LocateHandleBuffer     = efi_locate_handle_buffer_wrapper;
+       wrapper->LocateProtocol         = efi_locate_protocol_wrapper;
+       wrapper->InstallMultipleProtocolInterfaces
                = efi_install_multiple_protocol_interfaces_wrapper;
-       efi_bs_wrapper.UninstallMultipleProtocolInterfaces
+       wrapper->UninstallMultipleProtocolInterfaces
                = efi_uninstall_multiple_protocol_interfaces_wrapper;
-       efi_bs_wrapper.CreateEventEx    = efi_create_event_ex_wrapper;
-
-       return &efi_bs_wrapper;
+       wrapper->CreateEventEx          = efi_create_event_ex_wrapper;
 }
 
 /**
- * Wrap the calls made by a loaded image
+ * Wrap the public EFI system table
  *
- * @v handle           Image handle
+ * @v global           Patch global boot services table in-place
  */
-void efi_wrap ( EFI_HANDLE handle ) {
-       static EFI_SYSTEM_TABLE efi_systab_copy;
+void efi_wrap_systab ( int global ) {
+       static EFI_BOOT_SERVICES local;
+       EFI_BOOT_SERVICES *wrapper;
 
        /* Do nothing unless debugging is enabled */
        if ( ! DBG_LOG )
                return;
 
-       /* Construct modified system table */
-       if ( efi_systab != &efi_systab_copy ) {
-               memcpy ( &efi_systab_copy, efi_systab,
-                        sizeof ( efi_systab_copy ) );
-               efi_systab->BootServices = efi_wrap_bs();
-               efi_systab = &efi_systab_copy;
+       /* Preserve original system and boot services tables */
+       if ( ! efi_systab_pub ) {
+               efi_systab_pub = efi_systab;
+               efi_bs_orig = efi_systab_pub->BootServices;
+               memcpy ( &efi_bs_copy, efi_bs_orig, sizeof ( efi_bs_copy ) );
        }
 
+       /* Construct and use private system table */
+       if ( efi_systab != &efi_systab_priv ) {
+               memcpy ( &efi_systab_priv, efi_systab_pub,
+                        sizeof ( efi_systab_priv ) );
+               efi_systab_priv.BootServices = &efi_bs_copy;
+               efi_systab = &efi_systab_priv;
+       }
+
+       /* Wrap global or local boot services table as applicable */
+       wrapper = ( global ? efi_bs_orig : &local );
+       efi_wrap_bs ( wrapper );
+       efi_systab_pub->BootServices = wrapper;
+       DBGC ( colour, "WRAP installed %s wrappers\n",
+              ( global ? "global" : "local" ) );
+}
+
+/**
+ * Remove boot services table wrapper
+ *
+ */
+void efi_unwrap ( void ) {
+
+       /* Do nothing unless debugging is enabled */
+       if ( ! DBG_LOG )
+               return;
+
+       /* Do nothing if wrapping was never enabled */
+       if ( ! efi_systab_pub )
+               return;
+
+       /* Restore original system and boot services tables */
+       memcpy ( efi_bs_orig, &efi_bs_copy, sizeof ( *efi_bs_orig ) );
+       efi_systab_pub->BootServices = efi_bs_orig;
+
+       /* Switch back to using public system table */
+       efi_systab = efi_systab_pub;
+       DBGC ( colour, "WRAP uninstalled wrappers\n" );
+}
+
+/**
+ * Wrap calls made by a newly loaded image
+ *
+ * @v handle           Image handle
+ */
+void efi_wrap_image ( EFI_HANDLE handle ) {
+
+       /* Do nothing unless debugging is enabled */
+       if ( ! DBG_LOG )
+               return;
+
        /* Dump image information */
        efi_dump_image ( handle );
+
+       /* Patch public system table */
+       efi_wrap_systab ( 0 );
 }