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