diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 79890001d..f293c82c7 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -36,6 +36,7 @@ FILE_SECBOOT ( PERMITTED ); #include #include #include +#include #include #include #include @@ -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; }