diff options
Diffstat (limited to 'src/image/efi_image.c')
| -rw-r--r-- | src/image/efi_image.c | 130 |
1 files changed, 84 insertions, 46 deletions
diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 104753a85..2631530e7 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -18,9 +18,11 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <errno.h> #include <stdlib.h> +#include <string.h> #include <wchar.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_snp.h> @@ -33,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_image.h> #include <ipxe/efi/efi_shim.h> +#include <ipxe/efi/efi_fdt.h> #include <ipxe/image.h> #include <ipxe/init.h> #include <ipxe/features.h> @@ -123,6 +126,25 @@ static wchar_t * efi_image_cmdline ( struct image *image ) { } /** + * Install EFI Flattened Device Tree table (when no FDT support is present) + * + * @v cmdline Command line, or NULL + * @ret rc Return status code + */ +__weak int efi_fdt_install ( const char *cmdline __unused ) { + return 0; +} + +/** + * Uninstall EFI Flattened Device Tree table (when no FDT support is present) + * + * @ret rc Return status code + */ +__weak int efi_fdt_uninstall ( void ) { + return 0; +} + +/** * Execute EFI image * * @v image EFI image @@ -132,12 +154,10 @@ static int efi_image_exec ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; EFI_DEVICE_PATH_PROTOCOL *path; - union { - EFI_LOADED_IMAGE_PROTOCOL *image; - void *interface; - } loaded; + EFI_LOADED_IMAGE_PROTOCOL *loaded; struct image *shim; struct image *exec; + EFI_HANDLE device; EFI_HANDLE handle; EFI_MEMORY_TYPE type; wchar_t *cmdline; @@ -153,6 +173,7 @@ static int efi_image_exec ( struct image *image ) { rc = -ENODEV; goto err_no_snpdev; } + device = snpdev->handle; /* Use shim instead of directly executing image if applicable */ shim = ( efi_can_load ( image ) ? @@ -170,26 +191,33 @@ static int efi_image_exec ( struct image *image ) { goto err_register_image; /* Install file I/O protocols */ - if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) { + if ( ( rc = efi_file_install ( device ) ) != 0 ) { DBGC ( image, "EFIIMAGE %s could not install file protocol: " "%s\n", image->name, strerror ( rc ) ); goto err_file_install; } /* Install PXE base code protocol */ - if ( ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){ + if ( ( rc = efi_pxe_install ( device, snpdev->netdev ) ) != 0 ){ DBGC ( image, "EFIIMAGE %s could not install PXE protocol: " "%s\n", image->name, strerror ( rc ) ); goto err_pxe_install; } /* Install iPXE download protocol */ - if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) { + if ( ( rc = efi_download_install ( device ) ) != 0 ) { DBGC ( image, "EFIIMAGE %s could not install iPXE download " "protocol: %s\n", image->name, strerror ( rc ) ); goto err_download_install; } + /* Install Flattened Device Tree table */ + if ( ( rc = efi_fdt_install ( image->cmdline ) ) != 0 ) { + DBGC ( image, "EFIIMAGE %s could not install FDT: %s\n", + image->name, strerror ( rc ) ); + goto err_fdt_install; + } + /* Create device path for image */ path = efi_image_path ( exec, snpdev->path ); if ( ! path ) { @@ -210,18 +238,22 @@ static int efi_image_exec ( struct image *image ) { /* Install shim special handling if applicable */ if ( shim && - ( ( rc = efi_shim_install ( shim, snpdev->handle, - &cmdline ) ) != 0 ) ){ + ( ( rc = efi_shim_install ( shim, device, &cmdline ) ) != 0 ) ) { DBGC ( image, "EFIIMAGE %s could not install shim handling: " "%s\n", image->name, strerror ( rc ) ); goto err_shim_install; } - /* Attempt loading image */ + /* Attempt loading image + * + * LoadImage() does not (allegedly) modify the image content, + * but requires a non-const pointer to SourceBuffer. We + * therefore use the .rwdata field rather than .data. + */ handle = NULL; if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path, - user_to_virt ( exec->data, 0 ), - exec->len, &handle ) ) != 0 ) { + exec->rwdata, exec->len, + &handle ) ) != 0 ) { /* Not an EFI image */ rc = -EEFI_LOAD ( efirc ); DBGC ( image, "EFIIMAGE %s could not load: %s\n", @@ -234,45 +266,45 @@ static int efi_image_exec ( struct image *image ) { } /* Get the loaded image protocol for the newly loaded image */ - efirc = bs->OpenProtocol ( handle, &efi_loaded_image_protocol_guid, - &loaded.interface, efi_image_handle, - NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - if ( efirc ) { + if ( ( rc = efi_open ( handle, &efi_loaded_image_protocol_guid, + &loaded ) ) != 0 ) { /* Should never happen */ - rc = -EEFI ( efirc ); goto err_open_protocol; } /* Some EFI 1.10 implementations seem not to fill in DeviceHandle */ - if ( loaded.image->DeviceHandle == NULL ) { + if ( loaded->DeviceHandle == NULL ) { DBGC ( image, "EFIIMAGE %s filling in missing DeviceHandle\n", image->name ); - loaded.image->DeviceHandle = snpdev->handle; + loaded->DeviceHandle = device; } /* Sanity checks */ - assert ( loaded.image->ParentHandle == efi_image_handle ); - assert ( loaded.image->DeviceHandle == snpdev->handle ); - assert ( loaded.image->LoadOptionsSize == 0 ); - assert ( loaded.image->LoadOptions == NULL ); + assert ( loaded->ParentHandle == efi_image_handle ); + assert ( loaded->DeviceHandle == device ); + assert ( loaded->LoadOptionsSize == 0 ); + assert ( loaded->LoadOptions == NULL ); /* Record image code type */ - type = loaded.image->ImageCodeType; + type = loaded->ImageCodeType; /* Set command line */ - loaded.image->LoadOptions = cmdline; - loaded.image->LoadOptionsSize = + loaded->LoadOptions = cmdline; + loaded->LoadOptionsSize = ( ( wcslen ( cmdline ) + 1 /* NUL */ ) * sizeof ( wchar_t ) ); /* Release network devices for use via SNP */ efi_snp_release(); /* Wrap calls made by the loaded image (for debugging) */ - efi_wrap ( handle ); + efi_wrap_image ( handle ); /* Reset console since image will probably use it */ console_reset(); + /* Assume that image may cause SNP device to be removed */ + snpdev = NULL; + /* Start the image */ if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) { rc = -EEFI_START ( efirc ); @@ -291,6 +323,7 @@ static int efi_image_exec ( struct image *image ) { rc = 0; err_start_image: + efi_unwrap(); efi_snp_claim(); err_open_protocol: /* If there was no error, then the image must have been @@ -318,11 +351,13 @@ static int efi_image_exec ( struct image *image ) { err_cmdline: free ( path ); err_image_path: - efi_download_uninstall ( snpdev->handle ); + efi_fdt_uninstall(); + err_fdt_install: + efi_download_uninstall ( device ); err_download_install: - efi_pxe_uninstall ( snpdev->handle ); + efi_pxe_uninstall ( device ); err_pxe_install: - efi_file_uninstall ( snpdev->handle ); + efi_file_uninstall ( device ); err_file_install: unregister_image ( image ); err_register_image: @@ -348,11 +383,16 @@ static int efi_image_probe ( struct image *image ) { EFI_STATUS efirc; int rc; - /* Attempt loading image */ + /* Attempt loading image + * + * LoadImage() does not (allegedly) modify the image content, + * but requires a non-const pointer to SourceBuffer. We + * therefore use the .rwdata field rather than .data. + */ handle = NULL; if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, &empty_path, - user_to_virt ( image->data, 0 ), - image->len, &handle ) ) != 0 ) { + image->rwdata, image->len, + &handle ) ) != 0 ) { /* Not an EFI image */ rc = -EEFI_LOAD ( efirc ); DBGC ( image, "EFIIMAGE %s could not load: %s\n", @@ -394,42 +434,40 @@ static int efi_pe_image_probe ( struct image *image ) { const UINT16 magic = ( ( sizeof ( UINTN ) == sizeof ( uint32_t ) ) ? EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC : EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC ); - union { - EFI_IMAGE_DOS_HEADER dos; - EFI_IMAGE_OPTIONAL_HEADER_UNION pe; - } u; + const EFI_IMAGE_DOS_HEADER *dos; + const EFI_IMAGE_OPTIONAL_HEADER_UNION *pe; /* Check for existence of DOS header */ - if ( image->len < sizeof ( u.dos ) ) { + if ( image->len < sizeof ( *dos ) ) { DBGC ( image, "EFIIMAGE %s too short for DOS header\n", image->name ); return -ENOEXEC; } - copy_from_user ( &u.dos, image->data, 0, sizeof ( u.dos ) ); - if ( u.dos.e_magic != EFI_IMAGE_DOS_SIGNATURE ) { + dos = image->data; + if ( dos->e_magic != EFI_IMAGE_DOS_SIGNATURE ) { DBGC ( image, "EFIIMAGE %s missing MZ signature\n", image->name ); return -ENOEXEC; } /* Check for existence of PE header */ - if ( ( image->len < u.dos.e_lfanew ) || - ( ( image->len - u.dos.e_lfanew ) < sizeof ( u.pe ) ) ) { + if ( ( image->len < dos->e_lfanew ) || + ( ( image->len - dos->e_lfanew ) < sizeof ( *pe ) ) ) { DBGC ( image, "EFIIMAGE %s too short for PE header\n", image->name ); return -ENOEXEC; } - copy_from_user ( &u.pe, image->data, u.dos.e_lfanew, sizeof ( u.pe ) ); - if ( u.pe.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) { + pe = ( image->data + dos->e_lfanew ); + if ( pe->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) { DBGC ( image, "EFIIMAGE %s missing PE signature\n", image->name ); return -ENOEXEC; } /* Check PE header magic */ - if ( u.pe.Pe32.OptionalHeader.Magic != magic ) { + if ( pe->Pe32.OptionalHeader.Magic != magic ) { DBGC ( image, "EFIIMAGE %s incorrect magic %04x\n", - image->name, u.pe.Pe32.OptionalHeader.Magic ); + image->name, pe->Pe32.OptionalHeader.Magic ); return -ENOEXEC; } |
