1 /* ----------------------------------------------------------------------- *
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"]
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.
14 * ----------------------------------------------------------------------- */
19 * Chainload a hard disk (currently rather braindead.)
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]
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
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
38 * The mbr: syntax means search all the hard disks until one with a
39 * specific MBR serial number (bytes 440-443) is found.
41 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
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.
49 * loads the file <loader> **from the Syslinux filesystem**
50 * instead of loading the boot sector.
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.
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.
65 * equivalent to seg=0x2000 file=<loader> sethidden,
66 * used with WinNT's loaders
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
74 * equivalent to seg=0x60 file=<loader> sethidden,
75 * used with FreeDOS' kernel.sys.
79 * equivalent to seg=0x70 file=<loader> sethidden,
80 * used with DOS' io.sys.
83 * Similar to msdos=<loader> but prepares the special options
84 * for the Dell Real Mode Kernel.
87 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
88 * used with GRUB Legacy stage2 files.
91 * set an alternative config filename in stage2 of Grub Legacy,
92 * only applicable in combination with "grub=<loader>".
95 * pass the partition number to GRUB4DOS,
96 * used with GRUB4DOS' grldr.
99 * if the disk is not fd0/hd0, install a BIOS stub which swaps
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.
108 * update the "hidden sectors" (partition offset) field in a
109 * FAT/NTFS boot sector.
112 * keep the PXE and UNDI stacks in memory (PXELINUX only).
115 * loads ReactOS' FreeLdr.sys to 0:8000 and jumps to the PE entry-point
127 #include <syslinux/loadfile.h>
128 #include <syslinux/bootrm.h>
129 #include <syslinux/config.h>
130 #include <syslinux/video.h>
132 #define SECTOR 512 /* bytes/sector */
134 static struct options {
135 const char *loadfile;
157 static inline void error(const char *msg)
163 * Call int 13h, but with retry on failure. Especially floppies need this.
165 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
167 int retry = 6; /* Number of retries */
174 __intcall(0x13, inreg, outreg);
175 if (!(outreg->eflags.l & EFLAGS_CF))
176 return 0; /* CF=0, OK */
179 return -1; /* Error */
183 * Query disk parameters and EBIOS availability for a particular disk.
187 int ebios; /* EBIOS supported on this disk */
188 int cbios; /* CHS geometry is valid */
193 static int get_disk_params(int disk)
195 static com32sys_t getparm, parm, getebios, ebios;
197 disk_info.disk = disk;
198 disk_info.ebios = disk_info.cbios = 0;
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 */
206 __intcall(0x13, &getebios, &ebios);
208 if (!(ebios.eflags.l & EFLAGS_CF) &&
209 ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
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;
219 __intcall(0x13, &getparm, &parm);
221 if (parm.eflags.l & EFLAGS_CF)
222 return disk_info.ebios ? 0 : -1;
224 disk_info.head = parm.edx.b[1] + 1;
225 disk_info.sect = parm.ecx.b[0] & 0x3f;
226 if (disk_info.sect == 0) {
229 disk_info.cbios = 1; /* Valid geometry */
236 * Get a disk block and return a malloc'd buffer.
237 * Uses the disk number and information from disk_info.
247 /* Read count sectors from drive, starting at lba. Return a new buffer */
248 static void *read_sectors(uint64_t lba, uint8_t count)
251 struct ebios_dapa *dapa = __com32.cs_bounce;
252 void *buf = (char *)__com32.cs_bounce + SECTOR;
259 memset(&inreg, 0, sizeof inreg);
261 if (disk_info.ebios) {
262 dapa->len = sizeof(*dapa);
264 dapa->off = OFFS(buf);
265 dapa->seg = SEG(buf);
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 */
273 unsigned int c, h, s, t;
275 if (!disk_info.cbios) {
276 /* We failed to get the geometry */
279 return NULL; /* Can only read MBR */
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;
289 if (s >= 63 || h >= 256 || c >= 1024)
292 inreg.eax.b[0] = count;
293 inreg.eax.b[1] = 0x02; /* Read */
295 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1);
297 inreg.edx.b[0] = disk_info.disk;
298 inreg.ebx.w[0] = OFFS(buf);
302 if (int13_retry(&inreg, NULL))
305 data = malloc(count * SECTOR);
307 memcpy(data, buf, count * SECTOR);
311 static int write_sector(unsigned int lba, const void *data)
314 struct ebios_dapa *dapa = __com32.cs_bounce;
315 void *buf = (char *)__com32.cs_bounce + SECTOR;
317 memcpy(buf, data, SECTOR);
318 memset(&inreg, 0, sizeof inreg);
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);
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 */
332 unsigned int c, h, s, t;
334 if (!disk_info.cbios) {
335 /* We failed to get the geometry */
338 return -1; /* Can only write MBR */
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;
348 if (s >= 63 || h >= 256 || c >= 1024)
351 inreg.eax.w[0] = 0x0301; /* Write one sector */
353 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1);
355 inreg.edx.b[0] = disk_info.disk;
356 inreg.ebx.w[0] = OFFS(buf);
360 if (int13_retry(&inreg, NULL))
366 static int write_verify_sector(unsigned int lba, const void *buf)
371 rv = write_sector(lba, buf);
373 return rv; /* Write failure */
374 rb = read_sectors(lba, 1);
376 return -1; /* Readback failure */
377 rv = memcmp(buf, rb, SECTOR);
383 * CHS (cylinder, head, sector) value extraction macros.
384 * Taken from WinVBlock. Does not expand to an lvalue
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];
393 /* A DOS partition table entry */
395 uint8_t active_flag; /* 0x80 if "active" */
401 } __attribute__ ((packed));
403 static void mbr_part_dump(const struct part_entry *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",
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),
427 chs_cylinder(part->end),
428 chs_cylinder(part->end),
431 chs_sector(part->end),
432 chs_sector(part->end),
433 part->start_lba, part->start_lba, part->length, part->length);
441 struct part_entry table[4];
443 } __attribute__ ((packed));
444 static const uint16_t mbr_sig_magic = 0xAA55;
446 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
447 static int find_disk(uint32_t mbr_sig)
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);
466 /* Forward declaration */
467 struct disk_part_iter;
469 /* Partition-/scheme-specific routine returning the next partition */
470 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
473 /* Contains details for a partition under examination */
474 struct disk_part_iter {
475 /* The block holding the table we are part of */
477 /* The LBA for the beginning of data */
479 /* The partition number, as determined by our heuristic */
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 */
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 */
500 /* Real (not effective) index in the partition table */
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 */
508 /* Partition record size */
514 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
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 };
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;
532 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
535 if (part->private.ebr.parent_index == 4)
536 /* No extended partitions found */
538 part->private.ebr.lba_extended =
539 parent_table[part->private.ebr.parent_index].start_lba;
543 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
545 part->block = read_sectors(ebr_lba, 1);
547 error("Could not load EBR!\n");
550 ebr_table = ((const struct mbr *)part->block)->table;
551 dprintf("next_ebr_part:\n");
552 mbr_part_dump(ebr_table);
555 * Sanity check entry: must not extend outside the
556 * extended partition. This is necessary since some OSes
557 * put crap in some entries.
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;
565 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
566 dprintf("Insane logical partition!\n");
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);
575 part->record = ebr_table;
585 free(part->private.ebr.parent->block);
586 free(part->private.ebr.parent);
592 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
594 struct disk_part_iter *ebr_part;
595 /* Look at the partition table */
596 struct part_entry *table = ((struct mbr *)part->block)->table;
598 /* Look for data partitions */
599 while (++part->private.mbr_index < 4) {
600 uint8_t type = table[part->private.mbr_index].ostype;
602 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
603 /* Skip empty or extended partitions */
605 if (!table[part->private.mbr_index].length)
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));
615 error("Could not allocate extended partition iterator!\n");
618 /* Setup EBR iterator parameters */
619 ebr_part->block = NULL;
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);
629 dprintf("next_mbr_part:\n");
630 mbr_part_dump(table + part->private.mbr_index);
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;
650 * Be careful with endianness, you must adjust it yourself
651 * iff you are directly using the fourth data chunk
658 } __attribute__ ((packed));
661 * This walk-map effectively reverses the little-endian
662 * portions of the GUID in the output text
664 static const char guid_le_walk_map[] = {
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
680 static void guid_to_str(char *buf, const struct guid *id)
683 const char *walker = (const char *)id;
685 while (i < sizeof(guid_le_walk_map)) {
686 walker += guid_le_walk_map[i];
687 if (!guid_le_walk_map[i])
690 *buf = ((*walker & 0xF0) >> 4) + '0';
692 *buf += 'A' - '9' - 1;
694 *buf = (*walker & 0x0F) + '0';
696 *buf += 'A' - '9' - 1;
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
712 static int str_to_guid(const char *buf, struct guid *id)
714 char guid_seq[sizeof(struct guid) * 2];
716 char *walker = (char *)id;
718 while (*buf && i < sizeof(guid_seq)) {
720 /* Skip these three characters */
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;
735 error("Illegal character in GUID!\n");
742 /* Check for insufficient valid characters */
743 if (i < sizeof(guid_seq)) {
744 error("Too few GUID characters!\n");
749 while (i < sizeof(guid_le_walk_map)) {
750 if (!guid_le_walk_map[i])
752 walker += guid_le_walk_map[i];
762 /* A GPT partition */
770 } __attribute__ ((packed));
772 static void gpt_part_dump(const struct gpt_part *gpt_part)
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]);
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);
804 } fields __attribute__ ((packed));
807 } rev __attribute__ ((packed));
813 uint64_t lba_first_usable;
814 uint64_t lba_last_usable;
815 struct guid disk_guid;
819 uint32_t table_chksum;
821 } __attribute__ ((packed));
822 static const char gpt_sig_magic[] = "EFI PART";
825 static void gpt_dump(const struct gpt *gpt)
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",
844 gpt->rev.fields.major,
845 gpt->rev.fields.minor,
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);
859 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
861 const struct gpt_part *gpt_part = NULL;
863 while (++part->private.gpt.index < part->private.gpt.parts) {
865 (const struct gpt_part *)(part->block +
866 (part->private.gpt.index *
867 part->private.gpt.size));
868 if (!gpt_part->lba_first)
872 /* Were we the last partition? */
873 if (part->private.gpt.index == part->private.gpt.parts) {
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);
883 /* In a GPT scheme, we re-use the iterator */
893 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
895 const struct gpt *gpt_candidate;
898 * Ignore any passed partition iterator. The caller should
899 * have passed NULL. Allocate a new partition iterator
901 part = malloc(sizeof(*part));
903 error("Count not allocate partition iterator!\n");
907 part->block = read_sectors(0, 2);
909 error("Could not read two sectors!\n");
912 /* Check for an MBR */
913 if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
914 error("No MBR magic!\n");
917 /* Establish a pseudo-partition for the MBR (index 0) */
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 */
928 /* It looks like one */
929 /* TODO: Check checksum. Possibly try alternative GPT */
931 puts("Looks like a GPT disk.");
932 gpt_dump(gpt_candidate);
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 */
945 read_sectors(lba_table,
946 ((part->private.gpt.size * part->private.gpt.parts) +
947 SECTOR - 1) / SECTOR);
949 error("Could not read GPT partition list!\n");
953 /* Return the pseudo-partition's next partition, which is real */
954 return part->next(part);
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.
978 static int find_by_guid(const struct guid *gpt_guid,
979 struct disk_part_iter **boot_part)
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))) {
998 is_me = !memcmp(&header->disk_guid, gpt_guid, sizeof(*gpt_guid));
1001 /* Check for a matching partition */
1002 boot_part[0] = get_first_partition(NULL);
1003 while (boot_part[0]) {
1005 !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
1009 boot_part[0] = boot_part[0]->next(boot_part[0]);
1012 boot_part[0] = NULL;
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.
1026 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
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]);
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;
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))
1056 gpt_label_scanner++;
1058 if ((is_me = !strcmp(label, gpt_label)))
1060 boot_part[0] = boot_part[0]->next(boot_part[0]);
1069 static void do_boot(struct data_area *data, int ndata,
1070 struct syslinux_rm_regs *regs)
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;
1077 uint8_t driveno = regs->edx.b[0];
1078 uint8_t swapdrive = driveno & 0x80;
1081 mmap = syslinux_memory_map();
1084 error("Cannot read system memory map\n");
1089 for (i = 0; i < ndata; i++) {
1090 if (data[i].base + data[i].size > endimage)
1091 endimage = data[i].base + data[i].size;
1093 if (endimage > dosmem)
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))
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: ... */
1136 static uint8_t swapstub[1024];
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;
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];
1157 /* Mapping table; start out with identity mapping everything */
1158 for (i = 0; i < 256; i++)
1161 /* And the actual swap */
1162 p[driveno] = swapdrive;
1163 p[swapdrive] = driveno;
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;
1172 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
1176 endimage += sizeof swapstub;
1179 /* Tell the shuffler not to muck with this area... */
1180 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
1182 /* Force text mode */
1183 syslinux_force_text_mode();
1185 fputs("Booting...\n", stdout);
1186 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
1187 error("Chainboot failed!\n");
1191 error("Loader file too large\n");
1195 error("Out of memory\n");
1199 static int hide_unhide(struct mbr *mbr, int part)
1202 struct part_entry *pt;
1203 const uint16_t mask =
1204 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1209 bool write_back = false;
1211 for (i = 1; i <= 4; i++) {
1212 pt = mbr->table + i - 1;
1214 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1215 /* It's a hideable partition type */
1217 t &= ~0x10; /* unhide */
1219 t |= 0x10; /* hide */
1221 if (t != pt->ostype) {
1228 return write_verify_sector(0, mbr);
1233 static uint32_t get_file_lba(const char *filename)
1238 /* Start with clean registers */
1239 memset(&inregs, 0, sizeof(com32sys_t));
1241 /* Put the filename in the bounce buffer */
1242 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1244 /* Call comapi_open() which returns a structure pointer in SI
1245 * to a structure whose first member happens to be the LBA.
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);
1252 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1253 return 0; /* Filename not found */
1256 /* Since the first member is the LBA, we simply cast */
1257 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1259 /* Clean the registers for the next call */
1260 memset(&inregs, 0, sizeof(com32sys_t));
1262 /* Put the filename in the bounce buffer */
1263 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
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);
1274 static void usage(void)
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";
1307 int main(int argc, char *argv[])
1309 struct mbr *mbr = NULL;
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 */
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];
1324 static const char cmldr_signature[8] = "cmdcons";
1326 openconsole(&dev_null_r, &dev_stdcon_w);
1331 /* Prepare the register set */
1332 memset(®s, 0, sizeof regs);
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)) {
1343 bool add_entry = true;
1344 char *ep, *p = argv[i] + 4;
1346 v = strtoul(p, &ep, 0);
1347 if (ep == p || v < 0x50 || v > 0x9f000) {
1348 error("seg: Invalid segment\n");
1352 opt.offs = opt.entry = 0;
1355 v = strtoul(p, &ep, 0);
1357 error("seg: Invalid offset\n");
1362 if (*ep == '@' || *ep == '+') {
1363 add_entry = (*ep == '+');
1365 v = strtoul(p, &ep, 0);
1367 error("seg: Invalid entry point\n");
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;
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 */
1399 /* TODO: Properly parse the PE. Right now, this is hard-coded */
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;
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;
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;
1423 } else if (!strcmp(argv[i], "swap")) {
1425 } else if (!strcmp(argv[i], "noswap")) {
1427 } else if (!strcmp(argv[i], "hide")) {
1429 } else if (!strcmp(argv[i], "nohide")) {
1431 } else if (!strcmp(argv[i], "keeppxe")) {
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, ',');
1455 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1456 && argv[i + 1][0] <= '9') {
1457 partition = argv[++i];
1465 if (opt.grubcfg && !opt.grub) {
1466 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
1471 * Set up initial register values
1473 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1474 regs.ip = opt.entry;
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).
1481 if (opt.seg == 0 && opt.offs == 0x7c00)
1482 regs.esp.l = 0x7c00;
1486 if (!strncmp(drivename, "mbr", 3)) {
1487 drive = find_disk(strtoul(drivename + 4, NULL, 0));
1489 error("Unable to find requested MBR signature\n");
1492 } else if (!strncmp(drivename, "guid", 4) || !strncmp(drivename, "uuid", 4)) {
1493 if (str_to_guid(drivename + 5, &gpt_guid))
1495 drive = find_by_guid(&gpt_guid, &cur_part);
1497 error("Unable to find requested GPT disk/partition\n");
1500 } else if (!strncmp(drivename, "label", 5)) {
1501 if (!drivename[6]) {
1502 error("No label specified.\n");
1505 drive = find_by_label(drivename + 6, &cur_part);
1507 error("Unable to find requested partition by label\n");
1510 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1511 drivename[1] == 'd') {
1512 hd = drivename[0] == 'h';
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;
1518 sdi = syslinux_derivative_info();
1519 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1520 drive = 0x80; /* Boot drive not available */
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;
1530 error("Unparsable drive specification\n");
1534 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1535 regs.ebx.b[0] = regs.edx.b[0] = drive;
1537 /* Get the disk geometry and disk access setup */
1538 if (get_disk_params(drive)) {
1539 error("Cannot get disk parameters\n");
1544 if (!(mbr = read_sectors(0, 1))) {
1545 error("Cannot read Master Boot Record or sector 0\n");
1550 whichpart = strtoul(partition, NULL, 0);
1551 /* "guid:" or "label:" might have specified a partition */
1553 whichpart = cur_part->index;
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);
1560 if ((cur_part->index == whichpart)
1561 || (cur_part->lba_data == fs_lba))
1562 /* Found the partition to boot */
1564 cur_part = cur_part->next(cur_part);
1567 error("Requested partition not found!\n");
1570 whichpart = cur_part->index;
1573 if (!(drive & 0x80) && whichpart) {
1574 error("Warning: Partitions of floppy devices may not work\n");
1578 * GRLDR of GRUB4DOS wants the partition number in DH:
1579 * -1: whole drive (default)
1580 * 0-3: primary partitions
1581 * 4-*: logical partitions
1584 regs.edx.b[1] = whichpart - 1;
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");
1593 /* Do the actual chainloading */
1594 load_base = (opt.seg << 4) + opt.offs;
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");
1602 data[ndata].base = load_base;
1603 load_base = 0x7c00; /* If we also load a boot sector */
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) */
1610 const union syslinux_derivative_info *sdi;
1611 sdi = syslinux_derivative_info();
1613 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1614 /* Boot info table info (integers in little endian format)
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
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).
1627 LBA of primary volume descriptor should already be set to 16.
1630 isolinux_bin = (unsigned char *)data[ndata].data;
1632 /* Get LBA address of bootfile */
1633 file_lba = get_file_lba(opt.loadfile);
1635 if (file_lba == 0) {
1636 error("Failed to find LBA offset of the boot file\n");
1640 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1642 /* Set boot file length */
1643 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
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];
1650 while (chkhead < chktail)
1651 *checksum += *chkhead++;
1654 * Deal with possible fractional dword at the end;
1655 * this *should* never happen...
1657 if (data[ndata].size & 3) {
1659 memcpy(&xword, chkhead, data[ndata].size & 3);
1664 ("The isolinux= option is only valid when run from ISOLINUX\n");
1670 /* Layout of stage2 file (from byte 0x0 to 0x270) */
1671 struct grub_stage2_patch_area {
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;
1679 /* 0x208: install_partition variable */
1681 /* 0x208: sub-partition in sub-partition part2 */
1683 /* 0x209: sub-partition in top-level partition */
1685 /* 0x20a: top-level partiton number */
1687 /* 0x20b: BIOS drive number (must be 0) */
1689 } __attribute__ ((packed)) install_partition;
1691 /* 0x20c: deprecated (historical reason only) */
1692 uint32_t saved_entryno;
1693 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1695 /* 0x211: 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) */
1703 } __attribute__ ((packed)) * stage2;
1705 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1707 ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1711 stage2 = data[ndata].data;
1714 * Check the compatibility version number to see if we loaded a real
1715 * stage2 file or a stage2 file that we support.
1717 if (stage2->compat_version_major != 3
1718 || stage2->compat_version_minor != 2) {
1720 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1724 /* jump 0x200 bytes into the loadfile */
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.
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
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)
1741 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1744 * Partition numbers always start from zero.
1745 * Unused partition bytes must be set to 0xFF.
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
1753 stage2->install_partition.part1 = whichpart - 1;
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".
1761 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1763 ("The config filename length can't exceed 88 characters.\n");
1767 strcpy((char *)stage2->config_file, opt.grubcfg);
1772 /* DRMK entry is different than MS-DOS/PC-DOS */
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.
1777 int tsize = (data[ndata].size + 19) & 0xfffffff0;
1778 const union syslinux_derivative_info *sdi;
1780 sdi = syslinux_derivative_info();
1781 /* We should lookup the Syslinux partition offset and use it */
1782 fs_lba = *sdi->disk.partoffset;
1784 * fs_lba should be verified against the disk as some DRMK
1785 * variants will check and fail if it does not match
1787 dprintf(" fs_lba offset is %"PRIu64"\n", fs_lba);
1788 /* DRMK only uses a DWORD */
1789 if (fs_lba > 0xffffffff) {
1791 ("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
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 */
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);
1809 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1810 /* Actually read the boot sector */
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");
1817 data[ndata].size = SECTOR;
1818 data[ndata].base = load_base;
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) {
1826 ("Boot sector signature not found (unbootable disk/partition?)\n");
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.
1835 if (cur_part && opt.cmldr) {
1836 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1837 sizeof cmldr_signature);
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.
1845 if (cur_part && opt.sethidden) {
1846 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
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 *)
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 */
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 */
1869 /* Allocate the hand-over record */
1870 record = malloc(synth_size);
1872 error("Could not build GPT hand-over record!\n");
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;
1894 regs.eax.l = 0x54504721; /* '!GPT' */
1895 data[ndata].base = 0x7be;
1896 data[ndata].size = synth_size;
1897 data[ndata].data = (void *)record;
1899 regs.esi.w[0] = 0x7be;
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;
1908 handover_record = *cur_part->record;
1909 handover_record.start_lba = cur_part->lba_data;
1911 data[ndata].base = 0x7be;
1912 data[ndata].size = sizeof handover_record;
1913 data[ndata].data = &handover_record;
1915 regs.esi.w[0] = 0x7be;
1917 dprintf("MBR handover:\n");
1918 mbr_part_dump(&handover_record);
1922 do_boot(data, ndata, ®s);
1926 free(cur_part->block);
1927 free((void *)cur_part->record);