diff options
-rw-r--r-- | src/image/efi_image.c | 57 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi_shim.h | 16 | ||||
-rw-r--r-- | src/include/usr/shimmgmt.h | 16 | ||||
-rw-r--r-- | src/usr/shimmgmt.c | 85 |
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; +} |