Home | History | Annotate | Line # | Download | only in cd9660
cd9660_eltorito.c revision 1.3
      1 /*	$NetBSD: cd9660_eltorito.c,v 1.3 2005/10/28 21:51:35 dyoung Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
      5  * Perez-Rathke and Ram Vedam.  All rights reserved.
      6  *
      7  * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
      8  * Alan Perez-Rathke and Ram Vedam.
      9  *
     10  * Redistribution and use in source and binary forms, with or
     11  * without modification, are permitted provided that the following
     12  * conditions are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above
     16  *    copyright notice, this list of conditions and the following
     17  *    disclaimer in the documentation and/or other materials provided
     18  *    with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
     21  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24  * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
     25  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
     28  * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     32  * OF SUCH DAMAGE.
     33  */
     34 #include "cd9660.h"
     35 #include "cd9660_eltorito.h"
     36 
     37 #include <sys/cdefs.h>
     38 #if defined(__RCSID) && !defined(__lint)
     39 __RCSID("$NetBSD: cd9660_eltorito.c,v 1.3 2005/10/28 21:51:35 dyoung Exp $");
     40 #endif  /* !__lint */
     41 
     42 static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
     43 static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
     44 static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
     45     struct cd9660_boot_image *);
     46 static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
     47 static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
     48 #if 0
     49 static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
     50 #endif
     51 
     52 int
     53 cd9660_add_boot_disk(boot_info)
     54 const char * boot_info;
     55 {
     56 	struct stat stbuf;
     57 	char *temp;
     58 	char *sysname;
     59 	char *filename;
     60 	struct cd9660_boot_image *new_image,*tmp_image;
     61 
     62 	assert(boot_info != NULL);
     63 
     64 	if (*boot_info == '\0') {
     65 		warnx("Error: Boot disk information must be in the "
     66 		      "format 'system;filename'");
     67 		return 0;
     68 	}
     69 
     70 	/* First decode the boot information */
     71 	if ((temp = strdup(boot_info)) == NULL) {
     72 		warn("%s: strdup", __func__);
     73 		return 0;
     74 	}
     75 
     76 	sysname = temp;
     77 	filename = strchr(sysname, ';');
     78 	if (filename == NULL) {
     79 		warnx("supply boot disk information in the format "
     80 		    "'system;filename'");
     81 		return 0;
     82 	}
     83 
     84 	*filename++ = '\0';
     85 
     86 	printf("Found bootdisk with system %s, and filename %s\n",
     87 	    sysname, filename);
     88 	new_image = malloc(sizeof(struct cd9660_boot_image));
     89 	if (new_image == NULL) {
     90 		warn("%s: malloc", __func__);
     91 		return 0;
     92 	}
     93 	new_image->loadSegment = 0;	/* default for now */
     94 
     95 	/* Decode System */
     96 	if (strcmp(sysname, "i386") == 0)
     97 		new_image->system = ET_SYS_X86;
     98 	else if (strcmp(sysname, "powerpc") == 0)
     99 		new_image->system = ET_SYS_PPC;
    100 	else if (strcmp(sysname, "macppc") == 0 || strcmp(sysname, "mac68k"))
    101 		new_image->system = ET_SYS_MAC;
    102 	else {
    103 		warnx("boot disk system must be "
    104 		      "i386, powerpc, macppc, or mac68k");
    105 		return 0;
    106 	}
    107 
    108 
    109 	if ((new_image->filename = strdup(filename)) == NULL) {
    110 		warn("%s: strdup", __func__);
    111 		return 0;
    112 	}
    113 
    114 	free(temp);
    115 
    116 	/* Get information about the file */
    117 	if (lstat(new_image->filename, &stbuf) == -1)
    118 		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
    119 		    new_image->filename);
    120 
    121 	switch (stbuf.st_size) {
    122 	case 1440 * 1024:
    123 		new_image->targetMode = ET_MEDIA_144FDD;
    124 		printf("Assigned boot image to 1.44 emulation mode\n");
    125 		break;
    126 	case 1200 * 1024:
    127 		new_image->targetMode = ET_MEDIA_12FDD;
    128 		printf("Assigned boot image to 1.2 emulation mode\n");
    129 		break;
    130 	case 2880 * 1024:
    131 		new_image->targetMode = ET_MEDIA_288FDD;
    132 		printf("Assigned boot image to 2.88 emulation mode\n");
    133 		break;
    134 	default:
    135 		new_image->targetMode = ET_MEDIA_NOEM;
    136 		printf("Assigned boot image to no emulation mode\n");
    137 		break;
    138 	}
    139 
    140 	new_image->size = stbuf.st_size;
    141 	new_image->num_sectors =
    142 		CD9660_BLOCKS(diskStructure.sectorSize, new_image->size);
    143 	printf("New image has size %i, uses %i sectors of size %i\n",
    144 	    new_image->size, new_image->num_sectors, diskStructure.sectorSize);
    145 	new_image->sector = -1;
    146 	/* Bootable by default */
    147 	new_image->bootable = ET_BOOTABLE;
    148 	/* Add boot disk */
    149 
    150 	if (LIST_FIRST(&diskStructure.boot_images) == NULL) {
    151 		LIST_INSERT_HEAD(&diskStructure.boot_images, new_image,
    152 		    image_list);
    153 	} else {
    154 		tmp_image = LIST_FIRST(&diskStructure.boot_images);
    155 		while (LIST_NEXT(tmp_image, image_list) != NULL
    156 			&& LIST_NEXT(tmp_image, image_list)->system !=
    157 			    new_image->system) {
    158 			tmp_image = LIST_NEXT(tmp_image, image_list);
    159 		}
    160 		LIST_INSERT_AFTER(tmp_image, new_image,image_list);
    161 	}
    162 
    163 	/* TODO : Need to do anything about the boot image in the tree? */
    164 	diskStructure.is_bootable = 1;
    165 
    166 	return 1;
    167 }
    168 
    169 int
    170 cd9660_eltorito_add_boot_option(const char *option_string, const char* value)
    171 {
    172 	struct cd9660_boot_image *image;
    173 
    174 	assert(option_string != NULL);
    175 
    176 	/* Find the last image added */
    177 	image = LIST_FIRST(&diskStructure.boot_images);
    178 	if (image == NULL)
    179 		errx(EXIT_FAILURE, "Attempted to add boot option, "
    180 		    "but no boot images have been specified");
    181 
    182 	while (LIST_NEXT(image, image_list) != NULL)
    183 		image = LIST_NEXT(image, image_list);
    184 
    185 	/* TODO : These options are NOT copied yet */
    186 	if (strcmp(option_string, "no-emul-boot") == 0) {
    187 		image->targetMode = ET_MEDIA_NOEM;
    188 	} else if (strcmp(option_string, "no-boot") == 0) {
    189 		image->bootable = ET_NOT_BOOTABLE;
    190 	} else if (strcmp(option_string, "hard-disk-boot") == 0) {
    191 		image->targetMode = ET_MEDIA_HDD;
    192 	} else if (strcmp(option_string, "boot-load-size") == 0) {
    193 		/* TODO */
    194 	} else if (strcmp(option_string, "boot-load-segment") == 0) {
    195 		/* TODO */
    196 	} else {
    197 		return 0;
    198 	}
    199 	return 1;
    200 }
    201 
    202 static struct boot_catalog_entry *
    203 cd9660_init_boot_catalog_entry(void)
    204 {
    205 	struct boot_catalog_entry *temp;
    206 
    207 	temp = malloc(sizeof(struct boot_catalog_entry));
    208 	return temp;
    209 }
    210 
    211 static struct boot_catalog_entry *
    212 cd9660_boot_setup_validation_entry(char sys)
    213 {
    214 	struct boot_catalog_entry *validation_entry;
    215 	int16_t checksum;
    216 	unsigned char *csptr;
    217 	int i;
    218 
    219 	validation_entry = cd9660_init_boot_catalog_entry();
    220 
    221 	if (validation_entry == NULL) {
    222 		warnx("Error: memory allocation failed in "
    223 		      "cd9660_boot_setup_validation_entry");
    224 		return 0;
    225 	}
    226 	validation_entry->entry_data.VE.header_id[0] = 1;
    227 	validation_entry->entry_data.VE.platform_id[0] = sys;
    228 	validation_entry->entry_data.VE.key[0] = 0x55;
    229 	validation_entry->entry_data.VE.key[1] = 0xAA;
    230 
    231 	/* Calculate checksum */
    232 	checksum = 0;
    233 	cd9660_721(0, validation_entry->entry_data.VE.checksum);
    234 	csptr = (unsigned char*) &(validation_entry->entry_data.VE);
    235 	for (i = 0; i < sizeof (boot_catalog_validation_entry); i += 2) {
    236 		checksum += (int16_t) csptr[i];
    237 		checksum += ((int16_t) csptr[i + 1]) * 256;
    238 	}
    239 	checksum = -checksum;
    240 	cd9660_721(checksum, validation_entry->entry_data.VE.checksum);
    241 
    242 	return validation_entry;
    243 }
    244 
    245 static struct boot_catalog_entry *
    246 cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
    247 {
    248 	struct boot_catalog_entry *default_entry;
    249 
    250 	default_entry = cd9660_init_boot_catalog_entry();
    251 	if (default_entry == NULL)
    252 		return NULL;
    253 
    254 	cd9660_721(1, default_entry->entry_data.IE.sector_count);
    255 
    256 	default_entry->entry_data.IE.boot_indicator[0] = disk->bootable;
    257 	default_entry->entry_data.IE.media_type[0] = disk->targetMode;
    258 	cd9660_721(disk->loadSegment,
    259 	    default_entry->entry_data.IE.load_segment);
    260 	default_entry->entry_data.IE.system_type[0] = disk->system;
    261 	cd9660_721(1, default_entry->entry_data.IE.sector_count);
    262 	cd9660_731(disk->sector, default_entry->entry_data.IE.load_rba);
    263 
    264 	return default_entry;
    265 }
    266 
    267 static struct boot_catalog_entry *
    268 cd9660_boot_setup_section_head(char platform)
    269 {
    270 	struct boot_catalog_entry *sh;
    271 
    272 	sh = cd9660_init_boot_catalog_entry();
    273 	if (sh == NULL)
    274 		return NULL;
    275 	/* More by default. The last one will manually be set to 0x91 */
    276 	sh->entry_data.SH.header_indicator[0] = ET_SECTION_HEADER_MORE;
    277 	sh->entry_data.SH.platform_id[0] = platform;
    278 	sh->entry_data.SH.num_section_entries[0] = 0;
    279 	return sh;
    280 }
    281 
    282 static struct boot_catalog_entry *
    283 cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
    284 {
    285 	struct boot_catalog_entry *entry;
    286 
    287 	if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
    288 		return NULL;
    289 
    290 	entry->entry_data.SE.boot_indicator[0] = ET_BOOTABLE;
    291 	entry->entry_data.SE.media_type[0] = disk->targetMode;
    292 	cd9660_721(disk->loadSegment, entry->entry_data.SE.load_segment);
    293 	cd9660_721(1, entry->entry_data.SE.sector_count);
    294 
    295 	cd9660_731(disk->sector,entry->entry_data.IE.load_rba);
    296 	return entry;
    297 }
    298 
    299 #if 0
    300 static u_char
    301 cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
    302 {
    303 	/*
    304 		For hard drive booting, we need to examine the MBR to figure
    305 		out what the partition type is
    306 	*/
    307 	return 0;
    308 }
    309 #endif
    310 
    311 /*
    312  * Set up the BVD, Boot catalog, and the boot entries, but do no writing
    313  */
    314 int
    315 cd9660_setup_boot(int first_sector)
    316 {
    317 	int need_head;
    318 	int sector;
    319 	int used_sectors;
    320 	int num_entries = 0;
    321 	int catalog_sectors;
    322 	struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
    323 		*last_x86, *last_ppc, *last_mac, *last_head,
    324 		*valid_entry, *default_entry, *temp, *head_temp, *next;
    325 	struct cd9660_boot_image *tmp_disk;
    326 
    327 	head_temp = NULL;
    328 	need_head = 0;
    329 	x86_head = mac_head = ppc_head =
    330 		last_x86 = last_ppc = last_mac = last_head = NULL;
    331 
    332 	/* If there are no boot disks, don't bother building boot information */
    333 	if ((tmp_disk = LIST_FIRST(&diskStructure.boot_images)) == NULL)
    334 		return 0;
    335 
    336 	/* Point to catalog: For now assume it consumes one sector */
    337 	printf("Boot catalog will go in sector %i\n",first_sector);
    338 	diskStructure.boot_catalog_sector = first_sector;
    339 	cd9660_bothendian_dword(first_sector,
    340 		diskStructure.boot_descriptor->boot_catalog_pointer);
    341 	sector = first_sector +1;
    342 
    343 	/* Step 1: Generate boot catalog */
    344 	/* Step 1a: Validation entry */
    345 	valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
    346 	if (valid_entry == NULL)
    347 		return -1;
    348 
    349 	/*
    350 	 * Count how many boot images there are,
    351 	 * and how many sectors they consume.
    352 	 */
    353 	num_entries = 1;
    354 	used_sectors = 0;
    355 
    356 	LIST_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
    357 		used_sectors += tmp_disk->num_sectors;
    358 
    359 		/* One default entry per image */
    360 		num_entries++;
    361 	}
    362 	catalog_sectors = howmany(num_entries * 0x20, diskStructure.sectorSize);
    363 	used_sectors += catalog_sectors;
    364 
    365 	printf("boot: there will be %i entries consuming %i sectors. "
    366 	       "Catalog is %i sectors\n", num_entries, used_sectors,
    367 		catalog_sectors);
    368 
    369 	/* Populate sector numbers */
    370 	sector = first_sector + catalog_sectors;
    371 	LIST_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
    372 		tmp_disk->sector = sector;
    373 		sector += tmp_disk->num_sectors;
    374 	}
    375 
    376 	LIST_INSERT_HEAD(&diskStructure.boot_entries, valid_entry, ll_struct);
    377 
    378 	/* Step 1b: Initial/default entry */
    379 	/* TODO : PARAM */
    380 	tmp_disk = LIST_FIRST(&diskStructure.boot_images);
    381 	default_entry = cd9660_boot_setup_default_entry(tmp_disk);
    382 	if (default_entry == NULL) {
    383 		warnx("Error: memory allocation failed in cd9660_setup_boot");
    384 		return -1;
    385 	}
    386 
    387 	LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
    388 
    389 	/* Todo: multiple default entries? */
    390 
    391 	tmp_disk = LIST_NEXT(tmp_disk, image_list);
    392 
    393 	temp = default_entry;
    394 
    395 	/* If multiple boot images are given : */
    396 	while (tmp_disk != NULL) {
    397 		/* Step 2: Section header */
    398 		switch (tmp_disk->system) {
    399 		case ET_SYS_X86:
    400 			need_head = (x86_head == NULL);
    401 			if (!need_head)
    402 				head_temp = x86_head;
    403 			break;
    404 		case ET_SYS_PPC:
    405 			need_head = (ppc_head == NULL);
    406 			if (!need_head)
    407 				head_temp = ppc_head;
    408 			break;
    409 		case ET_SYS_MAC:
    410 			need_head = (mac_head == NULL);
    411 			if (!need_head)
    412 				head_temp = mac_head;
    413 			break;
    414 		}
    415 
    416 		if (need_head) {
    417 			head_temp =
    418 			    cd9660_boot_setup_section_head(tmp_disk->system);
    419 			if (head_temp == NULL) {
    420 				warnx("Error: memory allocation failed in "
    421 				      "cd9660_setup_boot");
    422 				return -1;
    423 			}
    424 			LIST_INSERT_AFTER(default_entry,head_temp, ll_struct);
    425 		}
    426 		head_temp->entry_data.SH.num_section_entries[0]++;
    427 
    428 		/* Step 2a: Section entry and extensions */
    429 		temp = cd9660_boot_setup_section_entry(tmp_disk);
    430 		if (temp == NULL) {
    431 			warnx("Error: memory allocation failed in "
    432 			      "cd9660_setup_boot");
    433 			return -1;
    434 		}
    435 
    436 		while ((next = LIST_NEXT(head_temp, ll_struct)) != NULL &&
    437 		       next->entry_type == ET_ENTRY_SE)
    438 			head_temp = LIST_NEXT(head_temp, ll_struct);
    439 
    440 		LIST_INSERT_AFTER(head_temp,temp, ll_struct);
    441 		tmp_disk = LIST_NEXT(tmp_disk, image_list);
    442 	}
    443 
    444 	/* TODO: Remaining boot disks when implemented */
    445 
    446 	return first_sector + used_sectors;
    447 }
    448 
    449 int
    450 cd9660_setup_boot_volume_descritpor(volume_descriptor *bvd)
    451 {
    452 	boot_volume_descriptor *bvdData =
    453 	    (boot_volume_descriptor*)bvd->volumeDescriptorData;
    454 
    455 	bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
    456 	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
    457 	bvdData->version[0] = 1;
    458 	memcpy(bvdData->boot_system_identifier, ET_ID, 23);
    459 	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
    460 	diskStructure.boot_descriptor =
    461 	    (boot_volume_descriptor*) bvd->volumeDescriptorData;
    462 	return 1;
    463 }
    464 
    465 int
    466 cd9660_write_boot(FILE *fd)
    467 {
    468 	struct boot_catalog_entry *e;
    469 	struct cd9660_boot_image *t;
    470 
    471 	/* write boot catalog */
    472 	fseek(fd, diskStructure.boot_catalog_sector * diskStructure.sectorSize,
    473 	    SEEK_SET);
    474 
    475 	printf("Writing boot catalog to sector %i\n",
    476 	    diskStructure.boot_catalog_sector);
    477 	LIST_FOREACH(e, &diskStructure.boot_entries, ll_struct) {
    478 		printf("Writing catalog entry of type %i\n", e->entry_type);
    479 		/*
    480 		 * It doesnt matter which one gets written
    481 		 * since they are the same size
    482 		 */
    483 		fwrite(&(e->entry_data.VE), 1, 32, fd);
    484 	}
    485 	printf("Finished writing boot catalog\n");
    486 
    487 	/* copy boot images */
    488 	LIST_FOREACH(t, &diskStructure.boot_images, image_list) {
    489 		printf("Writing boot image from %s to sectors %i\n",
    490 		    t->filename,t->sector);
    491 		cd9660_copy_file(fd, t->sector, t->filename);
    492 	}
    493 
    494 	return 0;
    495 }
    496