#include <string.h>
#include <errno.h>
#include <ipxe/console.h>
+#include <ipxe/disklog.h>
#include <ipxe/init.h>
#include <realmode.h>
#include <int13.h>
/** 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 )
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;
}
/**
- * Write character to console
+ * Write current logical block
*
- * @v character Character
+ * @ret rc Return status code
*/
-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 */
- }
+static int int13con_write ( void ) {
- /* 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;
+ /* Write block */
+ return int13con_rw ( INT13_EXTENDED_WRITE, int13con_disklog.lba );
+}
- /* Clear log buffer */
- memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) );
- int13con_offset = 0;
+/** INT13 disk log operations */
+static struct disklog_operations int13con_op = {
+ .write = int13con_write,
+};
- /* Move to next sector */
- int13con_lba++;
- }
+/**
+ * Write character to console
+ *
+ * @v character Character
+ */
+static void int13con_putchar ( int character ) {
- /* Clear busy flag */
- busy = 0;
+ /* Write character */
+ disklog_putchar ( &int13con_disklog, 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;
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 */
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;
}
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+#include <ipxe/disklog.h>
#include <config/console.h>
.section ".note.GNU-stack", "", @progbits
/* 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
/* 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
#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 )