aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;
+}