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