]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Use device path to locate filesystem from which we were loaded 129/head
authorMichael Brown <mcb30@ipxe.org>
Mon, 3 Aug 2020 14:26:25 +0000 (15:26 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 3 Aug 2020 14:41:30 +0000 (15:41 +0100)
The file:/ URI syntax may be used to refer to local files on the
filesystem from which the iPXE binary was loaded.  This is currently
implemented by directly using the DeviceHandle recorded in our
EFI_LOADED_IMAGE_PROTOCOL.

This mechanism will fail when a USB-enabled build of iPXE is loaded
from USB storage and subsequently installs its own USB host controller
drivers, since doing so will disconnect and reconnect the existing USB
storage drivers and thereby invalidate the original storage device
handle.

Fix by recording the device path for the loaded image's DeviceHandle
at initialisation time and later using the recorded device path to
locate the appropriate device handle.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/interface/efi/efi_init.c
src/interface/efi/efi_local.c

index e1041a5ec52ca049f66dbe5a3a6cb07175a42a83..70212b184c9a183d81509a1a4dbbc47247f454d6 100644 (file)
@@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/rotate.h>
 #include <ipxe/efi/efi.h>
 #include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_utils.h>
 #include <ipxe/efi/Protocol/LoadedImage.h>
 
 /** Image handle passed to entry point */
@@ -34,6 +35,9 @@ EFI_HANDLE efi_image_handle;
 /** Loaded image protocol for this image */
 EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
 
+/** Device path for the loaded image's device handle */
+EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
+
 /** System table passed to entry point
  *
  * We construct the symbol name efi_systab via the PLATFORM macro.
@@ -152,6 +156,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
        struct efi_protocol *prot;
        struct efi_config_table *tab;
        void *loaded_image;
+       void *device_path;
+       void *device_path_copy;
+       size_t device_path_len;
        EFI_STATUS efirc;
        int rc;
 
@@ -230,6 +237,33 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
        DBGC ( systab, "EFI image base address %p\n",
               efi_loaded_image->ImageBase );
 
+       /* Get loaded image's device handle's device path */
+       if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle,
+                               &efi_device_path_protocol_guid,
+                               &device_path, image_handle, NULL,
+                               EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( systab, "EFI could not get loaded image's device path: "
+                      "%s", strerror ( rc ) );
+               goto err_no_device_path;
+       }
+
+       /* Make a copy of the loaded image's device handle's device
+        * path, since the device handle itself may become invalidated
+        * when we load our own drivers.
+        */
+       device_path_len = ( efi_devpath_len ( device_path ) +
+                           sizeof ( EFI_DEVICE_PATH_PROTOCOL ) );
+       if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, device_path_len,
+                                         &device_path_copy ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               goto err_alloc_device_path;
+       }
+       memcpy ( device_path_copy, device_path, device_path_len );
+       efi_loaded_image_path = device_path_copy;
+       DBGC ( systab, "EFI image device path %s\n",
+              efi_devpath_text ( efi_loaded_image_path ) );
+
        /* 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
@@ -261,6 +295,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
  err_driver_install:
        bs->CloseEvent ( efi_shutdown_event );
  err_create_event:
+       bs->FreePool ( efi_loaded_image_path );
+ err_alloc_device_path:
+ err_no_device_path:
  err_no_loaded_image:
  err_missing_table:
  err_missing_protocol:
@@ -291,6 +328,9 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
        /* Uninstall exit boot services event */
        bs->CloseEvent ( efi_shutdown_event );
 
+       /* Free copy of loaded image's device handle's device path */
+       bs->FreePool ( efi_loaded_image_path );
+
        DBGC ( systab, "EFI image unloaded\n" );
 
        return 0;
index bd010ad2eee34f46aa5d3d83c09d737fb4885aa4..79ea822f31696c8a506c515bb21c14b74086b959 100644 (file)
@@ -307,6 +307,7 @@ static int efi_local_open_volume ( struct efi_local *local,
        EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
        int ( * check ) ( struct efi_local *local, EFI_HANDLE device,
                          EFI_FILE_PROTOCOL *root, const char *volume );
+       EFI_DEVICE_PATH_PROTOCOL *path;
        EFI_FILE_PROTOCOL *root;
        EFI_HANDLE *handles;
        EFI_HANDLE device;
@@ -328,8 +329,18 @@ static int efi_local_open_volume ( struct efi_local *local,
                }
                check = efi_local_check_volume_name;
        } else {
-               /* Use our loaded image's device handle */
-               handles = &efi_loaded_image->DeviceHandle;
+               /* Locate filesystem from which we were loaded */
+               path = efi_loaded_image_path;
+               if ( ( efirc = bs->LocateDevicePath ( protocol, &path,
+                                                     &device ) ) != 0 ) {
+                       rc = -EEFI ( efirc );
+                       DBGC ( local, "LOCAL %p could not locate file system "
+                              "on %s: %s\n", local,
+                              efi_devpath_text ( efi_loaded_image_path ),
+                              strerror ( rc ) );
+                       return rc;
+               }
+               handles = &device;
                num_handles = 1;
                check = NULL;
        }