[block] Replace gPXE block-device API with an iPXE asynchronous interface

The block device interface used in gPXE predates the invention of even
the old gPXE data-transfer interface, let alone the current iPXE
generic asynchronous interface mechanism.  Bring this old code up to
date, with the following benefits:

 o  Block device commands can be cancelled by the requestor.  The INT 13
    layer uses this to provide a global timeout on all INT 13 calls,
    with the result that an unexpected passive failure mode (such as
    an iSCSI target ACKing the request but never sending a response)
    will lead to a timeout that gets reported back to the INT 13 user,
    rather than simply freezing the system.

 o  INT 13,00 (reset drive) is now able to reset the underlying block
    device.  INT 13 users, such as DOS, that use INT 13,00 as a method
    for error recovery now have a chance of recovering.

 o  All block device commands are tagged, with a numerical tag that
    will show up in debugging output and in packet captures; this will
    allow easier interpretation of bug reports that include both
    sources of information.

 o  The extremely ugly hacks used to generate the boot firmware tables
    have been eradicated and replaced with a generic acpi_describe()
    method (exploiting the ability of iPXE interfaces to pass through
    methods to an underlying interface).  The ACPI tables are now
    built in a shared data block within .bss16, rather than each
    requiring dedicated space in .data16.

 o  The architecture-independent concept of a SAN device has been
    exposed to the iPXE core through the sanboot API, which provides
    calls to hook, unhook, boot, and describe SAN devices.  This
    allows for much more flexible usage patterns (such as hooking an
    empty SAN device and then running an OS installer via TFTP).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2010-09-03 16:11:51 +01:00
parent 46c7f99c66
commit 220495f8bf
47 changed files with 4904 additions and 3127 deletions

View File

@@ -1,336 +0,0 @@
/*
* Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
/**
* @file
*
* El Torito bootable ISO image format
*
*/
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <bootsector.h>
#include <int13.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/ramdisk.h>
#include <ipxe/init.h>
#define ISO9660_BLKSIZE 2048
#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
/** An El Torito Boot Record Volume Descriptor */
struct eltorito_vol_desc {
/** Boot record indicator; must be 0 */
uint8_t record_indicator;
/** ISO-9660 identifier; must be "CD001" */
uint8_t iso9660_id[5];
/** Version, must be 1 */
uint8_t version;
/** Boot system indicator; must be "EL TORITO SPECIFICATION" */
uint8_t system_indicator[32];
/** Unused */
uint8_t unused[32];
/** Boot catalog sector */
uint32_t sector;
} __attribute__ (( packed ));
/** An El Torito Boot Catalog Validation Entry */
struct eltorito_validation_entry {
/** Header ID; must be 1 */
uint8_t header_id;
/** Platform ID
*
* 0 = 80x86
* 1 = PowerPC
* 2 = Mac
*/
uint8_t platform_id;
/** Reserved */
uint16_t reserved;
/** ID string */
uint8_t id_string[24];
/** Checksum word */
uint16_t checksum;
/** Signature; must be 0xaa55 */
uint16_t signature;
} __attribute__ (( packed ));
/** A bootable entry in the El Torito Boot Catalog */
struct eltorito_boot_entry {
/** Boot indicator
*
* Must be @c ELTORITO_BOOTABLE for a bootable ISO image
*/
uint8_t indicator;
/** Media type
*
*/
uint8_t media_type;
/** Load segment */
uint16_t load_segment;
/** System type */
uint8_t filesystem;
/** Unused */
uint8_t reserved_a;
/** Sector count */
uint16_t length;
/** Starting sector */
uint32_t start;
/** Unused */
uint8_t reserved_b[20];
} __attribute__ (( packed ));
/** Boot indicator for a bootable ISO image */
#define ELTORITO_BOOTABLE 0x88
/** El Torito media types */
enum eltorito_media_type {
/** No emulation */
ELTORITO_NO_EMULATION = 0,
};
struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
/**
* Calculate 16-bit word checksum
*
* @v data Data to checksum
* @v len Length (in bytes, must be even)
* @ret sum Checksum
*/
static unsigned int word_checksum ( void *data, size_t len ) {
uint16_t *words;
uint16_t sum = 0;
for ( words = data ; len ; words++, len -= 2 ) {
sum += *words;
}
return sum;
}
/**
* Execute El Torito image
*
* @v image El Torito image
* @ret rc Return status code
*/
static int eltorito_exec ( struct image *image ) {
struct ramdisk ramdisk;
struct int13_drive int13_drive;
unsigned int load_segment = image->priv.ul;
unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
int rc;
memset ( &ramdisk, 0, sizeof ( ramdisk ) );
init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
memset ( &int13_drive, 0, sizeof ( int13_drive ) );
int13_drive.blockdev = &ramdisk.blockdev;
register_int13_drive ( &int13_drive );
if ( ( rc = call_bootsector ( load_segment, load_offset,
int13_drive.drive ) ) != 0 ) {
DBGC ( image, "ElTorito %p boot failed: %s\n",
image, strerror ( rc ) );
goto err;
}
rc = -ECANCELED; /* -EIMPOSSIBLE */
err:
unregister_int13_drive ( &int13_drive );
return rc;
}
/**
* Read and verify El Torito Boot Record Volume Descriptor
*
* @v image El Torito file
* @ret catalog_offset Offset of Boot Catalog
* @ret rc Return status code
*/
static int eltorito_read_voldesc ( struct image *image,
unsigned long *catalog_offset ) {
static const struct eltorito_vol_desc vol_desc_signature = {
.record_indicator = 0,
.iso9660_id = "CD001",
.version = 1,
.system_indicator = "EL TORITO SPECIFICATION",
};
struct eltorito_vol_desc vol_desc;
/* Sanity check */
if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
DBGC ( image, "ElTorito %p too short\n", image );
return -ENOEXEC;
}
/* Read and verify Boot Record Volume Descriptor */
copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
sizeof ( vol_desc ) );
if ( memcmp ( &vol_desc, &vol_desc_signature,
offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
DBGC ( image, "ElTorito %p invalid Boot Record Volume "
"Descriptor\n", image );
return -ENOEXEC;
}
*catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
image, *catalog_offset );
return 0;
}
/**
* Read and verify El Torito Boot Catalog
*
* @v image El Torito file
* @v catalog_offset Offset of Boot Catalog
* @ret boot_entry El Torito boot entry
* @ret rc Return status code
*/
static int eltorito_read_catalog ( struct image *image,
unsigned long catalog_offset,
struct eltorito_boot_entry *boot_entry ) {
struct eltorito_validation_entry validation_entry;
/* Sanity check */
if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
image, catalog_offset );
return -ENOEXEC;
}
/* Read and verify the Validation Entry of the Boot Catalog */
copy_from_user ( &validation_entry, image->data, catalog_offset,
sizeof ( validation_entry ) );
if ( word_checksum ( &validation_entry,
sizeof ( validation_entry ) ) != 0 ) {
DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
image );
return -ENOEXEC;
}
/* Read and verify the Initial/Default entry */
copy_from_user ( boot_entry, image->data,
( catalog_offset + sizeof ( validation_entry ) ),
sizeof ( *boot_entry ) );
if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
DBGC ( image, "ElTorito %p not bootable\n", image );
return -ENOEXEC;
}
if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
DBGC ( image, "ElTorito %p cannot support media type %d\n",
image, boot_entry->media_type );
return -ENOTSUP;
}
DBGC ( image, "ElTorito %p media type %d segment %04x\n",
image, boot_entry->media_type, boot_entry->load_segment );
return 0;
}
/**
* Load El Torito virtual disk image into memory
*
* @v image El Torito file
* @v boot_entry El Torito boot entry
* @ret rc Return status code
*/
static int eltorito_load_disk ( struct image *image,
struct eltorito_boot_entry *boot_entry ) {
unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
unsigned int load_segment;
userptr_t buffer;
int rc;
/* Sanity check */
if ( image->len < ( start + length ) ) {
DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
image );
return -ENOEXEC;
}
DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
image, start, length );
/* Calculate load address */
load_segment = boot_entry->load_segment;
buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
/* Verify and prepare segment */
if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, start, length );
return 0;
}
/**
* Load El Torito image into memory
*
* @v image El Torito file
* @ret rc Return status code
*/
static int eltorito_load ( struct image *image ) {
struct eltorito_boot_entry boot_entry;
unsigned long bootcat_offset;
int rc;
/* Read Boot Record Volume Descriptor, if present */
if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
return rc;
/* This is an El Torito image, valid or otherwise */
if ( ! image->type )
image->type = &eltorito_image_type;
/* Read Boot Catalog */
if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
&boot_entry ) ) != 0 )
return rc;
/* Load Virtual Disk image */
if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
return rc;
/* Record load segment in image private data field */
image->priv.ul = boot_entry.load_segment;
return 0;
}
/** El Torito image type */
struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
.name = "El Torito",
.load = eltorito_load,
.exec = eltorito_exec,
};