aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-03-13 14:22:08 +0000
committerMichael Brown <mcb30@ipxe.org>2023-03-13 14:40:47 +0000
commitfda90b253047594f9797d8b0b61760eb91d2de66 (patch)
tree6a591f54eb3ef89a8c64172c7ecb88b4821e7805
parent4b1c0bc715b4a2db03d6bde52c44bc2038c1cb7e (diff)
downloadipxe-fda90b253047594f9797d8b0b61760eb91d2de66.tar.gz
[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, the command line will be prepended with the name of the selected EFI image, and the selected EFI image will be exposed via the virtual filesystem as the fixed second stage file. Construct the default second stage alternative filename based on the EFI removable media filename, since that matches the pattern used by shim for all currently supported architectures. 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.c57
-rw-r--r--src/include/ipxe/efi/efi_shim.h16
-rw-r--r--src/include/usr/shimmgmt.h16
-rw-r--r--src/usr/shimmgmt.c85
4 files changed, 162 insertions, 12 deletions
diff --git a/src/image/efi_image.c b/src/image/efi_image.c
index 300733780..d9e77dd70 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_shim.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
@@ -55,6 +56,9 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
"Could not start image" )
#define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc )
+/** Registered shim, if any */
+struct image *efi_shim;
+
/**
* Create device path for image
*
@@ -104,20 +108,26 @@ 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 prefix Command line prefix, 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,
+ const char *prefix ) {
wchar_t *cmdline;
size_t len;
- len = ( strlen ( image->name ) +
+ len = ( ( prefix ?
+ ( strlen ( prefix ) + 1 /* NUL */ + 1 /* " " */ ) : 0 ) +
+ strlen ( image->name ) +
( image->cmdline ?
( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
if ( ! cmdline )
return NULL;
- efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
+ efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s%s%s",
+ ( prefix ? prefix : "" ),
+ ( prefix ? " " : "" ),
image->name,
( image->cmdline ? " " : "" ),
( image->cmdline ? image->cmdline : "" ) );
@@ -132,12 +142,17 @@ static wchar_t * efi_image_cmdline ( struct image *image ) {
*/
static int efi_image_exec ( struct image *image ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct image *shim = efi_shim;
struct efi_snp_device *snpdev;
EFI_DEVICE_PATH_PROTOCOL *path;
union {
EFI_LOADED_IMAGE_PROTOCOL *image;
void *interface;
} loaded;
+ struct image *exec;
+ struct image *second;
+ const char *prefix;
+ const char *altname;
EFI_HANDLE handle;
EFI_MEMORY_TYPE type;
wchar_t *cmdline;
@@ -153,15 +168,32 @@ static int efi_image_exec ( struct image *image ) {
goto err_no_snpdev;
}
+ /* Choose to execute image via shim if applicable */
+ if ( shim ) {
+ exec = shim;
+ second = image;
+ prefix = shim->name;
+ altname = shim->cmdline;
+ DBGC ( image, "EFIIMAGE %s (aka %s) executing via %s\n",
+ image->name, altname, shim->name );
+ } else {
+ exec = image;
+ second = NULL;
+ prefix = NULL;
+ altname = NULL;
+ }
+
/* Install file I/O protocols */
- if ( ( rc = efi_file_install ( snpdev->handle, NULL, NULL ) ) != 0 ) {
+ if ( ( rc = efi_file_install ( snpdev->handle, second,
+ altname ) ) != 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 ){
+ /* 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;
@@ -175,7 +207,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 );
@@ -184,7 +216,7 @@ static int efi_image_exec ( struct image *image ) {
}
/* Create command line for image */
- cmdline = efi_image_cmdline ( image );
+ cmdline = efi_image_cmdline ( image, prefix );
if ( ! cmdline ) {
DBGC ( image, "EFIIMAGE %s could not create command line\n",
image->name );
@@ -195,8 +227,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",
@@ -292,7 +324,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_shim.h b/src/include/ipxe/efi/efi_shim.h
new file mode 100644
index 000000000..d7abf5eb3
--- /dev/null
+++ b/src/include/ipxe/efi/efi_shim.h
@@ -0,0 +1,16 @@
+#ifndef _IPXE_EFI_SHIM_H
+#define _IPXE_EFI_SHIM_H
+
+/** @file
+ *
+ * EFI shim
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+struct image;
+
+extern struct image *efi_shim;
+
+#endif /* _IPXE_EFI_SHIM_H */
diff --git a/src/include/usr/shimmgmt.h b/src/include/usr/shimmgmt.h
new file mode 100644
index 000000000..19f13e782
--- /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, const char *altname );
+
+#endif /* _USR_SHIMMGMT_H */
diff --git a/src/usr/shimmgmt.c b/src/usr/shimmgmt.c
new file mode 100644
index 000000000..7c064bdcd
--- /dev/null
+++ b/src/usr/shimmgmt.c
@@ -0,0 +1,85 @@
+/*
+ * 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 <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <assert.h>
+#include <ipxe/image.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_shim.h>
+#include <usr/shimmgmt.h>
+
+/** @file
+ *
+ * EFI shim management
+ *
+ */
+
+/**
+ * Set shim image
+ *
+ * @v image Shim image, or NULL to clear shim
+ * @v altname Second stage alternative name, or NULL to use default
+ * @ret rc Return status code
+ */
+int shim ( struct image *image, const char *altname ) {
+ static wchar_t wbootpath[] = EFI_REMOVABLE_MEDIA_FILE_NAME;
+ char bootpath[ sizeof ( wbootpath ) / sizeof ( wbootpath[0] ) ];
+ char *bootname;
+ char *sep;
+ int rc;
+
+ /* Clear any existing shim */
+ image_put ( efi_shim );
+ efi_shim = NULL;
+
+ /* Do nothing more unless a shim is specified */
+ if ( ! image )
+ return 0;
+
+ /* Construct default second stage alternative name */
+ snprintf ( bootpath, sizeof ( bootpath ), "%ls", wbootpath );
+ sep = strrchr ( bootpath, '\\' );
+ assert ( sep != NULL );
+ bootname = ( sep + 1 );
+ assert ( strncasecmp ( bootname, "BOOT", 4 ) == 0 );
+ memcpy ( bootname, "GRUB", 4 );
+
+ /* Use default second stage alternative name, if not specified */
+ if ( ! altname )
+ altname = bootname;
+
+ /* Record second stage alternative name, if any */
+ if ( ( rc = image_set_cmdline ( image, altname ) ) != 0 )
+ return rc;
+
+ /* Record as shim */
+ efi_shim = image_get ( image );
+
+ DBGC ( image, "SHIM %s installed (altname %s)\n",
+ image->name, image->cmdline );
+ return 0;
+}