eltorito.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 /**
00022  * @file
00023  *
00024  * El Torito bootable ISO image format
00025  *
00026  */
00027 
00028 #include <stdint.h>
00029 #include <errno.h>
00030 #include <assert.h>
00031 #include <realmode.h>
00032 #include <bootsector.h>
00033 #include <int13.h>
00034 #include <gpxe/uaccess.h>
00035 #include <gpxe/image.h>
00036 #include <gpxe/segment.h>
00037 #include <gpxe/ramdisk.h>
00038 #include <gpxe/init.h>
00039 
00040 #define ISO9660_BLKSIZE 2048
00041 #define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
00042 
00043 /** An El Torito Boot Record Volume Descriptor */
00044 struct eltorito_vol_desc {
00045         /** Boot record indicator; must be 0 */
00046         uint8_t record_indicator;
00047         /** ISO-9660 identifier; must be "CD001" */
00048         uint8_t iso9660_id[5];
00049         /** Version, must be 1 */
00050         uint8_t version;
00051         /** Boot system indicator; must be "EL TORITO SPECIFICATION" */
00052         uint8_t system_indicator[32];
00053         /** Unused */
00054         uint8_t unused[32];
00055         /** Boot catalog sector */
00056         uint32_t sector;
00057 } __attribute__ (( packed ));
00058 
00059 /** An El Torito Boot Catalog Validation Entry */
00060 struct eltorito_validation_entry {
00061         /** Header ID; must be 1 */
00062         uint8_t header_id;
00063         /** Platform ID
00064          *
00065          * 0 = 80x86
00066          * 1 = PowerPC
00067          * 2 = Mac
00068          */
00069         uint8_t platform_id;
00070         /** Reserved */
00071         uint16_t reserved;
00072         /** ID string */
00073         uint8_t id_string[24];
00074         /** Checksum word */
00075         uint16_t checksum;
00076         /** Signature; must be 0xaa55 */
00077         uint16_t signature;
00078 } __attribute__ (( packed ));
00079 
00080 /** A bootable entry in the El Torito Boot Catalog */
00081 struct eltorito_boot_entry {
00082         /** Boot indicator
00083          *
00084          * Must be @c ELTORITO_BOOTABLE for a bootable ISO image
00085          */
00086         uint8_t indicator;
00087         /** Media type
00088          *
00089          */
00090         uint8_t media_type;
00091         /** Load segment */
00092         uint16_t load_segment;
00093         /** System type */
00094         uint8_t filesystem;
00095         /** Unused */
00096         uint8_t reserved_a;
00097         /** Sector count */
00098         uint16_t length;
00099         /** Starting sector */
00100         uint32_t start;
00101         /** Unused */
00102         uint8_t reserved_b[20];
00103 } __attribute__ (( packed ));
00104 
00105 /** Boot indicator for a bootable ISO image */
00106 #define ELTORITO_BOOTABLE 0x88
00107 
00108 /** El Torito media types */
00109 enum eltorito_media_type {
00110         /** No emulation */
00111         ELTORITO_NO_EMULATION = 0,
00112 };
00113 
00114 struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
00115 
00116 /**
00117  * Calculate 16-bit word checksum
00118  *
00119  * @v data              Data to checksum
00120  * @v len               Length (in bytes, must be even)
00121  * @ret sum             Checksum
00122  */
00123 static unsigned int word_checksum ( void *data, size_t len ) {
00124         uint16_t *words;
00125         uint16_t sum = 0;
00126 
00127         for ( words = data ; len ; words++, len -= 2 ) {
00128                 sum += *words;
00129         }
00130         return sum;
00131 }
00132 
00133 /**
00134  * Execute El Torito image
00135  *
00136  * @v image             El Torito image
00137  * @ret rc              Return status code
00138  */
00139 static int eltorito_exec ( struct image *image ) {
00140         struct ramdisk ramdisk;
00141         struct int13_drive int13_drive;
00142         unsigned int load_segment = image->priv.ul;
00143         unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
00144         int rc;
00145 
00146         memset ( &ramdisk, 0, sizeof ( ramdisk ) );
00147         init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
00148         
00149         memset ( &int13_drive, 0, sizeof ( int13_drive ) );
00150         int13_drive.blockdev = &ramdisk.blockdev;
00151         register_int13_drive ( &int13_drive );
00152 
00153         if ( ( rc = call_bootsector ( load_segment, load_offset, 
00154                                       int13_drive.drive ) ) != 0 ) {
00155                 DBGC ( image, "ElTorito %p boot failed: %s\n",
00156                        image, strerror ( rc ) );
00157                 goto err;
00158         }
00159         
00160         rc = -ECANCELED; /* -EIMPOSSIBLE */
00161  err:
00162         unregister_int13_drive ( &int13_drive );
00163         return rc;
00164 }
00165 
00166 /**
00167  * Read and verify El Torito Boot Record Volume Descriptor
00168  *
00169  * @v image             El Torito file
00170  * @ret catalog_offset  Offset of Boot Catalog
00171  * @ret rc              Return status code
00172  */
00173 static int eltorito_read_voldesc ( struct image *image,
00174                                    unsigned long *catalog_offset ) {
00175         static const struct eltorito_vol_desc vol_desc_signature = {
00176                 .record_indicator = 0,
00177                 .iso9660_id = "CD001",
00178                 .version = 1,
00179                 .system_indicator = "EL TORITO SPECIFICATION",
00180         };
00181         struct eltorito_vol_desc vol_desc;
00182 
00183         /* Sanity check */
00184         if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
00185                 DBGC ( image, "ElTorito %p too short\n", image );
00186                 return -ENOEXEC;
00187         }
00188 
00189         /* Read and verify Boot Record Volume Descriptor */
00190         copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
00191                          sizeof ( vol_desc ) );
00192         if ( memcmp ( &vol_desc, &vol_desc_signature,
00193                       offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
00194                 DBGC ( image, "ElTorito %p invalid Boot Record Volume "
00195                        "Descriptor\n", image );
00196                 return -ENOEXEC;
00197         }
00198         *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
00199 
00200         DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
00201                image, *catalog_offset );
00202 
00203         return 0;
00204 }
00205 
00206 /**
00207  * Read and verify El Torito Boot Catalog
00208  *
00209  * @v image             El Torito file
00210  * @v catalog_offset    Offset of Boot Catalog
00211  * @ret boot_entry      El Torito boot entry
00212  * @ret rc              Return status code
00213  */
00214 static int eltorito_read_catalog ( struct image *image,
00215                                    unsigned long catalog_offset,
00216                                    struct eltorito_boot_entry *boot_entry ) {
00217         struct eltorito_validation_entry validation_entry;
00218 
00219         /* Sanity check */
00220         if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
00221                 DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
00222                        image, catalog_offset );
00223                 return -ENOEXEC;
00224         }
00225 
00226         /* Read and verify the Validation Entry of the Boot Catalog */
00227         copy_from_user ( &validation_entry, image->data, catalog_offset,
00228                          sizeof ( validation_entry ) );
00229         if ( word_checksum ( &validation_entry,
00230                              sizeof ( validation_entry ) ) != 0 ) {
00231                 DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
00232                        image );
00233                 return -ENOEXEC;
00234         }
00235 
00236         /* Read and verify the Initial/Default entry */
00237         copy_from_user ( boot_entry, image->data,
00238                          ( catalog_offset + sizeof ( validation_entry ) ),
00239                          sizeof ( *boot_entry ) );
00240         if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
00241                 DBGC ( image, "ElTorito %p not bootable\n", image );
00242                 return -ENOEXEC;
00243         }
00244         if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
00245                 DBGC ( image, "ElTorito %p cannot support media type %d\n",
00246                        image, boot_entry->media_type );
00247                 return -ENOTSUP;
00248         }
00249 
00250         DBGC ( image, "ElTorito %p media type %d segment %04x\n",
00251                image, boot_entry->media_type, boot_entry->load_segment );
00252 
00253         return 0;
00254 }
00255 
00256 /**
00257  * Load El Torito virtual disk image into memory
00258  *
00259  * @v image             El Torito file
00260  * @v boot_entry        El Torito boot entry
00261  * @ret rc              Return status code
00262  */
00263 static int eltorito_load_disk ( struct image *image,
00264                                 struct eltorito_boot_entry *boot_entry ) {
00265         unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
00266         unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
00267         unsigned int load_segment;
00268         userptr_t buffer;
00269         int rc;
00270 
00271         /* Sanity check */
00272         if ( image->len < ( start + length ) ) {
00273                 DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
00274                        image );
00275                 return -ENOEXEC;
00276         }
00277         DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
00278                image, start, length );
00279 
00280         /* Calculate load address */
00281         load_segment = boot_entry->load_segment;
00282         buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
00283 
00284         /* Verify and prepare segment */
00285         if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
00286                 DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
00287                        image, strerror ( rc ) );
00288                 return rc;
00289         }
00290 
00291         /* Copy image to segment */
00292         memcpy_user ( buffer, 0, image->data, start, length );
00293 
00294         return 0;
00295 }
00296 
00297 /**
00298  * Load El Torito image into memory
00299  *
00300  * @v image             El Torito file
00301  * @ret rc              Return status code
00302  */
00303 static int eltorito_load ( struct image *image ) {
00304         struct eltorito_boot_entry boot_entry;
00305         unsigned long bootcat_offset;
00306         int rc;
00307 
00308         /* Read Boot Record Volume Descriptor, if present */
00309         if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
00310                 return rc;
00311 
00312         /* This is an El Torito image, valid or otherwise */
00313         if ( ! image->type )
00314                 image->type = &eltorito_image_type;
00315 
00316         /* Read Boot Catalog */
00317         if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
00318                                             &boot_entry ) ) != 0 )
00319                 return rc;
00320 
00321         /* Load Virtual Disk image */
00322         if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
00323                 return rc;
00324 
00325         /* Record load segment in image private data field */
00326         image->priv.ul = boot_entry.load_segment;
00327 
00328         return 0;
00329 }
00330 
00331 /** El Torito image type */
00332 struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
00333         .name = "El Torito",
00334         .load = eltorito_load,
00335         .exec = eltorito_exec,
00336 };

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1