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