cd9660_eltorito.c revision 1.7 1 /* $NetBSD: cd9660_eltorito.c,v 1.7 2005/10/30 06:45:46 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.7 2005/10/30 06:45:46 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 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 if (TAILQ_FIRST(&diskStructure.boot_images) == NULL) {
158 TAILQ_INSERT_HEAD(&diskStructure.boot_images, new_image,
159 image_list);
160 } else {
161 tmp_image = TAILQ_FIRST(&diskStructure.boot_images);
162 while (TAILQ_NEXT(tmp_image, image_list) != NULL
163 && TAILQ_NEXT(tmp_image, image_list)->system ==
164 new_image->system) {
165 tmp_image = TAILQ_NEXT(tmp_image, image_list);
166 }
167 TAILQ_INSERT_AFTER(&diskStructure.boot_images, tmp_image,
168 new_image, image_list);
169 }
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
177 int
178 cd9660_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 image = TAILQ_FIRST(&diskStructure.boot_images);
187 if (image == NULL)
188 errx(EXIT_FAILURE, "Attempted to add boot option, "
189 "but no boot images have been specified");
190
191 while (TAILQ_NEXT(image, image_list) != NULL)
192 image = TAILQ_NEXT(image, image_list);
193
194 /* TODO : These options are NOT copied yet */
195 if (strcmp(option_string, "no-emul-boot") == 0) {
196 image->targetMode = ET_MEDIA_NOEM;
197 } else if (strcmp(option_string, "no-boot") == 0) {
198 image->bootable = ET_NOT_BOOTABLE;
199 } else if (strcmp(option_string, "hard-disk-boot") == 0) {
200 image->targetMode = ET_MEDIA_HDD;
201 } else if (strcmp(option_string, "boot-load-segment") == 0) {
202 image->loadSegment = strtoul(value, &eptr, 16);
203 if (eptr == value || *eptr != '\0' || errno != ERANGE) {
204 warn("%s: strtoul", __func__);
205 return 0;
206 }
207 } else {
208 return 0;
209 }
210 return 1;
211 }
212
213 static struct boot_catalog_entry *
214 cd9660_init_boot_catalog_entry(void)
215 {
216 struct boot_catalog_entry *temp;
217
218 if ((temp = malloc(sizeof(*temp))) == NULL)
219 return NULL;
220
221 return memset(temp, 0, sizeof(*temp));
222 }
223
224 static struct boot_catalog_entry *
225 cd9660_boot_setup_validation_entry(char sys)
226 {
227 struct boot_catalog_entry *entry;
228 boot_catalog_validation_entry *ve;
229 int16_t checksum;
230 unsigned char *csptr;
231 int i;
232 entry = cd9660_init_boot_catalog_entry();
233
234 if (entry == NULL) {
235 warnx("Error: memory allocation failed in "
236 "cd9660_boot_setup_validation_entry");
237 return 0;
238 }
239 ve = &entry->entry_data.VE;
240
241 ve->header_id[0] = 1;
242 ve->platform_id[0] = sys;
243 ve->key[0] = 0x55;
244 ve->key[1] = 0xAA;
245
246 /* Calculate checksum */
247 checksum = 0;
248 cd9660_721(0, ve->checksum);
249 csptr = (unsigned char*)ve;
250 for (i = 0; i < sizeof(*ve); i += 2) {
251 checksum += (int16_t)csptr[i];
252 checksum += 256 * (int16_t)csptr[i + 1];
253 }
254 checksum = -checksum;
255 cd9660_721(checksum, ve->checksum);
256
257 ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
258 "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
259 ve->key[0], ve->key[1], checksum));
260 return entry;
261 }
262
263 static struct boot_catalog_entry *
264 cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
265 {
266 struct boot_catalog_entry *default_entry;
267 boot_catalog_initial_entry *ie;
268
269 default_entry = cd9660_init_boot_catalog_entry();
270 if (default_entry == NULL)
271 return NULL;
272
273 ie = &default_entry->entry_data.IE;
274
275 ie->boot_indicator[0] = disk->bootable;
276 ie->media_type[0] = disk->targetMode;
277 cd9660_721(disk->loadSegment, ie->load_segment);
278 ie->system_type[0] = disk->system;
279 cd9660_721(disk->num_sectors, ie->sector_count);
280 cd9660_731(disk->sector, ie->load_rba);
281
282 ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
283 "load segment %04x, system type %d, sector count %d, "
284 "load rba %d\n", __func__, ie->boot_indicator[0],
285 ie->media_type[0], disk->loadSegment, ie->system_type[0],
286 disk->num_sectors, disk->sector));
287 return default_entry;
288 }
289
290 static struct boot_catalog_entry *
291 cd9660_boot_setup_section_head(char platform)
292 {
293 struct boot_catalog_entry *entry;
294 boot_catalog_section_header *sh;
295
296 entry = cd9660_init_boot_catalog_entry();
297 if (entry == NULL)
298 return NULL;
299
300 sh = &entry->entry_data.SH;
301 /* More by default. The last one will manually be set to 0x91 */
302 sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
303 sh->platform_id[0] = platform;
304 sh->num_section_entries[0] = 0;
305 return entry;
306 }
307
308 static struct boot_catalog_entry *
309 cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
310 {
311 struct boot_catalog_entry *entry;
312 boot_catalog_section_entry *se;
313 if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
314 return NULL;
315
316 se = &entry->entry_data.SE;
317
318 se->boot_indicator[0] = ET_BOOTABLE;
319 se->media_type[0] = disk->targetMode;
320 cd9660_721(disk->loadSegment, se->load_segment);
321 cd9660_721(disk->num_sectors, se->sector_count);
322 cd9660_731(disk->sector, se->load_rba);
323 return entry;
324 }
325
326 #if 0
327 static u_char
328 cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
329 {
330 /*
331 For hard drive booting, we need to examine the MBR to figure
332 out what the partition type is
333 */
334 return 0;
335 }
336 #endif
337
338 /*
339 * Set up the BVD, Boot catalog, and the boot entries, but do no writing
340 */
341 int
342 cd9660_setup_boot(int first_sector)
343 {
344 int need_head;
345 int sector;
346 int used_sectors;
347 int num_entries = 0;
348 int catalog_sectors;
349 struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
350 *last_x86, *last_ppc, *last_mac, *last_head,
351 *valid_entry, *default_entry, *temp, *head_temp, *next;
352 struct cd9660_boot_image *tmp_disk;
353
354 head_temp = NULL;
355 need_head = 0;
356 x86_head = mac_head = ppc_head =
357 last_x86 = last_ppc = last_mac = last_head = NULL;
358
359 /* If there are no boot disks, don't bother building boot information */
360 if ((tmp_disk = TAILQ_FIRST(&diskStructure.boot_images)) == NULL)
361 return 0;
362
363 /* Point to catalog: For now assume it consumes one sector */
364 ELTORITO_DPRINTF(("Boot catalog will go in sector %i\n", first_sector));
365 diskStructure.boot_catalog_sector = first_sector;
366 cd9660_bothendian_dword(first_sector,
367 diskStructure.boot_descriptor->boot_catalog_pointer);
368
369 /* Step 1: Generate boot catalog */
370 /* Step 1a: Validation entry */
371 valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
372 if (valid_entry == NULL)
373 return -1;
374
375 /*
376 * Count how many boot images there are,
377 * and how many sectors they consume.
378 */
379 num_entries = 1;
380 used_sectors = 0;
381
382 TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
383 used_sectors += tmp_disk->num_sectors;
384
385 /* One default entry per image */
386 num_entries++;
387 }
388 catalog_sectors = howmany(num_entries * 0x20, diskStructure.sectorSize);
389 used_sectors += catalog_sectors;
390
391 printf("%s: there will be %i entries consuming %i sectors. "
392 "Catalog is %i sectors\n", __func__, num_entries, used_sectors,
393 catalog_sectors);
394
395 /* Populate sector numbers */
396 sector = first_sector + catalog_sectors;
397 TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
398 tmp_disk->sector = sector;
399 sector += tmp_disk->num_sectors;
400 }
401
402 LIST_INSERT_HEAD(&diskStructure.boot_entries, valid_entry, ll_struct);
403
404 /* Step 1b: Initial/default entry */
405 /* TODO : PARAM */
406 tmp_disk = TAILQ_FIRST(&diskStructure.boot_images);
407 default_entry = cd9660_boot_setup_default_entry(tmp_disk);
408 if (default_entry == NULL) {
409 warnx("Error: memory allocation failed in cd9660_setup_boot");
410 return -1;
411 }
412
413 LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
414
415 /* Todo: multiple default entries? */
416
417 tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
418
419 temp = default_entry;
420
421 /* If multiple boot images are given : */
422 while (tmp_disk != NULL) {
423 /* Step 2: Section header */
424 switch (tmp_disk->system) {
425 case ET_SYS_X86:
426 need_head = (x86_head == NULL);
427 if (!need_head)
428 head_temp = x86_head;
429 break;
430 case ET_SYS_PPC:
431 need_head = (ppc_head == NULL);
432 if (!need_head)
433 head_temp = ppc_head;
434 break;
435 case ET_SYS_MAC:
436 need_head = (mac_head == NULL);
437 if (!need_head)
438 head_temp = mac_head;
439 break;
440 }
441
442 if (need_head) {
443 head_temp =
444 cd9660_boot_setup_section_head(tmp_disk->system);
445 if (head_temp == NULL) {
446 warnx("Error: memory allocation failed in "
447 "cd9660_setup_boot");
448 return -1;
449 }
450 LIST_INSERT_AFTER(default_entry,head_temp, ll_struct);
451 }
452 head_temp->entry_data.SH.num_section_entries[0]++;
453
454 /* Step 2a: Section entry and extensions */
455 temp = cd9660_boot_setup_section_entry(tmp_disk);
456 if (temp == NULL) {
457 warnx("Error: memory allocation failed in "
458 "cd9660_setup_boot");
459 return -1;
460 }
461
462 while ((next = LIST_NEXT(head_temp, ll_struct)) != NULL &&
463 next->entry_type == ET_ENTRY_SE)
464 head_temp = LIST_NEXT(head_temp, ll_struct);
465
466 LIST_INSERT_AFTER(head_temp,temp, ll_struct);
467 tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
468 }
469
470 /* TODO: Remaining boot disks when implemented */
471
472 return first_sector + used_sectors;
473 }
474
475 int
476 cd9660_setup_boot_volume_descritpor(volume_descriptor *bvd)
477 {
478 boot_volume_descriptor *bvdData =
479 (boot_volume_descriptor*)bvd->volumeDescriptorData;
480
481 bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
482 memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
483 bvdData->version[0] = 1;
484 memcpy(bvdData->boot_system_identifier, ET_ID, 23);
485 memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
486 diskStructure.boot_descriptor =
487 (boot_volume_descriptor*) bvd->volumeDescriptorData;
488 return 1;
489 }
490
491 int
492 cd9660_write_boot(FILE *fd)
493 {
494 struct boot_catalog_entry *e;
495 struct cd9660_boot_image *t;
496
497 /* write boot catalog */
498 fseek(fd, diskStructure.boot_catalog_sector * diskStructure.sectorSize,
499 SEEK_SET);
500
501 printf("Writing boot catalog to sector %i\n",
502 diskStructure.boot_catalog_sector);
503 LIST_FOREACH(e, &diskStructure.boot_entries, ll_struct) {
504 printf("Writing catalog entry of type %i\n", e->entry_type);
505 /*
506 * It doesnt matter which one gets written
507 * since they are the same size
508 */
509 fwrite(&(e->entry_data.VE), 1, 32, fd);
510 }
511 printf("Finished writing boot catalog\n");
512
513 /* copy boot images */
514 TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) {
515 printf("Writing boot image from %s to sectors %i\n",
516 t->filename,t->sector);
517 cd9660_copy_file(fd, t->sector, t->filename);
518 }
519
520 return 0;
521 }
522