]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Allow driver to be unloaded
authorMichael Brown <mcb30@ipxe.org>
Mon, 10 Mar 2014 15:42:21 +0000 (15:42 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 10 Mar 2014 16:39:46 +0000 (16:39 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/efi/efi_driver.h
src/interface/efi/efi_bofm.c
src/interface/efi/efi_driver.c
src/interface/efi/efi_init.c
src/interface/efi/efi_pci.c

index d7eec96496df9ffa2f2e07839f536b2b95bf702d..afa574d0850847d870ee8c55707a586a181a2fb6 100644 (file)
@@ -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 */
index 8a8489bd8264ba402fcbc330b305850d93a87e5a..4982b22cc338e419589af08b527ddd48b3fde601 100644 (file)
@@ -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,
 };
index 13ed1f501eeb8cb77ab1f9de5c6676af6dcd0953..25145aca5bc6bf666d2a421ebb6f1a0f0ee7eaa7 100644 (file)
@@ -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 );
+}
index 2f6ca89c2aef7bd812482fcf560e3d5d2d60a3e6..b4ed5c1475a8ee9215d7e6eb0004375dd644854e 100644 (file)
@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <string.h>
 #include <errno.h>
 #include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
 #include <ipxe/efi/Protocol/LoadedImage.h>
 #include <ipxe/efi/Protocol/DevicePath.h>
 #include <ipxe/uuid.h>
@@ -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;
 }
index d6095d3e334d11aa42ac2330fb557426647f165b..dc7304a357875818dd5dd22fbd67b324d77cba69 100644 (file)
@@ -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; "