From 4bb521a8c4b324902651714915dfe6fd4a5c36af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 29 Jan 2023 18:48:08 +0000 Subject: [efi] Accept a command line passed to an iPXE image via LoadOptions Treat a command line passed to iPXE via UEFI LoadOptions as an image to be registered at startup, as is already done for the .lkrn, .pxe, and .exe BIOS images. Originally-implemented-by: Ladi Prosek Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_cmdline.h | 18 +++++ src/include/ipxe/errfile.h | 1 + src/interface/efi/efi_cmdline.c | 151 +++++++++++++++++++++++++++++++++++++ src/interface/efi/efi_init.c | 5 ++ 4 files changed, 175 insertions(+) create mode 100644 src/include/ipxe/efi/efi_cmdline.h create mode 100644 src/interface/efi/efi_cmdline.c diff --git a/src/include/ipxe/efi/efi_cmdline.h b/src/include/ipxe/efi/efi_cmdline.h new file mode 100644 index 00000000..45abd549 --- /dev/null +++ b/src/include/ipxe/efi/efi_cmdline.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFI_CMDLINE_H +#define _IPXE_EFI_CMDLINE_H + +/** @file + * + * EFI command line + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +extern const wchar_t *efi_cmdline; +extern size_t efi_cmdline_len; + +#endif /* _IPXE_EFI_CMDLINE_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index c3541e8a..7c3b0c43 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -401,6 +401,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_dynkeymap ( ERRFILE_OTHER | 0x00580000 ) #define ERRFILE_pci_cmd ( ERRFILE_OTHER | 0x00590000 ) #define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 ) +#define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 ) /** @} */ diff --git a/src/interface/efi/efi_cmdline.c b/src/interface/efi/efi_cmdline.c new file mode 100644 index 00000000..b33bebd8 --- /dev/null +++ b/src/interface/efi/efi_cmdline.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2023 Michael Brown . + * + * 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 ); + +/** @file + * + * EFI command line + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** EFI command line (may not be wNUL-terminated */ +const wchar_t *efi_cmdline; + +/** Length of EFI command line (in bytes) */ +size_t efi_cmdline_len; + +/** Internal copy of the command line */ +static char *efi_cmdline_copy; + +/** + * Free command line image + * + * @v refcnt Reference count + */ +static void efi_cmdline_free ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + DBGC ( image, "CMDLINE freeing command line\n" ); + free ( efi_cmdline_copy ); +} + +/** Embedded script representing the command line */ +static struct image efi_cmdline_image = { + .refcnt = REF_INIT ( efi_cmdline_free ), + .name = "", + .type = &script_image_type, +}; + +/** Colour for debug messages */ +#define colour &efi_cmdline_image + +/** + * Initialise EFI command line + * + * @ret rc Return status code + */ +static int efi_cmdline_init ( void ) { + char *cmdline; + size_t len; + int rc; + + /* Do nothing if no command line was specified */ + if ( ! efi_cmdline_len ) { + DBGC ( colour, "CMDLINE found no command line\n" ); + return 0; + } + + /* Allocate ASCII copy of command line */ + len = ( ( efi_cmdline_len / sizeof ( efi_cmdline[0] ) ) + 1 /* NUL */ ); + efi_cmdline_copy = malloc ( len ); + if ( ! efi_cmdline_copy ) { + rc = -ENOMEM; + goto err_alloc; + } + cmdline = efi_cmdline_copy; + snprintf ( cmdline, len, "%ls", efi_cmdline ); + DBGC ( colour, "CMDLINE found command line \"%s\"\n", cmdline ); + + /* Mark command line as consumed */ + efi_cmdline_len = 0; + + /* Strip image name and surrounding whitespace */ + while ( isspace ( *cmdline ) ) + cmdline++; + while ( *cmdline && ( ! isspace ( *cmdline ) ) ) + cmdline++; + while ( isspace ( *cmdline ) ) + cmdline++; + DBGC ( colour, "CMDLINE using command line \"%s\"\n", cmdline ); + + /* Prepare and register image */ + efi_cmdline_image.data = virt_to_user ( cmdline ); + efi_cmdline_image.len = strlen ( cmdline ); + if ( efi_cmdline_image.len && + ( ( rc = register_image ( &efi_cmdline_image ) ) != 0 ) ) { + DBGC ( colour, "CMDLINE could not register command line: %s\n", + strerror ( rc ) ); + goto err_register_image; + } + + /* Drop our reference to the image */ + image_put ( &efi_cmdline_image ); + + return 0; + + err_register_image: + image_put ( &efi_cmdline_image ); + err_alloc: + return rc; +} + +/** + * EFI command line startup function + * + */ +static void efi_cmdline_startup ( void ) { + int rc; + + /* Initialise command line */ + if ( ( rc = efi_cmdline_init() ) != 0 ) { + /* No way to report failure */ + return; + } +} + +/** Command line and initrd initialisation function */ +struct startup_fn efi_cmdline_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "efi_cmdline", + .startup = efi_cmdline_startup, +}; diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 5d98f9ff..d3c5042d 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** Image handle passed to entry point */ @@ -254,6 +255,10 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, DBGC ( systab, "EFI image base address %p\n", efi_loaded_image->ImageBase ); + /* Record command line */ + efi_cmdline = efi_loaded_image->LoadOptions; + efi_cmdline_len = efi_loaded_image->LoadOptionsSize; + /* Get loaded image's device handle's device path */ if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, &efi_device_path_protocol_guid, -- cgit v1.2.1