1b41ba229ea972adf7e5f5fbd122f383673e5d86
[profile/ivi/syslinux.git] / com32 / modules / chain.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *   Significant portions copyright (C) 2010 Shao Miller
6  *                                      [partition iteration, GPT, "fs"]
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
11  *   Boston MA 02111-1307, USA; either version 2 of the License, or
12  *   (at your option) any later version; incorporated herein by reference.
13  *
14  * ----------------------------------------------------------------------- */
15
16 /*
17  * chain.c
18  *
19  * Chainload a hard disk (currently rather braindead.)
20  *
21  * Usage: chain [options]
22  *        chain hd<disk#> [<partition>] [options]
23  *        chain fd<disk#> [options]
24  *        chain mbr:<id> [<partition>] [options]
25  *        chain guid:<guid> [<partition>] [options]
26  *        chain label:<label> [<partition>] [options]
27  *        chain boot [<partition>] [options]
28  *
29  * For example, "chain msdos=io.sys" will load DOS from the current Syslinux
30  * filesystem.  "chain hd0 1" will boot the first partition on the first hard
31  * disk.
32  *
33  * When none of the "hdX", "fdX", "mbr:", "guid:", "label:", "boot" or "fs"
34  * options are specified, the default behaviour is equivalent to "boot".
35  * "boot" means to use the current Syslinux drive, and you can also specify
36  * a partition.
37  *
38  * The mbr: syntax means search all the hard disks until one with a
39  * specific MBR serial number (bytes 440-443) is found.
40  *
41  * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
42  *
43  * "fs" will use the current Syslinux filesystem as the boot drive/partition.
44  * When booting from PXELINUX, you will most likely wish to specify a disk.
45  *
46  * Options:
47  *
48  * file=<loader>
49  *      loads the file <loader> **from the Syslinux filesystem**
50  *      instead of loading the boot sector.
51  *
52  * seg=<segment>
53  *      loads at and jumps to <seg>:0000 instead of 0000:7C00.
54  *
55  * isolinux=<loader>
56  *      chainload another version/build of the ISOLINUX bootloader and patch
57  *      the loader with appropriate parameters in memory.
58  *      This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
59  *      when you want more than one ISOLINUX per CD/DVD.
60  *
61  * ntldr=<loader>
62  *      equivalent to seg=0x2000 file=<loader> sethidden,
63  *      used with WinNT's loaders
64  *
65  * cmldr=<loader>
66  *      used with Recovery Console of Windows NT/2K/XP.
67  *      same as ntldr=<loader> & "cmdcons\0" written to
68  *      the system name field in the bootsector
69  *
70  * freedos=<loader>
71  *      equivalent to seg=0x60 file=<loader> sethidden,
72  *      used with FreeDOS' kernel.sys.
73  *
74  * msdos=<loader>
75  * pcdos=<loader>
76  *      equivalent to seg=0x70 file=<loader> sethidden,
77  *      used with DOS' io.sys.
78  *
79  * grub=<loader>
80  *      same as seg=0x800 file=<loader> & jumping to seg 0x820,
81  *      used with GRUB Legacy stage2 files.
82  *
83  * grubcfg=<filename>
84  *      set an alternative config filename in stage2 of Grub Legacy,
85  *      only applicable in combination with "grub=<loader>".
86  *
87  * grldr=<loader>
88  *      pass the partition number to GRUB4DOS,
89  *      used with GRUB4DOS' grldr.
90  *
91  * swap
92  *      if the disk is not fd0/hd0, install a BIOS stub which swaps
93  *      the drive numbers.
94  *
95  * hide
96  *      change type of primary partitions with IDs 01, 04, 06, 07,
97  *      0b, 0c, or 0e to 1x, except for the selected partition, which
98  *      is converted the other way.
99  *
100  * sethidden
101  *      update the "hidden sectors" (partition offset) field in a
102  *      FAT/NTFS boot sector.
103  *
104  * keeppxe
105  *      keep the PXE and UNDI stacks in memory (PXELINUX only).
106  */
107
108 #include <com32.h>
109 #include <stdlib.h>
110 #include <stdio.h>
111 #include <ctype.h>
112 #include <string.h>
113 #include <console.h>
114 #include <minmax.h>
115 #include <stdbool.h>
116 #include <dprintf.h>
117 #include <syslinux/loadfile.h>
118 #include <syslinux/bootrm.h>
119 #include <syslinux/config.h>
120 #include <syslinux/disk.h>
121 #include <syslinux/video.h>
122
123 static struct options {
124     const char *loadfile;
125     uint16_t keeppxe;
126     uint16_t seg;
127     bool isolinux;
128     bool cmldr;
129     bool grub;
130     bool grldr;
131     const char *grubcfg;
132     bool swap;
133     bool hide;
134     bool sethidden;
135 } opt;
136
137 struct data_area {
138     void *data;
139     addr_t base;
140     addr_t size;
141 };
142
143 static inline void error(const char *msg)
144 {
145     fputs(msg, stderr);
146 }
147
148 static struct disk_info diskinfo;
149
150 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
151 static int find_disk(uint32_t mbr_sig)
152 {
153     int drive;
154     bool is_me;
155     struct disk_dos_mbr *mbr;
156
157     for (drive = 0x80; drive <= 0xff; drive++) {
158         if (disk_get_params(drive, &diskinfo))
159             continue;           /* Drive doesn't exist */
160         if (!(mbr = disk_read_sectors(&diskinfo, 0, 1)))
161             continue;           /* Cannot read sector */
162         is_me = (mbr->disk_sig == mbr_sig);
163         free(mbr);
164         if (is_me)
165             return drive;
166     }
167     return -1;
168 }
169
170 /* Forward declaration */
171 struct disk_part_iter;
172
173 /* Partition-/scheme-specific routine returning the next partition */
174 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
175                                                        part);
176
177 /* Contains details for a partition under examination */
178 struct disk_part_iter {
179     /* The block holding the table we are part of */
180     char *block;
181     /* The LBA for the beginning of data */
182     uint64_t lba_data;
183     /* The partition number, as determined by our heuristic */
184     int index;
185     /* The DOS partition record to pass, if applicable */
186     const struct disk_dos_part_entry *record;
187     /* Function returning the next available partition */
188     disk_part_iter_func next;
189     /* Partition-/scheme-specific details */
190     union {
191         /* MBR specifics */
192         int mbr_index;
193         /* EBR specifics */
194         struct {
195             /* The first extended partition's start LBA */
196             uint64_t lba_extended;
197             /* Any applicable parent, or NULL */
198             struct disk_part_iter *parent;
199             /* The parent extended partition index */
200             int parent_index;
201         } ebr;
202         /* GPT specifics */
203         struct {
204             /* Real (not effective) index in the partition table */
205             int index;
206             /* Current partition GUID */
207             const struct guid *part_guid;
208             /* Current partition label */
209             const char *part_label;
210             /* Count of entries in GPT */
211             int parts;
212             /* Partition record size */
213             uint32_t size;
214         } gpt;
215     } private;
216 };
217
218 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
219 {
220     const struct disk_dos_part_entry *ebr_table;
221     const struct disk_dos_part_entry *parent_table =
222         ((const struct disk_dos_mbr *)part->private.ebr.parent->block)->table;
223     static const struct disk_dos_part_entry phony = {.start_lba = 0 };
224     uint64_t ebr_lba;
225
226     /* Don't look for a "next EBR" the first time around */
227     if (part->private.ebr.parent_index >= 0)
228         /* Look at the linked list */
229         ebr_table = ((const struct disk_dos_mbr *)part->block)->table + 1;
230     /* Do we need to look for an extended partition? */
231     if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
232         /* Start looking for an extended partition in the MBR */
233         while (++part->private.ebr.parent_index < 4) {
234             uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
235
236             if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
237                 break;
238         }
239         if (part->private.ebr.parent_index == 4)
240             /* No extended partitions found */
241             goto out_finished;
242         part->private.ebr.lba_extended =
243             parent_table[part->private.ebr.parent_index].start_lba;
244         ebr_table = &phony;
245     }
246     /* Load next EBR */
247     ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
248     free(part->block);
249     part->block = disk_read_sectors(&diskinfo, ebr_lba, 1);
250     if (!part->block) {
251         error("Could not load EBR!\n");
252         goto err_ebr;
253     }
254     ebr_table = ((const struct disk_dos_mbr *)part->block)->table;
255     dprintf("next_ebr_part:\n");
256     disk_dos_part_dump(ebr_table);
257
258     /*
259      * Sanity check entry: must not extend outside the
260      * extended partition.  This is necessary since some OSes
261      * put crap in some entries.
262      */
263     {
264         const struct disk_dos_mbr *mbr =
265             (const struct disk_dos_mbr *)part->private.ebr.parent->block;
266         const struct disk_dos_part_entry *extended =
267             mbr->table + part->private.ebr.parent_index;
268
269         if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
270             dprintf("Insane logical partition!\n");
271             goto err_insane;
272         }
273     }
274     /* Success */
275     part->lba_data = ebr_table[0].start_lba + ebr_lba;
276     dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
277     part->index++;
278     part->record = ebr_table;
279     return part;
280
281 err_insane:
282
283     free(part->block);
284     part->block = NULL;
285 err_ebr:
286
287 out_finished:
288     free(part->private.ebr.parent->block);
289     free(part->private.ebr.parent);
290     free(part->block);
291     free(part);
292     return NULL;
293 }
294
295 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
296 {
297     struct disk_part_iter *ebr_part;
298     /* Look at the partition table */
299     struct disk_dos_part_entry *table =
300         ((struct disk_dos_mbr *)part->block)->table;
301
302     /* Look for data partitions */
303     while (++part->private.mbr_index < 4) {
304         uint8_t type = table[part->private.mbr_index].ostype;
305
306         if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
307             /* Skip empty or extended partitions */
308             continue;
309         if (!table[part->private.mbr_index].length)
310             /* Empty */
311             continue;
312         break;
313     }
314     /* If we're currently the last partition, it's time for EBR processing */
315     if (part->private.mbr_index == 4) {
316         /* Allocate another iterator for extended partitions */
317         ebr_part = malloc(sizeof(*ebr_part));
318         if (!ebr_part) {
319             error("Could not allocate extended partition iterator!\n");
320             goto err_alloc;
321         }
322         /* Setup EBR iterator parameters */
323         ebr_part->block = NULL;
324         ebr_part->index = 4;
325         ebr_part->record = NULL;
326         ebr_part->next = next_ebr_part;
327         ebr_part->private.ebr.parent = part;
328         /* Trigger an initial EBR load */
329         ebr_part->private.ebr.parent_index = -1;
330         /* The EBR iterator is responsible for freeing us */
331         return next_ebr_part(ebr_part);
332     }
333     dprintf("next_mbr_part:\n");
334     disk_dos_part_dump(table + part->private.mbr_index);
335
336     /* Update parameters to reflect this new partition.  Re-use iterator */
337     part->lba_data = table[part->private.mbr_index].start_lba;
338     dprintf("Partition %d primary lba %u\n", part->index, part->lba_data);
339     part->index++;
340     part->record = table + part->private.mbr_index;
341     return part;
342
343     free(ebr_part);
344 err_alloc:
345
346     free(part->block);
347     free(part);
348     return NULL;
349 }
350
351 #if DEBUG
352 /*
353  * Fill a buffer with a textual GUID representation.
354  * The buffer must be >= char[37] and will be populated
355  * with an ASCII NUL C string terminator.
356  * Example: 11111111-2222-3333-4444-444444444444
357  * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
358  */
359 static void guid_to_str(char *buf, const struct guid *id)
360 {
361     unsigned int i = 0;
362     const char *walker = (const char *)id;
363
364     while (i < sizeof(guid_le_walk_map)) {
365         walker += guid_le_walk_map[i];
366         if (!guid_le_walk_map[i])
367             *buf = '-';
368         else {
369             *buf = ((*walker & 0xF0) >> 4) + '0';
370             if (*buf > '9')
371                 *buf += 'A' - '9' - 1;
372             buf++;
373             *buf = (*walker & 0x0F) + '0';
374             if (*buf > '9')
375                 *buf += 'A' - '9' - 1;
376         }
377         buf++;
378         i++;
379     }
380     *buf = 0;
381 }
382 #endif
383
384 /*
385  * Create a GUID structure from a textual GUID representation.
386  * The input buffer must be >= 32 hexadecimal chars and be
387  * terminated with an ASCII NUL.  Returns non-zero on failure.
388  * Example: 11111111-2222-3333-4444-444444444444
389  * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
390  */
391 static int str_to_guid(const char *buf, struct guid *id)
392 {
393     char guid_seq[sizeof(struct guid) * 2];
394     unsigned int i = 0;
395     char *walker = (char *)id;
396
397     while (*buf && i < sizeof(guid_seq)) {
398         switch (*buf) {
399             /* Skip these three characters */
400         case '{':
401         case '}':
402         case '-':
403             break;
404         default:
405             /* Copy something useful to the temp. sequence */
406             if ((*buf >= '0') && (*buf <= '9'))
407                 guid_seq[i] = *buf - '0';
408             else if ((*buf >= 'A') && (*buf <= 'F'))
409                 guid_seq[i] = *buf - 'A' + 10;
410             else if ((*buf >= 'a') && (*buf <= 'f'))
411                 guid_seq[i] = *buf - 'a' + 10;
412             else {
413                 /* Or not */
414                 error("Illegal character in GUID!\n");
415                 return -1;
416             }
417             i++;
418         }
419         buf++;
420     }
421     /* Check for insufficient valid characters */
422     if (i < sizeof(guid_seq)) {
423         error("Too few GUID characters!\n");
424         return -1;
425     }
426     buf = guid_seq;
427     i = 0;
428     while (i < sizeof(guid_le_walk_map)) {
429         if (!guid_le_walk_map[i])
430             i++;
431         walker += guid_le_walk_map[i];
432         *walker = *buf << 4;
433         buf++;
434         *walker |= *buf;
435         buf++;
436         i++;
437     }
438     return 0;
439 }
440
441 /* A GPT partition */
442 struct gpt_part {
443     struct guid type;
444     struct guid uid;
445     uint64_t lba_first;
446     uint64_t lba_last;
447     uint64_t attribs;
448     char name[72];
449 } __attribute__ ((packed));
450
451 static void gpt_part_dump(const struct gpt_part *gpt_part)
452 {
453 #ifdef DEBUG
454     unsigned int i;
455     char guid_text[37];
456
457     dprintf("----------------------------------\n"
458             "GPT part. LBA first __ : 0x%.16llx\n"
459             "GPT part. LBA last ___ : 0x%.16llx\n"
460             "GPT part. attribs ____ : 0x%.16llx\n"
461             "GPT part. name _______ : '",
462             gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
463     for (i = 0; i < sizeof(gpt_part->name); i++) {
464         if (gpt_part->name[i])
465             dprintf("%c", gpt_part->name[i]);
466     }
467     dprintf("'");
468     guid_to_str(guid_text, &gpt_part->type);
469     dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
470     guid_to_str(guid_text, &gpt_part->uid);
471     dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
472 #endif
473     (void)gpt_part;
474 }
475
476 /* A GPT header */
477 struct gpt {
478     char sig[8];
479     union {
480         struct {
481             uint16_t minor;
482             uint16_t major;
483         } fields __attribute__ ((packed));
484         uint32_t uint32;
485         char raw[4];
486     } rev __attribute__ ((packed));
487     uint32_t hdr_size;
488     uint32_t chksum;
489     char reserved1[4];
490     uint64_t lba_cur;
491     uint64_t lba_alt;
492     uint64_t lba_first_usable;
493     uint64_t lba_last_usable;
494     struct guid disk_guid;
495     uint64_t lba_table;
496     uint32_t part_count;
497     uint32_t part_size;
498     uint32_t table_chksum;
499     char reserved2[1];
500 } __attribute__ ((packed));
501 static const char gpt_sig_magic[] = "EFI PART";
502
503 #if DEBUG
504 static void gpt_dump(const struct gpt *gpt)
505 {
506     char guid_text[37];
507
508     printf("GPT sig ______________ : '%8.8s'\n"
509            "GPT major revision ___ : 0x%.4x\n"
510            "GPT minor revision ___ : 0x%.4x\n"
511            "GPT header size ______ : 0x%.8x\n"
512            "GPT header checksum __ : 0x%.8x\n"
513            "GPT reserved _________ : '%4.4s'\n"
514            "GPT LBA current ______ : 0x%.16llx\n"
515            "GPT LBA alternative __ : 0x%.16llx\n"
516            "GPT LBA first usable _ : 0x%.16llx\n"
517            "GPT LBA last usable __ : 0x%.16llx\n"
518            "GPT LBA part. table __ : 0x%.16llx\n"
519            "GPT partition count __ : 0x%.8x\n"
520            "GPT partition size ___ : 0x%.8x\n"
521            "GPT part. table chksum : 0x%.8x\n",
522            gpt->sig,
523            gpt->rev.fields.major,
524            gpt->rev.fields.minor,
525            gpt->hdr_size,
526            gpt->chksum,
527            gpt->reserved1,
528            gpt->lba_cur,
529            gpt->lba_alt,
530            gpt->lba_first_usable,
531            gpt->lba_last_usable,
532            gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
533     guid_to_str(guid_text, &gpt->disk_guid);
534     printf("GPT disk GUID ________ : {%s}\n", guid_text);
535 }
536 #endif
537
538 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
539 {
540     const struct gpt_part *gpt_part = NULL;
541
542     while (++part->private.gpt.index < part->private.gpt.parts) {
543         gpt_part =
544             (const struct gpt_part *)(part->block +
545                                       (part->private.gpt.index *
546                                        part->private.gpt.size));
547         if (!gpt_part->lba_first)
548             continue;
549         break;
550     }
551     /* Were we the last partition? */
552     if (part->private.gpt.index == part->private.gpt.parts) {
553         goto err_last;
554     }
555     part->lba_data = gpt_part->lba_first;
556     part->private.gpt.part_guid = &gpt_part->uid;
557     part->private.gpt.part_label = gpt_part->name;
558     /* Update our index */
559     part->index++;
560     gpt_part_dump(gpt_part);
561
562     /* In a GPT scheme, we re-use the iterator */
563     return part;
564
565 err_last:
566     free(part->block);
567     free(part);
568
569     return NULL;
570 }
571
572 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
573 {
574     const struct gpt *gpt_candidate;
575
576     /*
577      * Ignore any passed partition iterator.  The caller should
578      * have passed NULL.  Allocate a new partition iterator
579      */
580     part = malloc(sizeof(*part));
581     if (!part) {
582         error("Count not allocate partition iterator!\n");
583         goto err_alloc_iter;
584     }
585     /* Read MBR */
586     part->block = disk_read_sectors(&diskinfo, 0, 2);
587     if (!part->block) {
588         error("Could not read two sectors!\n");
589         goto err_read_mbr;
590     }
591     /* Check for an MBR */
592     if (((struct disk_dos_mbr *)part->block)->sig != disk_mbr_sig_magic) {
593         error("No MBR magic!\n");
594         goto err_mbr;
595     }
596     /* Establish a pseudo-partition for the MBR (index 0) */
597     part->index = 0;
598     part->record = NULL;
599     part->private.mbr_index = -1;
600     part->next = next_mbr_part;
601     /* Check for a GPT disk */
602     gpt_candidate = (const struct gpt *)(part->block + SECTOR);
603     if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
604         /* LBA for partition table */
605         uint64_t lba_table;
606
607         /* It looks like one */
608         /* TODO: Check checksum.  Possibly try alternative GPT */
609 #if DEBUG
610         puts("Looks like a GPT disk.");
611         gpt_dump(gpt_candidate);
612 #endif
613         /* TODO: Check table checksum (maybe) */
614         /* Note relevant GPT details */
615         part->next = next_gpt_part;
616         part->private.gpt.index = -1;
617         part->private.gpt.parts = gpt_candidate->part_count;
618         part->private.gpt.size = gpt_candidate->part_size;
619         lba_table = gpt_candidate->lba_table;
620         gpt_candidate = NULL;
621         /* Load the partition table */
622         free(part->block);
623         part->block =
624             disk_read_sectors(&diskinfo, lba_table,
625                               ((part->private.gpt.size *
626                                 part->private.gpt.parts) + SECTOR -
627                                1) / SECTOR);
628         if (!part->block) {
629             error("Could not read GPT partition list!\n");
630             goto err_gpt_table;
631         }
632     }
633     /* Return the pseudo-partition's next partition, which is real */
634     return part->next(part);
635
636 err_gpt_table:
637
638 err_mbr:
639
640     free(part->block);
641     part->block = NULL;
642 err_read_mbr:
643
644     free(part);
645 err_alloc_iter:
646
647     return NULL;
648 }
649
650 /*
651  * Search for a specific drive/partition, based on the GPT GUID.
652  * We return the disk drive number if found, as well as populating the
653  * boot_part pointer with the matching partition, if applicable.
654  * If no matching partition is found or the GUID is a disk GUID,
655  * boot_part will be populated with NULL.  If not matching disk is
656  * found, we return -1.
657  */
658 static int find_by_guid(const struct guid *gpt_guid,
659                         struct disk_part_iter **boot_part)
660 {
661     int drive;
662     bool is_me;
663     struct gpt *header;
664
665     for (drive = 0x80; drive <= 0xff; drive++) {
666         if (disk_get_params(drive, &diskinfo))
667             continue;           /* Drive doesn't exist */
668         if (!(header = disk_read_sectors(&diskinfo, 1, 1)))
669             continue;           /* Cannot read sector */
670         if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
671             /* Not a GPT disk */
672             free(header);
673             continue;
674         }
675 #if DEBUG
676         gpt_dump(header);
677 #endif
678         is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
679         free(header);
680         if (!is_me) {
681             /* Check for a matching partition */
682             boot_part[0] = get_first_partition(NULL);
683             while (boot_part[0]) {
684                 is_me =
685                     !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
686                             sizeof(*gpt_guid));
687                 if (is_me)
688                     break;
689                 boot_part[0] = boot_part[0]->next(boot_part[0]);
690             }
691         } else
692             boot_part[0] = NULL;
693         if (is_me)
694             return drive;
695     }
696     return -1;
697 }
698
699 /*
700  * Search for a specific partition, based on the GPT label.
701  * We return the disk drive number if found, as well as populating the
702  * boot_part pointer with the matching partition, if applicable.
703  * If no matching partition is found, boot_part will be populated with
704  * NULL and we return -1.
705  */
706 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
707 {
708     int drive;
709     bool is_me;
710
711     for (drive = 0x80; drive <= 0xff; drive++) {
712         if (disk_get_params(drive, &diskinfo))
713             continue;           /* Drive doesn't exist */
714         /* Check for a GPT disk */
715         boot_part[0] = get_first_partition(NULL);
716         if (!(boot_part[0]->next == next_gpt_part)) {
717             /* Not a GPT disk */
718             while (boot_part[0]) {
719                 /* Run through until the end */
720                 boot_part[0] = boot_part[0]->next(boot_part[0]);
721             }
722             continue;
723         }
724         /* Check for a matching partition */
725         while (boot_part[0]) {
726             char gpt_label[sizeof(((struct gpt_part *) NULL)->name)];
727             const char *gpt_label_scanner =
728                 boot_part[0]->private.gpt.part_label;
729             int j = 0;
730
731             /* Re-write the GPT partition label as ASCII */
732             while (gpt_label_scanner <
733                    boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
734                 if ((gpt_label[j] = *gpt_label_scanner))
735                     j++;
736                 gpt_label_scanner++;
737             }
738             if ((is_me = !strcmp(label, gpt_label)))
739                 break;
740             boot_part[0] = boot_part[0]->next(boot_part[0]);
741         }
742         if (is_me)
743             return drive;
744     }
745
746     return -1;
747 }
748
749 static void do_boot(struct data_area *data, int ndata,
750                     struct syslinux_rm_regs *regs)
751 {
752     uint16_t *const bios_fbm = (uint16_t *) 0x413;
753     addr_t dosmem = *bios_fbm << 10;    /* Technically a low bound */
754     struct syslinux_memmap *mmap;
755     struct syslinux_movelist *mlist = NULL;
756     addr_t endimage;
757     uint8_t driveno = regs->edx.b[0];
758     uint8_t swapdrive = driveno & 0x80;
759     int i;
760
761     mmap = syslinux_memory_map();
762
763     if (!mmap) {
764         error("Cannot read system memory map\n");
765         return;
766     }
767
768     endimage = 0;
769     for (i = 0; i < ndata; i++) {
770         if (data[i].base + data[i].size > endimage)
771             endimage = data[i].base + data[i].size;
772     }
773     if (endimage > dosmem)
774         goto too_big;
775
776     for (i = 0; i < ndata; i++) {
777         if (syslinux_add_movelist(&mlist, data[i].base,
778                                   (addr_t) data[i].data, data[i].size))
779             goto enomem;
780     }
781
782     if (opt.swap && driveno != swapdrive) {
783         static const uint8_t swapstub_master[] = {
784             /* The actual swap code */
785             0x53,               /* 00: push bx */
786             0x0f, 0xb6, 0xda,   /* 01: movzx bx,dl */
787             0x2e, 0x8a, 0x57, 0x60,     /* 04: mov dl,[cs:bx+0x60] */
788             0x5b,               /* 08: pop bx */
789             0xea, 0, 0, 0, 0,   /* 09: jmp far 0:0 */
790             0x90, 0x90,         /* 0E: nop; nop */
791             /* Code to install this in the right location */
792             /* Entry with DS = CS; ES = SI = 0; CX = 256 */
793             0x26, 0x66, 0x8b, 0x7c, 0x4c,       /* 10: mov edi,[es:si+4*0x13] */
794             0x66, 0x89, 0x3e, 0x0a, 0x00,       /* 15: mov [0x0A],edi */
795             0x26, 0x8b, 0x3e, 0x13, 0x04,       /* 1A: mov di,[es:0x413] */
796             0x4f,               /* 1F: dec di */
797             0x26, 0x89, 0x3e, 0x13, 0x04,       /* 20: mov [es:0x413],di */
798             0x66, 0xc1, 0xe7, 0x16,     /* 25: shl edi,16+6 */
799             0x26, 0x66, 0x89, 0x7c, 0x4c,       /* 29: mov [es:si+4*0x13],edi */
800             0x66, 0xc1, 0xef, 0x10,     /* 2E: shr edi,16 */
801             0x8e, 0xc7,         /* 32: mov es,di */
802             0x31, 0xff,         /* 34: xor di,di */
803             0xf3, 0x66, 0xa5,   /* 36: rep movsd */
804             0xbe, 0, 0,         /* 39: mov si,0 */
805             0xbf, 0, 0,         /* 3C: mov di,0 */
806             0x8e, 0xde,         /* 3F: mov ds,si */
807             0x8e, 0xc7,         /* 41: mov es,di */
808             0x66, 0xb9, 0, 0, 0, 0,     /* 43: mov ecx,0 */
809             0x66, 0xbe, 0, 0, 0, 0,     /* 49: mov esi,0 */
810             0x66, 0xbf, 0, 0, 0, 0,     /* 4F: mov edi,0 */
811             0xea, 0, 0, 0, 0,   /* 55: jmp 0:0 */
812             /* pad out to segment boundary */
813             0x90, 0x90,         /* 5A: ... */
814             0x90, 0x90, 0x90, 0x90,     /* 5C: ... */
815         };
816         static uint8_t swapstub[1024];
817         uint8_t *p;
818
819         /* Note: we can't rely on either INT 13h nor the dosmem
820            vector to be correct at this stage, so we have to use an
821            installer stub to put things in the right place.
822            Round the installer location to a 1K boundary so the only
823            possible overlap is the identity mapping. */
824         endimage = (endimage + 1023) & ~1023;
825
826         /* Create swap stub */
827         memcpy(swapstub, swapstub_master, sizeof swapstub_master);
828         *(uint16_t *) & swapstub[0x3a] = regs->ds;
829         *(uint16_t *) & swapstub[0x3d] = regs->es;
830         *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
831         *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
832         *(uint32_t *) & swapstub[0x51] = regs->edi.l;
833         *(uint16_t *) & swapstub[0x56] = regs->ip;
834         *(uint16_t *) & swapstub[0x58] = regs->cs;
835         p = &swapstub[sizeof swapstub_master];
836
837         /* Mapping table; start out with identity mapping everything */
838         for (i = 0; i < 256; i++)
839             p[i] = i;
840
841         /* And the actual swap */
842         p[driveno] = swapdrive;
843         p[swapdrive] = driveno;
844
845         /* Adjust registers */
846         regs->ds = regs->cs = endimage >> 4;
847         regs->es = regs->esi.l = 0;
848         regs->ecx.l = sizeof swapstub >> 2;
849         regs->ip = 0x10;        /* Installer offset */
850         regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
851
852         if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
853                                   sizeof swapstub))
854             goto enomem;
855
856         endimage += sizeof swapstub;
857     }
858
859     /* Tell the shuffler not to muck with this area... */
860     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
861
862     /* Force text mode */
863     syslinux_force_text_mode();
864
865     fputs("Booting...\n", stdout);
866     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
867     error("Chainboot failed!\n");
868     return;
869
870 too_big:
871     error("Loader file too large\n");
872     return;
873
874 enomem:
875     error("Out of memory\n");
876     return;
877 }
878
879 static int hide_unhide(struct disk_dos_mbr *mbr, int part)
880 {
881     int i;
882     struct disk_dos_part_entry *pt;
883     const uint16_t mask =
884         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
885                                                                                <<
886                                                                                0x0c)
887         | (1 << 0x0e);
888     uint8_t t;
889     bool write_back = false;
890
891     for (i = 1; i <= 4; i++) {
892         pt = mbr->table + i - 1;
893         t = pt->ostype;
894         if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
895             /* It's a hideable partition type */
896             if (i == part)
897                 t &= ~0x10;     /* unhide */
898             else
899                 t |= 0x10;      /* hide */
900         }
901         if (t != pt->ostype) {
902             write_back = true;
903             pt->ostype = t;
904         }
905     }
906
907     if (write_back)
908         return disk_write_verify_sector(&diskinfo, 0, mbr);
909
910     return 0;                   /* ok */
911 }
912
913 static uint32_t get_file_lba(const char *filename)
914 {
915     com32sys_t inregs;
916     uint32_t lba;
917
918     /* Start with clean registers */
919     memset(&inregs, 0, sizeof(com32sys_t));
920
921     /* Put the filename in the bounce buffer */
922     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
923
924     /* Call comapi_open() which returns a structure pointer in SI
925      * to a structure whose first member happens to be the LBA.
926      */
927     inregs.eax.w[0] = 0x0006;
928     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
929     inregs.es = SEG(__com32.cs_bounce);
930     __com32.cs_intcall(0x22, &inregs, &inregs);
931
932     if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
933         return 0;               /* Filename not found */
934     }
935
936     /* Since the first member is the LBA, we simply cast */
937     lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
938
939     /* Clean the registers for the next call */
940     memset(&inregs, 0, sizeof(com32sys_t));
941
942     /* Put the filename in the bounce buffer */
943     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
944
945     /* Call comapi_close() to free the structure */
946     inregs.eax.w[0] = 0x0008;
947     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
948     inregs.es = SEG(__com32.cs_bounce);
949     __com32.cs_intcall(0x22, &inregs, &inregs);
950
951     return lba;
952 }
953
954 static void usage(void)
955 {
956     static const char usage[] = "\
957 Usage:   chain.c32 [options]\n\
958          chain.c32 hd<disk#> [<partition>] [options]\n\
959          chain.c32 fd<disk#> [options]\n\
960          chain.c32 mbr:<id> [<partition>] [options]\n\
961          chain.c32 guid:<guid> [<partition>] [options]\n\
962          chain.c32 label:<label> [<partition>] [options]\n\
963          chain.c32 boot [<partition>] [options]\n\
964          chain.c32 fs [options]\n\
965 Options: file=<loader>      Load and execute file, instead of boot sector\n\
966          isolinux=<loader>  Load another version of ISOLINUX\n\
967          ntldr=<loader>     Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
968          cmldr=<loader>     Load Recovery Console of Windows NT/2K/XP/2003\n\
969          freedos=<loader>   Load FreeDOS KERNEL.SYS\n\
970          msdos=<loader>     Load MS-DOS IO.SYS\n\
971          pcdos=<loader>     Load PC-DOS IBMBIO.COM\n\
972          grub=<loader>      Load GRUB Legacy stage2\n\
973          grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
974          grldr=<loader>     Load GRUB4DOS grldr\n\
975          seg=<segment>      Jump to <seg>:0000, instead of 0000:7C00\n\
976          swap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
977          hide               Hide primary partitions, except selected partition\n\
978          sethidden          Set the FAT/NTFS hidden sectors field\n\
979          keeppxe            Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
980 See syslinux/com32/modules/chain.c for more information\n";
981     error(usage);
982 }
983
984 int main(int argc, char *argv[])
985 {
986     struct disk_dos_mbr *mbr = NULL;
987     char *p;
988     struct disk_part_iter *cur_part = NULL;
989     struct syslinux_rm_regs regs;
990     char *drivename, *partition;
991     int hd, drive, whichpart = 0;       /* MBR by default */
992     int i;
993     uint64_t fs_lba = 0;        /* Syslinux partition */
994     uint32_t file_lba = 0;
995     struct guid gpt_guid;
996     unsigned char *isolinux_bin;
997     uint32_t *checksum, *chkhead, *chktail;
998     struct data_area data[3];
999     int ndata = 0;
1000     addr_t load_base;
1001     static const char cmldr_signature[8] = "cmdcons";
1002
1003     openconsole(&dev_null_r, &dev_stdcon_w);
1004
1005     drivename = "boot";
1006     partition = NULL;
1007
1008     /* Prepare the register set */
1009     memset(&regs, 0, sizeof regs);
1010
1011     for (i = 1; i < argc; i++) {
1012         if (!strncmp(argv[i], "file=", 5)) {
1013             opt.loadfile = argv[i] + 5;
1014         } else if (!strncmp(argv[i], "seg=", 4)) {
1015             uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1016             if (segval < 0x50 || segval > 0x9f000) {
1017                 error("Invalid segment\n");
1018                 goto bail;
1019             }
1020             opt.seg = segval;
1021         } else if (!strncmp(argv[i], "isolinux=", 9)) {
1022             opt.loadfile = argv[i] + 9;
1023             opt.isolinux = true;
1024         } else if (!strncmp(argv[i], "ntldr=", 6)) {
1025             opt.seg = 0x2000;   /* NTLDR wants this address */
1026             opt.loadfile = argv[i] + 6;
1027             opt.sethidden = true;
1028         } else if (!strncmp(argv[i], "cmldr=", 6)) {
1029             opt.seg = 0x2000;   /* CMLDR wants this address */
1030             opt.loadfile = argv[i] + 6;
1031             opt.cmldr = true;
1032             opt.sethidden = true;
1033         } else if (!strncmp(argv[i], "freedos=", 8)) {
1034             opt.seg = 0x60;     /* FREEDOS wants this address */
1035             opt.loadfile = argv[i] + 8;
1036             opt.sethidden = true;
1037         } else if (!strncmp(argv[i], "msdos=", 6) ||
1038                    !strncmp(argv[i], "pcdos=", 6)) {
1039             opt.seg = 0x70;     /* MS-DOS 2.0+ wants this address */
1040             opt.loadfile = argv[i] + 6;
1041             opt.sethidden = true;
1042         } else if (!strncmp(argv[i], "grub=", 5)) {
1043             opt.seg = 0x800;    /* stage2 wants this address */
1044             opt.loadfile = argv[i] + 5;
1045             opt.grub = true;
1046         } else if (!strncmp(argv[i], "grubcfg=", 8)) {
1047             opt.grubcfg = argv[i] + 8;
1048         } else if (!strncmp(argv[i], "grldr=", 6)) {
1049             opt.loadfile = argv[i] + 6;
1050             opt.grldr = true;
1051         } else if (!strcmp(argv[i], "swap")) {
1052             opt.swap = true;
1053         } else if (!strcmp(argv[i], "noswap")) {
1054             opt.swap = false;
1055         } else if (!strcmp(argv[i], "hide")) {
1056             opt.hide = true;
1057         } else if (!strcmp(argv[i], "nohide")) {
1058             opt.hide = false;
1059         } else if (!strcmp(argv[i], "keeppxe")) {
1060             opt.keeppxe = 3;
1061         } else if (!strcmp(argv[i], "sethidden")) {
1062             opt.sethidden = true;
1063         } else if (!strcmp(argv[i], "nosethidden")) {
1064             opt.sethidden = false;
1065         } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1066                     && argv[i][1] == 'd')
1067                    || !strncmp(argv[i], "mbr:", 4)
1068                    || !strncmp(argv[i], "mbr=", 4)
1069                    || !strncmp(argv[i], "guid:", 5)
1070                    || !strncmp(argv[i], "guid=", 5)
1071                    || !strncmp(argv[i], "label:", 6)
1072                    || !strncmp(argv[i], "label=", 6)
1073                    || !strcmp(argv[i], "boot")
1074                    || !strncmp(argv[i], "boot,", 5)
1075                    || !strcmp(argv[i], "fs")) {
1076             drivename = argv[i];
1077             p = strchr(drivename, ',');
1078             if (p) {
1079                 *p = '\0';
1080                 partition = p + 1;
1081             } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1082                        && argv[i + 1][0] <= '9') {
1083                 partition = argv[++i];
1084             }
1085         } else {
1086             usage();
1087             goto bail;
1088         }
1089     }
1090
1091     if (opt.grubcfg && !opt.grub) {
1092         error("grubcfg=<filename> must be used together with grub=<loader>.\n");
1093         goto bail;
1094     }
1095
1096     if (opt.seg) {
1097         regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1098     } else {
1099         regs.ip = regs.esp.l = 0x7c00;
1100     }
1101
1102     hd = 0;
1103     if (!strncmp(drivename, "mbr", 3)) {
1104         drive = find_disk(strtoul(drivename + 4, NULL, 0));
1105         if (drive == -1) {
1106             error("Unable to find requested MBR signature\n");
1107             goto bail;
1108         }
1109     } else if (!strncmp(drivename, "guid", 4)) {
1110         if (str_to_guid(drivename + 5, &gpt_guid))
1111             goto bail;
1112         drive = find_by_guid(&gpt_guid, &cur_part);
1113         if (drive == -1) {
1114             error("Unable to find requested GPT disk/partition\n");
1115             goto bail;
1116         }
1117     } else if (!strncmp(drivename, "label", 5)) {
1118         if (!drivename[6]) {
1119             error("No label specified.\n");
1120             goto bail;
1121         }
1122         drive = find_by_label(drivename + 6, &cur_part);
1123         if (drive == -1) {
1124             error("Unable to find requested partition by label\n");
1125             goto bail;
1126         }
1127     } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1128                drivename[1] == 'd') {
1129         hd = drivename[0] == 'h';
1130         drivename += 2;
1131         drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1132     } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
1133         const union syslinux_derivative_info *sdi;
1134
1135         sdi = syslinux_derivative_info();
1136         if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1137             drive = 0x80;       /* Boot drive not available */
1138         else
1139             drive = sdi->disk.drive_number;
1140         if (!strcmp(drivename, "fs")
1141             && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
1142                 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
1143                 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
1144             /* We should lookup the Syslinux partition number and use it */
1145             fs_lba = *sdi->disk.partoffset;
1146     } else {
1147         error("Unparsable drive specification\n");
1148         goto bail;
1149     }
1150
1151     /* DOS kernels want the drive number in BL instead of DL.  Indulge them. */
1152     regs.ebx.b[0] = regs.edx.b[0] = drive;
1153
1154     /* Get the disk geometry and disk access setup */
1155     if (disk_get_params(drive, &diskinfo)) {
1156         error("Cannot get disk parameters\n");
1157         goto bail;
1158     }
1159
1160     /* Get MBR */
1161     if (!(mbr = disk_read_sectors(&diskinfo, 0, 1))) {
1162         error("Cannot read Master Boot Record or sector 0\n");
1163         goto bail;
1164     }
1165
1166     if (partition)
1167         whichpart = strtoul(partition, NULL, 0);
1168     /* "guid:" or "label:" might have specified a partition */
1169     if (cur_part)
1170         whichpart = cur_part->index;
1171
1172     /* Boot the MBR by default */
1173     if (!cur_part && (whichpart || fs_lba)) {
1174         /* Boot a partition, possibly the Syslinux partition itself */
1175         cur_part = get_first_partition(NULL);
1176         while (cur_part) {
1177             if ((cur_part->index == whichpart)
1178                 || (cur_part->lba_data == fs_lba))
1179                 /* Found the partition to boot */
1180                 break;
1181             cur_part = cur_part->next(cur_part);
1182         }
1183         if (!cur_part) {
1184             error("Requested partition not found!\n");
1185             goto bail;
1186         }
1187         whichpart = cur_part->index;
1188     }
1189
1190     if (!(drive & 0x80) && whichpart) {
1191         error("Warning: Partitions of floppy devices may not work\n");
1192     }
1193
1194     /* 
1195      * GRLDR of GRUB4DOS wants the partition number in DH:
1196      * -1:   whole drive (default)
1197      * 0-3:  primary partitions
1198      * 4-*:  logical partitions
1199      */
1200     if (opt.grldr)
1201         regs.edx.b[1] = whichpart - 1;
1202
1203     if (opt.hide) {
1204         if (whichpart < 1 || whichpart > 4)
1205             error("WARNING: hide specified without a non-primary partition\n");
1206         if (hide_unhide(mbr, whichpart))
1207             error("WARNING: failed to write MBR for 'hide'\n");
1208     }
1209
1210     /* Do the actual chainloading */
1211     load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1212
1213     if (opt.loadfile) {
1214         fputs("Loading the boot file...\n", stdout);
1215         if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1216             error("Failed to load the boot file\n");
1217             goto bail;
1218         }
1219         data[ndata].base = load_base;
1220         load_base = 0x7c00;     /* If we also load a boot sector */
1221
1222         /* Create boot info table: needed when you want to chainload
1223            another version of ISOLINUX (or another bootlaoder that needs
1224            the -boot-info-table switch of mkisofs)
1225            (will only work when run from ISOLINUX) */
1226         if (opt.isolinux) {
1227             const union syslinux_derivative_info *sdi;
1228             sdi = syslinux_derivative_info();
1229
1230             if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1231                 /* Boot info table info (integers in little endian format)
1232
1233                    Offset Name         Size      Meaning
1234                    8     bi_pvd       4 bytes   LBA of primary volume descriptor
1235                    12     bi_file      4 bytes   LBA of boot file
1236                    16     bi_length    4 bytes   Boot file length in bytes
1237                    20     bi_csum      4 bytes   32-bit checksum
1238                    24     bi_reserved  40 bytes  Reserved
1239
1240                    The 32-bit checksum is the sum of all the 32-bit words in the
1241                    boot file starting at byte offset 64. All linear block
1242                    addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1243
1244                    LBA of primary volume descriptor should already be set to 16. 
1245                  */
1246
1247                 isolinux_bin = (unsigned char *)data[ndata].data;
1248
1249                 /* Get LBA address of bootfile */
1250                 file_lba = get_file_lba(opt.loadfile);
1251
1252                 if (file_lba == 0) {
1253                     error("Failed to find LBA offset of the boot file\n");
1254                     goto bail;
1255                 }
1256                 /* Set it */
1257                 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1258
1259                 /* Set boot file length */
1260                 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1261
1262                 /* Calculate checksum */
1263                 checksum = (uint32_t *) & isolinux_bin[20];
1264                 chkhead = (uint32_t *) & isolinux_bin[64];
1265                 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1266                 *checksum = 0;
1267                 while (chkhead < chktail)
1268                     *checksum += *chkhead++;
1269
1270                 /*
1271                  * Deal with possible fractional dword at the end;
1272                  * this *should* never happen...
1273                  */
1274                 if (data[ndata].size & 3) {
1275                     uint32_t xword = 0;
1276                     memcpy(&xword, chkhead, data[ndata].size & 3);
1277                     *checksum += xword;
1278                 }
1279             } else {
1280                 error
1281                     ("The isolinux= option is only valid when run from ISOLINUX\n");
1282                 goto bail;
1283             }
1284         }
1285
1286         if (opt.grub) {
1287             /* Layout of stage2 file (from byte 0x0 to 0x270) */
1288             struct grub_stage2_patch_area {
1289                 /* 0x0 to 0x205 */
1290                 char unknown[0x206];
1291                 /* 0x206: compatibility version number major */
1292                 uint8_t compat_version_major;
1293                 /* 0x207: compatibility version number minor */
1294                 uint8_t compat_version_minor;
1295
1296                 /* 0x208: install_partition variable */
1297                 struct {
1298                     /* 0x208: sub-partition in sub-partition part2 */
1299                     uint8_t part3;
1300                     /* 0x209: sub-partition in top-level partition */
1301                     uint8_t part2;
1302                     /* 0x20a: top-level partiton number */
1303                     uint8_t part1;
1304                     /* 0x20b: BIOS drive number (must be 0) */
1305                     uint8_t drive;
1306                 } __attribute__ ((packed)) install_partition;
1307
1308                 /* 0x20c: deprecated (historical reason only) */
1309                 uint32_t saved_entryno;
1310                 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1311                 uint8_t stage2_id;
1312                 /* 0x211: force LBA */
1313                 uint8_t force_lba;
1314                 /* 0x212: version string (will probably be 0.97) */
1315                 char version_string[5];
1316                 /* 0x217: config filename */
1317                 char config_file[89];
1318                 /* 0x270: start of code (after jump from 0x200) */
1319                 char codestart[1];
1320             } __attribute__ ((packed)) *stage2;
1321
1322             if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1323                 error
1324                     ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1325                 goto bail;
1326             }
1327
1328             stage2 = data[ndata].data;
1329
1330             /*
1331              * Check the compatibility version number to see if we loaded a real
1332              * stage2 file or a stage2 file that we support.
1333              */
1334             if (stage2->compat_version_major != 3
1335                 || stage2->compat_version_minor != 2) {
1336                 error
1337                     ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1338                 goto bail;
1339             }
1340
1341             /* jump 0x200 bytes into the loadfile */
1342             regs.ip = 0x200;
1343
1344             /*
1345              * GRUB Legacy wants the partition number in the install_partition
1346              * variable, located at offset 0x208 of stage2.
1347              * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1348              *
1349              * It looks very similar to the "boot information format" of the
1350              * Multiboot specification:
1351              *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1352              *
1353              *   0x208 = part3: sub-partition in sub-partition part2
1354              *   0x209 = part2: sub-partition in top-level partition
1355              *   0x20a = part1: top-level partition number
1356              *   0x20b = drive: BIOS drive number (must be 0)
1357              *
1358              * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1359              * another location.
1360              *
1361              * Partition numbers always start from zero.
1362              * Unused partition bytes must be set to 0xFF. 
1363              *
1364              * We only care about top-level partition, so we only need to change
1365              * "part1" to the appropriate value:
1366              *   -1:   whole drive (default) (-1 = 0xFF)
1367              *   0-3:  primary partitions
1368              *   4-*:  logical partitions
1369              */
1370             stage2->install_partition.part1 = whichpart - 1;
1371
1372             /*
1373              * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1374              * config filename. The filename passed via grubcfg= will overwrite
1375              * the default config filename "/boot/grub/menu.lst".
1376              */
1377             if (opt.grubcfg) {
1378                 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1379                     error
1380                         ("The config filename length can't exceed 88 characters.\n");
1381                     goto bail;
1382                 }
1383
1384                 strcpy((char *)stage2->config_file, opt.grubcfg);
1385             }
1386         }
1387
1388         ndata++;
1389     }
1390
1391     if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1392         /* Actually read the boot sector */
1393         if (!cur_part) {
1394             data[ndata].data = mbr;
1395         } else
1396             if (!
1397                 (data[ndata].data =
1398                  disk_read_sectors(&diskinfo, cur_part->lba_data, 1))) {
1399             error("Cannot read boot sector\n");
1400             goto bail;
1401         }
1402         data[ndata].size = SECTOR;
1403         data[ndata].base = load_base;
1404
1405         if (!opt.loadfile) {
1406             const struct disk_dos_mbr *br =
1407                 (const struct disk_dos_mbr *)((char *)data[ndata].data +
1408                                               data[ndata].size -
1409                                               sizeof(struct disk_dos_mbr));
1410             if (br->sig != disk_mbr_sig_magic) {
1411                 error
1412                     ("Boot sector signature not found (unbootable disk/partition?)\n");
1413                 goto bail;
1414             }
1415         }
1416         /*
1417          * To boot the Recovery Console of Windows NT/2K/XP we need to write
1418          * the string "cmdcons\0" to memory location 0000:7C03.
1419          * Memory location 0000:7C00 contains the bootsector of the partition.
1420          */
1421         if (cur_part && opt.cmldr) {
1422             memcpy((char *)data[ndata].data + 3, cmldr_signature,
1423                    sizeof cmldr_signature);
1424         }
1425
1426         /*
1427          * Modify the hidden sectors (partition offset) copy in memory;
1428          * this modifies the field used by FAT and NTFS filesystems, and
1429          * possibly other boot loaders which use the same format.
1430          */
1431         if (cur_part && opt.sethidden) {
1432             *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1433         }
1434
1435         ndata++;
1436     }
1437
1438     if (cur_part) {
1439         if (cur_part->next == next_gpt_part) {
1440             /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1441             struct disk_dos_part_entry *record;
1442             /* Look at the GPT partition */
1443             const struct gpt_part *gp = (const struct gpt_part *)
1444                 (cur_part->block +
1445                  (cur_part->private.gpt.size * cur_part->private.gpt.index));
1446             /* Note the partition length */
1447             uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1448             /* The length of the hand-over */
1449             int synth_size =
1450                 sizeof(struct disk_dos_part_entry) + sizeof(uint32_t) +
1451                 cur_part->private.gpt.size;
1452             /* Will point to the partition record length in the hand-over */
1453             uint32_t *plen;
1454
1455             /* Allocate the hand-over record */
1456             record = malloc(synth_size);
1457             if (!record) {
1458                 error("Could not build GPT hand-over record!\n");
1459                 goto bail;
1460             }
1461             /* Synthesize the record */
1462             memset(record, 0, synth_size);
1463             record->active_flag = 0x80;
1464             record->ostype = 0xED;
1465             /* All bits set by default */
1466             record->start_lba = ~(uint32_t) 0;
1467             record->length = ~(uint32_t) 0;
1468             /* If these fit the precision, pass them on */
1469             if (cur_part->lba_data < record->start_lba)
1470                 record->start_lba = cur_part->lba_data;
1471             if (lba_count < record->length)
1472                 record->length = lba_count;
1473             /* Next comes the GPT partition record length */
1474             plen = (uint32_t *) (record + 1);
1475             plen[0] = cur_part->private.gpt.size;
1476             /* Next comes the GPT partition record copy */
1477             memcpy(plen + 1, gp, plen[0]);
1478             cur_part->record = record;
1479
1480             regs.eax.l = 0x54504721;    /* '!GPT' */
1481             data[ndata].base = 0x7be;
1482             data[ndata].size = synth_size;
1483             data[ndata].data = (void *)record;
1484             ndata++;
1485             regs.esi.w[0] = 0x7be;
1486
1487             dprintf("GPT handover:\n");
1488             disk_dos_part_dump(record);
1489             gpt_part_dump((struct gpt_part *)(plen + 1));
1490         } else if (cur_part->record) {
1491             /* MBR handover protocol */
1492             static struct disk_dos_part_entry handover_record;
1493
1494             handover_record = *cur_part->record;
1495             handover_record.start_lba = cur_part->lba_data;
1496
1497             data[ndata].base = 0x7be;
1498             data[ndata].size = sizeof handover_record;
1499             data[ndata].data = &handover_record;
1500             ndata++;
1501             regs.esi.w[0] = 0x7be;
1502
1503             dprintf("MBR handover:\n");
1504             disk_dos_part_dump(&handover_record);
1505         }
1506     }
1507
1508     do_boot(data, ndata, &regs);
1509
1510 bail:
1511     if (cur_part) {
1512         free(cur_part->block);
1513         free((void *)cur_part->record);
1514     }
1515     free(cur_part);
1516     free(mbr);
1517     return 255;
1518 }