Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / makefs / cd9660 / cd9660_eltorito.c
blobbabb973a8ac52a51842ee89265253d3c7c8f1bbe
1 /* $NetBSD: cd9660_eltorito.c,v 1.11 2006/04/22 17:38:20 christos Exp $ */
3 /*
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
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.
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.
34 #include "cd9660.h"
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 $");
40 #endif /* !__lint */
42 #ifdef DEBUG
43 #define ELTORITO_DPRINTF(__x) printf __x
44 #else
45 #define ELTORITO_DPRINTF(__x)
46 #endif
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
58 int
59 cd9660_add_boot_disk(const char *boot_info)
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;
68 assert(boot_info != NULL);
70 if (*boot_info == '\0') {
71 warnx("Error: Boot disk information must be in the "
72 "format 'system;filename'");
73 return 0;
76 /* First decode the boot information */
77 if ((temp = strdup(boot_info)) == NULL) {
78 warn("%s: strdup", __func__);
79 return 0;
82 sysname = temp;
83 filename = strchr(sysname, ';');
84 if (filename == NULL) {
85 warnx("supply boot disk information in the format "
86 "'system;filename'");
87 free(temp);
88 return 0;
91 *filename++ = '\0';
93 if (diskStructure.verbose_level > 0) {
94 printf("Found bootdisk with system %s, and filename %s\n",
95 sysname, filename);
97 if ((new_image = malloc(sizeof(*new_image))) == NULL) {
98 warn("%s: malloc", __func__);
99 free(temp);
100 return 0;
102 (void)memset(new_image, 0, sizeof(*new_image));
103 new_image->loadSegment = 0; /* default for now */
105 /* Decode System */
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;
113 else {
114 warnx("boot disk system must be "
115 "i386, powerpc, macppc, or mac68k");
116 free(temp);
117 free(new_image);
118 return 0;
122 if ((new_image->filename = strdup(filename)) == NULL) {
123 warn("%s: strdup", __func__);
124 free(temp);
125 free(new_image);
126 return 0;
129 free(temp);
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) {
137 case 1440 * 1024:
138 new_image->targetMode = ET_MEDIA_144FDD;
139 mode_msg = "Assigned boot image to 1.44 emulation mode";
140 break;
141 case 1200 * 1024:
142 new_image->targetMode = ET_MEDIA_12FDD;
143 mode_msg = "Assigned boot image to 1.2 emulation mode";
144 break;
145 case 2880 * 1024:
146 new_image->targetMode = ET_MEDIA_288FDD;
147 mode_msg = "Assigned boot image to 2.88 emulation mode";
148 break;
149 default:
150 new_image->targetMode = ET_MEDIA_NOEM;
151 mode_msg = "Assigned boot image to no emulation mode";
152 break;
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;
169 /* Add boot disk */
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)
174 break;
177 if (tmp_image == NULL) {
178 TAILQ_INSERT_HEAD(&diskStructure.boot_images, new_image,
179 image_list);
180 } else
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;
188 return 1;
192 cd9660_eltorito_add_boot_option(const char *option_string, const char *value)
194 char *eptr;
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)
202 break;
204 if (image == NULL)
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__);
218 return 0;
220 } else {
221 return 0;
223 return 1;
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)
232 return 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;
242 int16_t checksum;
243 unsigned char *csptr;
244 int i;
245 entry = cd9660_init_boot_catalog_entry();
247 if (entry == NULL) {
248 warnx("Error: memory allocation failed in "
249 "cd9660_boot_setup_validation_entry");
250 return 0;
252 ve = &entry->entry_data.VE;
254 ve->header_id[0] = 1;
255 ve->platform_id[0] = sys;
256 ve->key[0] = 0x55;
257 ve->key[1] = 0xAA;
259 /* Calculate checksum */
260 checksum = 0;
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));
273 return entry;
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)
284 return 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();
310 if (entry == NULL)
311 return NULL;
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;
318 return entry;
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)
327 return 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);
336 return entry;
339 #if 0
340 static u_char
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
347 return 0;
349 #endif
352 * Set up the BVD, Boot catalog, and the boot entries, but do no writing
355 cd9660_setup_boot(int first_sector)
357 int sector;
358 int used_sectors;
359 int num_entries = 0;
360 int catalog_sectors;
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;
365 headp = NULL;
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))
370 return 0;
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)
382 return -1;
385 * Count how many boot images there are,
386 * and how many sectors they consume.
388 num_entries = 1;
389 used_sectors = 0;
391 TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
392 used_sectors += tmp_disk->num_sectors;
394 /* One default entry per image */
395 num_entries++;
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 */
416 /* TODO : PARAM */
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");
421 return -1;
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) {
436 case ET_SYS_X86:
437 headp = &x86_head;
438 break;
439 case ET_SYS_PPC:
440 headp = &ppc_head;
441 break;
442 case ET_SYS_MAC:
443 headp = &mac_head;
444 break;
445 default:
446 warnx("%s: internal error: unknown system type",
447 __func__);
448 return -1;
451 if (*headp == NULL) {
452 head =
453 cd9660_boot_setup_section_head(tmp_disk->system);
454 if (head == NULL) {
455 warnx("Error: memory allocation failed in "
456 "cd9660_setup_boot");
457 return -1;
459 LIST_INSERT_AFTER(default_entry, head, ll_struct);
460 *headp = head;
461 } else
462 head = *headp;
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);
468 if (temp == NULL) {
469 warn("%s: cd9660_boot_setup_section_entry", __func__);
470 return -1;
473 while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
474 next->entry_type == ET_ENTRY_SE)
475 head = next;
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;
499 return 1;
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,
510 SEEK_SET);
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",
519 e->entry_type);
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);
539 return 0;