1/* 2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19FILE_LICENCE ( GPL2_OR_LATER ); 20 21/** 22 * @file 23 * 24 * El Torito bootable ISO image format 25 * 26 */ 27 28#include <stdint.h> 29#include <errno.h> 30#include <assert.h> 31#include <realmode.h> 32#include <bootsector.h> 33#include <int13.h> 34#include <gpxe/uaccess.h> 35#include <gpxe/image.h> 36#include <gpxe/segment.h> 37#include <gpxe/ramdisk.h> 38#include <gpxe/init.h> 39 40#define ISO9660_BLKSIZE 2048 41#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE ) 42 43/** An El Torito Boot Record Volume Descriptor */ 44struct eltorito_vol_desc { 45 /** Boot record indicator; must be 0 */ 46 uint8_t record_indicator; 47 /** ISO-9660 identifier; must be "CD001" */ 48 uint8_t iso9660_id[5]; 49 /** Version, must be 1 */ 50 uint8_t version; 51 /** Boot system indicator; must be "EL TORITO SPECIFICATION" */ 52 uint8_t system_indicator[32]; 53 /** Unused */ 54 uint8_t unused[32]; 55 /** Boot catalog sector */ 56 uint32_t sector; 57} __attribute__ (( packed )); 58 59/** An El Torito Boot Catalog Validation Entry */ 60struct eltorito_validation_entry { 61 /** Header ID; must be 1 */ 62 uint8_t header_id; 63 /** Platform ID 64 * 65 * 0 = 80x86 66 * 1 = PowerPC 67 * 2 = Mac 68 */ 69 uint8_t platform_id; 70 /** Reserved */ 71 uint16_t reserved; 72 /** ID string */ 73 uint8_t id_string[24]; 74 /** Checksum word */ 75 uint16_t checksum; 76 /** Signature; must be 0xaa55 */ 77 uint16_t signature; 78} __attribute__ (( packed )); 79 80/** A bootable entry in the El Torito Boot Catalog */ 81struct eltorito_boot_entry { 82 /** Boot indicator 83 * 84 * Must be @c ELTORITO_BOOTABLE for a bootable ISO image 85 */ 86 uint8_t indicator; 87 /** Media type 88 * 89 */ 90 uint8_t media_type; 91 /** Load segment */ 92 uint16_t load_segment; 93 /** System type */ 94 uint8_t filesystem; 95 /** Unused */ 96 uint8_t reserved_a; 97 /** Sector count */ 98 uint16_t length; 99 /** Starting sector */ 100 uint32_t start; 101 /** Unused */ 102 uint8_t reserved_b[20]; 103} __attribute__ (( packed )); 104 105/** Boot indicator for a bootable ISO image */ 106#define ELTORITO_BOOTABLE 0x88 107 108/** El Torito media types */ 109enum eltorito_media_type { 110 /** No emulation */ 111 ELTORITO_NO_EMULATION = 0, 112}; 113 114struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ); 115 116/** 117 * Calculate 16-bit word checksum 118 * 119 * @v data Data to checksum 120 * @v len Length (in bytes, must be even) 121 * @ret sum Checksum 122 */ 123static unsigned int word_checksum ( void *data, size_t len ) { 124 uint16_t *words; 125 uint16_t sum = 0; 126 127 for ( words = data ; len ; words++, len -= 2 ) { 128 sum += *words; 129 } 130 return sum; 131} 132 133/** 134 * Execute El Torito image 135 * 136 * @v image El Torito image 137 * @ret rc Return status code 138 */ 139static int eltorito_exec ( struct image *image ) { 140 struct ramdisk ramdisk; 141 struct int13_drive int13_drive; 142 unsigned int load_segment = image->priv.ul; 143 unsigned int load_offset = ( load_segment ? 0 : 0x7c00 ); 144 int rc; 145 146 memset ( &ramdisk, 0, sizeof ( ramdisk ) ); 147 init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE ); 148 149 memset ( &int13_drive, 0, sizeof ( int13_drive ) ); 150 int13_drive.blockdev = &ramdisk.blockdev; 151 register_int13_drive ( &int13_drive ); 152 153 if ( ( rc = call_bootsector ( load_segment, load_offset, 154 int13_drive.drive ) ) != 0 ) { 155 DBGC ( image, "ElTorito %p boot failed: %s\n", 156 image, strerror ( rc ) ); 157 goto err; 158 } 159 160 rc = -ECANCELED; /* -EIMPOSSIBLE */ 161 err: 162 unregister_int13_drive ( &int13_drive ); 163 return rc; 164} 165 166/** 167 * Read and verify El Torito Boot Record Volume Descriptor 168 * 169 * @v image El Torito file 170 * @ret catalog_offset Offset of Boot Catalog 171 * @ret rc Return status code 172 */ 173static int eltorito_read_voldesc ( struct image *image, 174 unsigned long *catalog_offset ) { 175 static const struct eltorito_vol_desc vol_desc_signature = { 176 .record_indicator = 0, 177 .iso9660_id = "CD001", 178 .version = 1, 179 .system_indicator = "EL TORITO SPECIFICATION", 180 }; 181 struct eltorito_vol_desc vol_desc; 182 183 /* Sanity check */ 184 if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) { 185 DBGC ( image, "ElTorito %p too short\n", image ); 186 return -ENOEXEC; 187 } 188 189 /* Read and verify Boot Record Volume Descriptor */ 190 copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET, 191 sizeof ( vol_desc ) ); 192 if ( memcmp ( &vol_desc, &vol_desc_signature, 193 offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) { 194 DBGC ( image, "ElTorito %p invalid Boot Record Volume " 195 "Descriptor\n", image ); 196 return -ENOEXEC; 197 } 198 *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE ); 199 200 DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n", 201 image, *catalog_offset ); 202 203 return 0; 204} 205 206/** 207 * Read and verify El Torito Boot Catalog 208 * 209 * @v image El Torito file 210 * @v catalog_offset Offset of Boot Catalog 211 * @ret boot_entry El Torito boot entry 212 * @ret rc Return status code 213 */ 214static int eltorito_read_catalog ( struct image *image, 215 unsigned long catalog_offset, 216 struct eltorito_boot_entry *boot_entry ) { 217 struct eltorito_validation_entry validation_entry; 218 219 /* Sanity check */ 220 if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) { 221 DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n", 222 image, catalog_offset ); 223 return -ENOEXEC; 224 } 225 226 /* Read and verify the Validation Entry of the Boot Catalog */ 227 copy_from_user ( &validation_entry, image->data, catalog_offset, 228 sizeof ( validation_entry ) ); 229 if ( word_checksum ( &validation_entry, 230 sizeof ( validation_entry ) ) != 0 ) { 231 DBGC ( image, "ElTorito %p bad Validation Entry checksum\n", 232 image ); 233 return -ENOEXEC; 234 } 235 236 /* Read and verify the Initial/Default entry */ 237 copy_from_user ( boot_entry, image->data, 238 ( catalog_offset + sizeof ( validation_entry ) ), 239 sizeof ( *boot_entry ) ); 240 if ( boot_entry->indicator != ELTORITO_BOOTABLE ) { 241 DBGC ( image, "ElTorito %p not bootable\n", image ); 242 return -ENOEXEC; 243 } 244 if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) { 245 DBGC ( image, "ElTorito %p cannot support media type %d\n", 246 image, boot_entry->media_type ); 247 return -ENOTSUP; 248 } 249 250 DBGC ( image, "ElTorito %p media type %d segment %04x\n", 251 image, boot_entry->media_type, boot_entry->load_segment ); 252 253 return 0; 254} 255 256/** 257 * Load El Torito virtual disk image into memory 258 * 259 * @v image El Torito file 260 * @v boot_entry El Torito boot entry 261 * @ret rc Return status code 262 */ 263static int eltorito_load_disk ( struct image *image, 264 struct eltorito_boot_entry *boot_entry ) { 265 unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE ); 266 unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE ); 267 unsigned int load_segment; 268 userptr_t buffer; 269 int rc; 270 271 /* Sanity check */ 272 if ( image->len < ( start + length ) ) { 273 DBGC ( image, "ElTorito %p virtual disk lies outside image\n", 274 image ); 275 return -ENOEXEC; 276 } 277 DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n", 278 image, start, length ); 279 280 /* Calculate load address */ 281 load_segment = boot_entry->load_segment; 282 buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) ); 283 284 /* Verify and prepare segment */ 285 if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) { 286 DBGC ( image, "ElTorito %p could not prepare segment: %s\n", 287 image, strerror ( rc ) ); 288 return rc; 289 } 290 291 /* Copy image to segment */ 292 memcpy_user ( buffer, 0, image->data, start, length ); 293 294 return 0; 295} 296 297/** 298 * Load El Torito image into memory 299 * 300 * @v image El Torito file 301 * @ret rc Return status code 302 */ 303static int eltorito_load ( struct image *image ) { 304 struct eltorito_boot_entry boot_entry; 305 unsigned long bootcat_offset; 306 int rc; 307 308 /* Read Boot Record Volume Descriptor, if present */ 309 if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 ) 310 return rc; 311 312 /* This is an El Torito image, valid or otherwise */ 313 if ( ! image->type ) 314 image->type = &eltorito_image_type; 315 316 /* Read Boot Catalog */ 317 if ( ( rc = eltorito_read_catalog ( image, bootcat_offset, 318 &boot_entry ) ) != 0 ) 319 return rc; 320 321 /* Load Virtual Disk image */ 322 if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 ) 323 return rc; 324 325 /* Record load segment in image private data field */ 326 image->priv.ul = boot_entry.load_segment; 327 328 return 0; 329} 330 331/** El Torito image type */ 332struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = { 333 .name = "El Torito", 334 .load = eltorito_load, 335 .exec = eltorito_exec, 336}; 337