diff --git a/src/arch/x86/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c index b073ca0e7..0cd2b0303 100644 --- a/src/arch/x86/interface/pcbios/int13con.c +++ b/src/arch/x86/interface/pcbios/int13con.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -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; } diff --git a/src/arch/x86/prefix/usbdisk.S b/src/arch/x86/prefix/usbdisk.S index ab4a011a3..389ef86bd 100644 --- a/src/arch/x86/prefix/usbdisk.S +++ b/src/arch/x86/prefix/usbdisk.S @@ -1,5 +1,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) +#include #include .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 */ diff --git a/src/core/disklog.c b/src/core/disklog.c new file mode 100644 index 000000000..bdcbf3a1c --- /dev/null +++ b/src/core/disklog.c @@ -0,0 +1,134 @@ +/* + * 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 + +/** @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; +} diff --git a/src/include/ipxe/disklog.h b/src/include/ipxe/disklog.h new file mode 100644 index 000000000..f7f56d25b --- /dev/null +++ b/src/include/ipxe/disklog.h @@ -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 +#include + +/** 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 */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 27c2bb97c..f452648ce 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -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 )