From 8de6b973c487605002143d65d0082bb2ed50714e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Mar 2014 15:42:21 +0000 Subject: [PATCH] [efi] Allow driver to be unloaded Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_driver.h | 1 + src/interface/efi/efi_bofm.c | 16 ++++- src/interface/efi/efi_driver.c | 46 ++++++++++++++- src/interface/efi/efi_init.c | 97 +++++++++++++++++++++++++++++++ src/interface/efi/efi_pci.c | 3 + 5 files changed, 159 insertions(+), 4 deletions(-) diff --git a/src/include/ipxe/efi/efi_driver.h b/src/include/ipxe/efi/efi_driver.h index d7eec9649..afa574d08 100644 --- a/src/include/ipxe/efi/efi_driver.h +++ b/src/include/ipxe/efi/efi_driver.h @@ -45,5 +45,6 @@ extern EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ); extern int efi_driver_install ( struct efi_driver *efidrv ); +extern void efi_driver_uninstall ( struct efi_driver *efidrv ); #endif /* _IPXE_EFI_DRIVER_H */ diff --git a/src/interface/efi/efi_bofm.c b/src/interface/efi/efi_bofm.c index 8a8489bd8..4982b22cc 100644 --- a/src/interface/efi/efi_bofm.c +++ b/src/interface/efi/efi_bofm.c @@ -387,7 +387,7 @@ static struct efi_driver efi_bofm_driver = * Install EFI BOFM driver * */ -static void efi_bofm_driver_init ( void ) { +static void efi_bofm_driver_startup ( void ) { struct efi_driver *efidrv = &efi_bofm_driver; int rc; @@ -401,7 +401,19 @@ static void efi_bofm_driver_init ( void ) { DBGC ( efidrv, "EFIBOFM driver installed\n" ); } +/** + * Shut down EFI BOFM driver + * + * @v booting System is shutting down for OS boot + */ +static void efi_bofm_driver_shutdown ( int booting __unused ) { + struct efi_driver *efidrv = &efi_bofm_driver; + + efi_driver_uninstall ( efidrv ); +} + /** EFI BOFM startup function */ struct startup_fn startup_bofm __startup_fn ( STARTUP_EARLY ) = { - .startup = efi_bofm_driver_init, + .startup = efi_bofm_driver_startup, + .shutdown = efi_bofm_driver_shutdown, }; diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 13ed1f501..25145aca5 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -122,7 +122,7 @@ efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, * Install EFI driver * * @v efidrv EFI driver - * @ret efirc EFI status code + * @ret rc Return status code */ int efi_driver_install ( struct efi_driver *efidrv ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; @@ -143,7 +143,9 @@ int efi_driver_install ( struct efi_driver *efidrv ) { efi_snprintf ( efidrv->wname, ( sizeof ( efidrv->wname ) / sizeof ( efidrv->wname[0] ) ), - PRODUCT_SHORT_NAME " - %s", efidrv->name ); + PRODUCT_SHORT_NAME "%s%s", + ( efidrv->name ? " - " : "" ), + ( efidrv->name ? efidrv->name : "" ) ); /* Install driver */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( @@ -160,3 +162,43 @@ int efi_driver_install ( struct efi_driver *efidrv ) { DBGC ( efidrv, "EFIDRV %s installed\n", efidrv->name ); return 0; } + +/** + * Uninstall EFI driver + * + * @v efidrv EFI driver + */ +void efi_driver_uninstall ( struct efi_driver *efidrv ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + UINTN num_handles; + EFI_STATUS efirc; + UINTN i; + int rc; + + /* Disconnect the driver from its devices */ + if ( ( efirc = bs->LocateHandleBuffer ( AllHandles, NULL, NULL, + &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( efidrv, "EFIDRV %s could not list handles: %s\n", + efidrv->name, strerror ( rc ) ); + /* No way to disconnect driver; leave it loaded */ + return; + } + DBGC ( efidrv, "EFIDRV %s disconnecting devices\n", efidrv->name ); + for ( i = 0 ; i < num_handles ; i++ ) { + bs->DisconnectController ( handles[i], + efidrv->driver.DriverBindingHandle, + NULL ); + } + bs->FreePool ( handles ); + + /* Uninstall the driver */ + bs->UninstallMultipleProtocolInterfaces ( + efidrv->driver.DriverBindingHandle, + &efi_driver_binding_protocol_guid, &efidrv->driver, + &efi_component_name2_protocol_guid, &efidrv->wtf, + NULL ); + DBGC ( efidrv, "EFIDRV %s uninstalled\n", efidrv->name ); +} diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 2f6ca89c2..b4ed5c147 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -50,6 +51,64 @@ static EFI_GUID efi_loaded_image_device_path_protocol_guid /** Event used to signal shutdown */ static EFI_EVENT efi_shutdown_event; +/* Forward declarations */ +static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); + +/** + * Check to see if driver supports a device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_image_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device __unused, + EFI_DEVICE_PATH_PROTOCOL *child __unused ) { + + return EFI_UNSUPPORTED; +} + +/** + * Attach driver to device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_image_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device __unused, + EFI_DEVICE_PATH_PROTOCOL *child __unused ) { + + return EFI_UNSUPPORTED; +} + +/** + * Detach driver from device + * + * @v driver EFI driver + * @v device EFI device + * @v pci PCI device + * @v num_children Number of child devices + * @v children List of child devices + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_image_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device __unused, UINTN num_children __unused, + EFI_HANDLE *children __unused ) { + + return EFI_UNSUPPORTED; +} + +/** EFI loaded image driver */ +static struct efi_driver efi_image_driver = + EFI_DRIVER_INIT ( NULL, efi_image_supported, efi_image_start, + efi_image_stop ); + /** * Shut down in preparation for booting an OS. * @@ -189,5 +248,43 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, return efirc; } + /* Install an EFI driver on the image handle, to allow the + * driver to be subsequently unloaded. + */ + efi_image_driver.driver.DriverBindingHandle = image_handle; + if ( ( rc = efi_driver_install ( &efi_image_driver ) ) != 0 ) { + DBGC ( systab, "EFI could not install loaded image driver: " + "%s\n", strerror ( rc ) ); + return EFIRC ( rc ); + } + + /* Install image unload method */ + efi_loaded_image->Unload = efi_unload; + + return 0; +} + +/** + * Shut down EFI environment + * + * @v image_handle Image handle + */ +static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_SYSTEM_TABLE *systab = efi_systab; + + DBGC ( systab, "EFI image unloading\n" ); + + /* Shut down */ + shutdown_exit(); + + /* Uninstall exit boot services event */ + bs->CloseEvent ( efi_shutdown_event ); + + /* Uninstall loaded image driver */ + efi_driver_uninstall ( &efi_image_driver ); + + DBGC ( systab, "EFI image unloaded\n" ); + return 0; } diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index d6095d3e3..dc7304a35 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -519,6 +519,9 @@ static void efipci_driver_shutdown ( int booting __unused ) { struct efi_pci_device *efipci; struct efi_pci_device *tmp; + /* Uninstall driver */ + efi_driver_uninstall ( efidrv ); + /* 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; "