Skip to content

Commit

Permalink
[efi] Use device path to locate filesystem from which we were loaded
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcb30 committed Aug 3, 2020
1 parent fd47fa8 commit c63e61d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 2 deletions.
40 changes: 40 additions & 0 deletions src/interface/efi/efi_init.c
Expand Up @@ -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 */
Expand All @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 13 additions & 2 deletions src/interface/efi/efi_local.c
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down

0 comments on commit c63e61d

Please sign in to comment.