chain.c32, libcom32: Move struct gpt as disk_gpt_header
[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 static void gpt_dump(const struct disk_gpt_header *gpt)
353 {
354     char guid_text[37];
355
356     printf("GPT sig ______________ : '%8.8s'\n"
357            "GPT major revision ___ : 0x%.4x\n"
358            "GPT minor revision ___ : 0x%.4x\n"
359            "GPT header size ______ : 0x%.8x\n"
360            "GPT header checksum __ : 0x%.8x\n"
361            "GPT reserved _________ : '%4.4s'\n"
362            "GPT LBA current ______ : 0x%.16llx\n"
363            "GPT LBA alternative __ : 0x%.16llx\n"
364            "GPT LBA first usable _ : 0x%.16llx\n"
365            "GPT LBA last usable __ : 0x%.16llx\n"
366            "GPT LBA part. table __ : 0x%.16llx\n"
367            "GPT partition count __ : 0x%.8x\n"
368            "GPT partition size ___ : 0x%.8x\n"
369            "GPT part. table chksum : 0x%.8x\n",
370            gpt->sig,
371            gpt->rev.fields.major,
372            gpt->rev.fields.minor,
373            gpt->hdr_size,
374            gpt->chksum,
375            gpt->reserved1,
376            gpt->lba_cur,
377            gpt->lba_alt,
378            gpt->lba_first_usable,
379            gpt->lba_last_usable,
380            gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
381     guid_to_str(guid_text, &gpt->disk_guid);
382     printf("GPT disk GUID ________ : {%s}\n", guid_text);
383 }
384 #endif
385
386 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
387 {
388     const struct disk_gpt_part_entry *gpt_part = NULL;
389
390     while (++part->private.gpt.index < part->private.gpt.parts) {
391         gpt_part =
392             (const struct disk_gpt_part_entry *)(part->block +
393                                                  (part->private.gpt.index *
394                                                   part->private.gpt.size));
395         if (!gpt_part->lba_first)
396             continue;
397         break;
398     }
399     /* Were we the last partition? */
400     if (part->private.gpt.index == part->private.gpt.parts) {
401         goto err_last;
402     }
403     part->lba_data = gpt_part->lba_first;
404     part->private.gpt.part_guid = &gpt_part->uid;
405     part->private.gpt.part_label = gpt_part->name;
406     /* Update our index */
407     part->index++;
408 #ifdef DEBUG
409     disk_gpt_part_dump(gpt_part);
410 #endif
411
412     /* In a GPT scheme, we re-use the iterator */
413     return part;
414
415 err_last:
416     free(part->block);
417     free(part);
418
419     return NULL;
420 }
421
422 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
423 {
424     const struct disk_gpt_header *gpt_candidate;
425
426     /*
427      * Ignore any passed partition iterator.  The caller should
428      * have passed NULL.  Allocate a new partition iterator
429      */
430     part = malloc(sizeof(*part));
431     if (!part) {
432         error("Count not allocate partition iterator!\n");
433         goto err_alloc_iter;
434     }
435     /* Read MBR */
436     part->block = disk_read_sectors(&diskinfo, 0, 2);
437     if (!part->block) {
438         error("Could not read two sectors!\n");
439         goto err_read_mbr;
440     }
441     /* Check for an MBR */
442     if (((struct disk_dos_mbr *)part->block)->sig != disk_mbr_sig_magic) {
443         error("No MBR magic!\n");
444         goto err_mbr;
445     }
446     /* Establish a pseudo-partition for the MBR (index 0) */
447     part->index = 0;
448     part->record = NULL;
449     part->private.mbr_index = -1;
450     part->next = next_mbr_part;
451     /* Check for a GPT disk */
452     gpt_candidate = (const struct disk_gpt_header *)(part->block + SECTOR);
453     if (!memcmp
454         (gpt_candidate->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
455         /* LBA for partition table */
456         uint64_t lba_table;
457
458         /* It looks like one */
459         /* TODO: Check checksum.  Possibly try alternative GPT */
460 #if DEBUG
461         puts("Looks like a GPT disk.");
462         gpt_dump(gpt_candidate);
463 #endif
464         /* TODO: Check table checksum (maybe) */
465         /* Note relevant GPT details */
466         part->next = next_gpt_part;
467         part->private.gpt.index = -1;
468         part->private.gpt.parts = gpt_candidate->part_count;
469         part->private.gpt.size = gpt_candidate->part_size;
470         lba_table = gpt_candidate->lba_table;
471         gpt_candidate = NULL;
472         /* Load the partition table */
473         free(part->block);
474         part->block =
475             disk_read_sectors(&diskinfo, lba_table,
476                               ((part->private.gpt.size *
477                                 part->private.gpt.parts) + SECTOR -
478                                1) / SECTOR);
479         if (!part->block) {
480             error("Could not read GPT partition list!\n");
481             goto err_gpt_table;
482         }
483     }
484     /* Return the pseudo-partition's next partition, which is real */
485     return part->next(part);
486
487 err_gpt_table:
488
489 err_mbr:
490
491     free(part->block);
492     part->block = NULL;
493 err_read_mbr:
494
495     free(part);
496 err_alloc_iter:
497
498     return NULL;
499 }
500
501 /*
502  * Search for a specific drive/partition, based on the GPT GUID.
503  * We return the disk drive number if found, as well as populating the
504  * boot_part pointer with the matching partition, if applicable.
505  * If no matching partition is found or the GUID is a disk GUID,
506  * boot_part will be populated with NULL.  If not matching disk is
507  * found, we return -1.
508  */
509 static int find_by_guid(const struct guid *gpt_guid,
510                         struct disk_part_iter **boot_part)
511 {
512     int drive;
513     bool is_me;
514     struct disk_gpt_header *header;
515
516     for (drive = 0x80; drive <= 0xff; drive++) {
517         if (disk_get_params(drive, &diskinfo))
518             continue;           /* Drive doesn't exist */
519         if (!(header = disk_read_sectors(&diskinfo, 1, 1)))
520             continue;           /* Cannot read sector */
521         if (memcmp
522             (&header->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
523             /* Not a GPT disk */
524             free(header);
525             continue;
526         }
527 #if DEBUG
528         gpt_dump(header);
529 #endif
530         is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
531         free(header);
532         if (!is_me) {
533             /* Check for a matching partition */
534             boot_part[0] = get_first_partition(NULL);
535             while (boot_part[0]) {
536                 is_me =
537                     !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
538                             sizeof(*gpt_guid));
539                 if (is_me)
540                     break;
541                 boot_part[0] = boot_part[0]->next(boot_part[0]);
542             }
543         } else
544             boot_part[0] = NULL;
545         if (is_me)
546             return drive;
547     }
548     return -1;
549 }
550
551 /*
552  * Search for a specific partition, based on the GPT label.
553  * We return the disk drive number if found, as well as populating the
554  * boot_part pointer with the matching partition, if applicable.
555  * If no matching partition is found, boot_part will be populated with
556  * NULL and we return -1.
557  */
558 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
559 {
560     int drive;
561     bool is_me;
562
563     for (drive = 0x80; drive <= 0xff; drive++) {
564         if (disk_get_params(drive, &diskinfo))
565             continue;           /* Drive doesn't exist */
566         /* Check for a GPT disk */
567         boot_part[0] = get_first_partition(NULL);
568         if (!(boot_part[0]->next == next_gpt_part)) {
569             /* Not a GPT disk */
570             while (boot_part[0]) {
571                 /* Run through until the end */
572                 boot_part[0] = boot_part[0]->next(boot_part[0]);
573             }
574             continue;
575         }
576         /* Check for a matching partition */
577         while (boot_part[0]) {
578             char gpt_label[sizeof(((struct disk_gpt_part_entry *) NULL)->name)];
579             const char *gpt_label_scanner =
580                 boot_part[0]->private.gpt.part_label;
581             int j = 0;
582
583             /* Re-write the GPT partition label as ASCII */
584             while (gpt_label_scanner <
585                    boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
586                 if ((gpt_label[j] = *gpt_label_scanner))
587                     j++;
588                 gpt_label_scanner++;
589             }
590             if ((is_me = !strcmp(label, gpt_label)))
591                 break;
592             boot_part[0] = boot_part[0]->next(boot_part[0]);
593         }
594         if (is_me)
595             return drive;
596     }
597
598     return -1;
599 }
600
601 static void do_boot(struct data_area *data, int ndata,
602                     struct syslinux_rm_regs *regs)
603 {
604     uint16_t *const bios_fbm = (uint16_t *) 0x413;
605     addr_t dosmem = *bios_fbm << 10;    /* Technically a low bound */
606     struct syslinux_memmap *mmap;
607     struct syslinux_movelist *mlist = NULL;
608     addr_t endimage;
609     uint8_t driveno = regs->edx.b[0];
610     uint8_t swapdrive = driveno & 0x80;
611     int i;
612
613     mmap = syslinux_memory_map();
614
615     if (!mmap) {
616         error("Cannot read system memory map\n");
617         return;
618     }
619
620     endimage = 0;
621     for (i = 0; i < ndata; i++) {
622         if (data[i].base + data[i].size > endimage)
623             endimage = data[i].base + data[i].size;
624     }
625     if (endimage > dosmem)
626         goto too_big;
627
628     for (i = 0; i < ndata; i++) {
629         if (syslinux_add_movelist(&mlist, data[i].base,
630                                   (addr_t) data[i].data, data[i].size))
631             goto enomem;
632     }
633
634     if (opt.swap && driveno != swapdrive) {
635         static const uint8_t swapstub_master[] = {
636             /* The actual swap code */
637             0x53,               /* 00: push bx */
638             0x0f, 0xb6, 0xda,   /* 01: movzx bx,dl */
639             0x2e, 0x8a, 0x57, 0x60,     /* 04: mov dl,[cs:bx+0x60] */
640             0x5b,               /* 08: pop bx */
641             0xea, 0, 0, 0, 0,   /* 09: jmp far 0:0 */
642             0x90, 0x90,         /* 0E: nop; nop */
643             /* Code to install this in the right location */
644             /* Entry with DS = CS; ES = SI = 0; CX = 256 */
645             0x26, 0x66, 0x8b, 0x7c, 0x4c,       /* 10: mov edi,[es:si+4*0x13] */
646             0x66, 0x89, 0x3e, 0x0a, 0x00,       /* 15: mov [0x0A],edi */
647             0x26, 0x8b, 0x3e, 0x13, 0x04,       /* 1A: mov di,[es:0x413] */
648             0x4f,               /* 1F: dec di */
649             0x26, 0x89, 0x3e, 0x13, 0x04,       /* 20: mov [es:0x413],di */
650             0x66, 0xc1, 0xe7, 0x16,     /* 25: shl edi,16+6 */
651             0x26, 0x66, 0x89, 0x7c, 0x4c,       /* 29: mov [es:si+4*0x13],edi */
652             0x66, 0xc1, 0xef, 0x10,     /* 2E: shr edi,16 */
653             0x8e, 0xc7,         /* 32: mov es,di */
654             0x31, 0xff,         /* 34: xor di,di */
655             0xf3, 0x66, 0xa5,   /* 36: rep movsd */
656             0xbe, 0, 0,         /* 39: mov si,0 */
657             0xbf, 0, 0,         /* 3C: mov di,0 */
658             0x8e, 0xde,         /* 3F: mov ds,si */
659             0x8e, 0xc7,         /* 41: mov es,di */
660             0x66, 0xb9, 0, 0, 0, 0,     /* 43: mov ecx,0 */
661             0x66, 0xbe, 0, 0, 0, 0,     /* 49: mov esi,0 */
662             0x66, 0xbf, 0, 0, 0, 0,     /* 4F: mov edi,0 */
663             0xea, 0, 0, 0, 0,   /* 55: jmp 0:0 */
664             /* pad out to segment boundary */
665             0x90, 0x90,         /* 5A: ... */
666             0x90, 0x90, 0x90, 0x90,     /* 5C: ... */
667         };
668         static uint8_t swapstub[1024];
669         uint8_t *p;
670
671         /* Note: we can't rely on either INT 13h nor the dosmem
672            vector to be correct at this stage, so we have to use an
673            installer stub to put things in the right place.
674            Round the installer location to a 1K boundary so the only
675            possible overlap is the identity mapping. */
676         endimage = (endimage + 1023) & ~1023;
677
678         /* Create swap stub */
679         memcpy(swapstub, swapstub_master, sizeof swapstub_master);
680         *(uint16_t *) & swapstub[0x3a] = regs->ds;
681         *(uint16_t *) & swapstub[0x3d] = regs->es;
682         *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
683         *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
684         *(uint32_t *) & swapstub[0x51] = regs->edi.l;
685         *(uint16_t *) & swapstub[0x56] = regs->ip;
686         *(uint16_t *) & swapstub[0x58] = regs->cs;
687         p = &swapstub[sizeof swapstub_master];
688
689         /* Mapping table; start out with identity mapping everything */
690         for (i = 0; i < 256; i++)
691             p[i] = i;
692
693         /* And the actual swap */
694         p[driveno] = swapdrive;
695         p[swapdrive] = driveno;
696
697         /* Adjust registers */
698         regs->ds = regs->cs = endimage >> 4;
699         regs->es = regs->esi.l = 0;
700         regs->ecx.l = sizeof swapstub >> 2;
701         regs->ip = 0x10;        /* Installer offset */
702         regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
703
704         if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
705                                   sizeof swapstub))
706             goto enomem;
707
708         endimage += sizeof swapstub;
709     }
710
711     /* Tell the shuffler not to muck with this area... */
712     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
713
714     /* Force text mode */
715     syslinux_force_text_mode();
716
717     fputs("Booting...\n", stdout);
718     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
719     error("Chainboot failed!\n");
720     return;
721
722 too_big:
723     error("Loader file too large\n");
724     return;
725
726 enomem:
727     error("Out of memory\n");
728     return;
729 }
730
731 static int hide_unhide(struct disk_dos_mbr *mbr, int part)
732 {
733     int i;
734     struct disk_dos_part_entry *pt;
735     const uint16_t mask =
736         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
737                                                                                <<
738                                                                                0x0c)
739         | (1 << 0x0e);
740     uint8_t t;
741     bool write_back = false;
742
743     for (i = 1; i <= 4; i++) {
744         pt = mbr->table + i - 1;
745         t = pt->ostype;
746         if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
747             /* It's a hideable partition type */
748             if (i == part)
749                 t &= ~0x10;     /* unhide */
750             else
751                 t |= 0x10;      /* hide */
752         }
753         if (t != pt->ostype) {
754             write_back = true;
755             pt->ostype = t;
756         }
757     }
758
759     if (write_back)
760         return disk_write_verify_sector(&diskinfo, 0, mbr);
761
762     return 0;                   /* ok */
763 }
764
765 static uint32_t get_file_lba(const char *filename)
766 {
767     com32sys_t inregs;
768     uint32_t lba;
769
770     /* Start with clean registers */
771     memset(&inregs, 0, sizeof(com32sys_t));
772
773     /* Put the filename in the bounce buffer */
774     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
775
776     /* Call comapi_open() which returns a structure pointer in SI
777      * to a structure whose first member happens to be the LBA.
778      */
779     inregs.eax.w[0] = 0x0006;
780     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
781     inregs.es = SEG(__com32.cs_bounce);
782     __com32.cs_intcall(0x22, &inregs, &inregs);
783
784     if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
785         return 0;               /* Filename not found */
786     }
787
788     /* Since the first member is the LBA, we simply cast */
789     lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
790
791     /* Clean the registers for the next call */
792     memset(&inregs, 0, sizeof(com32sys_t));
793
794     /* Put the filename in the bounce buffer */
795     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
796
797     /* Call comapi_close() to free the structure */
798     inregs.eax.w[0] = 0x0008;
799     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
800     inregs.es = SEG(__com32.cs_bounce);
801     __com32.cs_intcall(0x22, &inregs, &inregs);
802
803     return lba;
804 }
805
806 static void usage(void)
807 {
808     static const char usage[] = "\
809 Usage:   chain.c32 [options]\n\
810          chain.c32 hd<disk#> [<partition>] [options]\n\
811          chain.c32 fd<disk#> [options]\n\
812          chain.c32 mbr:<id> [<partition>] [options]\n\
813          chain.c32 guid:<guid> [<partition>] [options]\n\
814          chain.c32 label:<label> [<partition>] [options]\n\
815          chain.c32 boot [<partition>] [options]\n\
816          chain.c32 fs [options]\n\
817 Options: file=<loader>      Load and execute file, instead of boot sector\n\
818          isolinux=<loader>  Load another version of ISOLINUX\n\
819          ntldr=<loader>     Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
820          cmldr=<loader>     Load Recovery Console of Windows NT/2K/XP/2003\n\
821          freedos=<loader>   Load FreeDOS KERNEL.SYS\n\
822          msdos=<loader>     Load MS-DOS IO.SYS\n\
823          pcdos=<loader>     Load PC-DOS IBMBIO.COM\n\
824          grub=<loader>      Load GRUB Legacy stage2\n\
825          grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
826          grldr=<loader>     Load GRUB4DOS grldr\n\
827          seg=<segment>      Jump to <seg>:0000, instead of 0000:7C00\n\
828          swap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
829          hide               Hide primary partitions, except selected partition\n\
830          sethidden          Set the FAT/NTFS hidden sectors field\n\
831          keeppxe            Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
832 See syslinux/com32/modules/chain.c for more information\n";
833     error(usage);
834 }
835
836 int main(int argc, char *argv[])
837 {
838     struct disk_dos_mbr *mbr = NULL;
839     char *p;
840     struct disk_part_iter *cur_part = NULL;
841     struct syslinux_rm_regs regs;
842     char *drivename, *partition;
843     int hd, drive, whichpart = 0;       /* MBR by default */
844     int i;
845     uint64_t fs_lba = 0;        /* Syslinux partition */
846     uint32_t file_lba = 0;
847     struct guid gpt_guid;
848     unsigned char *isolinux_bin;
849     uint32_t *checksum, *chkhead, *chktail;
850     struct data_area data[3];
851     int ndata = 0;
852     addr_t load_base;
853     static const char cmldr_signature[8] = "cmdcons";
854
855     openconsole(&dev_null_r, &dev_stdcon_w);
856
857     drivename = "boot";
858     partition = NULL;
859
860     /* Prepare the register set */
861     memset(&regs, 0, sizeof regs);
862
863     for (i = 1; i < argc; i++) {
864         if (!strncmp(argv[i], "file=", 5)) {
865             opt.loadfile = argv[i] + 5;
866         } else if (!strncmp(argv[i], "seg=", 4)) {
867             uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
868             if (segval < 0x50 || segval > 0x9f000) {
869                 error("Invalid segment\n");
870                 goto bail;
871             }
872             opt.seg = segval;
873         } else if (!strncmp(argv[i], "isolinux=", 9)) {
874             opt.loadfile = argv[i] + 9;
875             opt.isolinux = true;
876         } else if (!strncmp(argv[i], "ntldr=", 6)) {
877             opt.seg = 0x2000;   /* NTLDR wants this address */
878             opt.loadfile = argv[i] + 6;
879             opt.sethidden = true;
880         } else if (!strncmp(argv[i], "cmldr=", 6)) {
881             opt.seg = 0x2000;   /* CMLDR wants this address */
882             opt.loadfile = argv[i] + 6;
883             opt.cmldr = true;
884             opt.sethidden = true;
885         } else if (!strncmp(argv[i], "freedos=", 8)) {
886             opt.seg = 0x60;     /* FREEDOS wants this address */
887             opt.loadfile = argv[i] + 8;
888             opt.sethidden = true;
889         } else if (!strncmp(argv[i], "msdos=", 6) ||
890                    !strncmp(argv[i], "pcdos=", 6)) {
891             opt.seg = 0x70;     /* MS-DOS 2.0+ wants this address */
892             opt.loadfile = argv[i] + 6;
893             opt.sethidden = true;
894         } else if (!strncmp(argv[i], "grub=", 5)) {
895             opt.seg = 0x800;    /* stage2 wants this address */
896             opt.loadfile = argv[i] + 5;
897             opt.grub = true;
898         } else if (!strncmp(argv[i], "grubcfg=", 8)) {
899             opt.grubcfg = argv[i] + 8;
900         } else if (!strncmp(argv[i], "grldr=", 6)) {
901             opt.loadfile = argv[i] + 6;
902             opt.grldr = true;
903         } else if (!strcmp(argv[i], "swap")) {
904             opt.swap = true;
905         } else if (!strcmp(argv[i], "noswap")) {
906             opt.swap = false;
907         } else if (!strcmp(argv[i], "hide")) {
908             opt.hide = true;
909         } else if (!strcmp(argv[i], "nohide")) {
910             opt.hide = false;
911         } else if (!strcmp(argv[i], "keeppxe")) {
912             opt.keeppxe = 3;
913         } else if (!strcmp(argv[i], "sethidden")) {
914             opt.sethidden = true;
915         } else if (!strcmp(argv[i], "nosethidden")) {
916             opt.sethidden = false;
917         } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
918                     && argv[i][1] == 'd')
919                    || !strncmp(argv[i], "mbr:", 4)
920                    || !strncmp(argv[i], "mbr=", 4)
921                    || !strncmp(argv[i], "guid:", 5)
922                    || !strncmp(argv[i], "guid=", 5)
923                    || !strncmp(argv[i], "label:", 6)
924                    || !strncmp(argv[i], "label=", 6)
925                    || !strcmp(argv[i], "boot")
926                    || !strncmp(argv[i], "boot,", 5)
927                    || !strcmp(argv[i], "fs")) {
928             drivename = argv[i];
929             p = strchr(drivename, ',');
930             if (p) {
931                 *p = '\0';
932                 partition = p + 1;
933             } else if (argv[i + 1] && argv[i + 1][0] >= '0'
934                        && argv[i + 1][0] <= '9') {
935                 partition = argv[++i];
936             }
937         } else {
938             usage();
939             goto bail;
940         }
941     }
942
943     if (opt.grubcfg && !opt.grub) {
944         error("grubcfg=<filename> must be used together with grub=<loader>.\n");
945         goto bail;
946     }
947
948     if (opt.seg) {
949         regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
950     } else {
951         regs.ip = regs.esp.l = 0x7c00;
952     }
953
954     hd = 0;
955     if (!strncmp(drivename, "mbr", 3)) {
956         drive = find_disk(strtoul(drivename + 4, NULL, 0));
957         if (drive == -1) {
958             error("Unable to find requested MBR signature\n");
959             goto bail;
960         }
961     } else if (!strncmp(drivename, "guid", 4)) {
962         if (str_to_guid(drivename + 5, &gpt_guid))
963             goto bail;
964         drive = find_by_guid(&gpt_guid, &cur_part);
965         if (drive == -1) {
966             error("Unable to find requested GPT disk/partition\n");
967             goto bail;
968         }
969     } else if (!strncmp(drivename, "label", 5)) {
970         if (!drivename[6]) {
971             error("No label specified.\n");
972             goto bail;
973         }
974         drive = find_by_label(drivename + 6, &cur_part);
975         if (drive == -1) {
976             error("Unable to find requested partition by label\n");
977             goto bail;
978         }
979     } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
980                drivename[1] == 'd') {
981         hd = drivename[0] == 'h';
982         drivename += 2;
983         drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
984     } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
985         const union syslinux_derivative_info *sdi;
986
987         sdi = syslinux_derivative_info();
988         if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
989             drive = 0x80;       /* Boot drive not available */
990         else
991             drive = sdi->disk.drive_number;
992         if (!strcmp(drivename, "fs")
993             && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
994                 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
995                 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
996             /* We should lookup the Syslinux partition number and use it */
997             fs_lba = *sdi->disk.partoffset;
998     } else {
999         error("Unparsable drive specification\n");
1000         goto bail;
1001     }
1002
1003     /* DOS kernels want the drive number in BL instead of DL.  Indulge them. */
1004     regs.ebx.b[0] = regs.edx.b[0] = drive;
1005
1006     /* Get the disk geometry and disk access setup */
1007     if (disk_get_params(drive, &diskinfo)) {
1008         error("Cannot get disk parameters\n");
1009         goto bail;
1010     }
1011
1012     /* Get MBR */
1013     if (!(mbr = disk_read_sectors(&diskinfo, 0, 1))) {
1014         error("Cannot read Master Boot Record or sector 0\n");
1015         goto bail;
1016     }
1017
1018     if (partition)
1019         whichpart = strtoul(partition, NULL, 0);
1020     /* "guid:" or "label:" might have specified a partition */
1021     if (cur_part)
1022         whichpart = cur_part->index;
1023
1024     /* Boot the MBR by default */
1025     if (!cur_part && (whichpart || fs_lba)) {
1026         /* Boot a partition, possibly the Syslinux partition itself */
1027         cur_part = get_first_partition(NULL);
1028         while (cur_part) {
1029             if ((cur_part->index == whichpart)
1030                 || (cur_part->lba_data == fs_lba))
1031                 /* Found the partition to boot */
1032                 break;
1033             cur_part = cur_part->next(cur_part);
1034         }
1035         if (!cur_part) {
1036             error("Requested partition not found!\n");
1037             goto bail;
1038         }
1039         whichpart = cur_part->index;
1040     }
1041
1042     if (!(drive & 0x80) && whichpart) {
1043         error("Warning: Partitions of floppy devices may not work\n");
1044     }
1045
1046     /* 
1047      * GRLDR of GRUB4DOS wants the partition number in DH:
1048      * -1:   whole drive (default)
1049      * 0-3:  primary partitions
1050      * 4-*:  logical partitions
1051      */
1052     if (opt.grldr)
1053         regs.edx.b[1] = whichpart - 1;
1054
1055     if (opt.hide) {
1056         if (whichpart < 1 || whichpart > 4)
1057             error("WARNING: hide specified without a non-primary partition\n");
1058         if (hide_unhide(mbr, whichpart))
1059             error("WARNING: failed to write MBR for 'hide'\n");
1060     }
1061
1062     /* Do the actual chainloading */
1063     load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1064
1065     if (opt.loadfile) {
1066         fputs("Loading the boot file...\n", stdout);
1067         if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1068             error("Failed to load the boot file\n");
1069             goto bail;
1070         }
1071         data[ndata].base = load_base;
1072         load_base = 0x7c00;     /* If we also load a boot sector */
1073
1074         /* Create boot info table: needed when you want to chainload
1075            another version of ISOLINUX (or another bootlaoder that needs
1076            the -boot-info-table switch of mkisofs)
1077            (will only work when run from ISOLINUX) */
1078         if (opt.isolinux) {
1079             const union syslinux_derivative_info *sdi;
1080             sdi = syslinux_derivative_info();
1081
1082             if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1083                 /* Boot info table info (integers in little endian format)
1084
1085                    Offset Name         Size      Meaning
1086                    8     bi_pvd       4 bytes   LBA of primary volume descriptor
1087                    12     bi_file      4 bytes   LBA of boot file
1088                    16     bi_length    4 bytes   Boot file length in bytes
1089                    20     bi_csum      4 bytes   32-bit checksum
1090                    24     bi_reserved  40 bytes  Reserved
1091
1092                    The 32-bit checksum is the sum of all the 32-bit words in the
1093                    boot file starting at byte offset 64. All linear block
1094                    addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1095
1096                    LBA of primary volume descriptor should already be set to 16. 
1097                  */
1098
1099                 isolinux_bin = (unsigned char *)data[ndata].data;
1100
1101                 /* Get LBA address of bootfile */
1102                 file_lba = get_file_lba(opt.loadfile);
1103
1104                 if (file_lba == 0) {
1105                     error("Failed to find LBA offset of the boot file\n");
1106                     goto bail;
1107                 }
1108                 /* Set it */
1109                 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1110
1111                 /* Set boot file length */
1112                 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1113
1114                 /* Calculate checksum */
1115                 checksum = (uint32_t *) & isolinux_bin[20];
1116                 chkhead = (uint32_t *) & isolinux_bin[64];
1117                 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1118                 *checksum = 0;
1119                 while (chkhead < chktail)
1120                     *checksum += *chkhead++;
1121
1122                 /*
1123                  * Deal with possible fractional dword at the end;
1124                  * this *should* never happen...
1125                  */
1126                 if (data[ndata].size & 3) {
1127                     uint32_t xword = 0;
1128                     memcpy(&xword, chkhead, data[ndata].size & 3);
1129                     *checksum += xword;
1130                 }
1131             } else {
1132                 error
1133                     ("The isolinux= option is only valid when run from ISOLINUX\n");
1134                 goto bail;
1135             }
1136         }
1137
1138         if (opt.grub) {
1139             /* Layout of stage2 file (from byte 0x0 to 0x270) */
1140             struct grub_stage2_patch_area {
1141                 /* 0x0 to 0x205 */
1142                 char unknown[0x206];
1143                 /* 0x206: compatibility version number major */
1144                 uint8_t compat_version_major;
1145                 /* 0x207: compatibility version number minor */
1146                 uint8_t compat_version_minor;
1147
1148                 /* 0x208: install_partition variable */
1149                 struct {
1150                     /* 0x208: sub-partition in sub-partition part2 */
1151                     uint8_t part3;
1152                     /* 0x209: sub-partition in top-level partition */
1153                     uint8_t part2;
1154                     /* 0x20a: top-level partiton number */
1155                     uint8_t part1;
1156                     /* 0x20b: BIOS drive number (must be 0) */
1157                     uint8_t drive;
1158                 } __attribute__ ((packed)) install_partition;
1159
1160                 /* 0x20c: deprecated (historical reason only) */
1161                 uint32_t saved_entryno;
1162                 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1163                 uint8_t stage2_id;
1164                 /* 0x211: force LBA */
1165                 uint8_t force_lba;
1166                 /* 0x212: version string (will probably be 0.97) */
1167                 char version_string[5];
1168                 /* 0x217: config filename */
1169                 char config_file[89];
1170                 /* 0x270: start of code (after jump from 0x200) */
1171                 char codestart[1];
1172             } __attribute__ ((packed)) *stage2;
1173
1174             if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1175                 error
1176                     ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1177                 goto bail;
1178             }
1179
1180             stage2 = data[ndata].data;
1181
1182             /*
1183              * Check the compatibility version number to see if we loaded a real
1184              * stage2 file or a stage2 file that we support.
1185              */
1186             if (stage2->compat_version_major != 3
1187                 || stage2->compat_version_minor != 2) {
1188                 error
1189                     ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1190                 goto bail;
1191             }
1192
1193             /* jump 0x200 bytes into the loadfile */
1194             regs.ip = 0x200;
1195
1196             /*
1197              * GRUB Legacy wants the partition number in the install_partition
1198              * variable, located at offset 0x208 of stage2.
1199              * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1200              *
1201              * It looks very similar to the "boot information format" of the
1202              * Multiboot specification:
1203              *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1204              *
1205              *   0x208 = part3: sub-partition in sub-partition part2
1206              *   0x209 = part2: sub-partition in top-level partition
1207              *   0x20a = part1: top-level partition number
1208              *   0x20b = drive: BIOS drive number (must be 0)
1209              *
1210              * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1211              * another location.
1212              *
1213              * Partition numbers always start from zero.
1214              * Unused partition bytes must be set to 0xFF. 
1215              *
1216              * We only care about top-level partition, so we only need to change
1217              * "part1" to the appropriate value:
1218              *   -1:   whole drive (default) (-1 = 0xFF)
1219              *   0-3:  primary partitions
1220              *   4-*:  logical partitions
1221              */
1222             stage2->install_partition.part1 = whichpart - 1;
1223
1224             /*
1225              * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1226              * config filename. The filename passed via grubcfg= will overwrite
1227              * the default config filename "/boot/grub/menu.lst".
1228              */
1229             if (opt.grubcfg) {
1230                 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1231                     error
1232                         ("The config filename length can't exceed 88 characters.\n");
1233                     goto bail;
1234                 }
1235
1236                 strcpy((char *)stage2->config_file, opt.grubcfg);
1237             }
1238         }
1239
1240         ndata++;
1241     }
1242
1243     if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1244         /* Actually read the boot sector */
1245         if (!cur_part) {
1246             data[ndata].data = mbr;
1247         } else
1248             if (!
1249                 (data[ndata].data =
1250                  disk_read_sectors(&diskinfo, cur_part->lba_data, 1))) {
1251             error("Cannot read boot sector\n");
1252             goto bail;
1253         }
1254         data[ndata].size = SECTOR;
1255         data[ndata].base = load_base;
1256
1257         if (!opt.loadfile) {
1258             const struct disk_dos_mbr *br =
1259                 (const struct disk_dos_mbr *)((char *)data[ndata].data +
1260                                               data[ndata].size -
1261                                               sizeof(struct disk_dos_mbr));
1262             if (br->sig != disk_mbr_sig_magic) {
1263                 error
1264                     ("Boot sector signature not found (unbootable disk/partition?)\n");
1265                 goto bail;
1266             }
1267         }
1268         /*
1269          * To boot the Recovery Console of Windows NT/2K/XP we need to write
1270          * the string "cmdcons\0" to memory location 0000:7C03.
1271          * Memory location 0000:7C00 contains the bootsector of the partition.
1272          */
1273         if (cur_part && opt.cmldr) {
1274             memcpy((char *)data[ndata].data + 3, cmldr_signature,
1275                    sizeof cmldr_signature);
1276         }
1277
1278         /*
1279          * Modify the hidden sectors (partition offset) copy in memory;
1280          * this modifies the field used by FAT and NTFS filesystems, and
1281          * possibly other boot loaders which use the same format.
1282          */
1283         if (cur_part && opt.sethidden) {
1284             *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1285         }
1286
1287         ndata++;
1288     }
1289
1290     if (cur_part) {
1291         if (cur_part->next == next_gpt_part) {
1292             /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1293             struct disk_dos_part_entry *record;
1294             /* Look at the GPT partition */
1295             const struct disk_gpt_part_entry *gp =
1296                 (const struct disk_gpt_part_entry *)
1297                 (cur_part->block +
1298                  (cur_part->private.gpt.size * cur_part->private.gpt.index));
1299             /* Note the partition length */
1300             uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1301             /* The length of the hand-over */
1302             int synth_size =
1303                 sizeof(struct disk_dos_part_entry) + sizeof(uint32_t) +
1304                 cur_part->private.gpt.size;
1305             /* Will point to the partition record length in the hand-over */
1306             uint32_t *plen;
1307
1308             /* Allocate the hand-over record */
1309             record = malloc(synth_size);
1310             if (!record) {
1311                 error("Could not build GPT hand-over record!\n");
1312                 goto bail;
1313             }
1314             /* Synthesize the record */
1315             memset(record, 0, synth_size);
1316             record->active_flag = 0x80;
1317             record->ostype = 0xED;
1318             /* All bits set by default */
1319             record->start_lba = ~(uint32_t) 0;
1320             record->length = ~(uint32_t) 0;
1321             /* If these fit the precision, pass them on */
1322             if (cur_part->lba_data < record->start_lba)
1323                 record->start_lba = cur_part->lba_data;
1324             if (lba_count < record->length)
1325                 record->length = lba_count;
1326             /* Next comes the GPT partition record length */
1327             plen = (uint32_t *) (record + 1);
1328             plen[0] = cur_part->private.gpt.size;
1329             /* Next comes the GPT partition record copy */
1330             memcpy(plen + 1, gp, plen[0]);
1331             cur_part->record = record;
1332
1333             regs.eax.l = 0x54504721;    /* '!GPT' */
1334             data[ndata].base = 0x7be;
1335             data[ndata].size = synth_size;
1336             data[ndata].data = (void *)record;
1337             ndata++;
1338             regs.esi.w[0] = 0x7be;
1339
1340             dprintf("GPT handover:\n");
1341             disk_dos_part_dump(record);
1342 #ifdef DEBUG
1343             disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
1344 #endif
1345         } else if (cur_part->record) {
1346             /* MBR handover protocol */
1347             static struct disk_dos_part_entry handover_record;
1348
1349             handover_record = *cur_part->record;
1350             handover_record.start_lba = cur_part->lba_data;
1351
1352             data[ndata].base = 0x7be;
1353             data[ndata].size = sizeof handover_record;
1354             data[ndata].data = &handover_record;
1355             ndata++;
1356             regs.esi.w[0] = 0x7be;
1357
1358             dprintf("MBR handover:\n");
1359             disk_dos_part_dump(&handover_record);
1360         }
1361     }
1362
1363     do_boot(data, ndata, &regs);
1364
1365 bail:
1366     if (cur_part) {
1367         free(cur_part->block);
1368         free((void *)cur_part->record);
1369     }
1370     free(cur_part);
1371     free(mbr);
1372     return 255;
1373 }