[efi] Allow wrapping the global boot services table in situ

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>
pull/1193/merge
Michael Brown 2025-03-20 12:25:26 +00:00
parent f68c8b09e3
commit 1a602c92ac
3 changed files with 144 additions and 80 deletions

View 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

View 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 */

View 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;
/* 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
= efi_install_protocol_interface_wrapper;
efi_bs_wrapper.ReinstallProtocolInterface
= efi_reinstall_protocol_interface_wrapper;
efi_bs_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
= 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
= 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
= efi_install_multiple_protocol_interfaces_wrapper;
efi_bs_wrapper.UninstallMultipleProtocolInterfaces
= efi_uninstall_multiple_protocol_interfaces_wrapper;
efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper;
return &efi_bs_wrapper;
}
/**
* Wrap the calls made by a loaded image
*
* @v handle Image handle
*/
void efi_wrap ( EFI_HANDLE handle ) {
static EFI_SYSTEM_TABLE efi_systab_copy;
/* 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;
/* Build boot services table wrapper */
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;
wrapper->ReinstallProtocolInterface
= efi_reinstall_protocol_interface_wrapper;
wrapper->UninstallProtocolInterface
= efi_uninstall_protocol_interface_wrapper;
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;
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;
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;
wrapper->UninstallMultipleProtocolInterfaces
= efi_uninstall_multiple_protocol_interfaces_wrapper;
wrapper->CreateEventEx = efi_create_event_ex_wrapper;
}
/**
* Wrap the public EFI system table
*
* @v global Patch global boot services table in-place
*/
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;
/* 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 );
}