[disklog] Generalise disk log console mechanism

Split out the generic portions of the INT13 disk log console support
to a separate file that can be shared between BIOS and UEFI platforms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2026-04-07 13:22:10 +01:00
parent ec38e98d40
commit 480b6f021f
5 changed files with 264 additions and 89 deletions

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
#include <ipxe/console.h>
#include <ipxe/disklog.h>
#include <ipxe/init.h>
#include <realmode.h>
#include <int13.h>
@ -53,21 +54,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Disk drive number */
#define INT13CON_DRIVE 0x80
/** Log partition type */
#define INT13CON_PARTITION_TYPE 0xe0
/** Maximum number of outstanding unwritten characters */
#define INT13CON_MAX_UNWRITTEN 64
/** Log partition header */
struct int13con_header {
/** Magic signature */
char magic[10];
} __attribute__ (( packed ));
/** Log partition magic signature */
#define INT13CON_MAGIC "iPXE LOG\n\n"
/** Original INT13 vector */
static struct segoff __bss16 ( int13con_vector );
#define int13con_vector __use_data16 ( int13con_vector )
@ -80,17 +66,8 @@ static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
static struct int13_disk_address __bss16 ( int13con_address );
#define int13con_address __use_data16 ( int13con_address )
/** Current LBA */
static uint64_t int13con_lba;
/** Maximum LBA */
static uint64_t int13con_max_lba;
/** Current offset within sector */
static size_t int13con_offset;
/** Number of unwritten characters */
static size_t int13con_unwritten;
/** Disk log */
static struct disklog int13con_disklog;
struct console_driver int13con __console_driver;
@ -130,57 +107,31 @@ static int int13con_rw ( unsigned int op, uint64_t lba ) {
return 0;
}
/**
* Write current logical block
*
* @ret rc Return status code
*/
static int int13con_write ( void ) {
/* Write block */
return int13con_rw ( INT13_EXTENDED_WRITE, int13con_disklog.lba );
}
/** INT13 disk log operations */
static struct disklog_operations int13con_op = {
.write = int13con_write,
};
/**
* Write character to console
*
* @v character Character
*/
static void int13con_putchar ( int character ) {
static int busy;
int rc;
/* Ignore if we are already mid-logging */
if ( busy )
return;
busy = 1;
/* Write character to buffer */
int13con_buffer[int13con_offset++] = character;
int13con_unwritten++;
/* Write sector to disk, if applicable */
if ( ( int13con_offset == INT13_BLKSIZE ) ||
( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) ||
( character == '\n' ) ) {
/* Write sector to disk */
if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE,
int13con_lba ) ) != 0 ) {
DBG ( "INT13CON could not write log\n" );
/* Ignore and continue; there's nothing we can do */
}
/* Reset count of unwritten characters */
int13con_unwritten = 0;
}
/* Move to next sector, if applicable */
if ( int13con_offset == INT13_BLKSIZE ) {
/* Disable console if we have run out of space */
if ( int13con_lba >= int13con_max_lba )
int13con.disabled = 1;
/* Clear log buffer */
memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) );
int13con_offset = 0;
/* Move to next sector */
int13con_lba++;
}
/* Clear busy flag */
busy = 0;
/* Write character */
disklog_putchar ( &int13con_disklog, character );
}
/**
@ -191,8 +142,6 @@ static void int13con_putchar ( int character ) {
static int int13con_find ( void ) {
struct master_boot_record *mbr =
( ( struct master_boot_record * ) int13con_buffer );
struct int13con_header *hdr =
( ( struct int13con_header * ) int13con_buffer );
struct partition_table_entry part[4];
unsigned int i;
int rc;
@ -215,7 +164,7 @@ static int int13con_find ( void ) {
for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) {
/* Skip partitions of the wrong type */
if ( part[i].type != INT13CON_PARTITION_TYPE )
if ( part[i].type != DISKLOG_PARTITION_TYPE )
continue;
/* Read partition header */
@ -226,24 +175,22 @@ static int int13con_find ( void ) {
continue;
}
/* Check partition header */
if ( memcmp ( hdr->magic, INT13CON_MAGIC,
sizeof ( hdr->magic ) ) != 0 ) {
DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) );
DBG2_HDA ( 0, hdr, sizeof ( *hdr ) );
/* Initialise disk log console */
disklog_init ( &int13con_disklog, &int13con_op, &int13con,
int13con_buffer, INT13_BLKSIZE, part[i].start,
( part[i].start + part[i].length - 1 ) );
/* Open disk log console */
if ( ( rc = disklog_open ( &int13con_disklog ) ) != 0 ) {
DBG ( "INT13CON partition %d could not initialise: "
"%s\n", ( i + 1 ), strerror ( rc ) );
continue;
}
/* Found log partition */
DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ),
part[i].start, ( part[i].start + part[i].length ) );
int13con_lba = part[i].start;
int13con_max_lba = ( part[i].start + part[i].length - 1 );
/* Initialise log buffer */
memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0,
( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) );
int13con_offset = sizeof ( hdr->magic );
DBG ( "INT13CON partition %d at [%08llx,%08llx)\n", ( i + 1 ),
( ( unsigned long long ) int13con_disklog.lba ),
( ( unsigned long long ) int13con_disklog.max_lba ) );
return 0;
}

View File

@ -1,5 +1,6 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <ipxe/disklog.h>
#include <config/console.h>
.section ".note.GNU-stack", "", @progbits
@ -61,7 +62,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
/* Partition 3: log partition (for CONSOLE_INT13) */
.if LOGPART
partition 0x00, 0xe0, LOGSTART, LOGCOUNT
partition 0x00, DISKLOG_PARTITION_TYPE, LOGSTART, LOGCOUNT
.else
.space 16
.endif
@ -76,7 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
/* Skip to start of log partition */
.if LOGPART
.org CYLADDR(LOGSTART)
.ascii "iPXE LOG\n\n"
.ascii DISKLOG_MAGIC
.endif
/* Skip to start of boot partition */

134
src/core/disklog.c Normal file
View File

@ -0,0 +1,134 @@
/*
* 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/disklog.h>
/** @file
*
* Disk log console
*
*/
/**
* Open disk log console
*
* @v disklog Disk log
* @ret rc Return status code
*
* The data buffer must already contain the initial logical block.
*/
int disklog_open ( struct disklog *disklog ) {
struct disklog_header *hdr =
( ( struct disklog_header * ) disklog->buffer );
/* Sanity checks */
assert ( disklog->console != NULL );
assert ( disklog->buffer != NULL );
assert ( disklog->blksize > 0 );
assert ( ( disklog->blksize & ( disklog->blksize - 1 ) ) == 0 );
assert ( disklog->lba <= disklog->max_lba );
assert ( disklog->op != NULL );
assert ( disklog->op->write != NULL );
/* Check magic signature */
if ( ( disklog->blksize < sizeof ( *hdr ) ) ||
( memcmp ( hdr->magic, DISKLOG_MAGIC,
sizeof ( hdr->magic ) ) != 0 ) ) {
DBGC ( disklog, "DISKLOG has bad magic signature\n" );
return -EINVAL;
}
/* Initialise buffer */
disklog->offset = sizeof ( *hdr );
disklog->unwritten = 0;
memset ( ( disklog->buffer + sizeof ( *hdr ) ), 0,
( disklog->blksize - sizeof ( *hdr ) ) );
/* Enable console */
disklog->console->disabled = 0;
return 0;
}
/**
* Write character to disk log console
*
* @v disklog Disk log
* @v character Character
*/
void disklog_putchar ( struct disklog *disklog, int character ) {
static int busy;
int rc;
/* Ignore if we are already mid-logging */
if ( busy )
return;
busy = 1;
/* Sanity checks */
assert ( disklog->offset < disklog->blksize );
/* Write character to buffer */
disklog->buffer[disklog->offset++] = character;
disklog->unwritten++;
/* Write sector to disk, if applicable */
if ( ( disklog->offset == disklog->blksize ) ||
( disklog->unwritten == DISKLOG_MAX_UNWRITTEN ) ||
( character == '\n' ) ) {
/* Write sector to disk */
if ( ( rc = disklog->op->write() ) != 0 ) {
DBGC ( disklog, "DISKLOG could not write: %s\n",
strerror ( rc ) );
/* Ignore and continue; there's nothing we can do */
}
/* Reset count of unwritten characters */
disklog->unwritten = 0;
}
/* Move to next sector, if applicable */
if ( disklog->offset == disklog->blksize ) {
/* Disable console if we have run out of space */
if ( disklog->lba >= disklog->max_lba )
disklog->console->disabled = 1;
/* Clear log buffer */
memset ( disklog->buffer, 0, disklog->blksize );
disklog->offset = 0;
/* Move to next sector */
disklog->lba++;
}
/* Clear busy flag */
busy = 0;
}

View File

@ -0,0 +1,92 @@
#ifndef _IPXE_DISKLOG_H
#define _IPXE_DISKLOG_H
/** @file
*
* Disk log console
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FILE_SECBOOT ( PERMITTED );
#ifndef ASSEMBLY
#include <stdint.h>
#include <ipxe/console.h>
/** Disk log partition header */
struct disklog_header {
/** Magic signature */
char magic[10];
} __attribute__ (( packed ));
/** A disk log */
struct disklog {
/** Console device */
struct console_driver *console;
/** Disk log operations */
struct disklog_operations *op;
/** Logical block data buffer */
uint8_t *buffer;
/** Logical block size */
size_t blksize;
/** Current logical block index */
uint64_t lba;
/** Maximum logical block index */
uint64_t max_lba;
/** Current offset within logical block */
unsigned int offset;
/** Current number of unwritten characters */
unsigned int unwritten;
};
/** Disk log operations */
struct disklog_operations {
/**
* Write current logical block
*
* @ret rc Return status code
*/
int ( * write ) ( void );
};
/**
* Initialise disk log console
*
* @v disklog Disk log
* @v op Disk log operations
* @v console Console device
* @v buffer Data buffer
* @v blksize Logical block size
* @v lba Starting logical block index
* @v max_lba Maximum logical block index
*/
static inline __attribute__ (( always_inline )) void
disklog_init ( struct disklog *disklog, struct disklog_operations *op,
struct console_driver *console, void *buffer, size_t blksize,
uint64_t lba, uint64_t max_lba ) {
disklog->op = op;
disklog->console = console;
disklog->buffer = buffer;
disklog->blksize = blksize;
disklog->lba = lba;
disklog->max_lba = max_lba;
}
extern int disklog_open ( struct disklog *disklog );
extern void disklog_putchar ( struct disklog *disklog, int character );
#endif /* ASSEMBLY */
/** Disk log partition type */
#define DISKLOG_PARTITION_TYPE 0xe0
/** Disk log partition magic signature */
#define DISKLOG_MAGIC "iPXE LOG\n\n"
/** Maximum number of outstanding unwritten characters */
#define DISKLOG_MAX_UNWRITTEN 64
#endif /* _IPXE_DISKLOG_H */

View File

@ -90,6 +90,7 @@ FILE_SECBOOT ( PERMITTED );
#define ERRFILE_efi_connect ( ERRFILE_CORE | 0x00310000 )
#define ERRFILE_gpio ( ERRFILE_CORE | 0x00320000 )
#define ERRFILE_spcr ( ERRFILE_CORE | 0x00330000 )
#define ERRFILE_disklog ( ERRFILE_CORE | 0x00340000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )