cd9660_eltorito.c revision 1.9 1 /* $NetBSD: cd9660_eltorito.c,v 1.9 2005/10/31 23:19:54 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.9 2005/10/31 23:19:54 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 printf("Finished writing boot catalog\n");
522
523 /* copy boot images */
524 TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) {
525 if (diskStructure.verbose_level > 0) {
526 printf("Writing boot image from %s to sectors %d\n",
527 t->filename, t->sector);
528 }
529 cd9660_copy_file(fd, t->sector, t->filename);
530 }
531
532 return 0;
533 }
534