summaryrefslogtreecommitdiffstats
path: root/src/image/efi_image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/image/efi_image.c')
-rw-r--r--src/image/efi_image.c130
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;
}