summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2020-08-03 15:26:25 +0100
committerMichael Brown <mcb30@ipxe.org>2020-08-03 15:41:30 +0100
commitc63e61df75c815da1dc93196bbb6a931743b736f (patch)
tree94ed0f9cf3d127dd1c1c0ca6bed5b539031996a4
parentfd47fa8fe1a3542621d5c97af349b3ae38146585 (diff)
downloadqemu-ipxe-issue123.tar.gz
[efi] Use device path to locate filesystem from which we were loadedissue123
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>
-rw-r--r--src/interface/efi/efi_init.c40
-rw-r--r--src/interface/efi/efi_local.c15
2 files changed, 53 insertions, 2 deletions
diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c
index e1041a5e..70212b18 100644
--- a/src/interface/efi/efi_init.c
+++ b/src/interface/efi/efi_init.c
@@ -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;
diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c
index bd010ad2..79ea822f 100644
--- a/src/interface/efi/efi_local.c
+++ b/src/interface/efi/efi_local.c
@@ -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;
}