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