cd9660_eltorito.c revision 1.1
1/* $NetBSD: cd9660_eltorito.c,v 1.1 2005/08/13 01:53:01 fvdl 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.1 2005/08/13 01:53:01 fvdl Exp $"); 40#endif /* !__lint */ 41 42static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void); 43static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); 44static struct boot_catalog_entry *cd9660_boot_setup_default_entry( 45 struct cd9660_boot_image *); 46static struct boot_catalog_entry *cd9660_boot_setup_section_head(char); 47static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); 48#if 0 49static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *); 50#endif 51 52int 53cd9660_add_boot_disk(boot_info) 54const 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 /* First decode the boot information */ 65 temp = malloc(strlen(boot_info) + 1); 66 if (temp == NULL) { 67 warnx("Error initializing memory in cd9660_add_boot_disk"); 68 return 0; 69 } 70 71 strcpy(temp, boot_info); 72 73 sysname = temp; 74 if (*sysname == '\0') { 75 warnx("Error: Boot disk information must be in the " 76 "format 'system;filename'"); 77 return 0; 78 } 79 80 filename = strchr(sysname, ';'); 81 if (filename == NULL) { 82 warnx("Error: Boot disk information must be in the " 83 "format 'system;filename'"); 84 return 0; 85 } 86 87 *filename = '\0'; 88 filename++; 89 90 printf("Found bootdisk with system %s, and filename %s\n", 91 sysname, filename); 92 new_image = malloc(sizeof(struct cd9660_boot_image)); 93 if (new_image == NULL) { 94 warnx("Error initializing memory in cd9660_add_boot_disk"); 95 return 0; 96 } 97 new_image->loadSegment = 0; /* default for now */ 98 99 /* Decode System */ 100 if (strcmp(sysname,"x86") == 0) 101 new_image->system = ET_SYS_X86; 102 else if (strcmp(sysname,"ppc") == 0) 103 new_image->system = ET_SYS_PPC; 104 else if (strcmp(sysname,"mac") == 0) 105 new_image->system = ET_SYS_MAC; 106 else { 107 warnx("Error: Boot disk system must be one of the " 108 "following: x86, ppc, mac"); 109 return 0; 110 } 111 112 new_image->filename = malloc(strlen(filename) + 1); 113 if (new_image->filename == NULL) { 114 warnx("Error initializing memory in cd9660_add_boot_disk"); 115 return 0; 116 } 117 118 strcpy(new_image->filename, filename); 119 free(temp); 120 121 /* Get information about the file */ 122 if (lstat(new_image->filename, &stbuf) == -1) 123 errx(1, "Can't lstat `%s'", new_image->filename); 124 125 switch (stbuf.st_size) { 126 case 1440 * 1024: 127 new_image->targetMode = ET_MEDIA_144FDD; 128 printf("Assigned boot image to 1.44 emulation mode\n"); 129 break; 130 case 1200 * 1024: 131 new_image->targetMode = ET_MEDIA_12FDD; 132 printf("Assigned boot image to 1.2 emulation mode\n"); 133 break; 134 case 2880 * 1024: 135 new_image->targetMode = ET_MEDIA_288FDD; 136 printf("Assigned boot image to 2.88 emulation mode\n"); 137 break; 138 default: 139 new_image->targetMode = ET_MEDIA_NOEM; 140 printf("Assigned boot image to no emulation mode\n"); 141 break; 142 } 143 144 new_image->size = stbuf.st_size; 145 new_image->num_sectors = 146 CD9660_BLOCKS(diskStructure.sectorSize, new_image->size); 147 printf("New image has size %i, uses %i sectors of size %i\n", 148 new_image->size, new_image->num_sectors, diskStructure.sectorSize); 149 new_image->sector = -1; 150 /* Bootable by default */ 151 new_image->bootable = ET_BOOTABLE; 152 /* Add boot disk */ 153 154 if (diskStructure.boot_images.lh_first == NULL) { 155 LIST_INSERT_HEAD(&(diskStructure.boot_images), new_image, 156 image_list); 157 } else { 158 tmp_image = diskStructure.boot_images.lh_first; 159 while (tmp_image->image_list.le_next != 0 160 && tmp_image->image_list.le_next->system != 161 new_image->system) { 162 tmp_image = tmp_image->image_list.le_next; 163 } 164 LIST_INSERT_AFTER(tmp_image, new_image,image_list); 165 } 166 167 /* TODO : Need to do anything about the boot image in the tree? */ 168 diskStructure.is_bootable = 1; 169 170 return 1; 171} 172 173int 174cd9660_eltorito_add_boot_option(const char *option_string, const char* value) 175{ 176 struct cd9660_boot_image *image; 177 178 assert(option_string != NULL); 179 180 /* Find the last image added */ 181 image = diskStructure.boot_images.lh_first; 182 if (image == NULL) 183 errx(1, "Attempted to add boot option, but no boot images " 184 "have been specified"); 185 186 while (image->image_list.le_next != NULL) 187 image = image->image_list.le_next; 188 189 /* TODO : These options are NOT copied yet */ 190 if (strcmp(option_string, "no-emul-boot") == 0) { 191 image->targetMode = ET_MEDIA_NOEM; 192 } else if (strcmp(option_string, "no-boot") == 0) { 193 image->bootable = ET_NOT_BOOTABLE; 194 } else if (strcmp(option_string, "hard-disk-boot") == 0) { 195 image->targetMode = ET_MEDIA_HDD; 196 } else if (strcmp(option_string, "boot-load-size") == 0) { 197 /* TODO */ 198 } else if (strcmp(option_string, "boot-load-segment") == 0) { 199 /* TODO */ 200 } else { 201 return 0; 202 } 203 return 1; 204} 205 206static struct boot_catalog_entry * 207cd9660_init_boot_catalog_entry(void) 208{ 209 struct boot_catalog_entry *temp; 210 211 temp = malloc(sizeof(struct boot_catalog_entry)); 212 return temp; 213} 214 215static struct boot_catalog_entry * 216cd9660_boot_setup_validation_entry(char sys) 217{ 218 struct boot_catalog_entry *validation_entry; 219 int16_t checksum; 220 unsigned char *csptr; 221 int i; 222 223 validation_entry = cd9660_init_boot_catalog_entry(); 224 225 if (validation_entry == NULL) { 226 warnx("Error: memory allocation failed in " 227 "cd9660_boot_setup_validation_entry"); 228 return 0; 229 } 230 validation_entry->entry_data.VE.header_id[0] = 1; 231 validation_entry->entry_data.VE.platform_id[0] = sys; 232 validation_entry->entry_data.VE.key[0] = 0x55; 233 validation_entry->entry_data.VE.key[1] = 0xAA; 234 235 /* Calculate checksum */ 236 checksum = 0; 237 cd9660_721(0, validation_entry->entry_data.VE.checksum); 238 csptr = (unsigned char*) &(validation_entry->entry_data.VE); 239 for (i = 0; i < sizeof (boot_catalog_validation_entry); i += 2) { 240 checksum += (int16_t) csptr[i]; 241 checksum += ((int16_t) csptr[i + 1]) * 256; 242 } 243 checksum = -checksum; 244 cd9660_721(checksum, validation_entry->entry_data.VE.checksum); 245 246 return validation_entry; 247} 248 249static struct boot_catalog_entry * 250cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk) 251{ 252 struct boot_catalog_entry *default_entry; 253 254 default_entry = cd9660_init_boot_catalog_entry(); 255 if (default_entry == NULL) 256 return NULL; 257 258 cd9660_721(1, default_entry->entry_data.IE.sector_count); 259 260 default_entry->entry_data.IE.boot_indicator[0] = disk->bootable; 261 default_entry->entry_data.IE.media_type[0] = disk->targetMode; 262 cd9660_721(disk->loadSegment, 263 default_entry->entry_data.IE.load_segment); 264 default_entry->entry_data.IE.system_type[0] = disk->system; 265 cd9660_721(1, default_entry->entry_data.IE.sector_count); 266 cd9660_731(disk->sector, default_entry->entry_data.IE.load_rba); 267 268 return default_entry; 269} 270 271static struct boot_catalog_entry * 272cd9660_boot_setup_section_head(char platform) 273{ 274 struct boot_catalog_entry *sh; 275 276 sh = cd9660_init_boot_catalog_entry(); 277 if (sh == NULL) 278 return NULL; 279 /* More by default. The last one will manually be set to 0x91 */ 280 sh->entry_data.SH.header_indicator[0] = ET_SECTION_HEADER_MORE; 281 sh->entry_data.SH.platform_id[0] = platform; 282 sh->entry_data.SH.num_section_entries[0] = 0; 283 return sh; 284} 285 286static struct boot_catalog_entry * 287cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk) 288{ 289 struct boot_catalog_entry *entry; 290 291 if ((entry = cd9660_init_boot_catalog_entry()) == NULL) 292 return NULL; 293 294 entry->entry_data.SE.boot_indicator[0] = ET_BOOTABLE; 295 entry->entry_data.SE.media_type[0] = disk->targetMode; 296 cd9660_721(disk->loadSegment, entry->entry_data.SE.load_segment); 297 cd9660_721(1, entry->entry_data.SE.sector_count); 298 299 cd9660_731(disk->sector,entry->entry_data.IE.load_rba); 300 return entry; 301} 302 303#if 0 304static u_char 305cd9660_boot_get_system_type(struct cd9660_boot_image *disk) 306{ 307 /* 308 For hard drive booting, we need to examine the MBR to figure 309 out what the partition type is 310 */ 311 return 0; 312} 313#endif 314 315/* 316 * Set up the BVD, Boot catalog, and the boot entries, but do no writing 317 */ 318int 319cd9660_setup_boot(int first_sector) 320{ 321 int need_head; 322 int sector; 323 int used_sectors; 324 int num_entries = 0; 325 int catalog_sectors; 326 struct boot_catalog_entry *x86_head, *mac_head, *ppc_head, 327 *last_x86, *last_ppc, *last_mac, *last_head, 328 *valid_entry, *default_entry, *temp, *head_temp; 329 struct cd9660_boot_image *tmp_disk; 330 331 head_temp = NULL; 332 need_head = 0; 333 x86_head = mac_head = ppc_head = 334 last_x86 = last_ppc = last_mac = last_head = NULL; 335 336 /* If there are no boot disks, don't bother building boot information */ 337 if ((tmp_disk = diskStructure.boot_images.lh_first) == NULL) 338 return 0; 339 340 /* Point to catalog: For now assume it consumes one sector */ 341 printf("Boot catalog will go in sector %i\n",first_sector); 342 diskStructure.boot_catalog_sector = first_sector; 343 cd9660_bothendian_dword(first_sector, 344 diskStructure.boot_descriptor->boot_catalog_pointer); 345 sector = first_sector +1; 346 347 /* Step 1: Generate boot catalog */ 348 /* Step 1a: Validation entry */ 349 valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86); 350 if (valid_entry == NULL) 351 return -1; 352 353 /* 354 * Count how many boot images there are, 355 * and how many sectors they consume. 356 */ 357 num_entries = 1; 358 used_sectors = 0; 359 360 for (tmp_disk = diskStructure.boot_images.lh_first; tmp_disk != NULL; 361 tmp_disk = tmp_disk->image_list.le_next) { 362 used_sectors += tmp_disk->num_sectors; 363 364 /* One default entry per image */ 365 num_entries++; 366 } 367 catalog_sectors = howmany(num_entries * 0x20, diskStructure.sectorSize); 368 used_sectors += catalog_sectors; 369 370 printf("boot: there will be %i entries consuming %i sectors. " 371 "Catalog is %i sectors\n", num_entries, used_sectors, 372 catalog_sectors); 373 374 /* Populate sector numbers */ 375 sector = first_sector + catalog_sectors; 376 for (tmp_disk = diskStructure.boot_images.lh_first; tmp_disk != NULL; 377 tmp_disk = tmp_disk->image_list.le_next) { 378 tmp_disk->sector = sector; 379 sector += tmp_disk->num_sectors; 380 } 381 382 LIST_INSERT_HEAD(&diskStructure.boot_entries, valid_entry, ll_struct); 383 384 /* Step 1b: Initial/default entry */ 385 /* TODO : PARAM */ 386 tmp_disk = diskStructure.boot_images.lh_first; 387 default_entry = cd9660_boot_setup_default_entry(tmp_disk); 388 if (default_entry == NULL) { 389 warnx("Error: memory allocation failed in cd9660_setup_boot"); 390 return -1; 391 } 392 393 LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct); 394 395 /* Todo: multiple default entries? */ 396 397 tmp_disk = tmp_disk->image_list.le_next; 398 399 temp = default_entry; 400 401 /* If multiple boot images are given : */ 402 while (tmp_disk != NULL) { 403 /* Step 2: Section header */ 404 switch (tmp_disk->system) { 405 case ET_SYS_X86: 406 need_head = (x86_head == NULL); 407 if (!need_head) 408 head_temp = x86_head; 409 break; 410 case ET_SYS_PPC: 411 need_head = (ppc_head == NULL); 412 if (!need_head) 413 head_temp = ppc_head; 414 break; 415 case ET_SYS_MAC: 416 need_head = (mac_head == NULL); 417 if (!need_head) 418 head_temp = mac_head; 419 break; 420 } 421 422 if (need_head) { 423 head_temp = 424 cd9660_boot_setup_section_head(tmp_disk->system); 425 if (head_temp == NULL) { 426 warnx("Error: memory allocation failed in " 427 "cd9660_setup_boot"); 428 return -1; 429 } 430 LIST_INSERT_AFTER(default_entry,head_temp, ll_struct); 431 } 432 head_temp->entry_data.SH.num_section_entries[0]++; 433 434 /* Step 2a: Section entry and extensions */ 435 temp = cd9660_boot_setup_section_entry(tmp_disk); 436 if (temp == NULL) { 437 warnx("Error: memory allocation failed in " 438 "cd9660_setup_boot"); 439 return -1; 440 } 441 442 while (head_temp->ll_struct.le_next != NULL) { 443 if (head_temp->ll_struct.le_next->entry_type 444 != ET_ENTRY_SE) 445 break; 446 head_temp = head_temp->ll_struct.le_next; 447 } 448 449 LIST_INSERT_AFTER(head_temp,temp, ll_struct); 450 tmp_disk = tmp_disk->image_list.le_next; 451 } 452 453 /* TODO: Remaining boot disks when implemented */ 454 455 return first_sector + used_sectors; 456} 457 458int 459cd9660_setup_boot_volume_descritpor(volume_descriptor *bvd) 460{ 461 boot_volume_descriptor *bvdData = 462 (boot_volume_descriptor*)bvd->volumeDescriptorData; 463 464 bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT; 465 memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); 466 bvdData->version[0] = 1; 467 memcpy(bvdData->boot_system_identifier, ET_ID, 23); 468 memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); 469 diskStructure.boot_descriptor = 470 (boot_volume_descriptor*) bvd->volumeDescriptorData; 471 return 1; 472} 473 474int 475cd9660_write_boot(FILE *fd) 476{ 477 struct boot_catalog_entry *e; 478 struct cd9660_boot_image *t; 479 480 /* write boot catalog */ 481 fseek(fd, diskStructure.boot_catalog_sector * diskStructure.sectorSize, 482 SEEK_SET); 483 484 printf("Writing boot catalog to sector %i\n", 485 diskStructure.boot_catalog_sector); 486 for (e = diskStructure.boot_entries.lh_first; e != NULL; 487 e = e->ll_struct.le_next) { 488 printf("Writing catalog entry of type %i\n", e->entry_type); 489 /* 490 * It doesnt matter which one gets written 491 * since they are the same size 492 */ 493 fwrite(&(e->entry_data.VE), 1, 32, fd); 494 } 495 printf("Finished writing boot catalog\n"); 496 497 /* copy boot images */ 498 for (t = diskStructure.boot_images.lh_first; t != NULL; 499 t = t->image_list.le_next) { 500 printf("Writing boot image from %s to sectors %i\n", 501 t->filename,t->sector); 502 cd9660_copy_file(fd, t->sector, t->filename); 503 } 504 505 return 0; 506} 507