mirror of
https://github.com/ipxe/ipxe
synced 2026-05-04 03:03:43 +03:00
[efi] Install protocols onto a dedicated device handle
When we fall back to using our own loaded image's device handle (instead of the most recently opened SNP device handle), we may find that some protocols (e.g. EFI_SIMPLE_FILE_SYSTEM_PROTOCOL) are already present on this handle. Fix by creating a child device handle with an added Uri() device path component, and installing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and others onto this handle instead of onto the identified parent device handle. This also provides a way for us to communicate the image URI to a chainloaded iPXE, so that that iPXE can set its current working URI appropriately. A side effect of this change is that the EFI_SIMPLE_NETWORK_PROTOCOL will be found on the parent of the loaded image's device handle, rather than directly on the loaded image's device handle. This will not cause problems for a chainloaded iPXE, since that will already use LocateDevicePath() to find EFI_SIMPLE_NETWORK_PROTOCOL (or the EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL created by MnpDxe) and so will already find the instance on the parent device handle. If other UEFI executables are found to exist that do require the protocols to be installed directly on the loaded image's device handle, then we could potentially install copies of these protocol instances on the device handle. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
+103
-13
@@ -36,6 +36,7 @@ FILE_SECBOOT ( PERMITTED );
|
||||
#include <ipxe/efi/efi_image.h>
|
||||
#include <ipxe/efi/efi_shim.h>
|
||||
#include <ipxe/efi/efi_fdt.h>
|
||||
#include <ipxe/efi/efi_utils.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/features.h>
|
||||
@@ -61,7 +62,7 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
|
||||
#define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc )
|
||||
|
||||
/**
|
||||
* Create device path for image
|
||||
* Create a URI device path for image
|
||||
*
|
||||
* @v image EFI image
|
||||
* @v parent Parent device path
|
||||
@@ -70,7 +71,51 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
|
||||
* The caller must eventually free() the device path.
|
||||
*/
|
||||
static EFI_DEVICE_PATH_PROTOCOL *
|
||||
efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
|
||||
efi_image_uripath ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
URI_DEVICE_PATH *uripath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
size_t uri_len;
|
||||
size_t prefix_len;
|
||||
size_t uripath_len;
|
||||
size_t len;
|
||||
|
||||
/* Calculate device path lengths */
|
||||
prefix_len = efi_path_len ( parent );
|
||||
uri_len = format_uri ( image->uri, NULL, 0 );
|
||||
uripath_len = ( sizeof ( *uripath ) + uri_len + 1 /* NUL */ );
|
||||
len = ( prefix_len + uripath_len + sizeof ( *end ) );
|
||||
|
||||
/* Allocate device path */
|
||||
path = zalloc ( len );
|
||||
if ( ! path )
|
||||
return NULL;
|
||||
|
||||
/* Construct device path */
|
||||
memcpy ( path, parent, prefix_len );
|
||||
uripath = ( ( ( void * ) path ) + prefix_len );
|
||||
uripath->Header.Type = MESSAGING_DEVICE_PATH;
|
||||
uripath->Header.SubType = MSG_URI_DP;
|
||||
uripath->Header.Length[0] = ( uripath_len & 0xff );
|
||||
uripath->Header.Length[1] = ( uripath_len >> 8 );
|
||||
format_uri ( image->uri, uripath->Uri, ( uri_len + 1 /* NUL */ ) );
|
||||
end = ( ( ( void * ) uripath ) + uripath_len );
|
||||
efi_path_terminate ( end );
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file device path for image
|
||||
*
|
||||
* @v image EFI image
|
||||
* @v parent Parent device path
|
||||
* @ret path Device path, or NULL on failure
|
||||
*
|
||||
* The caller must eventually free() the device path.
|
||||
*/
|
||||
static EFI_DEVICE_PATH_PROTOCOL *
|
||||
efi_image_filepath ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
FILEPATH_DEVICE_PATH *filepath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
@@ -154,11 +199,13 @@ static int efi_image_exec ( struct image *image ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
struct efi_snp_device *snpdev;
|
||||
struct net_device *netdev;
|
||||
EFI_DEVICE_PATH_PROTOCOL *basepath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *devpath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *imgpath;
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded;
|
||||
struct image *shim;
|
||||
struct image *exec;
|
||||
EFI_HANDLE parent;
|
||||
EFI_HANDLE device;
|
||||
EFI_HANDLE handle;
|
||||
EFI_MEMORY_TYPE type;
|
||||
@@ -167,24 +214,56 @@ static int efi_image_exec ( struct image *image ) {
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Find an appropriate device handle to use */
|
||||
/* Find an appropriate parent device handle to use */
|
||||
snpdev = last_opened_snpdev();
|
||||
if ( snpdev ) {
|
||||
/* We have a netX: use this as the base device */
|
||||
device = snpdev->handle;
|
||||
devpath = snpdev->path;
|
||||
parent = snpdev->handle;
|
||||
basepath = snpdev->path;
|
||||
netdev = netdev_get ( snpdev->netdev );
|
||||
DBGC ( image, "EFIIMAGE %s using %s %s\n", image->name,
|
||||
netdev->name, efi_devpath_text ( devpath ) );
|
||||
netdev->name, efi_devpath_text ( basepath ) );
|
||||
} else {
|
||||
/* We have no netX: fall back to using our own loaded
|
||||
* image's device.
|
||||
*/
|
||||
device = efi_loaded_image->DeviceHandle;
|
||||
devpath = efi_loaded_image_path;
|
||||
parent = efi_loaded_image->DeviceHandle;
|
||||
basepath = efi_loaded_image_path;
|
||||
netdev = NULL;
|
||||
DBGC ( image, "EFIIMAGE %s using %s\n",
|
||||
image->name, efi_devpath_text ( devpath ) );
|
||||
image->name, efi_devpath_text ( basepath ) );
|
||||
}
|
||||
|
||||
/* Construct URI device path */
|
||||
devpath = efi_image_uripath ( image, basepath );
|
||||
if ( ! devpath ) {
|
||||
DBGC ( image, "EFIIMAGE %s could not create URI device path\n",
|
||||
image->name );
|
||||
rc = -ENOMEM;
|
||||
goto err_device_path;
|
||||
}
|
||||
|
||||
/* Create device handle */
|
||||
device = NULL;
|
||||
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
||||
&device,
|
||||
&efi_device_path_protocol_guid, devpath,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( image, "EFIIMAGE %s could not install %s: %s\n",
|
||||
image->name, efi_devpath_text ( devpath ),
|
||||
strerror ( rc ) );
|
||||
goto err_path_install;
|
||||
}
|
||||
DBGC ( image, "EFIIMAGE %s device is %s\n",
|
||||
image->name, efi_handle_name ( device ) );
|
||||
|
||||
/* Add as a child of the parent device */
|
||||
if ( ( rc = efi_child_add ( parent, device ) ) != 0 ) {
|
||||
DBGC ( image, "EFIIMAGE %s could not become child of %s: %s\n",
|
||||
image->name, efi_handle_name ( parent ),
|
||||
strerror ( rc ) );
|
||||
goto err_child_add;
|
||||
}
|
||||
|
||||
/* Use shim instead of directly executing image if applicable */
|
||||
@@ -231,14 +310,16 @@ static int efi_image_exec ( struct image *image ) {
|
||||
goto err_fdt_install;
|
||||
}
|
||||
|
||||
/* Create device path for image */
|
||||
imgpath = efi_image_path ( exec, devpath );
|
||||
/* Create file device path for image */
|
||||
imgpath = efi_image_filepath ( exec, devpath );
|
||||
if ( ! imgpath ) {
|
||||
DBGC ( image, "EFIIMAGE %s could not create device path\n",
|
||||
image->name );
|
||||
DBGC ( image, "EFIIMAGE %s could not create file device "
|
||||
"path\n", image->name );
|
||||
rc = -ENOMEM;
|
||||
goto err_image_path;
|
||||
}
|
||||
DBGC ( image, "EFIIMAGE %s file is %s\n",
|
||||
image->name, efi_devpath_text ( imgpath ) );
|
||||
|
||||
/* Create command line for image */
|
||||
cmdline = efi_image_cmdline ( image );
|
||||
@@ -373,6 +454,15 @@ static int efi_image_exec ( struct image *image ) {
|
||||
unregister_image ( image );
|
||||
err_register_image:
|
||||
image->flags ^= toggle;
|
||||
efi_child_del ( parent, device );
|
||||
err_child_add:
|
||||
bs->UninstallMultipleProtocolInterfaces (
|
||||
device,
|
||||
&efi_device_path_protocol_guid, devpath,
|
||||
NULL );
|
||||
err_path_install:
|
||||
free ( devpath );
|
||||
err_device_path:
|
||||
netdev_put ( netdev );
|
||||
return rc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user