mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-10-30 07:56:50 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89c75c3c19 | |||
| 57f3fcee5e | |||
| ee786d987f | |||
| 4e3ef0a50e | |||
| 95cc2bffd7 | |||
| e9ea2285c8 | |||
| ad38558c25 | |||
| 7ec04b7416 | |||
| 6e85f25ca6 | |||
| bacfb46821 | |||
| fdf09d852d | |||
| c5475b2b2a |
@ -290,6 +290,9 @@ REQUIRE_OBJECT ( cert_cmd );
|
||||
#ifdef IMAGE_MEM_CMD
|
||||
REQUIRE_OBJECT ( image_mem_cmd );
|
||||
#endif
|
||||
#ifdef SHIM_CMD
|
||||
REQUIRE_OBJECT ( shim_cmd );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Drag in miscellaneous objects
|
||||
|
||||
@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
//#define CONSOLE_SYSLOG /* Syslog console */
|
||||
//#define CONSOLE_SYSLOGS /* Encrypted syslog console */
|
||||
//#define CONSOLE_VMWARE /* VMware logfile console */
|
||||
//#define CONSOLE_DEBUGCON /* Bochs/QEMU/KVM debug port console */
|
||||
#define CONSOLE_DEBUGCON /* Bochs/QEMU/KVM debug port console */
|
||||
//#define CONSOLE_INT13 /* INT13 disk log console */
|
||||
|
||||
/*
|
||||
|
||||
@ -47,6 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define USB_BLOCK /* USB block devices */
|
||||
|
||||
#define REBOOT_CMD /* Reboot command */
|
||||
#define SHIM_CMD /* EFI shim command */
|
||||
|
||||
#if defined ( __i386__ ) || defined ( __x86_64__ )
|
||||
#define IOAPI_X86
|
||||
|
||||
@ -160,6 +160,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
//#define CERT_CMD /* Certificate management commands */
|
||||
//#define IMAGE_MEM_CMD /* Read memory command */
|
||||
#define IMAGE_ARCHIVE_CMD /* Archive image management commands */
|
||||
//#define SHIM_CMD /* EFI shim command */
|
||||
|
||||
/*
|
||||
* ROM-specific options
|
||||
|
||||
126
src/hci/commands/shim_cmd.c
Normal file
126
src/hci/commands/shim_cmd.c
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 <getopt.h>
|
||||
#include <ipxe/command.h>
|
||||
#include <ipxe/parseopt.h>
|
||||
#include <ipxe/efi/efi_image.h>
|
||||
#include <usr/imgmgmt.h>
|
||||
#include <usr/shimmgmt.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI shim command
|
||||
*
|
||||
*/
|
||||
|
||||
/** "shim" options */
|
||||
struct shim_options {
|
||||
/** Download timeout */
|
||||
unsigned long timeout;
|
||||
/** Crutch image name or URI */
|
||||
char *crutch;
|
||||
/** Require third party loader */
|
||||
int require_loader;
|
||||
/** Allow PXE base code protocol */
|
||||
int allow_pxe;
|
||||
};
|
||||
|
||||
/** "shim" option list */
|
||||
static struct option_descriptor shim_opts[] = {
|
||||
OPTION_DESC ( "timeout", 't', required_argument,
|
||||
struct shim_options, timeout, parse_timeout ),
|
||||
OPTION_DESC ( "crutch", 'c', required_argument,
|
||||
struct shim_options, crutch, parse_string ),
|
||||
OPTION_DESC ( "require-loader", 'l', no_argument,
|
||||
struct shim_options, require_loader, parse_flag ),
|
||||
OPTION_DESC ( "allow-pxe", 'p', no_argument,
|
||||
struct shim_options, allow_pxe, parse_flag ),
|
||||
};
|
||||
|
||||
/** "shim" command descriptor */
|
||||
static struct command_descriptor shim_cmd =
|
||||
COMMAND_DESC ( struct shim_options, shim_opts, 0, 1, NULL );
|
||||
|
||||
/**
|
||||
* The "shim" command
|
||||
*
|
||||
* @v argc Argument count
|
||||
* @v argv Argument list
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int shim_exec ( int argc, char **argv ) {
|
||||
struct shim_options opts;
|
||||
struct image *image = NULL;
|
||||
struct image *crutch = NULL;
|
||||
struct image *kernel;
|
||||
char *name_uri;
|
||||
int download;
|
||||
int rc;
|
||||
|
||||
/* Parse options */
|
||||
if ( ( rc = parse_options ( argc, argv, &shim_cmd, &opts ) ) != 0 )
|
||||
goto err_parse;
|
||||
|
||||
/* Decide whether or not to download images */
|
||||
kernel = find_image_tag ( &selected_image );
|
||||
download = ( ! ( kernel && efi_can_load ( kernel ) ) );
|
||||
|
||||
/* Parse name/URI string */
|
||||
name_uri = argv[optind];
|
||||
|
||||
/* Acquire image, if applicable */
|
||||
if ( download && name_uri &&
|
||||
( ( rc = imgacquire ( name_uri, opts.timeout,
|
||||
&image ) ) != 0 ) ) {
|
||||
goto err_image;
|
||||
}
|
||||
|
||||
/* Acquire crutch image, if applicable */
|
||||
if ( download && opts.crutch &&
|
||||
( ( rc = imgacquire ( opts.crutch, opts.timeout,
|
||||
&crutch ) ) != 0 ) ) {
|
||||
goto err_crutch;
|
||||
}
|
||||
|
||||
/* (Un)register as shim */
|
||||
if ( ( rc = shim ( image, crutch, opts.require_loader,
|
||||
opts.allow_pxe ) ) != 0 )
|
||||
goto err_shim;
|
||||
|
||||
err_shim:
|
||||
err_crutch:
|
||||
err_image:
|
||||
err_parse:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Shim commands */
|
||||
struct command shim_commands[] __command = {
|
||||
{
|
||||
.name = "shim",
|
||||
.exec = shim_exec,
|
||||
},
|
||||
};
|
||||
@ -31,6 +31,8 @@ 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/efi/efi_shim.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/features.h>
|
||||
@ -109,18 +111,14 @@ efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
|
||||
*/
|
||||
static wchar_t * efi_image_cmdline ( struct image *image ) {
|
||||
wchar_t *cmdline;
|
||||
size_t len;
|
||||
|
||||
len = ( strlen ( image->name ) +
|
||||
( image->cmdline ?
|
||||
( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
|
||||
cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
|
||||
if ( ! cmdline )
|
||||
/* Allocate and construct command line */
|
||||
if ( efi_asprintf ( &cmdline, "%s%s%s", image->name,
|
||||
( image->cmdline ? " " : "" ),
|
||||
( image->cmdline ? image->cmdline : "" ) ) < 0 ) {
|
||||
return NULL;
|
||||
efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
|
||||
image->name,
|
||||
( image->cmdline ? " " : "" ),
|
||||
( image->cmdline ? image->cmdline : "" ) );
|
||||
}
|
||||
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
@ -138,6 +136,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 +154,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;
|
||||
@ -182,7 +191,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 );
|
||||
@ -199,11 +208,20 @@ static int efi_image_exec ( struct image *image ) {
|
||||
goto err_cmdline;
|
||||
}
|
||||
|
||||
/* Install shim special handling if applicable */
|
||||
if ( shim &&
|
||||
( ( rc = efi_shim_install ( shim, snpdev->handle,
|
||||
&cmdline ) ) != 0 ) ){
|
||||
DBGC ( image, "EFIIMAGE %s could not install shim handling: "
|
||||
"%s\n", image->name, strerror ( rc ) );
|
||||
goto err_shim_install;
|
||||
}
|
||||
|
||||
/* 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",
|
||||
@ -293,6 +311,9 @@ static int efi_image_exec ( struct image *image ) {
|
||||
if ( rc != 0 )
|
||||
bs->UnloadImage ( handle );
|
||||
err_load_image:
|
||||
if ( shim )
|
||||
efi_shim_uninstall();
|
||||
err_shim_install:
|
||||
free ( cmdline );
|
||||
err_cmdline:
|
||||
free ( path );
|
||||
|
||||
29
src/include/ipxe/efi/Protocol/ShimLock.h
Normal file
29
src/include/ipxe/efi/Protocol/ShimLock.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
|
||||
#define _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI "shim lock" protocol
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( BSD3 );
|
||||
|
||||
#define EFI_SHIM_LOCK_PROTOCOL_GUID \
|
||||
{ 0x605dab50, 0xe046, 0x4300, \
|
||||
{ 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }
|
||||
|
||||
typedef
|
||||
EFI_STATUS
|
||||
(*EFI_SHIM_LOCK_VERIFY) (
|
||||
IN VOID *buffer,
|
||||
IN UINT32 size
|
||||
);
|
||||
|
||||
typedef struct _EFI_SHIM_LOCK_PROTOCOL {
|
||||
EFI_SHIM_LOCK_VERIFY Verify;
|
||||
VOID *Reserved1;
|
||||
VOID *Reserved2;
|
||||
} EFI_SHIM_LOCK_PROTOCOL;
|
||||
|
||||
#endif /*_IPXE_EFI_SHIM_LOCK_PROTOCOL_H */
|
||||
@ -197,6 +197,7 @@ extern EFI_GUID efi_pci_io_protocol_guid;
|
||||
extern EFI_GUID efi_pci_root_bridge_io_protocol_guid;
|
||||
extern EFI_GUID efi_pxe_base_code_protocol_guid;
|
||||
extern EFI_GUID efi_serial_io_protocol_guid;
|
||||
extern EFI_GUID efi_shim_lock_protocol_guid;
|
||||
extern EFI_GUID efi_simple_file_system_protocol_guid;
|
||||
extern EFI_GUID efi_simple_network_protocol_guid;
|
||||
extern EFI_GUID efi_simple_pointer_protocol_guid;
|
||||
|
||||
27
src/include/ipxe/efi/efi_image.h
Normal file
27
src/include/ipxe/efi/efi_image.h
Normal file
@ -0,0 +1,27 @@
|
||||
#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_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 */
|
||||
24
src/include/ipxe/efi/efi_shim.h
Normal file
24
src/include/ipxe/efi/efi_shim.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _IPXE_EFI_SHIM_H
|
||||
#define _IPXE_EFI_SHIM_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
|
||||
extern int efi_shim_require_loader;
|
||||
extern int efi_shim_allow_pxe;
|
||||
extern struct image_tag efi_shim __image_tag;
|
||||
extern struct image_tag efi_shim_crutch __image_tag;
|
||||
|
||||
extern int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
|
||||
wchar_t **cmdline );
|
||||
extern void efi_shim_uninstall ( void );
|
||||
|
||||
#endif /* _IPXE_EFI_SHIM_H */
|
||||
@ -19,6 +19,8 @@ extern int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt,
|
||||
va_list args );
|
||||
extern int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize,
|
||||
const char *fmt, ... );
|
||||
extern int efi_vasprintf ( wchar_t **strp, const char *fmt, va_list args );
|
||||
extern int efi_asprintf ( wchar_t **strp, const char *fmt, ... );
|
||||
|
||||
/**
|
||||
* Write a formatted string to a wide-character buffer
|
||||
|
||||
@ -78,6 +78,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
|
||||
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
|
||||
#define ERRFILE_acpimac ( ERRFILE_CORE | 0x00280000 )
|
||||
#define ERRFILE_efi_strings ( ERRFILE_CORE | 0x00290000 )
|
||||
|
||||
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
|
||||
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
|
||||
@ -404,6 +405,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 )
|
||||
#define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 )
|
||||
#define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 )
|
||||
#define ERRFILE_efi_shim ( ERRFILE_OTHER | 0x005d0000 )
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -256,6 +256,15 @@ static inline void image_untrust ( struct image *image ) {
|
||||
image->flags &= ~IMAGE_TRUSTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark image as hidden
|
||||
*
|
||||
* @v image Image
|
||||
*/
|
||||
static inline void image_hide ( struct image *image ) {
|
||||
image->flags |= IMAGE_HIDDEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag image
|
||||
*
|
||||
|
||||
17
src/include/usr/shimmgmt.h
Normal file
17
src/include/usr/shimmgmt.h
Normal file
@ -0,0 +1,17 @@
|
||||
#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, struct image *crutch,
|
||||
int require_loader, int allow_pxe );
|
||||
|
||||
#endif /* _USR_SHIMMGMT_H */
|
||||
@ -143,6 +143,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = {
|
||||
"PxeBaseCode" },
|
||||
{ &efi_serial_io_protocol_guid,
|
||||
"SerialIo" },
|
||||
{ &efi_shim_lock_protocol_guid,
|
||||
"ShimLock" },
|
||||
{ &efi_simple_file_system_protocol_guid,
|
||||
"SimpleFileSystem" },
|
||||
{ &efi_simple_network_protocol_guid,
|
||||
|
||||
@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
|
||||
#include <ipxe/efi/Protocol/PxeBaseCode.h>
|
||||
#include <ipxe/efi/Protocol/SerialIo.h>
|
||||
#include <ipxe/efi/Protocol/ShimLock.h>
|
||||
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
|
||||
#include <ipxe/efi/Protocol/SimpleNetwork.h>
|
||||
#include <ipxe/efi/Protocol/SimplePointer.h>
|
||||
@ -227,6 +228,10 @@ EFI_GUID efi_pxe_base_code_protocol_guid
|
||||
EFI_GUID efi_serial_io_protocol_guid
|
||||
= EFI_SERIAL_IO_PROTOCOL_GUID;
|
||||
|
||||
/** Shim lock protocol GUID */
|
||||
EFI_GUID efi_shim_lock_protocol_guid
|
||||
= EFI_SHIM_LOCK_PROTOCOL_GUID;
|
||||
|
||||
/** Simple file system protocol GUID */
|
||||
EFI_GUID efi_simple_file_system_protocol_guid
|
||||
= EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
|
||||
256
src/interface/efi/efi_shim.c
Normal file
256
src/interface/efi/efi_shim.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 (at your option) 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 <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/efi_strings.h>
|
||||
#include <ipxe/efi/efi_shim.h>
|
||||
#include <ipxe/efi/Protocol/PxeBaseCode.h>
|
||||
#include <ipxe/efi/Protocol/ShimLock.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/**
|
||||
* Require use of a third party loader binary
|
||||
*
|
||||
* The UEFI shim is gradually becoming less capable of directly
|
||||
* executing a Linux kernel image, due to an ever increasing list of
|
||||
* assumptions that it will only ever be used in conjunction with a
|
||||
* second stage loader binary such as GRUB.
|
||||
*
|
||||
* For example: shim will erroneously complain if the image that it
|
||||
* loads and executes does not in turn call in to the "shim lock
|
||||
* protocol" to verify a separate newly loaded binary before calling
|
||||
* ExitBootServices(), even if no such separate binary is used or
|
||||
* required.
|
||||
*
|
||||
* Experience shows that there is unfortunately no point in trying to
|
||||
* get a fix for this upstreamed into shim. We therefore default to
|
||||
* reducing the Secure Boot attack surface by removing, where
|
||||
* possible, this spurious requirement for the use of an additional
|
||||
* second stage loader.
|
||||
*
|
||||
* This option may be used to require the use of an additional second
|
||||
* stage loader binary, in case this behaviour is ever desirable.
|
||||
*/
|
||||
int efi_shim_require_loader = 0;
|
||||
|
||||
/**
|
||||
* Allow use of PXE base code protocol
|
||||
*
|
||||
* We provide shim with access to all of the relevant downloaded files
|
||||
* via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim
|
||||
* will instead try to redownload the files via TFTP since it prefers
|
||||
* to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle.
|
||||
*
|
||||
* Experience shows that there is unfortunately no point in trying to
|
||||
* get a fix for this upstreamed into shim. We therefore default to
|
||||
* working around this undesirable behaviour by stopping the PXE base
|
||||
* code protocol before invoking shim.
|
||||
*
|
||||
* This option may be used to allow shim to use the PXE base code
|
||||
* protocol, in case this behaviour is ever desirable.
|
||||
*/
|
||||
int efi_shim_allow_pxe = 0;
|
||||
|
||||
/** UEFI shim image */
|
||||
struct image_tag efi_shim __image_tag = {
|
||||
.name = "SHIM",
|
||||
};
|
||||
|
||||
/** UEFI shim crutch image */
|
||||
struct image_tag efi_shim_crutch __image_tag = {
|
||||
.name = "SHIMCRUTCH",
|
||||
};
|
||||
|
||||
/** Original GetMemoryMap() function */
|
||||
static EFI_GET_MEMORY_MAP efi_shim_orig_map;
|
||||
|
||||
/**
|
||||
* Unlock UEFI shim
|
||||
*
|
||||
* @v len Memory map size
|
||||
* @v map Memory map
|
||||
* @v key Memory map key
|
||||
* @v desclen Descriptor size
|
||||
* @v descver Descriptor version
|
||||
* @ret efirc EFI status code
|
||||
*
|
||||
*/
|
||||
static EFIAPI EFI_STATUS efi_shim_unlock ( UINTN *len,
|
||||
EFI_MEMORY_DESCRIPTOR *map,
|
||||
UINTN *key, UINTN *desclen,
|
||||
UINT32 *descver ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
uint8_t empty[0];
|
||||
union {
|
||||
EFI_SHIM_LOCK_PROTOCOL *lock;
|
||||
void *interface;
|
||||
} u;
|
||||
EFI_STATUS efirc;
|
||||
|
||||
/* Locate shim lock protocol */
|
||||
if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
|
||||
NULL, &u.interface ) ) == 0 ) {
|
||||
u.lock->Verify ( empty, sizeof ( empty ) );
|
||||
DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
|
||||
}
|
||||
|
||||
/* Hand off to original GetMemoryMap() */
|
||||
return efi_shim_orig_map ( len, map, key, desclen, descver );
|
||||
}
|
||||
|
||||
/**
|
||||
* Inhibit use of PXE base code
|
||||
*
|
||||
* @v handle EFI handle
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_shim_inhibit_pxe ( EFI_HANDLE handle ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
union {
|
||||
EFI_PXE_BASE_CODE_PROTOCOL *pxe;
|
||||
void *interface;
|
||||
} u;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Locate PXE base code */
|
||||
if ( ( efirc = bs->OpenProtocol ( handle,
|
||||
&efi_pxe_base_code_protocol_guid,
|
||||
&u.interface, efi_image_handle, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
|
||||
strerror ( rc ) );
|
||||
goto err_no_base;
|
||||
}
|
||||
|
||||
/* Stop PXE base code */
|
||||
if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
|
||||
strerror ( rc ) );
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
rc = 0;
|
||||
DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
|
||||
|
||||
err_stop:
|
||||
bs->CloseProtocol ( handle, &efi_pxe_base_code_protocol_guid,
|
||||
efi_image_handle, NULL );
|
||||
err_no_base:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update command line
|
||||
*
|
||||
* @v shim Shim image
|
||||
* @v cmdline Command line to update
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
|
||||
wchar_t *shimcmdline;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Construct new command line */
|
||||
len = ( shim->cmdline ?
|
||||
efi_asprintf ( &shimcmdline, "%s %s", shim->name,
|
||||
shim->cmdline ) :
|
||||
efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
|
||||
*cmdline ) );
|
||||
if ( len < 0 ) {
|
||||
rc = len;
|
||||
DBGC ( &efi_shim, "SHIM could not construct command line: "
|
||||
"%s\n", strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Replace command line */
|
||||
free ( *cmdline );
|
||||
*cmdline = shimcmdline;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install UEFI shim special handling
|
||||
*
|
||||
* @v shim Shim image
|
||||
* @v handle EFI device handle
|
||||
* @v cmdline Command line to update
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
|
||||
wchar_t **cmdline ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
int rc;
|
||||
|
||||
/* Intercept GetMemoryMap() via boot services table */
|
||||
efi_shim_orig_map = bs->GetMemoryMap;
|
||||
if ( ! efi_shim_require_loader )
|
||||
bs->GetMemoryMap = efi_shim_unlock;
|
||||
|
||||
/* Stop PXE base code */
|
||||
if ( ( ! efi_shim_allow_pxe ) &&
|
||||
( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
|
||||
goto err_inhibit_pxe;
|
||||
}
|
||||
|
||||
/* Update command line */
|
||||
if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
|
||||
goto err_cmdline;
|
||||
|
||||
return 0;
|
||||
|
||||
err_cmdline:
|
||||
err_inhibit_pxe:
|
||||
bs->GetMemoryMap = efi_shim_orig_map;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
void efi_shim_uninstall ( void ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
|
||||
/* Restore original GetMemoryMap() */
|
||||
bs->GetMemoryMap = efi_shim_orig_map;
|
||||
}
|
||||
@ -25,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/vsprintf.h>
|
||||
#include <ipxe/efi/efi_strings.h>
|
||||
|
||||
@ -150,3 +152,45 @@ int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ) {
|
||||
va_end ( args );
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a formatted string to newly allocated memory.
|
||||
*
|
||||
* @v wstrp Pointer to hold allocated string
|
||||
* @v fmt Format string
|
||||
* @v args Arguments corresponding to the format string
|
||||
* @ret len Length of formatted string (in wide characters)
|
||||
*/
|
||||
int efi_vasprintf ( wchar_t **wstrp, const char *fmt, va_list args ) {
|
||||
size_t len;
|
||||
va_list args_tmp;
|
||||
|
||||
/* Calculate length needed for string */
|
||||
va_copy ( args_tmp, args );
|
||||
len = ( efi_vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 );
|
||||
va_end ( args_tmp );
|
||||
|
||||
/* Allocate and fill string */
|
||||
*wstrp = malloc ( len * sizeof ( **wstrp ) );
|
||||
if ( ! *wstrp )
|
||||
return -ENOMEM;
|
||||
return efi_vsnprintf ( *wstrp, len, fmt, args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a formatted string to newly allocated memory.
|
||||
*
|
||||
* @v wstrp Pointer to hold allocated string
|
||||
* @v fmt Format string
|
||||
* @v ... Arguments corresponding to the format string
|
||||
* @ret len Length of formatted string (in wide characters)
|
||||
*/
|
||||
int efi_asprintf ( wchar_t **wstrp, const char *fmt, ... ) {
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
va_start ( args, fmt );
|
||||
len = efi_vasprintf ( wstrp, fmt, args );
|
||||
va_end ( args );
|
||||
return len;
|
||||
}
|
||||
|
||||
63
src/usr/shimmgmt.c
Normal file
63
src/usr/shimmgmt.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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_shim.h>
|
||||
#include <usr/shimmgmt.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI shim management
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set shim image
|
||||
*
|
||||
* @v image Shim image, or NULL to clear shim
|
||||
* @v crutch Shim crutch image, or NULL to clear crutch
|
||||
* @v require_loader Require use of a third party loader
|
||||
* @v allow_pxe Allow use of PXE base code
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int shim ( struct image *image, struct image *crutch,
|
||||
int require_loader, int allow_pxe ) {
|
||||
|
||||
/* Record (or clear) shim and crutch images */
|
||||
image_tag ( image, &efi_shim );
|
||||
image_tag ( crutch, &efi_shim_crutch );
|
||||
|
||||
/* Avoid including images in constructed initrd */
|
||||
if ( image )
|
||||
image_hide ( image );
|
||||
if ( crutch )
|
||||
image_hide ( crutch );
|
||||
|
||||
/* Record configuration */
|
||||
efi_shim_require_loader = require_loader;
|
||||
efi_shim_allow_pxe = allow_pxe;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user