xref: /netbsd-src/usr.sbin/makefs/cd9660/cd9660_eltorito.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
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