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