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