mirror of
https://github.com/ipxe/ipxe.git
synced 2026-04-22 06:02:04 +02:00
[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 <mcb30@ipxe.org>
This commit is contained in:
parent
480b6f021f
commit
07887332cf
@ -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
|
||||
|
||||
@ -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 )
|
||||
|
||||
301
src/interface/efi/efi_disklog.c
Normal file
301
src/interface/efi/efi_disklog.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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 );
|
||||
FILE_SECBOOT ( PERMITTED );
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ipxe/console.h>
|
||||
#include <ipxe/disklog.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/umalloc.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/Protocol/BlockIo.h>
|
||||
#include <ipxe/efi/Protocol/PartitionInfo.h>
|
||||
#include <config/console.h>
|
||||
|
||||
/** @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 );
|
||||
Loading…
x
Reference in New Issue
Block a user