aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-05-15 16:28:40 +0100
committerMichael Brown <mcb30@ipxe.org>2023-05-17 15:41:10 +0100
commitc5475b2b2a8b78ab005d7d7244abaf9d4fb45dd3 (patch)
tree3570b6832cfe040b67dbb65a13f21e7ea095a5e6
parentc4a8d90387437425faec91bdee42a254944bab76 (diff)
downloadipxe-c5475b2b2a8b78ab005d7d7244abaf9d4fb45dd3.zip
ipxe-c5475b2b2a8b78ab005d7d7244abaf9d4fb45dd3.tar.gz
ipxe-c5475b2b2a8b78ab005d7d7244abaf9d4fb45dd3.tar.bz2
[efi] Add support for executing images via a shim
Add support for using a shim as a helper to execute an EFI image. When a shim has been specified via shim(), the shim image will be passed to LoadImage() instead of the selected EFI image and the command line will be prepended with the name of the selected EFI image. The selected EFI image will be accessible to the shim via the virtual filesystem as a hidden file. Do not install the EFI PXE APIs when using a shim, since if shim finds EFI_PXE_BASE_CODE_PROTOCOL on the loaded image's device handle then it will attempt to download files afresh instead of using the files already downloaded by iPXE and exposed via the EFI_SIMPLE_FILE_SYSTEM protocol. (Experience shows that there is no point in trying to get a fix for this upstreamed into shim.) Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/image/efi_image.c71
-rw-r--r--src/include/ipxe/efi/efi_image.h29
-rw-r--r--src/include/ipxe/image.h9
-rw-r--r--src/include/usr/shimmgmt.h16
-rw-r--r--src/usr/shimmgmt.c52
5 files changed, 161 insertions, 16 deletions
diff --git a/src/image/efi_image.c b/src/image/efi_image.c
index 6a7bba0..7649d8f 100644
--- a/src/image/efi_image.c
+++ b/src/image/efi_image.c
@@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi_wrap.h>
#include <ipxe/efi/efi_pxe.h>
#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_image.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
@@ -55,6 +56,11 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
"Could not start image" )
#define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc )
+/** EFI shim image */
+struct image_tag efi_shim __image_tag = {
+ .name = "SHIM",
+};
+
/**
* Create device path for image
*
@@ -104,23 +110,43 @@ efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
/**
* Create command line for image
*
- * @v image EFI image
+ * @v image EFI image
+ * @v shim Shim image, or NULL
* @ret cmdline Command line, or NULL on failure
*/
-static wchar_t * efi_image_cmdline ( struct image *image ) {
+static wchar_t * efi_image_cmdline ( struct image *image,
+ struct image *shim ) {
+ const char *arg0;
+ const char *arg1;
+ const char *args;
wchar_t *cmdline;
size_t len;
- len = ( strlen ( image->name ) +
- ( image->cmdline ?
- ( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
+ /* Select command line components */
+ arg0 = image->name;
+ arg1 = NULL;
+ args = image->cmdline;
+ if ( shim ) {
+ arg0 = shim->name;
+ if ( shim->cmdline ) {
+ /* "<shim.efi> <shim explicit cmdline>" */
+ args = shim->cmdline;
+ } else {
+ /* "<shim.efi> <image.efi> <image cmdline>" */
+ arg1 = image->name;
+ }
+ }
+
+ /* Allocate and construct command line */
+ len = ( strlen ( arg0 ) +
+ ( arg1 ? ( 1 /* " " */ + strlen ( arg1 ) ) : 0 ) +
+ ( args ? ( 1 /* " " */ + strlen ( args ) ) : 0 ) );
cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
if ( ! cmdline )
return NULL;
- efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
- image->name,
- ( image->cmdline ? " " : "" ),
- ( image->cmdline ? image->cmdline : "" ) );
+ efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s%s%s", arg0,
+ ( arg1 ? " " : "" ), arg1, ( args ? " " : "" ), args );
+
return cmdline;
}
@@ -138,6 +164,8 @@ static int efi_image_exec ( struct image *image ) {
EFI_LOADED_IMAGE_PROTOCOL *image;
void *interface;
} loaded;
+ struct image *shim;
+ struct image *exec;
EFI_HANDLE handle;
EFI_MEMORY_TYPE type;
wchar_t *cmdline;
@@ -154,6 +182,15 @@ static int efi_image_exec ( struct image *image ) {
goto err_no_snpdev;
}
+ /* Use shim instead of directly executing image if applicable */
+ shim = ( efi_can_load ( image ) ?
+ NULL : find_image_tag ( &efi_shim ) );
+ exec = ( shim ? shim : image );
+ if ( shim ) {
+ DBGC ( image, "EFIIMAGE %s executing via %s\n",
+ image->name, shim->name );
+ }
+
/* Re-register as a hidden image to allow for access via file I/O */
toggle = ( ~image->flags & IMAGE_HIDDEN );
image->flags |= IMAGE_HIDDEN;
@@ -167,8 +204,9 @@ static int efi_image_exec ( struct image *image ) {
goto err_file_install;
}
- /* Install PXE base code protocol */
- if ( ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){
+ /* Install PXE base code protocol (unless using a shim) */
+ if ( ( ! shim ) &&
+ ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){
DBGC ( image, "EFIIMAGE %s could not install PXE protocol: "
"%s\n", image->name, strerror ( rc ) );
goto err_pxe_install;
@@ -182,7 +220,7 @@ static int efi_image_exec ( struct image *image ) {
}
/* Create device path for image */
- path = efi_image_path ( image, snpdev->path );
+ path = efi_image_path ( exec, snpdev->path );
if ( ! path ) {
DBGC ( image, "EFIIMAGE %s could not create device path\n",
image->name );
@@ -191,7 +229,7 @@ static int efi_image_exec ( struct image *image ) {
}
/* Create command line for image */
- cmdline = efi_image_cmdline ( image );
+ cmdline = efi_image_cmdline ( image, shim );
if ( ! cmdline ) {
DBGC ( image, "EFIIMAGE %s could not create command line\n",
image->name );
@@ -202,8 +240,8 @@ static int efi_image_exec ( struct image *image ) {
/* Attempt loading image */
handle = NULL;
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path,
- user_to_virt ( image->data, 0 ),
- image->len, &handle ) ) != 0 ) {
+ user_to_virt ( exec->data, 0 ),
+ exec->len, &handle ) ) != 0 ) {
/* Not an EFI image */
rc = -EEFI_LOAD ( efirc );
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
@@ -299,7 +337,8 @@ static int efi_image_exec ( struct image *image ) {
err_image_path:
efi_download_uninstall ( snpdev->handle );
err_download_install:
- efi_pxe_uninstall ( snpdev->handle );
+ if ( ! shim )
+ efi_pxe_uninstall ( snpdev->handle );
err_pxe_install:
efi_file_uninstall ( snpdev->handle );
err_file_install:
diff --git a/src/include/ipxe/efi/efi_image.h b/src/include/ipxe/efi/efi_image.h
new file mode 100644
index 0000000..23a6e0f
--- /dev/null
+++ b/src/include/ipxe/efi/efi_image.h
@@ -0,0 +1,29 @@
+#ifndef _IPXE_EFI_IMAGE_H
+#define _IPXE_EFI_IMAGE_H
+
+/** @file
+ *
+ * EFI images
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/image.h>
+
+extern struct image_tag efi_shim __image_tag;
+
+extern struct image_type efi_image_type[] __image_type ( PROBE_NORMAL );
+
+/**
+ * Check if EFI image can be loaded directly
+ *
+ * @v image EFI image
+ * @ret can_load EFI image can be loaded directly
+ */
+static inline int efi_can_load ( struct image *image ) {
+
+ return ( image->type == efi_image_type );
+}
+
+#endif /* _IPXE_EFI_IMAGE_H */
diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h
index cd51188..bfbf236 100644
--- a/src/include/ipxe/image.h
+++ b/src/include/ipxe/image.h
@@ -257,6 +257,15 @@ static inline void image_untrust ( struct image *image ) {
}
/**
+ * Mark image as hidden
+ *
+ * @v image Image
+ */
+static inline void image_hide ( struct image *image ) {
+ image->flags |= IMAGE_HIDDEN;
+}
+
+/**
* Tag image
*
* @v image Image
diff --git a/src/include/usr/shimmgmt.h b/src/include/usr/shimmgmt.h
new file mode 100644
index 0000000..aef7681
--- /dev/null
+++ b/src/include/usr/shimmgmt.h
@@ -0,0 +1,16 @@
+#ifndef _USR_SHIMMGMT_H
+#define _USR_SHIMMGMT_H
+
+/** @file
+ *
+ * EFI shim management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/image.h>
+
+extern int shim ( struct image *image );
+
+#endif /* _USR_SHIMMGMT_H */
diff --git a/src/usr/shimmgmt.c b/src/usr/shimmgmt.c
new file mode 100644
index 0000000..6b2ca85
--- /dev/null
+++ b/src/usr/shimmgmt.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_image.h>
+#include <usr/shimmgmt.h>
+
+/** @file
+ *
+ * EFI shim management
+ *
+ */
+
+/**
+ * Set shim image
+ *
+ * @v image Shim image, or NULL to clear shim
+ * @ret rc Return status code
+ */
+int shim ( struct image *image ) {
+
+ /* Record (or clear) shim image */
+ image_tag ( image, &efi_shim );
+
+ /* Avoid including image in constructed initrd */
+ if ( image )
+ image_hide ( image );
+
+ return 0;
+}