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