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