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