mirror of
https://github.com/ipxe/ipxe
synced 2025-12-23 21:41:43 +03:00
The INT13 console type (CONSOLE_INT13) autodetects at initialisation time a magic partition to be used for logging iPXE console output. If the INT13 drive number mapping is subsequently changed (e.g. because iPXE was used to perform a SAN boot), then the console logging output will be written to the incorrect disk. Fix by recording the INT13 vector at initialisation time, and using this original vector to emulate INT13 calls for all subsequent accesses. This should be robust against drive remapping performed either by ourselves or by another bootloader (e.g. a chainloaded undionly.kpxe which then performs a SAN boot). Signed-off-by: Michael Brown <mcb30@ipxe.org>
300 lines
7.8 KiB
C
300 lines
7.8 KiB
C
/*
|
|
* Copyright (C) 2015 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 (at your option) 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 );
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ipxe/console.h>
|
|
#include <ipxe/init.h>
|
|
#include <realmode.h>
|
|
#include <int13.h>
|
|
#include <config/console.h>
|
|
|
|
/** @file
|
|
*
|
|
* INT13 disk log console
|
|
*
|
|
*/
|
|
|
|
/* Set default console usage if applicable */
|
|
#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) )
|
|
#undef CONSOLE_INT13
|
|
#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
|
|
#endif
|
|
|
|
/** 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 )
|
|
|
|
/** Sector buffer */
|
|
static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
|
|
#define int13con_buffer __use_data16 ( int13con_buffer )
|
|
|
|
/** Disk address packet */
|
|
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;
|
|
|
|
struct console_driver int13con __console_driver;
|
|
|
|
/**
|
|
* Read/write disk sector
|
|
*
|
|
* @v op Operation
|
|
* @v lba Logical block address
|
|
* @ret rc Return status code
|
|
*/
|
|
static int int13con_rw ( unsigned int op, uint64_t lba ) {
|
|
uint8_t error;
|
|
|
|
/* Construct disk address packet */
|
|
int13con_address.bufsize = sizeof ( int13con_address );
|
|
int13con_address.count = 1;
|
|
int13con_address.buffer.segment = rm_ds;
|
|
int13con_address.buffer.offset = __from_data16 ( int13con_buffer );
|
|
int13con_address.lba = lba;
|
|
|
|
/* Emulate INT13 via original vector. We do this since iPXE
|
|
* (or another subsequent bootloader) may hook INT13 and remap
|
|
* drive numbers.
|
|
*/
|
|
__asm__ ( REAL_CODE ( "pushfw\n\t"
|
|
"cli\n\t"
|
|
"lcall *int13con_vector\n\t" )
|
|
: "=a" ( error )
|
|
: "0" ( op << 8 ), "d" ( INT13CON_DRIVE ),
|
|
"S" ( __from_data16 ( &int13con_address ) ) );
|
|
if ( error ) {
|
|
DBG ( "INT13CON operation %04x failed: %02x\n",
|
|
op, error );
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Find log partition
|
|
*
|
|
* @ret rc Return status code
|
|
*/
|
|
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;
|
|
|
|
/* Read MBR */
|
|
if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) {
|
|
DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) );
|
|
return rc;
|
|
}
|
|
|
|
/* Check MBR magic */
|
|
if ( mbr->magic != INT13_MBR_MAGIC ) {
|
|
DBG ( "INT13CON incorrect MBR magic\n" );
|
|
DBG2_HDA ( 0, mbr, sizeof ( *mbr ) );
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Look for magic partition */
|
|
memcpy ( part, mbr->partitions, sizeof ( part ) );
|
|
for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) {
|
|
|
|
/* Skip partitions of the wrong type */
|
|
if ( part[i].type != INT13CON_PARTITION_TYPE )
|
|
continue;
|
|
|
|
/* Read partition header */
|
|
if ( ( rc = int13con_rw ( INT13_EXTENDED_READ,
|
|
part[i].start ) ) != 0 ) {
|
|
DBG ( "INT13CON partition %d could not read header: "
|
|
"%s\n", ( i + 1 ), strerror ( rc ) );
|
|
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 ) );
|
|
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 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
DBG ( "INT13CON found no log partition\n" );
|
|
return -ENOENT;
|
|
}
|
|
|
|
/**
|
|
* Initialise INT13 console
|
|
*
|
|
*/
|
|
static void int13con_init ( void ) {
|
|
uint8_t error;
|
|
uint16_t check;
|
|
unsigned int discard_c;
|
|
unsigned int discard_d;
|
|
int rc;
|
|
|
|
/* Check for INT13 extensions */
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t"
|
|
"setc %%al\n\t" )
|
|
: "=a" ( error ), "=b" ( check ),
|
|
"=c" ( discard_c ), "=d" ( discard_d )
|
|
: "0" ( INT13_EXTENSION_CHECK << 8 ),
|
|
"1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) );
|
|
if ( error || ( check != 0xaa55 ) ) {
|
|
DBG ( "INT13CON missing extensions (%02x,%04x)\n",
|
|
error, check );
|
|
return;
|
|
}
|
|
|
|
/* Store original INT13 vector */
|
|
copy_from_real ( &int13con_vector, 0, ( 0x13 * 4 ),
|
|
sizeof ( int13con_vector ) );
|
|
DBG ( "INT13CON using original INT13 vector %04x:%04x\n",
|
|
int13con_vector.segment, int13con_vector.offset );
|
|
|
|
/* Locate log partition */
|
|
if ( ( rc = int13con_find() ) != 0)
|
|
return;
|
|
|
|
/* Enable console */
|
|
int13con.disabled = 0;
|
|
}
|
|
|
|
/**
|
|
* INT13 console initialisation function
|
|
*/
|
|
struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = {
|
|
.initialise = int13con_init,
|
|
};
|
|
|
|
/** INT13 console driver */
|
|
struct console_driver int13con __console_driver = {
|
|
.putchar = int13con_putchar,
|
|
.disabled = CONSOLE_DISABLED,
|
|
.usage = CONSOLE_INT13,
|
|
};
|