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.
53 * loads at and jumps to <seg>:0000 instead of 0000:7C00.
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.
62 * equivalent to seg=0x2000 file=<loader> sethidden,
63 * used with WinNT's loaders
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
71 * equivalent to seg=0x60 file=<loader> sethidden,
72 * used with FreeDOS' kernel.sys.
76 * equivalent to seg=0x70 file=<loader> sethidden,
77 * used with DOS' io.sys.
80 * Similar to msdos=<loader> but prepares the special options
81 * for the Dell Real Mode Kernel.
84 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
85 * used with GRUB Legacy stage2 files.
88 * set an alternative config filename in stage2 of Grub Legacy,
89 * only applicable in combination with "grub=<loader>".
92 * pass the partition number to GRUB4DOS,
93 * used with GRUB4DOS' grldr.
96 * if the disk is not fd0/hd0, install a BIOS stub which swaps
100 * change type of primary partitions with IDs 01, 04, 06, 07,
101 * 0b, 0c, or 0e to 1x, except for the selected partition, which
102 * is converted the other way.
105 * update the "hidden sectors" (partition offset) field in a
106 * FAT/NTFS boot sector.
109 * keep the PXE and UNDI stacks in memory (PXELINUX only).
121 #include <syslinux/loadfile.h>
122 #include <syslinux/bootrm.h>
123 #include <syslinux/config.h>
124 #include <syslinux/video.h>
126 #define SECTOR 512 /* bytes/sector */
128 static struct options {
129 const char *loadfile;
149 static inline void error(const char *msg)
155 * Call int 13h, but with retry on failure. Especially floppies need this.
157 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
159 int retry = 6; /* Number of retries */
166 __intcall(0x13, inreg, outreg);
167 if (!(outreg->eflags.l & EFLAGS_CF))
168 return 0; /* CF=0, OK */
171 return -1; /* Error */
175 * Query disk parameters and EBIOS availability for a particular disk.
179 int ebios; /* EBIOS supported on this disk */
180 int cbios; /* CHS geometry is valid */
185 static int get_disk_params(int disk)
187 static com32sys_t getparm, parm, getebios, ebios;
189 disk_info.disk = disk;
190 disk_info.ebios = disk_info.cbios = 0;
192 /* Get EBIOS support */
193 getebios.eax.w[0] = 0x4100;
194 getebios.ebx.w[0] = 0x55aa;
195 getebios.edx.b[0] = disk;
196 getebios.eflags.b[0] = 0x3; /* CF set */
198 __intcall(0x13, &getebios, &ebios);
200 if (!(ebios.eflags.l & EFLAGS_CF) &&
201 ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
205 /* Get disk parameters -- really only useful for
206 hard disks, but if we have a partitioned floppy
207 it's actually our best chance... */
208 getparm.eax.b[1] = 0x08;
209 getparm.edx.b[0] = disk;
211 __intcall(0x13, &getparm, &parm);
213 if (parm.eflags.l & EFLAGS_CF)
214 return disk_info.ebios ? 0 : -1;
216 disk_info.head = parm.edx.b[1] + 1;
217 disk_info.sect = parm.ecx.b[0] & 0x3f;
218 if (disk_info.sect == 0) {
221 disk_info.cbios = 1; /* Valid geometry */
228 * Get a disk block and return a malloc'd buffer.
229 * Uses the disk number and information from disk_info.
239 /* Read count sectors from drive, starting at lba. Return a new buffer */
240 static void *read_sectors(uint64_t lba, uint8_t count)
243 struct ebios_dapa *dapa = __com32.cs_bounce;
244 void *buf = (char *)__com32.cs_bounce + SECTOR;
251 memset(&inreg, 0, sizeof inreg);
253 if (disk_info.ebios) {
254 dapa->len = sizeof(*dapa);
256 dapa->off = OFFS(buf);
257 dapa->seg = SEG(buf);
260 inreg.esi.w[0] = OFFS(dapa);
261 inreg.ds = SEG(dapa);
262 inreg.edx.b[0] = disk_info.disk;
263 inreg.eax.b[1] = 0x42; /* Extended read */
265 unsigned int c, h, s, t;
267 if (!disk_info.cbios) {
268 /* We failed to get the geometry */
271 return NULL; /* Can only read MBR */
275 s = lba % disk_info.sect;
276 t = lba / disk_info.sect; /* Track = head*cyl */
277 h = t % disk_info.head;
278 c = t / disk_info.head;
281 if (s >= 63 || h >= 256 || c >= 1024)
284 inreg.eax.b[0] = count;
285 inreg.eax.b[1] = 0x02; /* Read */
287 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
289 inreg.edx.b[0] = disk_info.disk;
290 inreg.ebx.w[0] = OFFS(buf);
294 if (int13_retry(&inreg, NULL))
297 data = malloc(count * SECTOR);
299 memcpy(data, buf, count * SECTOR);
303 static int write_sector(unsigned int lba, const void *data)
306 struct ebios_dapa *dapa = __com32.cs_bounce;
307 void *buf = (char *)__com32.cs_bounce + SECTOR;
309 memcpy(buf, data, SECTOR);
310 memset(&inreg, 0, sizeof inreg);
312 if (disk_info.ebios) {
313 dapa->len = sizeof(*dapa);
314 dapa->count = 1; /* 1 sector */
315 dapa->off = OFFS(buf);
316 dapa->seg = SEG(buf);
319 inreg.esi.w[0] = OFFS(dapa);
320 inreg.ds = SEG(dapa);
321 inreg.edx.b[0] = disk_info.disk;
322 inreg.eax.w[0] = 0x4300; /* Extended write */
324 unsigned int c, h, s, t;
326 if (!disk_info.cbios) {
327 /* We failed to get the geometry */
330 return -1; /* Can only write MBR */
334 s = lba % disk_info.sect;
335 t = lba / disk_info.sect; /* Track = head*cyl */
336 h = t % disk_info.head;
337 c = t / disk_info.head;
340 if (s >= 63 || h >= 256 || c >= 1024)
343 inreg.eax.w[0] = 0x0301; /* Write one sector */
345 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
347 inreg.edx.b[0] = disk_info.disk;
348 inreg.ebx.w[0] = OFFS(buf);
352 if (int13_retry(&inreg, NULL))
358 static int write_verify_sector(unsigned int lba, const void *buf)
363 rv = write_sector(lba, buf);
365 return rv; /* Write failure */
366 rb = read_sectors(lba, 1);
368 return -1; /* Readback failure */
369 rv = memcmp(buf, rb, SECTOR);
375 * CHS (cylinder, head, sector) value extraction macros.
376 * Taken from WinVBlock. Does not expand to an lvalue
378 #define chs_head(chs) chs[0]
379 #define chs_sector(chs) (chs[1] & 0x3F)
380 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
381 #define chs_cyl_low(chs) ((uint16_t)chs[2])
382 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
383 typedef uint8_t chs[3];
385 /* A DOS partition table entry */
387 uint8_t active_flag; /* 0x80 if "active" */
393 } __attribute__ ((packed));
395 static void mbr_part_dump(const struct part_entry *part)
398 dprintf("Partition status _____ : 0x%.2x\n"
399 "Partition CHS start\n"
400 " Cylinder ___________ : 0x%.4x (%u)\n"
401 " Head _______________ : 0x%.2x (%u)\n"
402 " Sector _____________ : 0x%.2x (%u)\n"
403 "Partition type _______ : 0x%.2x\n"
404 "Partition CHS end\n"
405 " Cylinder ___________ : 0x%.4x (%u)\n"
406 " Head _______________ : 0x%.2x (%u)\n"
407 " Sector _____________ : 0x%.2x (%u)\n"
408 "Partition LBA start __ : 0x%.8x (%u)\n"
409 "Partition LBA count __ : 0x%.8x (%u)\n"
410 "-------------------------------\n",
412 chs_cylinder(part->start),
413 chs_cylinder(part->start),
414 chs_head(part->start),
415 chs_head(part->start),
416 chs_sector(part->start),
417 chs_sector(part->start),
419 chs_cylinder(part->end),
420 chs_cylinder(part->end),
423 chs_sector(part->end),
424 chs_sector(part->end),
436 struct part_entry table[4];
438 } __attribute__ ((packed));
439 static const uint16_t mbr_sig_magic = 0xAA55;
441 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
442 static int find_disk(uint32_t mbr_sig)
448 for (drive = 0x80; drive <= 0xff; drive++) {
449 if (get_disk_params(drive))
450 continue; /* Drive doesn't exist */
451 if (!(mbr = read_sectors(0, 1)))
452 continue; /* Cannot read sector */
453 is_me = (mbr->disk_sig == mbr_sig);
461 /* Forward declaration */
462 struct disk_part_iter;
464 /* Partition-/scheme-specific routine returning the next partition */
465 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
468 /* Contains details for a partition under examination */
469 struct disk_part_iter {
470 /* The block holding the table we are part of */
472 /* The LBA for the beginning of data */
474 /* The partition number, as determined by our heuristic */
476 /* The DOS partition record to pass, if applicable */
477 const struct part_entry *record;
478 /* Function returning the next available partition */
479 disk_part_iter_func next;
480 /* Partition-/scheme-specific details */
486 /* The first extended partition's start LBA */
487 uint64_t lba_extended;
488 /* Any applicable parent, or NULL */
489 struct disk_part_iter *parent;
490 /* The parent extended partition index */
495 /* Real (not effective) index in the partition table */
497 /* Current partition GUID */
498 const struct guid *part_guid;
499 /* Current partition label */
500 const char *part_label;
501 /* Count of entries in GPT */
503 /* Partition record size */
509 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
511 const struct part_entry *ebr_table;
512 const struct part_entry *parent_table =
513 ((const struct mbr *)part->private.ebr.parent->block)->table;
514 static const struct part_entry phony = {.start_lba = 0 };
517 /* Don't look for a "next EBR" the first time around */
518 if (part->private.ebr.parent_index >= 0)
519 /* Look at the linked list */
520 ebr_table = ((const struct mbr *)part->block)->table + 1;
521 /* Do we need to look for an extended partition? */
522 if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
523 /* Start looking for an extended partition in the MBR */
524 while (++part->private.ebr.parent_index < 4) {
525 uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
527 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
530 if (part->private.ebr.parent_index == 4)
531 /* No extended partitions found */
533 part->private.ebr.lba_extended =
534 parent_table[part->private.ebr.parent_index].start_lba;
538 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
540 part->block = read_sectors(ebr_lba, 1);
542 error("Could not load EBR!\n");
545 ebr_table = ((const struct mbr *)part->block)->table;
546 dprintf("next_ebr_part:\n");
547 mbr_part_dump(ebr_table);
550 * Sanity check entry: must not extend outside the
551 * extended partition. This is necessary since some OSes
552 * put crap in some entries.
555 const struct mbr *mbr =
556 (const struct mbr *)part->private.ebr.parent->block;
557 const struct part_entry *extended =
558 mbr->table + part->private.ebr.parent_index;
560 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
561 dprintf("Insane logical partition!\n");
566 part->lba_data = ebr_table[0].start_lba + ebr_lba;
567 dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
569 part->record = ebr_table;
579 free(part->private.ebr.parent->block);
580 free(part->private.ebr.parent);
586 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
588 struct disk_part_iter *ebr_part;
589 /* Look at the partition table */
590 struct part_entry *table = ((struct mbr *)part->block)->table;
592 /* Look for data partitions */
593 while (++part->private.mbr_index < 4) {
594 uint8_t type = table[part->private.mbr_index].ostype;
596 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
597 /* Skip empty or extended partitions */
599 if (!table[part->private.mbr_index].length)
604 /* If we're currently the last partition, it's time for EBR processing */
605 if (part->private.mbr_index == 4) {
606 /* Allocate another iterator for extended partitions */
607 ebr_part = malloc(sizeof(*ebr_part));
609 error("Could not allocate extended partition iterator!\n");
612 /* Setup EBR iterator parameters */
613 ebr_part->block = NULL;
615 ebr_part->record = NULL;
616 ebr_part->next = next_ebr_part;
617 ebr_part->private.ebr.parent = part;
618 /* Trigger an initial EBR load */
619 ebr_part->private.ebr.parent_index = -1;
620 /* The EBR iterator is responsible for freeing us */
621 return next_ebr_part(ebr_part);
623 dprintf("next_mbr_part:\n");
624 mbr_part_dump(table + part->private.mbr_index);
626 /* Update parameters to reflect this new partition. Re-use iterator */
627 part->lba_data = table[part->private.mbr_index].start_lba;
628 dprintf("Partition %d primary lba %u\n", part->private.mbr_index, part->lba_data);
629 part->index = part->private.mbr_index + 1;
630 part->record = table + part->private.mbr_index;
643 * Be careful with endianness, you must adjust it yourself
644 * iff you are directly using the fourth data chunk
651 } __attribute__ ((packed));
654 * This walk-map effectively reverses the little-endian
655 * portions of the GUID in the output text
657 static const char guid_le_walk_map[] = {
667 * Fill a buffer with a textual GUID representation.
668 * The buffer must be >= char[37] and will be populated
669 * with an ASCII NUL C string terminator.
670 * Example: 11111111-2222-3333-4444-444444444444
671 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
673 static void guid_to_str(char *buf, const struct guid *id)
676 const char *walker = (const char *)id;
678 while (i < sizeof(guid_le_walk_map)) {
679 walker += guid_le_walk_map[i];
680 if (!guid_le_walk_map[i])
683 *buf = ((*walker & 0xF0) >> 4) + '0';
685 *buf += 'A' - '9' - 1;
687 *buf = (*walker & 0x0F) + '0';
689 *buf += 'A' - '9' - 1;
699 * Create a GUID structure from a textual GUID representation.
700 * The input buffer must be >= 32 hexadecimal chars and be
701 * terminated with an ASCII NUL. Returns non-zero on failure.
702 * Example: 11111111-2222-3333-4444-444444444444
703 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
705 static int str_to_guid(const char *buf, struct guid *id)
707 char guid_seq[sizeof(struct guid) * 2];
709 char *walker = (char *)id;
711 while (*buf && i < sizeof(guid_seq)) {
713 /* Skip these three characters */
719 /* Copy something useful to the temp. sequence */
720 if ((*buf >= '0') && (*buf <= '9'))
721 guid_seq[i] = *buf - '0';
722 else if ((*buf >= 'A') && (*buf <= 'F'))
723 guid_seq[i] = *buf - 'A' + 10;
724 else if ((*buf >= 'a') && (*buf <= 'f'))
725 guid_seq[i] = *buf - 'a' + 10;
728 error("Illegal character in GUID!\n");
735 /* Check for insufficient valid characters */
736 if (i < sizeof(guid_seq)) {
737 error("Too few GUID characters!\n");
742 while (i < sizeof(guid_le_walk_map)) {
743 if (!guid_le_walk_map[i])
745 walker += guid_le_walk_map[i];
755 /* A GPT partition */
763 } __attribute__ ((packed));
765 static void gpt_part_dump(const struct gpt_part *gpt_part)
771 dprintf("----------------------------------\n"
772 "GPT part. LBA first __ : 0x%.16llx\n"
773 "GPT part. LBA last ___ : 0x%.16llx\n"
774 "GPT part. attribs ____ : 0x%.16llx\n"
775 "GPT part. name _______ : '",
776 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
777 for (i = 0; i < sizeof(gpt_part->name); i++) {
778 if (gpt_part->name[i])
779 dprintf("%c", gpt_part->name[i]);
782 guid_to_str(guid_text, &gpt_part->type);
783 dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
784 guid_to_str(guid_text, &gpt_part->uid);
785 dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
797 } fields __attribute__ ((packed));
800 } rev __attribute__ ((packed));
806 uint64_t lba_first_usable;
807 uint64_t lba_last_usable;
808 struct guid disk_guid;
812 uint32_t table_chksum;
814 } __attribute__ ((packed));
815 static const char gpt_sig_magic[] = "EFI PART";
818 static void gpt_dump(const struct gpt *gpt)
822 printf("GPT sig ______________ : '%8.8s'\n"
823 "GPT major revision ___ : 0x%.4x\n"
824 "GPT minor revision ___ : 0x%.4x\n"
825 "GPT header size ______ : 0x%.8x\n"
826 "GPT header checksum __ : 0x%.8x\n"
827 "GPT reserved _________ : '%4.4s'\n"
828 "GPT LBA current ______ : 0x%.16llx\n"
829 "GPT LBA alternative __ : 0x%.16llx\n"
830 "GPT LBA first usable _ : 0x%.16llx\n"
831 "GPT LBA last usable __ : 0x%.16llx\n"
832 "GPT LBA part. table __ : 0x%.16llx\n"
833 "GPT partition count __ : 0x%.8x\n"
834 "GPT partition size ___ : 0x%.8x\n"
835 "GPT part. table chksum : 0x%.8x\n",
837 gpt->rev.fields.major,
838 gpt->rev.fields.minor,
844 gpt->lba_first_usable,
845 gpt->lba_last_usable,
846 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
847 guid_to_str(guid_text, &gpt->disk_guid);
848 printf("GPT disk GUID ________ : {%s}\n", guid_text);
852 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
854 const struct gpt_part *gpt_part = NULL;
856 while (++part->private.gpt.index < part->private.gpt.parts) {
858 (const struct gpt_part *)(part->block +
859 (part->private.gpt.index *
860 part->private.gpt.size));
861 if (!gpt_part->lba_first)
865 /* Were we the last partition? */
866 if (part->private.gpt.index == part->private.gpt.parts) {
869 part->lba_data = gpt_part->lba_first;
870 part->private.gpt.part_guid = &gpt_part->uid;
871 part->private.gpt.part_label = gpt_part->name;
872 /* Update our index */
873 part->index = part->private.gpt.index + 1;
874 gpt_part_dump(gpt_part);
876 /* In a GPT scheme, we re-use the iterator */
886 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
888 const struct gpt *gpt_candidate;
891 * Ignore any passed partition iterator. The caller should
892 * have passed NULL. Allocate a new partition iterator
894 part = malloc(sizeof(*part));
896 error("Count not allocate partition iterator!\n");
900 part->block = read_sectors(0, 2);
902 error("Could not read two sectors!\n");
905 /* Check for an MBR */
906 if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
907 error("No MBR magic!\n");
910 /* Establish a pseudo-partition for the MBR (index 0) */
913 part->private.mbr_index = -1;
914 part->next = next_mbr_part;
915 /* Check for a GPT disk */
916 gpt_candidate = (const struct gpt *)(part->block + SECTOR);
917 if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
918 /* LBA for partition table */
921 /* It looks like one */
922 /* TODO: Check checksum. Possibly try alternative GPT */
924 puts("Looks like a GPT disk.");
925 gpt_dump(gpt_candidate);
927 /* TODO: Check table checksum (maybe) */
928 /* Note relevant GPT details */
929 part->next = next_gpt_part;
930 part->private.gpt.index = -1;
931 part->private.gpt.parts = gpt_candidate->part_count;
932 part->private.gpt.size = gpt_candidate->part_size;
933 lba_table = gpt_candidate->lba_table;
934 gpt_candidate = NULL;
935 /* Load the partition table */
938 read_sectors(lba_table,
939 ((part->private.gpt.size * part->private.gpt.parts) +
940 SECTOR - 1) / SECTOR);
942 error("Could not read GPT partition list!\n");
946 /* Return the pseudo-partition's next partition, which is real */
947 return part->next(part);
964 * Search for a specific drive/partition, based on the GPT GUID.
965 * We return the disk drive number if found, as well as populating the
966 * boot_part pointer with the matching partition, if applicable.
967 * If no matching partition is found or the GUID is a disk GUID,
968 * boot_part will be populated with NULL. If not matching disk is
969 * found, we return -1.
971 static int find_by_guid(const struct guid *gpt_guid,
972 struct disk_part_iter **boot_part)
978 for (drive = 0x80; drive <= 0xff; drive++) {
979 if (get_disk_params(drive))
980 continue; /* Drive doesn't exist */
981 if (!(header = read_sectors(1, 1)))
982 continue; /* Cannot read sector */
983 if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
991 is_me = !memcmp(&header->disk_guid, gpt_guid, sizeof(*gpt_guid));
994 /* Check for a matching partition */
995 boot_part[0] = get_first_partition(NULL);
996 while (boot_part[0]) {
998 !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
1002 boot_part[0] = boot_part[0]->next(boot_part[0]);
1005 boot_part[0] = NULL;
1013 * Search for a specific partition, based on the GPT label.
1014 * We return the disk drive number if found, as well as populating the
1015 * boot_part pointer with the matching partition, if applicable.
1016 * If no matching partition is found, boot_part will be populated with
1017 * NULL and we return -1.
1019 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
1024 for (drive = 0x80; drive <= 0xff; drive++) {
1025 if (get_disk_params(drive))
1026 continue; /* Drive doesn't exist */
1027 /* Check for a GPT disk */
1028 boot_part[0] = get_first_partition(NULL);
1029 if (!(boot_part[0]->next == next_gpt_part)) {
1030 /* Not a GPT disk */
1031 while (boot_part[0]) {
1032 /* Run through until the end */
1033 boot_part[0] = boot_part[0]->next(boot_part[0]);
1037 /* Check for a matching partition */
1038 while (boot_part[0]) {
1039 char gpt_label[sizeof(((struct gpt_part *) NULL)->name)];
1040 const char *gpt_label_scanner =
1041 boot_part[0]->private.gpt.part_label;
1044 /* Re-write the GPT partition label as ASCII */
1045 while (gpt_label_scanner <
1046 boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
1047 if ((gpt_label[j] = *gpt_label_scanner))
1049 gpt_label_scanner++;
1051 if ((is_me = !strcmp(label, gpt_label)))
1053 boot_part[0] = boot_part[0]->next(boot_part[0]);
1062 static void do_boot(struct data_area *data, int ndata,
1063 struct syslinux_rm_regs *regs)
1065 uint16_t *const bios_fbm = (uint16_t *) 0x413;
1066 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
1067 struct syslinux_memmap *mmap;
1068 struct syslinux_movelist *mlist = NULL;
1070 uint8_t driveno = regs->edx.b[0];
1071 uint8_t swapdrive = driveno & 0x80;
1074 mmap = syslinux_memory_map();
1077 error("Cannot read system memory map\n");
1082 for (i = 0; i < ndata; i++) {
1083 if (data[i].base + data[i].size > endimage)
1084 endimage = data[i].base + data[i].size;
1086 if (endimage > dosmem)
1089 for (i = 0; i < ndata; i++) {
1090 if (syslinux_add_movelist(&mlist, data[i].base,
1091 (addr_t) data[i].data, data[i].size))
1095 if (opt.swap && driveno != swapdrive) {
1096 static const uint8_t swapstub_master[] = {
1097 /* The actual swap code */
1098 0x53, /* 00: push bx */
1099 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
1100 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
1101 0x5b, /* 08: pop bx */
1102 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
1103 0x90, 0x90, /* 0E: nop; nop */
1104 /* Code to install this in the right location */
1105 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
1106 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
1107 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
1108 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
1109 0x4f, /* 1F: dec di */
1110 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
1111 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
1112 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
1113 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
1114 0x8e, 0xc7, /* 32: mov es,di */
1115 0x31, 0xff, /* 34: xor di,di */
1116 0xf3, 0x66, 0xa5, /* 36: rep movsd */
1117 0xbe, 0, 0, /* 39: mov si,0 */
1118 0xbf, 0, 0, /* 3C: mov di,0 */
1119 0x8e, 0xde, /* 3F: mov ds,si */
1120 0x8e, 0xc7, /* 41: mov es,di */
1121 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
1122 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
1123 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
1124 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
1125 /* pad out to segment boundary */
1126 0x90, 0x90, /* 5A: ... */
1127 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
1129 static uint8_t swapstub[1024];
1132 /* Note: we can't rely on either INT 13h nor the dosmem
1133 vector to be correct at this stage, so we have to use an
1134 installer stub to put things in the right place.
1135 Round the installer location to a 1K boundary so the only
1136 possible overlap is the identity mapping. */
1137 endimage = (endimage + 1023) & ~1023;
1139 /* Create swap stub */
1140 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
1141 *(uint16_t *) & swapstub[0x3a] = regs->ds;
1142 *(uint16_t *) & swapstub[0x3d] = regs->es;
1143 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
1144 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
1145 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
1146 *(uint16_t *) & swapstub[0x56] = regs->ip;
1147 *(uint16_t *) & swapstub[0x58] = regs->cs;
1148 p = &swapstub[sizeof swapstub_master];
1150 /* Mapping table; start out with identity mapping everything */
1151 for (i = 0; i < 256; i++)
1154 /* And the actual swap */
1155 p[driveno] = swapdrive;
1156 p[swapdrive] = driveno;
1158 /* Adjust registers */
1159 regs->ds = regs->cs = endimage >> 4;
1160 regs->es = regs->esi.l = 0;
1161 regs->ecx.l = sizeof swapstub >> 2;
1162 regs->ip = 0x10; /* Installer offset */
1163 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
1165 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
1169 endimage += sizeof swapstub;
1172 /* Tell the shuffler not to muck with this area... */
1173 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
1175 /* Force text mode */
1176 syslinux_force_text_mode();
1178 fputs("Booting...\n", stdout);
1179 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
1180 error("Chainboot failed!\n");
1184 error("Loader file too large\n");
1188 error("Out of memory\n");
1192 static int hide_unhide(struct mbr *mbr, int part)
1195 struct part_entry *pt;
1196 const uint16_t mask =
1197 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1202 bool write_back = false;
1204 for (i = 1; i <= 4; i++) {
1205 pt = mbr->table + i - 1;
1207 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1208 /* It's a hideable partition type */
1210 t &= ~0x10; /* unhide */
1212 t |= 0x10; /* hide */
1214 if (t != pt->ostype) {
1221 return write_verify_sector(0, mbr);
1226 static uint32_t get_file_lba(const char *filename)
1231 /* Start with clean registers */
1232 memset(&inregs, 0, sizeof(com32sys_t));
1234 /* Put the filename in the bounce buffer */
1235 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1237 /* Call comapi_open() which returns a structure pointer in SI
1238 * to a structure whose first member happens to be the LBA.
1240 inregs.eax.w[0] = 0x0006;
1241 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1242 inregs.es = SEG(__com32.cs_bounce);
1243 __com32.cs_intcall(0x22, &inregs, &inregs);
1245 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1246 return 0; /* Filename not found */
1249 /* Since the first member is the LBA, we simply cast */
1250 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1252 /* Clean the registers for the next call */
1253 memset(&inregs, 0, sizeof(com32sys_t));
1255 /* Put the filename in the bounce buffer */
1256 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1258 /* Call comapi_close() to free the structure */
1259 inregs.eax.w[0] = 0x0008;
1260 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1261 inregs.es = SEG(__com32.cs_bounce);
1262 __com32.cs_intcall(0x22, &inregs, &inregs);
1267 static void usage(void)
1269 static const char usage[] = "\
1270 Usage: chain.c32 [options]\n\
1271 chain.c32 hd<disk#> [<partition>] [options]\n\
1272 chain.c32 fd<disk#> [options]\n\
1273 chain.c32 mbr:<id> [<partition>] [options]\n\
1274 chain.c32 guid:<guid> [<partition>] [options]\n\
1275 chain.c32 label:<label> [<partition>] [options]\n\
1276 chain.c32 boot [<partition>] [options]\n\
1277 chain.c32 fs [options]\n\
1278 Options: file=<loader> Load and execute file, instead of boot sector\n\
1279 isolinux=<loader> Load another version of ISOLINUX\n\
1280 ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
1281 cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
1282 freedos=<loader> Load FreeDOS KERNEL.SYS\n\
1283 msdos=<loader> Load MS-DOS IO.SYS\n\
1284 pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
1285 drmk=<loader> Load DRMK DELLBIO.BIN\n\
1286 grub=<loader> Load GRUB Legacy stage2\n\
1287 grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
1288 grldr=<loader> Load GRUB4DOS grldr\n\
1289 seg=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\
1290 swap Swap drive numbers, if bootdisk is not fd0/hd0\n\
1291 hide Hide primary partitions, except selected partition\n\
1292 sethidden Set the FAT/NTFS hidden sectors field\n\
1293 keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
1294 See syslinux/com32/modules/chain.c for more information\n";
1298 int main(int argc, char *argv[])
1300 struct mbr *mbr = NULL;
1302 struct disk_part_iter *cur_part = NULL;
1303 struct syslinux_rm_regs regs;
1304 char *drivename, *partition;
1305 int hd, drive, whichpart = 0; /* MBR by default */
1307 uint64_t fs_lba = 0; /* Syslinux partition */
1308 uint32_t file_lba = 0;
1309 struct guid gpt_guid;
1310 unsigned char *isolinux_bin;
1311 uint32_t *checksum, *chkhead, *chktail;
1312 struct data_area data[3];
1315 static const char cmldr_signature[8] = "cmdcons";
1317 openconsole(&dev_null_r, &dev_stdcon_w);
1322 /* Prepare the register set */
1323 memset(®s, 0, sizeof regs);
1325 for (i = 1; i < argc; i++) {
1326 if (!strncmp(argv[i], "file=", 5)) {
1327 opt.loadfile = argv[i] + 5;
1328 } else if (!strncmp(argv[i], "seg=", 4)) {
1329 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1330 if (segval < 0x50 || segval > 0x9f000) {
1331 error("Invalid segment\n");
1335 } else if (!strncmp(argv[i], "isolinux=", 9)) {
1336 opt.loadfile = argv[i] + 9;
1337 opt.isolinux = true;
1338 } else if (!strncmp(argv[i], "ntldr=", 6)) {
1339 opt.seg = 0x2000; /* NTLDR wants this address */
1340 opt.loadfile = argv[i] + 6;
1341 opt.sethidden = true;
1342 } else if (!strncmp(argv[i], "cmldr=", 6)) {
1343 opt.seg = 0x2000; /* CMLDR wants this address */
1344 opt.loadfile = argv[i] + 6;
1346 opt.sethidden = true;
1347 } else if (!strncmp(argv[i], "freedos=", 8)) {
1348 opt.seg = 0x60; /* FREEDOS wants this address */
1349 opt.loadfile = argv[i] + 8;
1350 opt.sethidden = true;
1351 } else if (!strncmp(argv[i], "msdos=", 6) ||
1352 !strncmp(argv[i], "pcdos=", 6)) {
1353 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
1354 opt.loadfile = argv[i] + 6;
1355 opt.sethidden = true;
1356 } else if (!strncmp(argv[i], "drmk=", 5)) {
1357 opt.seg = 0x70; /* DRMK wants this address */
1358 opt.loadfile = argv[i] + 5;
1359 opt.sethidden = true;
1361 } else if (!strncmp(argv[i], "grub=", 5)) {
1362 opt.seg = 0x800; /* stage2 wants this address */
1363 opt.loadfile = argv[i] + 5;
1365 } else if (!strncmp(argv[i], "grubcfg=", 8)) {
1366 opt.grubcfg = argv[i] + 8;
1367 } else if (!strncmp(argv[i], "grldr=", 6)) {
1368 opt.loadfile = argv[i] + 6;
1370 } else if (!strcmp(argv[i], "swap")) {
1372 } else if (!strcmp(argv[i], "noswap")) {
1374 } else if (!strcmp(argv[i], "hide")) {
1376 } else if (!strcmp(argv[i], "nohide")) {
1378 } else if (!strcmp(argv[i], "keeppxe")) {
1380 } else if (!strcmp(argv[i], "sethidden")) {
1381 opt.sethidden = true;
1382 } else if (!strcmp(argv[i], "nosethidden")) {
1383 opt.sethidden = false;
1384 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1385 && argv[i][1] == 'd')
1386 || !strncmp(argv[i], "mbr:", 4)
1387 || !strncmp(argv[i], "mbr=", 4)
1388 || !strncmp(argv[i], "guid:", 5)
1389 || !strncmp(argv[i], "guid=", 5)
1390 || !strncmp(argv[i], "uuid:", 5)
1391 || !strncmp(argv[i], "uuid=", 5)
1392 || !strncmp(argv[i], "label:", 6)
1393 || !strncmp(argv[i], "label=", 6)
1394 || !strcmp(argv[i], "boot")
1395 || !strncmp(argv[i], "boot,", 5)
1396 || !strcmp(argv[i], "fs")) {
1397 drivename = argv[i];
1398 p = strchr(drivename, ',');
1402 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1403 && argv[i + 1][0] <= '9') {
1404 partition = argv[++i];
1412 if (opt.grubcfg && !opt.grub) {
1413 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
1418 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1420 regs.ip = regs.esp.l = 0x7c00;
1424 if (!strncmp(drivename, "mbr", 3)) {
1425 drive = find_disk(strtoul(drivename + 4, NULL, 0));
1427 error("Unable to find requested MBR signature\n");
1430 } else if (!strncmp(drivename, "guid", 4) ||
1431 !strncmp(drivename, "uuid", 4)) {
1432 if (str_to_guid(drivename + 5, &gpt_guid))
1434 drive = find_by_guid(&gpt_guid, &cur_part);
1436 error("Unable to find requested GPT disk/partition\n");
1439 } else if (!strncmp(drivename, "label", 5)) {
1440 if (!drivename[6]) {
1441 error("No label specified.\n");
1444 drive = find_by_label(drivename + 6, &cur_part);
1446 error("Unable to find requested partition by label\n");
1449 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1450 drivename[1] == 'd') {
1451 hd = drivename[0] == 'h';
1453 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1454 } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
1455 const union syslinux_derivative_info *sdi;
1457 sdi = syslinux_derivative_info();
1458 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1459 drive = 0x80; /* Boot drive not available */
1461 drive = sdi->disk.drive_number;
1462 if (!strcmp(drivename, "fs")
1463 && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
1464 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
1465 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
1466 /* We should lookup the Syslinux partition number and use it */
1467 fs_lba = *sdi->disk.partoffset;
1469 error("Unparsable drive specification\n");
1473 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1474 regs.ebx.b[0] = regs.edx.b[0] = drive;
1476 /* Get the disk geometry and disk access setup */
1477 if (get_disk_params(drive)) {
1478 error("Cannot get disk parameters\n");
1483 if (!(mbr = read_sectors(0, 1))) {
1484 error("Cannot read Master Boot Record or sector 0\n");
1489 whichpart = strtoul(partition, NULL, 0);
1490 /* "guid:" or "label:" might have specified a partition */
1492 whichpart = cur_part->index;
1494 /* Boot the MBR by default */
1495 if (!cur_part && (whichpart || fs_lba)) {
1496 /* Boot a partition, possibly the Syslinux partition itself */
1497 cur_part = get_first_partition(NULL);
1499 if ((cur_part->index == whichpart)
1500 || (cur_part->lba_data == fs_lba))
1501 /* Found the partition to boot */
1503 cur_part = cur_part->next(cur_part);
1506 error("Requested partition not found!\n");
1509 whichpart = cur_part->index;
1512 if (!(drive & 0x80) && whichpart) {
1513 error("Warning: Partitions of floppy devices may not work\n");
1517 * GRLDR of GRUB4DOS wants the partition number in DH:
1518 * -1: whole drive (default)
1519 * 0-3: primary partitions
1520 * 4-*: logical partitions
1523 regs.edx.b[1] = whichpart - 1;
1526 if (whichpart < 1 || whichpart > 4)
1527 error("WARNING: hide specified without a non-primary partition\n");
1528 if (hide_unhide(mbr, whichpart))
1529 error("WARNING: failed to write MBR for 'hide'\n");
1532 /* Do the actual chainloading */
1533 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1536 fputs("Loading the boot file...\n", stdout);
1537 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1538 error("Failed to load the boot file\n");
1541 data[ndata].base = load_base;
1542 load_base = 0x7c00; /* If we also load a boot sector */
1544 /* Create boot info table: needed when you want to chainload
1545 another version of ISOLINUX (or another bootlaoder that needs
1546 the -boot-info-table switch of mkisofs)
1547 (will only work when run from ISOLINUX) */
1549 const union syslinux_derivative_info *sdi;
1550 sdi = syslinux_derivative_info();
1552 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1553 /* Boot info table info (integers in little endian format)
1555 Offset Name Size Meaning
1556 8 bi_pvd 4 bytes LBA of primary volume descriptor
1557 12 bi_file 4 bytes LBA of boot file
1558 16 bi_length 4 bytes Boot file length in bytes
1559 20 bi_csum 4 bytes 32-bit checksum
1560 24 bi_reserved 40 bytes Reserved
1562 The 32-bit checksum is the sum of all the 32-bit words in the
1563 boot file starting at byte offset 64. All linear block
1564 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1566 LBA of primary volume descriptor should already be set to 16.
1569 isolinux_bin = (unsigned char *)data[ndata].data;
1571 /* Get LBA address of bootfile */
1572 file_lba = get_file_lba(opt.loadfile);
1574 if (file_lba == 0) {
1575 error("Failed to find LBA offset of the boot file\n");
1579 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1581 /* Set boot file length */
1582 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1584 /* Calculate checksum */
1585 checksum = (uint32_t *) & isolinux_bin[20];
1586 chkhead = (uint32_t *) & isolinux_bin[64];
1587 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1589 while (chkhead < chktail)
1590 *checksum += *chkhead++;
1593 * Deal with possible fractional dword at the end;
1594 * this *should* never happen...
1596 if (data[ndata].size & 3) {
1598 memcpy(&xword, chkhead, data[ndata].size & 3);
1603 ("The isolinux= option is only valid when run from ISOLINUX\n");
1609 /* Layout of stage2 file (from byte 0x0 to 0x270) */
1610 struct grub_stage2_patch_area {
1612 char unknown[0x206];
1613 /* 0x206: compatibility version number major */
1614 uint8_t compat_version_major;
1615 /* 0x207: compatibility version number minor */
1616 uint8_t compat_version_minor;
1618 /* 0x208: install_partition variable */
1620 /* 0x208: sub-partition in sub-partition part2 */
1622 /* 0x209: sub-partition in top-level partition */
1624 /* 0x20a: top-level partiton number */
1626 /* 0x20b: BIOS drive number (must be 0) */
1628 } __attribute__ ((packed)) install_partition;
1630 /* 0x20c: deprecated (historical reason only) */
1631 uint32_t saved_entryno;
1632 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1634 /* 0x211: force LBA */
1636 /* 0x212: version string (will probably be 0.97) */
1637 char version_string[5];
1638 /* 0x217: config filename */
1639 char config_file[89];
1640 /* 0x270: start of code (after jump from 0x200) */
1642 } __attribute__ ((packed)) *stage2;
1644 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1646 ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1650 stage2 = data[ndata].data;
1653 * Check the compatibility version number to see if we loaded a real
1654 * stage2 file or a stage2 file that we support.
1656 if (stage2->compat_version_major != 3
1657 || stage2->compat_version_minor != 2) {
1659 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1663 /* jump 0x200 bytes into the loadfile */
1667 * GRUB Legacy wants the partition number in the install_partition
1668 * variable, located at offset 0x208 of stage2.
1669 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1671 * It looks very similar to the "boot information format" of the
1672 * Multiboot specification:
1673 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1675 * 0x208 = part3: sub-partition in sub-partition part2
1676 * 0x209 = part2: sub-partition in top-level partition
1677 * 0x20a = part1: top-level partition number
1678 * 0x20b = drive: BIOS drive number (must be 0)
1680 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1683 * Partition numbers always start from zero.
1684 * Unused partition bytes must be set to 0xFF.
1686 * We only care about top-level partition, so we only need to change
1687 * "part1" to the appropriate value:
1688 * -1: whole drive (default) (-1 = 0xFF)
1689 * 0-3: primary partitions
1690 * 4-*: logical partitions
1692 stage2->install_partition.part1 = whichpart - 1;
1695 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1696 * config filename. The filename passed via grubcfg= will overwrite
1697 * the default config filename "/boot/grub/menu.lst".
1700 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1702 ("The config filename length can't exceed 88 characters.\n");
1706 strcpy((char *)stage2->config_file, opt.grubcfg);
1711 /* DRMK entry is different than MS-DOS/PC-DOS */
1713 * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
1714 * We only really need 4 new, usable bytes at the end.
1716 int tsize = (data[ndata].size + 19) & 0xfffffff0;
1717 const union syslinux_derivative_info *sdi;
1719 sdi = syslinux_derivative_info();
1720 /* We should lookup the Syslinux partition offset and use it */
1721 fs_lba = *sdi->disk.partoffset;
1723 * fs_lba should be verified against the disk as some DRMK
1724 * variants will check and fail if it does not match
1726 dprintf(" fs_lba offset is %d\n", fs_lba);
1727 /* DRMK only uses a DWORD */
1728 if (fs_lba > 0xffffffff) {
1729 error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
1731 regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */
1732 if (!realloc(data[ndata].data, tsize)) {
1733 error("Failed to realloc for DRMK\n");
1734 goto bail; /* We'll never make it */
1736 data[ndata].size = tsize;
1737 /* ds:bp is assumed by DRMK to be the boot sector */
1738 /* offset 28 is the FAT HiddenSectors value */
1739 regs.ds = (tsize >> 4) + (opt.seg - 2);
1740 /* "Patch" into tail of the new space */
1741 *(int *)(data[ndata].data + tsize - 4) = (int)(fs_lba & 0xffffffff);
1747 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1748 /* Actually read the boot sector */
1750 data[ndata].data = mbr;
1751 } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
1752 error("Cannot read boot sector\n");
1755 data[ndata].size = SECTOR;
1756 data[ndata].base = load_base;
1758 if (!opt.loadfile) {
1759 const struct mbr *br =
1760 (const struct mbr *)((char *)data[ndata].data +
1761 data[ndata].size - sizeof(struct mbr));
1762 if (br->sig != mbr_sig_magic) {
1764 ("Boot sector signature not found (unbootable disk/partition?)\n");
1769 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1770 * the string "cmdcons\0" to memory location 0000:7C03.
1771 * Memory location 0000:7C00 contains the bootsector of the partition.
1773 if (cur_part && opt.cmldr) {
1774 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1775 sizeof cmldr_signature);
1779 * Modify the hidden sectors (partition offset) copy in memory;
1780 * this modifies the field used by FAT and NTFS filesystems, and
1781 * possibly other boot loaders which use the same format.
1783 if (cur_part && opt.sethidden) {
1784 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1791 if (cur_part->next == next_gpt_part) {
1792 /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1793 struct part_entry *record;
1794 /* Look at the GPT partition */
1795 const struct gpt_part *gp = (const struct gpt_part *)
1797 (cur_part->private.gpt.size * cur_part->private.gpt.index));
1798 /* Note the partition length */
1799 uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1800 /* The length of the hand-over */
1802 sizeof(struct part_entry) + sizeof(uint32_t) +
1803 cur_part->private.gpt.size;
1804 /* Will point to the partition record length in the hand-over */
1807 /* Allocate the hand-over record */
1808 record = malloc(synth_size);
1810 error("Could not build GPT hand-over record!\n");
1813 /* Synthesize the record */
1814 memset(record, 0, synth_size);
1815 record->active_flag = 0x80;
1816 record->ostype = 0xED;
1817 /* All bits set by default */
1818 record->start_lba = ~(uint32_t) 0;
1819 record->length = ~(uint32_t) 0;
1820 /* If these fit the precision, pass them on */
1821 if (cur_part->lba_data < record->start_lba)
1822 record->start_lba = cur_part->lba_data;
1823 if (lba_count < record->length)
1824 record->length = lba_count;
1825 /* Next comes the GPT partition record length */
1826 plen = (uint32_t *) (record + 1);
1827 plen[0] = cur_part->private.gpt.size;
1828 /* Next comes the GPT partition record copy */
1829 memcpy(plen + 1, gp, plen[0]);
1830 cur_part->record = record;
1832 regs.eax.l = 0x54504721; /* '!GPT' */
1833 data[ndata].base = 0x7be;
1834 data[ndata].size = synth_size;
1835 data[ndata].data = (void *)record;
1837 regs.esi.w[0] = 0x7be;
1839 dprintf("GPT handover:\n");
1840 mbr_part_dump(record);
1841 gpt_part_dump((struct gpt_part *)(plen + 1));
1842 } else if (cur_part->record) {
1843 /* MBR handover protocol */
1844 static struct part_entry handover_record;
1846 handover_record = *cur_part->record;
1847 handover_record.start_lba = cur_part->lba_data;
1849 data[ndata].base = 0x7be;
1850 data[ndata].size = sizeof handover_record;
1851 data[ndata].data = &handover_record;
1853 regs.esi.w[0] = 0x7be;
1855 dprintf("MBR handover:\n");
1856 mbr_part_dump(&handover_record);
1860 do_boot(data, ndata, ®s);
1864 free(cur_part->block);
1865 free((void *)cur_part->record);