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