From: Michael Brown Date: Tue, 7 Apr 2026 12:19:57 +0000 (+0100) Subject: [efi] Add disk log console support X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=07887332cf4e1a97ef8ae1d0ceee983eb024eead;p=thirdparty%2Fipxe.git [efi] Add disk log console support Add support for a disk log partition console, using the same on-disk structures as for the BIOS INT13 console. Signed-off-by: Michael Brown --- diff --git a/src/config/config_efi.c b/src/config/config_efi.c index 8daaa4329..1278bf5bb 100644 --- a/src/config/config_efi.c +++ b/src/config/config_efi.c @@ -47,6 +47,10 @@ REQUIRE_OBJECT ( efi_fbcon ); #ifdef CONSOLE_FRAMEBUFFER REQUIRE_OBJECT ( efi_fbcon ); #endif +#ifdef CONSOLE_DISKLOG +REQUIRE_OBJECT ( efi_disklog ); +#endif + #ifdef DOWNLOAD_PROTO_FILE REQUIRE_OBJECT ( efi_local ); #endif diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index f452648ce..dbd3c9963 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -91,6 +91,7 @@ FILE_SECBOOT ( PERMITTED ); #define ERRFILE_gpio ( ERRFILE_CORE | 0x00320000 ) #define ERRFILE_spcr ( ERRFILE_CORE | 0x00330000 ) #define ERRFILE_disklog ( ERRFILE_CORE | 0x00340000 ) +#define ERRFILE_efi_disklog ( ERRFILE_CORE | 0x00350000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/interface/efi/efi_disklog.c b/src/interface/efi/efi_disklog.c new file mode 100644 index 000000000..789ba7518 --- /dev/null +++ b/src/interface/efi/efi_disklog.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2026 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_SECBOOT ( PERMITTED ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI disk log console + * + */ + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_DISKLOG ) && CONSOLE_EXPLICIT ( CONSOLE_DISKLOG ) ) +#undef CONSOLE_DISKLOG +#define CONSOLE_DISKLOG ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +/** EFI disk log console device handle */ +static EFI_HANDLE efi_disklog_handle; + +/** EFI disk log console block I/O protocol */ +static EFI_BLOCK_IO_PROTOCOL *efi_disklog_block; + +/** EFI disk log console media ID */ +static UINT32 efi_disklog_media_id; + +/** EFI disk log console */ +static struct disklog efi_disklog; + +struct console_driver efi_disklog_console __console_driver; + +/** + * Write current logical block + * + * @ret rc Return status code + */ +static int efi_disklog_write ( void ) { + EFI_BLOCK_IO_PROTOCOL *block = efi_disklog_block; + struct disklog *disklog = &efi_disklog; + EFI_STATUS efirc; + int rc; + + /* Write disk block */ + if ( ( efirc = block->WriteBlocks ( block, efi_disklog_media_id, + disklog->lba, disklog->blksize, + disklog->buffer ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( disklog, "EFIDISKLOG %s could not write LBA %#llx: " + "%s\n", efi_handle_name ( efi_disklog_handle ), + ( ( unsigned long long ) disklog->lba ), + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** EFI disk log console operations */ +static struct disklog_operations efi_disklog_op = { + .write = efi_disklog_write, +}; + +/** + * Write character to console + * + * @v character Character + */ +static void efi_disklog_putchar ( int character ) { + + /* Write character */ + disklog_putchar ( &efi_disklog, character ); +} + +/** + * Open EFI disk log partition + * + * @v handle Block device handle + * @ret rc Return status code + */ +static int efi_disklog_open ( EFI_HANDLE handle ) { + struct disklog *disklog = &efi_disklog; + EFI_BLOCK_IO_PROTOCOL *block; + EFI_BLOCK_IO_MEDIA *media; + EFI_PARTITION_INFO_PROTOCOL *part; + void *buffer; + EFI_STATUS efirc; + int rc; + + /* Record handle */ + efi_disklog_handle = handle; + + /* Open block I/O protocol for ephemeral usage */ + if ( ( rc = efi_open ( handle, &efi_block_io_protocol_guid, + &block ) ) != 0 ) { + DBGC ( disklog, "EFIDISKLOG %s could not open: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_open; + } + media = block->Media; + efi_disklog_block = block; + efi_disklog_media_id = media->MediaId; + + /* Check this is a partition */ + if ( ! media->LogicalPartition ) { + DBGC2 ( disklog, "EFIDISKLOG %s is not a partition\n", + efi_handle_name ( handle ) ); + rc = -ENOTTY; + goto err_not_partition; + } + + /* Check partition type (if exposed by the platform) */ + if ( ( rc = efi_open ( handle, &efi_partition_info_protocol_guid, + &part ) ) == 0 ) { + if ( ( part->Type != PARTITION_TYPE_MBR ) || + ( part->Info.Mbr.OSIndicator != DISKLOG_PARTITION_TYPE )){ + DBGC ( disklog, "EFIDISKLOG %s is not a log " + "partition\n", efi_handle_name ( handle ) ); + rc = -ENOTTY; + goto err_not_log; + } + } else { + DBGC2 ( disklog, "EFIDISKLOG %s has no partition info\n", + efi_handle_name ( handle ) ); + /* Continue anyway */ + } + + /* Allocate buffer */ + buffer = umalloc ( media->BlockSize ); + if ( ! buffer ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Read partition signature */ + if ( ( efirc = block->ReadBlocks ( block, efi_disklog_media_id, 0, + media->BlockSize, buffer ) ) != 0 ){ + rc = -EEFI ( efirc ); + DBGC ( disklog, "EFIDISKLOG %s could not read block 0: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_read; + } + + /* Initialise disk log console */ + disklog_init ( disklog, &efi_disklog_op, &efi_disklog_console, buffer, + media->BlockSize, 0, media->LastBlock ); + + /* Open disk log console */ + if ( ( rc = disklog_open ( disklog ) ) != 0 ) { + DBGC ( disklog, "EFIDISKLOG %s could not initialise log: " + "%s\n", efi_handle_name ( handle ), strerror ( rc ) ); + goto err_init; + } + + /* Reopen handle for long-term use */ + if ( ( rc = efi_open_by_driver ( handle, &efi_block_io_protocol_guid, + &block ) ) != 0 ) { + DBGC ( disklog, "EFIDISKLOG %s could not reopen: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_reopen; + } + if ( block != efi_disklog_block ) { + DBGC ( disklog, "EFIDISKLOG %s changed during reopening\n", + efi_handle_name ( handle ) ); + rc = -EPIPE; + goto err_reopen_mismatch; + } + + DBGC ( disklog, "EFIDISKLOG using %s\n", efi_handle_name ( handle ) ); + DBGC2 ( disklog, "EFIDISKLOG has %zd-byte LBA [%#x,%#llx]\n", + disklog->blksize, 0, + ( ( unsigned long long ) disklog->max_lba ) ); + return 0; + + err_reopen_mismatch: + efi_close_by_driver ( handle, &efi_block_io_protocol_guid ); + err_reopen: + err_init: + err_read: + ufree ( buffer ); + err_alloc: + err_not_log: + err_not_partition: + efi_disklog_block = NULL; + err_open: + efi_disklog_handle = NULL; + return rc; +} + +/** + * Initialise EFI disk log console + * + */ +static void efi_disklog_init ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_block_io_protocol_guid; + EFI_HANDLE *handles; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Locate all block I/O protocol handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &count, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_disklog, "EFIDISKLOG could not locate block I/O: " + "%s\n", strerror ( rc ) ); + goto err_locate; + } + + /* Try each handle in turn */ + for ( i = 0 ; i < count ; i++ ) { + if ( ( rc = efi_disklog_open ( handles[i] ) ) == 0 ) + break; + } + + bs->FreePool ( handles ); + err_locate: + return; +} + +/** + * EFI disk log console initialisation function + */ +struct init_fn efi_disklog_init_fn __init_fn ( INIT_CONSOLE ) = { + .name = "disklog", + .initialise = efi_disklog_init, +}; + +/** + * Shut down EFI disk log console + * + * @v booting System is shutting down for OS boot + */ +static void efi_disklog_shutdown ( int booting __unused ) { + struct disklog *disklog = &efi_disklog; + + /* Do nothing if we have no EFI disk log console */ + if ( ! efi_disklog_handle ) + return; + + /* Close EFI disk log console */ + DBGC ( disklog, "EFIDISKLOG %s closed\n", + efi_handle_name ( efi_disklog_handle ) ); + efi_close_by_driver ( efi_disklog_handle, + &efi_block_io_protocol_guid ); + ufree ( disklog->buffer ); + disklog->buffer = NULL; + efi_disklog_handle = NULL; + efi_disklog_block = NULL; +} + +/** EFI disk log console shutdown function */ +struct startup_fn efi_disklog_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .name = "disklog", + .shutdown = efi_disklog_shutdown, +}; + +/** EFI disk log console driver */ +struct console_driver efi_disklog_console __console_driver = { + .putchar = efi_disklog_putchar, + .disabled = CONSOLE_DISABLED, + .usage = CONSOLE_DISKLOG, +}; + +/* Request a log partition from genfsimg */ +IPXE_NOTE ( DISKLOG );