diff options
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/arch/i386/image/eltorito.c')
-rw-r--r-- | contrib/syslinux-4.02/gpxe/src/arch/i386/image/eltorito.c | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/image/eltorito.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/image/eltorito.c new file mode 100644 index 0000000..53eb2c0 --- /dev/null +++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/image/eltorito.c @@ -0,0 +1,336 @@ +/* + * 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 <gpxe/uaccess.h> +#include <gpxe/image.h> +#include <gpxe/segment.h> +#include <gpxe/ramdisk.h> +#include <gpxe/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, +}; |