1 /* $NetBSD: cd9660_eltorito.c,v 1.11 2006/04/22 17:38:20 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
35 #include "cd9660_eltorito.h"
37 #include <sys/cdefs.h>
38 #if defined(__RCSID) && !defined(__lint)
39 __RCSID("$NetBSD: cd9660_eltorito.c,v 1.11 2006/04/22 17:38:20 christos Exp $");
43 #define ELTORITO_DPRINTF(__x) printf __x
45 #define ELTORITO_DPRINTF(__x)
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);
55 static u_char
cd9660_boot_get_system_type(struct cd9660_boot_image
*);
59 cd9660_add_boot_disk(const char *boot_info
)
66 struct cd9660_boot_image
*new_image
, *tmp_image
;
68 assert(boot_info
!= NULL
);
70 if (*boot_info
== '\0') {
71 warnx("Error: Boot disk information must be in the "
72 "format 'system;filename'");
76 /* First decode the boot information */
77 if ((temp
= strdup(boot_info
)) == NULL
) {
78 warn("%s: strdup", __func__
);
83 filename
= strchr(sysname
, ';');
84 if (filename
== NULL
) {
85 warnx("supply boot disk information in the format "
93 if (diskStructure
.verbose_level
> 0) {
94 printf("Found bootdisk with system %s, and filename %s\n",
97 if ((new_image
= malloc(sizeof(*new_image
))) == NULL
) {
98 warn("%s: malloc", __func__
);
102 (void)memset(new_image
, 0, sizeof(*new_image
));
103 new_image
->loadSegment
= 0; /* default for now */
106 if (strcmp(sysname
, "i386") == 0)
107 new_image
->system
= ET_SYS_X86
;
108 else if (strcmp(sysname
, "powerpc") == 0)
109 new_image
->system
= ET_SYS_PPC
;
110 else if (strcmp(sysname
, "macppc") == 0 ||
111 strcmp(sysname
, "mac68k") == 0)
112 new_image
->system
= ET_SYS_MAC
;
114 warnx("boot disk system must be "
115 "i386, powerpc, macppc, or mac68k");
122 if ((new_image
->filename
= strdup(filename
)) == NULL
) {
123 warn("%s: strdup", __func__
);
131 /* Get information about the file */
132 if (lstat(new_image
->filename
, &stbuf
) == -1)
133 err(EXIT_FAILURE
, "%s: lstat(\"%s\")", __func__
,
134 new_image
->filename
);
136 switch (stbuf
.st_size
) {
138 new_image
->targetMode
= ET_MEDIA_144FDD
;
139 mode_msg
= "Assigned boot image to 1.44 emulation mode";
142 new_image
->targetMode
= ET_MEDIA_12FDD
;
143 mode_msg
= "Assigned boot image to 1.2 emulation mode";
146 new_image
->targetMode
= ET_MEDIA_288FDD
;
147 mode_msg
= "Assigned boot image to 2.88 emulation mode";
150 new_image
->targetMode
= ET_MEDIA_NOEM
;
151 mode_msg
= "Assigned boot image to no emulation mode";
155 if (diskStructure
.verbose_level
> 0)
156 printf("%s\n", mode_msg
);
158 new_image
->size
= stbuf
.st_size
;
159 new_image
->num_sectors
=
160 howmany(new_image
->size
, diskStructure
.sectorSize
) *
161 howmany(diskStructure
.sectorSize
, 512);
162 if (diskStructure
.verbose_level
> 0) {
163 printf("New image has size %d, uses %d 512-byte sectors\n",
164 new_image
->size
, new_image
->num_sectors
);
166 new_image
->sector
= -1;
167 /* Bootable by default */
168 new_image
->bootable
= ET_BOOTABLE
;
171 /* Group images for the same platform together. */
172 TAILQ_FOREACH(tmp_image
, &diskStructure
.boot_images
, image_list
) {
173 if (tmp_image
->system
!= new_image
->system
)
177 if (tmp_image
== NULL
) {
178 TAILQ_INSERT_HEAD(&diskStructure
.boot_images
, new_image
,
181 TAILQ_INSERT_BEFORE(tmp_image
, new_image
, image_list
);
183 new_image
->serialno
= diskStructure
.image_serialno
++;
185 /* TODO : Need to do anything about the boot image in the tree? */
186 diskStructure
.is_bootable
= 1;
192 cd9660_eltorito_add_boot_option(const char *option_string
, const char *value
)
195 struct cd9660_boot_image
*image
;
197 assert(option_string
!= NULL
);
199 /* Find the last image added */
200 TAILQ_FOREACH(image
, &diskStructure
.boot_images
, image_list
) {
201 if (image
->serialno
+ 1 == diskStructure
.image_serialno
)
205 errx(EXIT_FAILURE
, "Attempted to add boot option, "
206 "but no boot images have been specified");
208 if (strcmp(option_string
, "no-emul-boot") == 0) {
209 image
->targetMode
= ET_MEDIA_NOEM
;
210 } else if (strcmp(option_string
, "no-boot") == 0) {
211 image
->bootable
= ET_NOT_BOOTABLE
;
212 } else if (strcmp(option_string
, "hard-disk-boot") == 0) {
213 image
->targetMode
= ET_MEDIA_HDD
;
214 } else if (strcmp(option_string
, "boot-load-segment") == 0) {
215 image
->loadSegment
= strtoul(value
, &eptr
, 16);
216 if (eptr
== value
|| *eptr
!= '\0' || errno
!= ERANGE
) {
217 warn("%s: strtoul", __func__
);
226 static struct boot_catalog_entry
*
227 cd9660_init_boot_catalog_entry(void)
229 struct boot_catalog_entry
*temp
;
231 if ((temp
= malloc(sizeof(*temp
))) == NULL
)
234 return memset(temp
, 0, sizeof(*temp
));
237 static struct boot_catalog_entry
*
238 cd9660_boot_setup_validation_entry(char sys
)
240 struct boot_catalog_entry
*entry
;
241 boot_catalog_validation_entry
*ve
;
243 unsigned char *csptr
;
245 entry
= cd9660_init_boot_catalog_entry();
248 warnx("Error: memory allocation failed in "
249 "cd9660_boot_setup_validation_entry");
252 ve
= &entry
->entry_data
.VE
;
254 ve
->header_id
[0] = 1;
255 ve
->platform_id
[0] = sys
;
259 /* Calculate checksum */
261 cd9660_721(0, ve
->checksum
);
262 csptr
= (unsigned char*)ve
;
263 for (i
= 0; i
< sizeof(*ve
); i
+= 2) {
264 checksum
+= (int16_t)csptr
[i
];
265 checksum
+= 256 * (int16_t)csptr
[i
+ 1];
267 checksum
= -checksum
;
268 cd9660_721(checksum
, ve
->checksum
);
270 ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
271 "checksum %04x\n", __func__
, ve
->header_id
[0], ve
->platform_id
[0],
272 ve
->key
[0], ve
->key
[1], checksum
));
276 static struct boot_catalog_entry
*
277 cd9660_boot_setup_default_entry(struct cd9660_boot_image
*disk
)
279 struct boot_catalog_entry
*default_entry
;
280 boot_catalog_initial_entry
*ie
;
282 default_entry
= cd9660_init_boot_catalog_entry();
283 if (default_entry
== NULL
)
286 ie
= &default_entry
->entry_data
.IE
;
288 ie
->boot_indicator
[0] = disk
->bootable
;
289 ie
->media_type
[0] = disk
->targetMode
;
290 cd9660_721(disk
->loadSegment
, ie
->load_segment
);
291 ie
->system_type
[0] = disk
->system
;
292 cd9660_721(disk
->num_sectors
, ie
->sector_count
);
293 cd9660_731(disk
->sector
, ie
->load_rba
);
295 ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
296 "load segment %04x, system type %d, sector count %d, "
297 "load rba %d\n", __func__
, ie
->boot_indicator
[0],
298 ie
->media_type
[0], disk
->loadSegment
, ie
->system_type
[0],
299 disk
->num_sectors
, disk
->sector
));
300 return default_entry
;
303 static struct boot_catalog_entry
*
304 cd9660_boot_setup_section_head(char platform
)
306 struct boot_catalog_entry
*entry
;
307 boot_catalog_section_header
*sh
;
309 entry
= cd9660_init_boot_catalog_entry();
313 sh
= &entry
->entry_data
.SH
;
314 /* More by default. The last one will manually be set to 0x91 */
315 sh
->header_indicator
[0] = ET_SECTION_HEADER_MORE
;
316 sh
->platform_id
[0] = platform
;
317 sh
->num_section_entries
[0] = 0;
321 static struct boot_catalog_entry
*
322 cd9660_boot_setup_section_entry(struct cd9660_boot_image
*disk
)
324 struct boot_catalog_entry
*entry
;
325 boot_catalog_section_entry
*se
;
326 if ((entry
= cd9660_init_boot_catalog_entry()) == NULL
)
329 se
= &entry
->entry_data
.SE
;
331 se
->boot_indicator
[0] = ET_BOOTABLE
;
332 se
->media_type
[0] = disk
->targetMode
;
333 cd9660_721(disk
->loadSegment
, se
->load_segment
);
334 cd9660_721(disk
->num_sectors
, se
->sector_count
);
335 cd9660_731(disk
->sector
, se
->load_rba
);
341 cd9660_boot_get_system_type(struct cd9660_boot_image
*disk
)
344 For hard drive booting, we need to examine the MBR to figure
345 out what the partition type is
352 * Set up the BVD, Boot catalog, and the boot entries, but do no writing
355 cd9660_setup_boot(int first_sector
)
361 struct boot_catalog_entry
*x86_head
, *mac_head
, *ppc_head
,
362 *valid_entry
, *default_entry
, *temp
, *head
, **headp
, *next
;
363 struct cd9660_boot_image
*tmp_disk
;
366 x86_head
= mac_head
= ppc_head
= NULL
;
368 /* If there are no boot disks, don't bother building boot information */
369 if (TAILQ_EMPTY(&diskStructure
.boot_images
))
372 /* Point to catalog: For now assume it consumes one sector */
373 ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector
));
374 diskStructure
.boot_catalog_sector
= first_sector
;
375 cd9660_bothendian_dword(first_sector
,
376 diskStructure
.boot_descriptor
->boot_catalog_pointer
);
378 /* Step 1: Generate boot catalog */
379 /* Step 1a: Validation entry */
380 valid_entry
= cd9660_boot_setup_validation_entry(ET_SYS_X86
);
381 if (valid_entry
== NULL
)
385 * Count how many boot images there are,
386 * and how many sectors they consume.
391 TAILQ_FOREACH(tmp_disk
, &diskStructure
.boot_images
, image_list
) {
392 used_sectors
+= tmp_disk
->num_sectors
;
394 /* One default entry per image */
397 catalog_sectors
= howmany(num_entries
* 0x20, diskStructure
.sectorSize
);
398 used_sectors
+= catalog_sectors
;
400 if (diskStructure
.verbose_level
> 0) {
401 printf("%s: there will be %i entries consuming %i sectors. "
402 "Catalog is %i sectors\n", __func__
, num_entries
,
403 used_sectors
, catalog_sectors
);
406 /* Populate sector numbers */
407 sector
= first_sector
+ catalog_sectors
;
408 TAILQ_FOREACH(tmp_disk
, &diskStructure
.boot_images
, image_list
) {
409 tmp_disk
->sector
= sector
;
410 sector
+= tmp_disk
->num_sectors
;
413 LIST_INSERT_HEAD(&diskStructure
.boot_entries
, valid_entry
, ll_struct
);
415 /* Step 1b: Initial/default entry */
417 tmp_disk
= TAILQ_FIRST(&diskStructure
.boot_images
);
418 default_entry
= cd9660_boot_setup_default_entry(tmp_disk
);
419 if (default_entry
== NULL
) {
420 warnx("Error: memory allocation failed in cd9660_setup_boot");
424 LIST_INSERT_AFTER(valid_entry
, default_entry
, ll_struct
);
426 /* Todo: multiple default entries? */
428 tmp_disk
= TAILQ_NEXT(tmp_disk
, image_list
);
430 temp
= default_entry
;
432 /* If multiple boot images are given : */
433 while (tmp_disk
!= NULL
) {
434 /* Step 2: Section header */
435 switch (tmp_disk
->system
) {
446 warnx("%s: internal error: unknown system type",
451 if (*headp
== NULL
) {
453 cd9660_boot_setup_section_head(tmp_disk
->system
);
455 warnx("Error: memory allocation failed in "
456 "cd9660_setup_boot");
459 LIST_INSERT_AFTER(default_entry
, head
, ll_struct
);
464 head
->entry_data
.SH
.num_section_entries
[0]++;
466 /* Step 2a: Section entry and extensions */
467 temp
= cd9660_boot_setup_section_entry(tmp_disk
);
469 warn("%s: cd9660_boot_setup_section_entry", __func__
);
473 while ((next
= LIST_NEXT(head
, ll_struct
)) != NULL
&&
474 next
->entry_type
== ET_ENTRY_SE
)
477 LIST_INSERT_AFTER(head
, temp
, ll_struct
);
478 tmp_disk
= TAILQ_NEXT(tmp_disk
, image_list
);
481 /* TODO: Remaining boot disks when implemented */
483 return first_sector
+ used_sectors
;
487 cd9660_setup_boot_volume_descriptor(volume_descriptor
*bvd
)
489 boot_volume_descriptor
*bvdData
=
490 (boot_volume_descriptor
*)bvd
->volumeDescriptorData
;
492 bvdData
->boot_record_indicator
[0] = ISO_VOLUME_DESCRIPTOR_BOOT
;
493 memcpy(bvdData
->identifier
, ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
494 bvdData
->version
[0] = 1;
495 memcpy(bvdData
->boot_system_identifier
, ET_ID
, 23);
496 memcpy(bvdData
->identifier
, ISO_VOLUME_DESCRIPTOR_STANDARD_ID
, 5);
497 diskStructure
.boot_descriptor
=
498 (boot_volume_descriptor
*) bvd
->volumeDescriptorData
;
503 cd9660_write_boot(FILE *fd
)
505 struct boot_catalog_entry
*e
;
506 struct cd9660_boot_image
*t
;
508 /* write boot catalog */
509 fseek(fd
, diskStructure
.boot_catalog_sector
* diskStructure
.sectorSize
,
512 if (diskStructure
.verbose_level
> 0) {
513 printf("Writing boot catalog to sector %d\n",
514 diskStructure
.boot_catalog_sector
);
516 LIST_FOREACH(e
, &diskStructure
.boot_entries
, ll_struct
) {
517 if (diskStructure
.verbose_level
> 0) {
518 printf("Writing catalog entry of type %d\n",
522 * It doesnt matter which one gets written
523 * since they are the same size
525 fwrite(&(e
->entry_data
.VE
), 1, 32, fd
);
527 if (diskStructure
.verbose_level
> 0)
528 printf("Finished writing boot catalog\n");
530 /* copy boot images */
531 TAILQ_FOREACH(t
, &diskStructure
.boot_images
, image_list
) {
532 if (diskStructure
.verbose_level
> 0) {
533 printf("Writing boot image from %s to sectors %d\n",
534 t
->filename
, t
->sector
);
536 cd9660_copy_file(fd
, t
->sector
, t
->filename
);