1 /* $NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $ */
4 * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
5 * Perez-Rathke and Ram Vedam. All rights reserved.
7 * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
8 * Alan Perez-Rathke and Ram Vedam.
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
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.
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
37 #include "cd9660_eltorito.h"
38 #include <sys/bootblock.h>
41 #include <sys/cdefs.h>
42 #if defined(__RCSID) && !defined(__lint)
43 __RCSID("$NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $");
47 #define ELTORITO_DPRINTF(__x) printf __x
49 #define ELTORITO_DPRINTF(__x)
54 static struct boot_catalog_entry
*cd9660_init_boot_catalog_entry(void);
55 static struct boot_catalog_entry
*cd9660_boot_setup_validation_entry(char);
56 static struct boot_catalog_entry
*cd9660_boot_setup_default_entry(
57 struct cd9660_boot_image
*);
58 static struct boot_catalog_entry
*cd9660_boot_setup_section_head(char);
59 static struct boot_catalog_entry
*cd9660_boot_setup_validation_entry(char);
61 static u_char
cd9660_boot_get_system_type(struct cd9660_boot_image
*);
65 cd9660_add_boot_disk(iso9660_disk
*diskStructure
, const char *boot_info
)
72 struct cd9660_boot_image
*new_image
, *tmp_image
;
74 assert(boot_info
!= NULL
);
76 if (*boot_info
== '\0') {
77 warnx("Error: Boot disk information must be in the "
78 "format 'system;filename'");
82 /* First decode the boot information */
83 temp
= estrdup(boot_info
);
86 filename
= strchr(sysname
, ';');
87 if (filename
== NULL
) {
88 warnx("supply boot disk information in the format "
96 if (diskStructure
->verbose_level
> 0) {
97 printf("Found bootdisk with system %s, and filename %s\n",
100 new_image
= ecalloc(1, sizeof(*new_image
));
101 new_image
->loadSegment
= 0; /* default for now */
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
;
112 warnx("boot disk system must be "
113 "i386, powerpc, macppc, or mac68k");
120 new_image
->filename
= estrdup(filename
);
124 /* Get information about the file */
125 if (lstat(new_image
->filename
, &stbuf
) == -1)
126 err(EXIT_FAILURE
, "%s: lstat(\"%s\")", __func__
,
127 new_image
->filename
);
129 switch (stbuf
.st_size
) {
131 new_image
->targetMode
= ET_MEDIA_144FDD
;
132 mode_msg
= "Assigned boot image to 1.44 emulation mode";
135 new_image
->targetMode
= ET_MEDIA_12FDD
;
136 mode_msg
= "Assigned boot image to 1.2 emulation mode";
139 new_image
->targetMode
= ET_MEDIA_288FDD
;
140 mode_msg
= "Assigned boot image to 2.88 emulation mode";
143 new_image
->targetMode
= ET_MEDIA_NOEM
;
144 mode_msg
= "Assigned boot image to no emulation mode";
148 if (diskStructure
->verbose_level
> 0)
149 printf("%s\n", mode_msg
);
151 new_image
->size
= stbuf
.st_size
;
152 new_image
->num_sectors
=
153 howmany(new_image
->size
, diskStructure
->sectorSize
) *
154 howmany(diskStructure
->sectorSize
, 512);
155 if (diskStructure
->verbose_level
> 0) {
156 printf("New image has size %d, uses %d 512-byte sectors\n",
157 new_image
->size
, new_image
->num_sectors
);
159 new_image
->sector
= -1;
160 /* Bootable by default */
161 new_image
->bootable
= ET_BOOTABLE
;
164 /* Group images for the same platform together. */
165 TAILQ_FOREACH(tmp_image
, &diskStructure
->boot_images
, image_list
) {
166 if (tmp_image
->system
!= new_image
->system
)
170 if (tmp_image
== NULL
) {
171 TAILQ_INSERT_HEAD(&diskStructure
->boot_images
, new_image
,
174 TAILQ_INSERT_BEFORE(tmp_image
, new_image
, image_list
);
176 new_image
->serialno
= diskStructure
->image_serialno
++;
178 /* TODO : Need to do anything about the boot image in the tree? */
179 diskStructure
->is_bootable
= 1;
185 cd9660_eltorito_add_boot_option(iso9660_disk
*diskStructure
,
186 const char *option_string
, const char *value
)
189 struct cd9660_boot_image
*image
;
191 assert(option_string
!= NULL
);
193 /* Find the last image added */
194 TAILQ_FOREACH(image
, &diskStructure
->boot_images
, image_list
) {
195 if (image
->serialno
+ 1 == diskStructure
->image_serialno
)
199 errx(EXIT_FAILURE
, "Attempted to add boot option, "
200 "but no boot images have been specified");
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__
);
220 static struct boot_catalog_entry
*
221 cd9660_init_boot_catalog_entry(void)
223 return ecalloc(1, sizeof(struct boot_catalog_entry
));
226 static struct boot_catalog_entry
*
227 cd9660_boot_setup_validation_entry(char sys
)
229 struct boot_catalog_entry
*entry
;
230 boot_catalog_validation_entry
*ve
;
232 unsigned char *csptr
;
234 entry
= cd9660_init_boot_catalog_entry();
236 ve
= &entry
->entry_data
.VE
;
238 ve
->header_id
[0] = 1;
239 ve
->platform_id
[0] = sys
;
243 /* Calculate checksum */
245 cd9660_721(0, ve
->checksum
);
246 csptr
= (unsigned char*)ve
;
247 for (i
= 0; i
< sizeof(*ve
); i
+= 2) {
248 checksum
+= (int16_t)csptr
[i
];
249 checksum
+= 256 * (int16_t)csptr
[i
+ 1];
251 checksum
= -checksum
;
252 cd9660_721(checksum
, ve
->checksum
);
254 ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
255 "checksum %04x\n", __func__
, ve
->header_id
[0], ve
->platform_id
[0],
256 ve
->key
[0], ve
->key
[1], checksum
));
260 static struct boot_catalog_entry
*
261 cd9660_boot_setup_default_entry(struct cd9660_boot_image
*disk
)
263 struct boot_catalog_entry
*default_entry
;
264 boot_catalog_initial_entry
*ie
;
266 default_entry
= cd9660_init_boot_catalog_entry();
267 if (default_entry
== NULL
)
270 ie
= &default_entry
->entry_data
.IE
;
272 ie
->boot_indicator
[0] = disk
->bootable
;
273 ie
->media_type
[0] = disk
->targetMode
;
274 cd9660_721(disk
->loadSegment
, ie
->load_segment
);
275 ie
->system_type
[0] = disk
->system
;
276 cd9660_721(disk
->num_sectors
, ie
->sector_count
);
277 cd9660_731(disk
->sector
, ie
->load_rba
);
279 ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
280 "load segment %04x, system type %d, sector count %d, "
281 "load rba %d\n", __func__
, ie
->boot_indicator
[0],
282 ie
->media_type
[0], disk
->loadSegment
, ie
->system_type
[0],
283 disk
->num_sectors
, disk
->sector
));
284 return default_entry
;
287 static struct boot_catalog_entry
*
288 cd9660_boot_setup_section_head(char platform
)
290 struct boot_catalog_entry
*entry
;
291 boot_catalog_section_header
*sh
;
293 entry
= cd9660_init_boot_catalog_entry();
297 sh
= &entry
->entry_data
.SH
;
298 /* More by default. The last one will manually be set to 0x91 */
299 sh
->header_indicator
[0] = ET_SECTION_HEADER_MORE
;
300 sh
->platform_id
[0] = platform
;
301 sh
->num_section_entries
[0] = 0;
305 static struct boot_catalog_entry
*
306 cd9660_boot_setup_section_entry(struct cd9660_boot_image
*disk
)
308 struct boot_catalog_entry
*entry
;
309 boot_catalog_section_entry
*se
;
310 if ((entry
= cd9660_init_boot_catalog_entry()) == NULL
)
313 se
= &entry
->entry_data
.SE
;
315 se
->boot_indicator
[0] = ET_BOOTABLE
;
316 se
->media_type
[0] = disk
->targetMode
;
317 cd9660_721(disk
->loadSegment
, se
->load_segment
);
318 cd9660_721(disk
->num_sectors
, se
->sector_count
);
319 cd9660_731(disk
->sector
, se
->load_rba
);
325 cd9660_boot_get_system_type(struct cd9660_boot_image
*disk
)
328 For hard drive booting, we need to examine the MBR to figure
329 out what the partition type is
336 * Set up the BVD, Boot catalog, and the boot entries, but do no writing
339 cd9660_setup_boot(iso9660_disk
*diskStructure
, int first_sector
)
345 struct boot_catalog_entry
*x86_head
, *mac_head
, *ppc_head
,
346 *valid_entry
, *default_entry
, *temp
, *head
, **headp
, *next
;
347 struct cd9660_boot_image
*tmp_disk
;
350 x86_head
= mac_head
= ppc_head
= NULL
;
352 /* If there are no boot disks, don't bother building boot information */
353 if (TAILQ_EMPTY(&diskStructure
->boot_images
))
356 /* Point to catalog: For now assume it consumes one sector */
357 ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector
));
358 diskStructure
->boot_catalog_sector
= first_sector
;
359 cd9660_bothendian_dword(first_sector
,
360 diskStructure
->boot_descriptor
->boot_catalog_pointer
);
362 /* Step 1: Generate boot catalog */
363 /* Step 1a: Validation entry */
364 valid_entry
= cd9660_boot_setup_validation_entry(ET_SYS_X86
);
365 if (valid_entry
== NULL
)
369 * Count how many boot images there are,
370 * and how many sectors they consume.
375 TAILQ_FOREACH(tmp_disk
, &diskStructure
->boot_images
, image_list
) {
376 used_sectors
+= tmp_disk
->num_sectors
;
378 /* One default entry per image */
381 catalog_sectors
= howmany(num_entries
* 0x20, diskStructure
->sectorSize
);
382 used_sectors
+= catalog_sectors
;
384 if (diskStructure
->verbose_level
> 0) {
385 printf("%s: there will be %i entries consuming %i sectors. "
386 "Catalog is %i sectors\n", __func__
, num_entries
,
387 used_sectors
, catalog_sectors
);
390 /* Populate sector numbers */
391 sector
= first_sector
+ catalog_sectors
;
392 TAILQ_FOREACH(tmp_disk
, &diskStructure
->boot_images
, image_list
) {
393 tmp_disk
->sector
= sector
;
394 sector
+= tmp_disk
->num_sectors
;
397 LIST_INSERT_HEAD(&diskStructure
->boot_entries
, valid_entry
, ll_struct
);
399 /* Step 1b: Initial/default entry */
401 tmp_disk
= TAILQ_FIRST(&diskStructure
->boot_images
);
402 default_entry
= cd9660_boot_setup_default_entry(tmp_disk
);
403 if (default_entry
== NULL
) {
404 warnx("Error: memory allocation failed in cd9660_setup_boot");
408 LIST_INSERT_AFTER(valid_entry
, default_entry
, ll_struct
);
410 /* Todo: multiple default entries? */
412 tmp_disk
= TAILQ_NEXT(tmp_disk
, image_list
);
414 temp
= default_entry
;
416 /* If multiple boot images are given : */
417 while (tmp_disk
!= NULL
) {
418 /* Step 2: Section header */
419 switch (tmp_disk
->system
) {
430 warnx("%s: internal error: unknown system type",
435 if (*headp
== NULL
) {
437 cd9660_boot_setup_section_head(tmp_disk
->system
);
439 warnx("Error: memory allocation failed in "
440 "cd9660_setup_boot");
443 LIST_INSERT_AFTER(default_entry
, head
, ll_struct
);
448 head
->entry_data
.SH
.num_section_entries
[0]++;
450 /* Step 2a: Section entry and extensions */
451 temp
= cd9660_boot_setup_section_entry(tmp_disk
);
453 warn("%s: cd9660_boot_setup_section_entry", __func__
);
457 while ((next
= LIST_NEXT(head
, ll_struct
)) != NULL
&&
458 next
->entry_type
== ET_ENTRY_SE
)
461 LIST_INSERT_AFTER(head
, temp
, ll_struct
);
462 tmp_disk
= TAILQ_NEXT(tmp_disk
, image_list
);
465 /* TODO: Remaining boot disks when implemented */
467 return first_sector
+ used_sectors
;
471 cd9660_setup_boot_volume_descriptor(iso9660_disk
*diskStructure
,
472 volume_descriptor
*bvd
)
474 boot_volume_descriptor
*bvdData
=
475 (boot_volume_descriptor
*)bvd
->volumeDescriptorData
;
477 bvdData
->boot_record_indicator
[0] = ISO_VOLUME_DESCRIPTOR_BOOT
;
478 memcpy(bvdData
->identifier
, ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
479 bvdData
->version
[0] = 1;
480 memcpy(bvdData
->boot_system_identifier
, ET_ID
, 23);
481 memcpy(bvdData
->identifier
, ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
482 diskStructure
->boot_descriptor
=
483 (boot_volume_descriptor
*) bvd
->volumeDescriptorData
;
488 cd9660_write_mbr_partition_entry(FILE *fd
, int idx
, off_t sector_start
,
489 off_t nsectors
, int type
)
494 if (fseeko(fd
, (off_t
)(idx
) * 16 + 0x1be, SEEK_SET
) == -1)
497 val
= 0x80; /* Bootable */
498 fwrite(&val
, sizeof(val
), 1, fd
);
500 val
= 0xff; /* CHS begin */
501 fwrite(&val
, sizeof(val
), 1, fd
);
502 fwrite(&val
, sizeof(val
), 1, fd
);
503 fwrite(&val
, sizeof(val
), 1, fd
);
505 val
= type
; /* Part type */
506 fwrite(&val
, sizeof(val
), 1, fd
);
508 val
= 0xff; /* CHS end */
509 fwrite(&val
, sizeof(val
), 1, fd
);
510 fwrite(&val
, sizeof(val
), 1, fd
);
511 fwrite(&val
, sizeof(val
), 1, fd
);
514 lba
= htole32(sector_start
);
515 fwrite(&lba
, sizeof(lba
), 1, fd
);
516 lba
= htole32(nsectors
);
517 fwrite(&lba
, sizeof(lba
), 1, fd
);
523 cd9660_write_apm_partition_entry(FILE *fd
, int idx
, int total_partitions
,
524 off_t sector_start
, off_t nsectors
, off_t sector_size
,
525 const char *part_name
, const char *part_type
)
527 uint32_t apm32
, part_status
;
530 /* See Apple Tech Note 1189 for the details about the pmPartStatus
532 * Below the flags which are default:
538 part_status
= APPLE_PS_VALID
| APPLE_PS_ALLOCATED
| APPLE_PS_READABLE
|
541 if (fseeko(fd
, (off_t
)(idx
+ 1) * sector_size
, SEEK_SET
) == -1)
545 apm16
= htobe16(0x504d);
546 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
548 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
550 /* Total number of partitions */
551 apm32
= htobe32(total_partitions
);
552 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
554 apm32
= htobe32(sector_start
);
555 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
556 apm32
= htobe32(nsectors
);
557 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
559 fwrite(part_name
, strlen(part_name
) + 1, 1, fd
);
560 fseek(fd
, 32 - strlen(part_name
) - 1, SEEK_CUR
);
561 fwrite(part_type
, strlen(part_type
) + 1, 1, fd
);
562 fseek(fd
, 32 - strlen(part_type
) - 1, SEEK_CUR
);
566 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
568 apm32
= htobe32(nsectors
);
569 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
571 apm32
= htobe32(part_status
);
572 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
578 cd9660_write_boot(iso9660_disk
*diskStructure
, FILE *fd
)
580 struct boot_catalog_entry
*e
;
581 struct cd9660_boot_image
*t
;
582 int apm_partitions
= 0;
583 int mbr_partitions
= 0;
585 /* write boot catalog */
586 if (fseeko(fd
, (off_t
)diskStructure
->boot_catalog_sector
*
587 diskStructure
->sectorSize
, SEEK_SET
) == -1)
590 if (diskStructure
->verbose_level
> 0) {
591 printf("Writing boot catalog to sector %" PRId64
"\n",
592 diskStructure
->boot_catalog_sector
);
594 LIST_FOREACH(e
, &diskStructure
->boot_entries
, ll_struct
) {
595 if (diskStructure
->verbose_level
> 0) {
596 printf("Writing catalog entry of type %d\n",
600 * It doesnt matter which one gets written
601 * since they are the same size
603 fwrite(&(e
->entry_data
.VE
), 1, 32, fd
);
605 if (diskStructure
->verbose_level
> 0)
606 printf("Finished writing boot catalog\n");
608 /* copy boot images */
609 TAILQ_FOREACH(t
, &diskStructure
->boot_images
, image_list
) {
610 if (diskStructure
->verbose_level
> 0) {
611 printf("Writing boot image from %s to sectors %d\n",
612 t
->filename
, t
->sector
);
614 cd9660_copy_file(diskStructure
, fd
, t
->sector
, t
->filename
);
616 if (t
->system
== ET_SYS_MAC
)
618 if (t
->system
== ET_SYS_PPC
)
622 /* some systems need partition tables as well */
623 if (mbr_partitions
> 0 || diskStructure
->chrp_boot
) {
626 fseek(fd
, 0x1fe, SEEK_SET
);
627 sig
= htole16(0xaa55);
628 fwrite(&sig
, sizeof(sig
), 1, fd
);
632 /* Write ISO9660 descriptor, enclosing the whole disk */
633 if (diskStructure
->chrp_boot
)
634 cd9660_write_mbr_partition_entry(fd
, mbr_partitions
++,
635 0, diskStructure
->totalSectors
*
636 (diskStructure
->sectorSize
/ 512), 0x96);
638 /* Write all partition entries */
639 TAILQ_FOREACH(t
, &diskStructure
->boot_images
, image_list
) {
640 if (t
->system
!= ET_SYS_PPC
)
642 cd9660_write_mbr_partition_entry(fd
, mbr_partitions
++,
643 t
->sector
* (diskStructure
->sectorSize
/ 512),
644 t
->num_sectors
* (diskStructure
->sectorSize
/ 512),
645 0x41 /* PReP Boot */);
649 if (apm_partitions
> 0) {
650 /* Write DDR and global APM info */
655 fseek(fd
, 0, SEEK_SET
);
656 apm16
= htobe16(0x4552);
657 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
658 /* Device block size */
659 apm16
= htobe16(512);
660 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
661 /* Device block count */
662 apm32
= htobe32(diskStructure
->totalSectors
*
663 (diskStructure
->sectorSize
/ 512));
664 fwrite(&apm32
, sizeof(apm32
), 1, fd
);
667 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
668 fwrite(&apm16
, sizeof(apm16
), 1, fd
);
670 /* Count total needed entries */
671 total_parts
= 2 + apm_partitions
; /* Self + ISO9660 */
673 /* Write self-descriptor */
674 cd9660_write_apm_partition_entry(fd
, 0, total_parts
, 1,
675 total_parts
, 512, "Apple", "Apple_partition_map");
677 /* Write all partition entries */
679 TAILQ_FOREACH(t
, &diskStructure
->boot_images
, image_list
) {
680 if (t
->system
!= ET_SYS_MAC
)
683 cd9660_write_apm_partition_entry(fd
,
684 1 + apm_partitions
++, total_parts
,
685 t
->sector
* (diskStructure
->sectorSize
/ 512),
686 t
->num_sectors
* (diskStructure
->sectorSize
/ 512),
687 512, "CD Boot", "Apple_Bootstrap");
690 /* Write ISO9660 descriptor, enclosing the whole disk */
691 cd9660_write_apm_partition_entry(fd
, 2 + apm_partitions
,
692 total_parts
, 0, diskStructure
->totalSectors
*
693 (diskStructure
->sectorSize
/ 512), 512, "ISO9660",