[efi] Ensure that all drivers are shut down before the OS boots

Reported-by: Itay Gazit <itayg@mellanox.co.il>
Suggested-by: Michael R Turner <mikeyt@us.ibm.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/1/head
Michael Brown 2011-03-29 21:35:06 +01:00
parent 797c29adda
commit fc7239bdc8
3 changed files with 51 additions and 32 deletions

View File

@ -26,20 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 ); FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
/** Event used to signal shutdown */
static EFI_EVENT efi_shutdown_event;
/**
* Shut down in preparation for booting an OS.
*
* This hook gets called at ExitBootServices time in order to make sure that
* the network cards are properly shut down before the OS takes over.
*/
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
void *context __unused ) {
shutdown_boot();
}
/** /**
* Execute EFI image * Execute EFI image
* *
@ -64,30 +50,14 @@ static int efi_image_exec ( struct image *image ) {
return -ENOEXEC; return -ENOEXEC;
} }
/* Be sure to shut down the NIC at ExitBootServices time, or else
* DMA from the card can corrupt the OS.
*/
efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK, efi_shutdown_hook,
NULL, &efi_shutdown_event );
if ( efirc ) {
rc = EFIRC_TO_RC ( efirc );
goto done;
}
/* Start the image */ /* Start the image */
if ( ( efirc = bs->StartImage ( handle, &exit_data_size, if ( ( efirc = bs->StartImage ( handle, &exit_data_size,
&exit_data ) ) != 0 ) { &exit_data ) ) != 0 ) {
DBGC ( image, "EFIIMAGE %p returned with status %s\n", DBGC ( image, "EFIIMAGE %p returned with status %s\n",
image, efi_strerror ( efirc ) ); image, efi_strerror ( efirc ) );
} }
rc = EFIRC_TO_RC ( efirc ); rc = EFIRC_TO_RC ( efirc );
/* Remove the shutdown hook */
bs->CloseEvent ( efi_shutdown_event );
done:
/* Unload the image. We can't leave it loaded, because we /* Unload the image. We can't leave it loaded, because we
* have no "unload" operation. * have no "unload" operation.
*/ */

View File

@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi.h> #include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/LoadedImage.h> #include <ipxe/efi/Protocol/LoadedImage.h>
#include <ipxe/uuid.h> #include <ipxe/uuid.h>
#include <ipxe/init.h>
/** Image handle passed to entry point */ /** Image handle passed to entry point */
EFI_HANDLE efi_image_handle; EFI_HANDLE efi_image_handle;
@ -36,6 +37,21 @@ EFI_SYSTEM_TABLE *efi_systab;
static EFI_GUID efi_loaded_image_protocol_guid static EFI_GUID efi_loaded_image_protocol_guid
= EFI_LOADED_IMAGE_PROTOCOL_GUID; = EFI_LOADED_IMAGE_PROTOCOL_GUID;
/** Event used to signal shutdown */
static EFI_EVENT efi_shutdown_event;
/**
* Shut down in preparation for booting an OS.
*
* This hook gets called at ExitBootServices time in order to make
* sure that everything is properly shut down before the OS takes
* over.
*/
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
void *context __unused ) {
shutdown_boot();
}
/** /**
* Look up EFI configuration table * Look up EFI configuration table
* *
@ -129,5 +145,18 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
} }
} }
/* EFI is perfectly capable of gracefully shutting down any
* loaded devices if it decides to fall back to a legacy boot.
* For no particularly comprehensible reason, it doesn't
* bother doing so when ExitBootServices() is called.
*/
if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK, efi_shutdown_hook,
NULL, &efi_shutdown_event ) ) != 0 ) {
DBGC ( systab, "EFI could not create ExitBootServices event: "
"%s\n", efi_strerror ( efirc ) );
return efirc;
}
return 0; return 0;
} }

View File

@ -489,7 +489,7 @@ static struct efi_driver efipci_driver =
* Install EFI PCI driver * Install EFI PCI driver
* *
*/ */
static void efipci_driver_init ( void ) { static void efipci_driver_startup ( void ) {
struct efi_driver *efidrv = &efipci_driver; struct efi_driver *efidrv = &efipci_driver;
EFI_STATUS efirc; EFI_STATUS efirc;
@ -503,7 +503,27 @@ static void efipci_driver_init ( void ) {
DBGC ( efidrv, "EFIPCI driver installed\n" ); DBGC ( efidrv, "EFIPCI driver installed\n" );
} }
/**
* Shut down EFI PCI driver
*
* @v booting System is shutting down for OS boot
*/
static void efipci_driver_shutdown ( int booting __unused ) {
struct efi_driver *efidrv = &efipci_driver;
struct efi_pci_device *efipci;
struct efi_pci_device *tmp;
/* Shut down any remaining devices */
list_for_each_entry_safe ( efipci, tmp, &efi_pci_devices, list ) {
DBGC ( efipci, "EFIPCI " PCI_FMT " still active at shutdown; "
"forcing close\n", PCI_ARGS ( &efipci->pci ) );
pci_remove ( &efipci->pci );
efipci_destroy ( efidrv, efipci );
}
}
/** EFI PCI startup function */ /** EFI PCI startup function */
struct startup_fn startup_pci __startup_fn ( STARTUP_NORMAL ) = { struct startup_fn startup_pci __startup_fn ( STARTUP_NORMAL ) = {
.startup = efipci_driver_init, .startup = efipci_driver_startup,
.shutdown = efipci_driver_shutdown,
}; };