1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 * Significant portions copyright (C) 2010 Shao Miller
6 * [partition iteration, GPT, "fs"]
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 for the Dell Real Mode Kernel. Currently, this is only known to work on a partition at C,H,S 0,1,1
83 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
84 * used with GRUB Legacy stage2 files.
87 * set an alternative config filename in stage2 of Grub Legacy,
88 * only applicable in combination with "grub=<loader>".
91 * pass the partition number to GRUB4DOS,
92 * used with GRUB4DOS' grldr.
95 * if the disk is not fd0/hd0, install a BIOS stub which swaps
99 * change type of primary partitions with IDs 01, 04, 06, 07,
100 * 0b, 0c, or 0e to 1x, except for the selected partition, which
101 * is converted the other way.
104 * update the "hidden sectors" (partition offset) field in a
105 * FAT/NTFS boot sector.
108 * keep the PXE and UNDI stacks in memory (PXELINUX only).
120 #include <syslinux/loadfile.h>
121 #include <syslinux/bootrm.h>
122 #include <syslinux/config.h>
123 #include <syslinux/video.h>
125 #define SECTOR 512 /* bytes/sector */
127 static struct options {
128 const char *loadfile;
148 static inline void error(const char *msg)
154 * Call int 13h, but with retry on failure. Especially floppies need this.
156 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
158 int retry = 6; /* Number of retries */
165 __intcall(0x13, inreg, outreg);
166 if (!(outreg->eflags.l & EFLAGS_CF))
167 return 0; /* CF=0, OK */
170 return -1; /* Error */
174 * Query disk parameters and EBIOS availability for a particular disk.
178 int ebios; /* EBIOS supported on this disk */
179 int cbios; /* CHS geometry is valid */
184 static int get_disk_params(int disk)
186 static com32sys_t getparm, parm, getebios, ebios;
188 disk_info.disk = disk;
189 disk_info.ebios = disk_info.cbios = 0;
191 /* Get EBIOS support */
192 getebios.eax.w[0] = 0x4100;
193 getebios.ebx.w[0] = 0x55aa;
194 getebios.edx.b[0] = disk;
195 getebios.eflags.b[0] = 0x3; /* CF set */
197 __intcall(0x13, &getebios, &ebios);
199 if (!(ebios.eflags.l & EFLAGS_CF) &&
200 ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
204 /* Get disk parameters -- really only useful for
205 hard disks, but if we have a partitioned floppy
206 it's actually our best chance... */
207 getparm.eax.b[1] = 0x08;
208 getparm.edx.b[0] = disk;
210 __intcall(0x13, &getparm, &parm);
212 if (parm.eflags.l & EFLAGS_CF)
213 return disk_info.ebios ? 0 : -1;
215 disk_info.head = parm.edx.b[1] + 1;
216 disk_info.sect = parm.ecx.b[0] & 0x3f;
217 if (disk_info.sect == 0) {
220 disk_info.cbios = 1; /* Valid geometry */
227 * Get a disk block and return a malloc'd buffer.
228 * Uses the disk number and information from disk_info.
238 /* Read count sectors from drive, starting at lba. Return a new buffer */
239 static void *read_sectors(uint64_t lba, uint8_t count)
242 struct ebios_dapa *dapa = __com32.cs_bounce;
243 void *buf = (char *)__com32.cs_bounce + SECTOR;
250 memset(&inreg, 0, sizeof inreg);
252 if (disk_info.ebios) {
253 dapa->len = sizeof(*dapa);
255 dapa->off = OFFS(buf);
256 dapa->seg = SEG(buf);
259 inreg.esi.w[0] = OFFS(dapa);
260 inreg.ds = SEG(dapa);
261 inreg.edx.b[0] = disk_info.disk;
262 inreg.eax.b[1] = 0x42; /* Extended read */
264 unsigned int c, h, s, t;
266 if (!disk_info.cbios) {
267 /* We failed to get the geometry */
270 return NULL; /* Can only read MBR */
274 s = lba % disk_info.sect;
275 t = lba / disk_info.sect; /* Track = head*cyl */
276 h = t % disk_info.head;
277 c = t / disk_info.head;
280 if (s >= 63 || h >= 256 || c >= 1024)
283 inreg.eax.b[0] = count;
284 inreg.eax.b[1] = 0x02; /* Read */
286 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
288 inreg.edx.b[0] = disk_info.disk;
289 inreg.ebx.w[0] = OFFS(buf);
293 if (int13_retry(&inreg, NULL))
296 data = malloc(count * SECTOR);
298 memcpy(data, buf, count * SECTOR);
302 static int write_sector(unsigned int lba, const void *data)
305 struct ebios_dapa *dapa = __com32.cs_bounce;
306 void *buf = (char *)__com32.cs_bounce + SECTOR;
308 memcpy(buf, data, SECTOR);
309 memset(&inreg, 0, sizeof inreg);
311 if (disk_info.ebios) {
312 dapa->len = sizeof(*dapa);
313 dapa->count = 1; /* 1 sector */
314 dapa->off = OFFS(buf);
315 dapa->seg = SEG(buf);
318 inreg.esi.w[0] = OFFS(dapa);
319 inreg.ds = SEG(dapa);
320 inreg.edx.b[0] = disk_info.disk;
321 inreg.eax.w[0] = 0x4300; /* Extended write */
323 unsigned int c, h, s, t;
325 if (!disk_info.cbios) {
326 /* We failed to get the geometry */
329 return -1; /* Can only write MBR */
333 s = lba % disk_info.sect;
334 t = lba / disk_info.sect; /* Track = head*cyl */
335 h = t % disk_info.head;
336 c = t / disk_info.head;
339 if (s >= 63 || h >= 256 || c >= 1024)
342 inreg.eax.w[0] = 0x0301; /* Write one sector */
344 inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
346 inreg.edx.b[0] = disk_info.disk;
347 inreg.ebx.w[0] = OFFS(buf);
351 if (int13_retry(&inreg, NULL))
357 static int write_verify_sector(unsigned int lba, const void *buf)
362 rv = write_sector(lba, buf);
364 return rv; /* Write failure */
365 rb = read_sectors(lba, 1);
367 return -1; /* Readback failure */
368 rv = memcmp(buf, rb, SECTOR);
374 * CHS (cylinder, head, sector) value extraction macros.
375 * Taken from WinVBlock. Does not expand to an lvalue
377 #define chs_head(chs) chs[0]
378 #define chs_sector(chs) (chs[1] & 0x3F)
379 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
380 #define chs_cyl_low(chs) ((uint16_t)chs[2])
381 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
382 typedef uint8_t chs[3];
384 /* A DOS partition table entry */
386 uint8_t active_flag; /* 0x80 if "active" */
392 } __attribute__ ((packed));
394 static void mbr_part_dump(const struct part_entry *part)
397 dprintf("Partition status _____ : 0x%.2x\n"
398 "Partition CHS start\n"
399 " Cylinder ___________ : 0x%.4x (%u)\n"
400 " Head _______________ : 0x%.2x (%u)\n"
401 " Sector _____________ : 0x%.2x (%u)\n"
402 "Partition type _______ : 0x%.2x\n"
403 "Partition CHS end\n"
404 " Cylinder ___________ : 0x%.4x (%u)\n"
405 " Head _______________ : 0x%.2x (%u)\n"
406 " Sector _____________ : 0x%.2x (%u)\n"
407 "Partition LBA start __ : 0x%.8x (%u)\n"
408 "Partition LBA count __ : 0x%.8x (%u)\n"
409 "-------------------------------\n",
411 chs_cylinder(part->start),
412 chs_cylinder(part->start),
413 chs_head(part->start),
414 chs_head(part->start),
415 chs_sector(part->start),
416 chs_sector(part->start),
418 chs_cylinder(part->end),
419 chs_cylinder(part->end),
422 chs_sector(part->end),
423 chs_sector(part->end),
435 struct part_entry table[4];
437 } __attribute__ ((packed));
438 static const uint16_t mbr_sig_magic = 0xAA55;
440 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
441 static int find_disk(uint32_t mbr_sig)
447 for (drive = 0x80; drive <= 0xff; drive++) {
448 if (get_disk_params(drive))
449 continue; /* Drive doesn't exist */
450 if (!(mbr = read_sectors(0, 1)))
451 continue; /* Cannot read sector */
452 is_me = (mbr->disk_sig == mbr_sig);
460 /* Forward declaration */
461 struct disk_part_iter;
463 /* Partition-/scheme-specific routine returning the next partition */
464 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
467 /* Contains details for a partition under examination */
468 struct disk_part_iter {
469 /* The block holding the table we are part of */
471 /* The LBA for the beginning of data */
473 /* The partition number, as determined by our heuristic */
475 /* The DOS partition record to pass, if applicable */
476 const struct part_entry *record;
477 /* Function returning the next available partition */
478 disk_part_iter_func next;
479 /* Partition-/scheme-specific details */
485 /* The first extended partition's start LBA */
486 uint64_t lba_extended;
487 /* Any applicable parent, or NULL */
488 struct disk_part_iter *parent;
489 /* The parent extended partition index */
494 /* Real (not effective) index in the partition table */
496 /* Current partition GUID */
497 const struct guid *part_guid;
498 /* Current partition label */
499 const char *part_label;
500 /* Count of entries in GPT */
502 /* Partition record size */
508 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
510 const struct part_entry *ebr_table;
511 const struct part_entry *parent_table =
512 ((const struct mbr *)part->private.ebr.parent->block)->table;
513 static const struct part_entry phony = {.start_lba = 0 };
516 /* Don't look for a "next EBR" the first time around */
517 if (part->private.ebr.parent_index >= 0)
518 /* Look at the linked list */
519 ebr_table = ((const struct mbr *)part->block)->table + 1;
520 /* Do we need to look for an extended partition? */
521 if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
522 /* Start looking for an extended partition in the MBR */
523 while (++part->private.ebr.parent_index < 4) {
524 uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
526 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
529 if (part->private.ebr.parent_index == 4)
530 /* No extended partitions found */
532 part->private.ebr.lba_extended =
533 parent_table[part->private.ebr.parent_index].start_lba;
537 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
539 part->block = read_sectors(ebr_lba, 1);
541 error("Could not load EBR!\n");
544 ebr_table = ((const struct mbr *)part->block)->table;
545 dprintf("next_ebr_part:\n");
546 mbr_part_dump(ebr_table);
549 * Sanity check entry: must not extend outside the
550 * extended partition. This is necessary since some OSes
551 * put crap in some entries.
554 const struct mbr *mbr =
555 (const struct mbr *)part->private.ebr.parent->block;
556 const struct part_entry *extended =
557 mbr->table + part->private.ebr.parent_index;
559 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
560 dprintf("Insane logical partition!\n");
565 part->lba_data = ebr_table[0].start_lba + ebr_lba;
566 dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
568 part->record = ebr_table;
578 free(part->private.ebr.parent->block);
579 free(part->private.ebr.parent);
585 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
587 struct disk_part_iter *ebr_part;
588 /* Look at the partition table */
589 struct part_entry *table = ((struct mbr *)part->block)->table;
591 /* Look for data partitions */
592 while (++part->private.mbr_index < 4) {
593 uint8_t type = table[part->private.mbr_index].ostype;
595 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
596 /* Skip empty or extended partitions */
598 if (!table[part->private.mbr_index].length)
603 /* If we're currently the last partition, it's time for EBR processing */
604 if (part->private.mbr_index == 4) {
605 /* Allocate another iterator for extended partitions */
606 ebr_part = malloc(sizeof(*ebr_part));
608 error("Could not allocate extended partition iterator!\n");
611 /* Setup EBR iterator parameters */
612 ebr_part->block = NULL;
614 ebr_part->record = NULL;
615 ebr_part->next = next_ebr_part;
616 ebr_part->private.ebr.parent = part;
617 /* Trigger an initial EBR load */
618 ebr_part->private.ebr.parent_index = -1;
619 /* The EBR iterator is responsible for freeing us */
620 return next_ebr_part(ebr_part);
622 dprintf("next_mbr_part:\n");
623 mbr_part_dump(table + part->private.mbr_index);
625 /* Update parameters to reflect this new partition. Re-use iterator */
626 part->lba_data = table[part->private.mbr_index].start_lba;
627 dprintf("Partition %d primary lba %u\n", part->private.mbr_index, part->lba_data);
628 part->index = part->private.mbr_index + 1;
629 part->record = table + part->private.mbr_index;
642 * Be careful with endianness, you must adjust it yourself
643 * iff you are directly using the fourth data chunk
650 } __attribute__ ((packed));
653 * This walk-map effectively reverses the little-endian
654 * portions of the GUID in the output text
656 static const char guid_le_walk_map[] = {
666 * Fill a buffer with a textual GUID representation.
667 * The buffer must be >= char[37] and will be populated
668 * with an ASCII NUL C string terminator.
669 * Example: 11111111-2222-3333-4444-444444444444
670 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
672 static void guid_to_str(char *buf, const struct guid *id)
675 const char *walker = (const char *)id;
677 while (i < sizeof(guid_le_walk_map)) {
678 walker += guid_le_walk_map[i];
679 if (!guid_le_walk_map[i])
682 *buf = ((*walker & 0xF0) >> 4) + '0';
684 *buf += 'A' - '9' - 1;
686 *buf = (*walker & 0x0F) + '0';
688 *buf += 'A' - '9' - 1;
698 * Create a GUID structure from a textual GUID representation.
699 * The input buffer must be >= 32 hexadecimal chars and be
700 * terminated with an ASCII NUL. Returns non-zero on failure.
701 * Example: 11111111-2222-3333-4444-444444444444
702 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
704 static int str_to_guid(const char *buf, struct guid *id)
706 char guid_seq[sizeof(struct guid) * 2];
708 char *walker = (char *)id;
710 while (*buf && i < sizeof(guid_seq)) {
712 /* Skip these three characters */
718 /* Copy something useful to the temp. sequence */
719 if ((*buf >= '0') && (*buf <= '9'))
720 guid_seq[i] = *buf - '0';
721 else if ((*buf >= 'A') && (*buf <= 'F'))
722 guid_seq[i] = *buf - 'A' + 10;
723 else if ((*buf >= 'a') && (*buf <= 'f'))
724 guid_seq[i] = *buf - 'a' + 10;
727 error("Illegal character in GUID!\n");
734 /* Check for insufficient valid characters */
735 if (i < sizeof(guid_seq)) {
736 error("Too few GUID characters!\n");
741 while (i < sizeof(guid_le_walk_map)) {
742 if (!guid_le_walk_map[i])
744 walker += guid_le_walk_map[i];
754 /* A GPT partition */
762 } __attribute__ ((packed));
764 static void gpt_part_dump(const struct gpt_part *gpt_part)
770 dprintf("----------------------------------\n"
771 "GPT part. LBA first __ : 0x%.16llx\n"
772 "GPT part. LBA last ___ : 0x%.16llx\n"
773 "GPT part. attribs ____ : 0x%.16llx\n"
774 "GPT part. name _______ : '",
775 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
776 for (i = 0; i < sizeof(gpt_part->name); i++) {
777 if (gpt_part->name[i])
778 dprintf("%c", gpt_part->name[i]);
781 guid_to_str(guid_text, &gpt_part->type);
782 dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
783 guid_to_str(guid_text, &gpt_part->uid);
784 dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
796 } fields __attribute__ ((packed));
799 } rev __attribute__ ((packed));
805 uint64_t lba_first_usable;
806 uint64_t lba_last_usable;
807 struct guid disk_guid;
811 uint32_t table_chksum;
813 } __attribute__ ((packed));
814 static const char gpt_sig_magic[] = "EFI PART";
817 static void gpt_dump(const struct gpt *gpt)
821 printf("GPT sig ______________ : '%8.8s'\n"
822 "GPT major revision ___ : 0x%.4x\n"
823 "GPT minor revision ___ : 0x%.4x\n"
824 "GPT header size ______ : 0x%.8x\n"
825 "GPT header checksum __ : 0x%.8x\n"
826 "GPT reserved _________ : '%4.4s'\n"
827 "GPT LBA current ______ : 0x%.16llx\n"
828 "GPT LBA alternative __ : 0x%.16llx\n"
829 "GPT LBA first usable _ : 0x%.16llx\n"
830 "GPT LBA last usable __ : 0x%.16llx\n"
831 "GPT LBA part. table __ : 0x%.16llx\n"
832 "GPT partition count __ : 0x%.8x\n"
833 "GPT partition size ___ : 0x%.8x\n"
834 "GPT part. table chksum : 0x%.8x\n",
836 gpt->rev.fields.major,
837 gpt->rev.fields.minor,
843 gpt->lba_first_usable,
844 gpt->lba_last_usable,
845 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
846 guid_to_str(guid_text, &gpt->disk_guid);
847 printf("GPT disk GUID ________ : {%s}\n", guid_text);
851 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
853 const struct gpt_part *gpt_part = NULL;
855 while (++part->private.gpt.index < part->private.gpt.parts) {
857 (const struct gpt_part *)(part->block +
858 (part->private.gpt.index *
859 part->private.gpt.size));
860 if (!gpt_part->lba_first)
864 /* Were we the last partition? */
865 if (part->private.gpt.index == part->private.gpt.parts) {
868 part->lba_data = gpt_part->lba_first;
869 part->private.gpt.part_guid = &gpt_part->uid;
870 part->private.gpt.part_label = gpt_part->name;
871 /* Update our index */
872 part->index = part->private.gpt.index + 1;
873 gpt_part_dump(gpt_part);
875 /* In a GPT scheme, we re-use the iterator */
885 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
887 const struct gpt *gpt_candidate;
890 * Ignore any passed partition iterator. The caller should
891 * have passed NULL. Allocate a new partition iterator
893 part = malloc(sizeof(*part));
895 error("Count not allocate partition iterator!\n");
899 part->block = read_sectors(0, 2);
901 error("Could not read two sectors!\n");
904 /* Check for an MBR */
905 if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
906 error("No MBR magic!\n");
909 /* Establish a pseudo-partition for the MBR (index 0) */
912 part->private.mbr_index = -1;
913 part->next = next_mbr_part;
914 /* Check for a GPT disk */
915 gpt_candidate = (const struct gpt *)(part->block + SECTOR);
916 if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
917 /* LBA for partition table */
920 /* It looks like one */
921 /* TODO: Check checksum. Possibly try alternative GPT */
923 puts("Looks like a GPT disk.");
924 gpt_dump(gpt_candidate);
926 /* TODO: Check table checksum (maybe) */
927 /* Note relevant GPT details */
928 part->next = next_gpt_part;
929 part->private.gpt.index = -1;
930 part->private.gpt.parts = gpt_candidate->part_count;
931 part->private.gpt.size = gpt_candidate->part_size;
932 lba_table = gpt_candidate->lba_table;
933 gpt_candidate = NULL;
934 /* Load the partition table */
937 read_sectors(lba_table,
938 ((part->private.gpt.size * part->private.gpt.parts) +
939 SECTOR - 1) / SECTOR);
941 error("Could not read GPT partition list!\n");
945 /* Return the pseudo-partition's next partition, which is real */
946 return part->next(part);
963 * Search for a specific drive/partition, based on the GPT GUID.
964 * We return the disk drive number if found, as well as populating the
965 * boot_part pointer with the matching partition, if applicable.
966 * If no matching partition is found or the GUID is a disk GUID,
967 * boot_part will be populated with NULL. If not matching disk is
968 * found, we return -1.
970 static int find_by_guid(const struct guid *gpt_guid,
971 struct disk_part_iter **boot_part)
977 for (drive = 0x80; drive <= 0xff; drive++) {
978 if (get_disk_params(drive))
979 continue; /* Drive doesn't exist */
980 if (!(header = read_sectors(1, 1)))
981 continue; /* Cannot read sector */
982 if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
990 is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
993 /* Check for a matching partition */
994 boot_part[0] = get_first_partition(NULL);
995 while (boot_part[0]) {
997 !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
1001 boot_part[0] = boot_part[0]->next(boot_part[0]);
1004 boot_part[0] = NULL;
1012 * Search for a specific partition, based on the GPT label.
1013 * We return the disk drive number if found, as well as populating the
1014 * boot_part pointer with the matching partition, if applicable.
1015 * If no matching partition is found, boot_part will be populated with
1016 * NULL and we return -1.
1018 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
1023 for (drive = 0x80; drive <= 0xff; drive++) {
1024 if (get_disk_params(drive))
1025 continue; /* Drive doesn't exist */
1026 /* Check for a GPT disk */
1027 boot_part[0] = get_first_partition(NULL);
1028 if (!(boot_part[0]->next == next_gpt_part)) {
1029 /* Not a GPT disk */
1030 while (boot_part[0]) {
1031 /* Run through until the end */
1032 boot_part[0] = boot_part[0]->next(boot_part[0]);
1036 /* Check for a matching partition */
1037 while (boot_part[0]) {
1038 char gpt_label[sizeof(((struct gpt_part *) NULL)->name)];
1039 const char *gpt_label_scanner =
1040 boot_part[0]->private.gpt.part_label;
1043 /* Re-write the GPT partition label as ASCII */
1044 while (gpt_label_scanner <
1045 boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
1046 if ((gpt_label[j] = *gpt_label_scanner))
1048 gpt_label_scanner++;
1050 if ((is_me = !strcmp(label, gpt_label)))
1052 boot_part[0] = boot_part[0]->next(boot_part[0]);
1061 static void do_boot(struct data_area *data, int ndata,
1062 struct syslinux_rm_regs *regs)
1064 uint16_t *const bios_fbm = (uint16_t *) 0x413;
1065 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
1066 struct syslinux_memmap *mmap;
1067 struct syslinux_movelist *mlist = NULL;
1069 uint8_t driveno = regs->edx.b[0];
1070 uint8_t swapdrive = driveno & 0x80;
1073 mmap = syslinux_memory_map();
1076 error("Cannot read system memory map\n");
1081 for (i = 0; i < ndata; i++) {
1082 if (data[i].base + data[i].size > endimage)
1083 endimage = data[i].base + data[i].size;
1085 if (endimage > dosmem)
1088 for (i = 0; i < ndata; i++) {
1089 if (syslinux_add_movelist(&mlist, data[i].base,
1090 (addr_t) data[i].data, data[i].size))
1094 if (opt.swap && driveno != swapdrive) {
1095 static const uint8_t swapstub_master[] = {
1096 /* The actual swap code */
1097 0x53, /* 00: push bx */
1098 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
1099 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
1100 0x5b, /* 08: pop bx */
1101 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
1102 0x90, 0x90, /* 0E: nop; nop */
1103 /* Code to install this in the right location */
1104 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
1105 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
1106 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
1107 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
1108 0x4f, /* 1F: dec di */
1109 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
1110 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
1111 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
1112 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
1113 0x8e, 0xc7, /* 32: mov es,di */
1114 0x31, 0xff, /* 34: xor di,di */
1115 0xf3, 0x66, 0xa5, /* 36: rep movsd */
1116 0xbe, 0, 0, /* 39: mov si,0 */
1117 0xbf, 0, 0, /* 3C: mov di,0 */
1118 0x8e, 0xde, /* 3F: mov ds,si */
1119 0x8e, 0xc7, /* 41: mov es,di */
1120 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
1121 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
1122 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
1123 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
1124 /* pad out to segment boundary */
1125 0x90, 0x90, /* 5A: ... */
1126 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
1128 static uint8_t swapstub[1024];
1131 /* Note: we can't rely on either INT 13h nor the dosmem
1132 vector to be correct at this stage, so we have to use an
1133 installer stub to put things in the right place.
1134 Round the installer location to a 1K boundary so the only
1135 possible overlap is the identity mapping. */
1136 endimage = (endimage + 1023) & ~1023;
1138 /* Create swap stub */
1139 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
1140 *(uint16_t *) & swapstub[0x3a] = regs->ds;
1141 *(uint16_t *) & swapstub[0x3d] = regs->es;
1142 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
1143 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
1144 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
1145 *(uint16_t *) & swapstub[0x56] = regs->ip;
1146 *(uint16_t *) & swapstub[0x58] = regs->cs;
1147 p = &swapstub[sizeof swapstub_master];
1149 /* Mapping table; start out with identity mapping everything */
1150 for (i = 0; i < 256; i++)
1153 /* And the actual swap */
1154 p[driveno] = swapdrive;
1155 p[swapdrive] = driveno;
1157 /* Adjust registers */
1158 regs->ds = regs->cs = endimage >> 4;
1159 regs->es = regs->esi.l = 0;
1160 regs->ecx.l = sizeof swapstub >> 2;
1161 regs->ip = 0x10; /* Installer offset */
1162 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
1164 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
1168 endimage += sizeof swapstub;
1171 /* Tell the shuffler not to muck with this area... */
1172 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
1174 /* Force text mode */
1175 syslinux_force_text_mode();
1177 fputs("Booting...\n", stdout);
1178 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
1179 error("Chainboot failed!\n");
1183 error("Loader file too large\n");
1187 error("Out of memory\n");
1191 static int hide_unhide(struct mbr *mbr, int part)
1194 struct part_entry *pt;
1195 const uint16_t mask =
1196 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1201 bool write_back = false;
1203 for (i = 1; i <= 4; i++) {
1204 pt = mbr->table + i - 1;
1206 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1207 /* It's a hideable partition type */
1209 t &= ~0x10; /* unhide */
1211 t |= 0x10; /* hide */
1213 if (t != pt->ostype) {
1220 return write_verify_sector(0, mbr);
1225 static uint32_t get_file_lba(const char *filename)
1230 /* Start with clean registers */
1231 memset(&inregs, 0, sizeof(com32sys_t));
1233 /* Put the filename in the bounce buffer */
1234 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1236 /* Call comapi_open() which returns a structure pointer in SI
1237 * to a structure whose first member happens to be the LBA.
1239 inregs.eax.w[0] = 0x0006;
1240 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1241 inregs.es = SEG(__com32.cs_bounce);
1242 __com32.cs_intcall(0x22, &inregs, &inregs);
1244 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1245 return 0; /* Filename not found */
1248 /* Since the first member is the LBA, we simply cast */
1249 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1251 /* Clean the registers for the next call */
1252 memset(&inregs, 0, sizeof(com32sys_t));
1254 /* Put the filename in the bounce buffer */
1255 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1257 /* Call comapi_close() to free the structure */
1258 inregs.eax.w[0] = 0x0008;
1259 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1260 inregs.es = SEG(__com32.cs_bounce);
1261 __com32.cs_intcall(0x22, &inregs, &inregs);
1266 static void usage(void)
1268 static const char usage[] = "\
1269 Usage: chain.c32 [options]\n\
1270 chain.c32 hd<disk#> [<partition>] [options]\n\
1271 chain.c32 fd<disk#> [options]\n\
1272 chain.c32 mbr:<id> [<partition>] [options]\n\
1273 chain.c32 guid:<guid> [<partition>] [options]\n\
1274 chain.c32 label:<label> [<partition>] [options]\n\
1275 chain.c32 boot [<partition>] [options]\n\
1276 chain.c32 fs [options]\n\
1277 Options: file=<loader> Load and execute file, instead of boot sector\n\
1278 isolinux=<loader> Load another version of ISOLINUX\n\
1279 ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
1280 cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
1281 freedos=<loader> Load FreeDOS KERNEL.SYS\n\
1282 msdos=<loader> Load MS-DOS IO.SYS\n\
1283 pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
1284 drmk=<loader> Load DRMK DELLBIO.BIN\n\
1285 grub=<loader> Load GRUB Legacy stage2\n\
1286 grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
1287 grldr=<loader> Load GRUB4DOS grldr\n\
1288 seg=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\
1289 swap Swap drive numbers, if bootdisk is not fd0/hd0\n\
1290 hide Hide primary partitions, except selected partition\n\
1291 sethidden Set the FAT/NTFS hidden sectors field\n\
1292 keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
1293 See syslinux/com32/modules/chain.c for more information\n";
1297 int main(int argc, char *argv[])
1299 struct mbr *mbr = NULL;
1301 struct disk_part_iter *cur_part = NULL;
1302 struct syslinux_rm_regs regs;
1303 char *drivename, *partition;
1304 int hd, drive, whichpart = 0; /* MBR by default */
1306 uint64_t fs_lba = 0; /* Syslinux partition */
1307 uint32_t file_lba = 0;
1308 struct guid gpt_guid;
1309 unsigned char *isolinux_bin;
1310 uint32_t *checksum, *chkhead, *chktail;
1311 struct data_area data[3];
1314 static const char cmldr_signature[8] = "cmdcons";
1316 openconsole(&dev_null_r, &dev_stdcon_w);
1321 /* Prepare the register set */
1322 memset(®s, 0, sizeof regs);
1324 for (i = 1; i < argc; i++) {
1325 if (!strncmp(argv[i], "file=", 5)) {
1326 opt.loadfile = argv[i] + 5;
1327 } else if (!strncmp(argv[i], "seg=", 4)) {
1328 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1329 if (segval < 0x50 || segval > 0x9f000) {
1330 error("Invalid segment\n");
1334 } else if (!strncmp(argv[i], "isolinux=", 9)) {
1335 opt.loadfile = argv[i] + 9;
1336 opt.isolinux = true;
1337 } else if (!strncmp(argv[i], "ntldr=", 6)) {
1338 opt.seg = 0x2000; /* NTLDR wants this address */
1339 opt.loadfile = argv[i] + 6;
1340 opt.sethidden = true;
1341 } else if (!strncmp(argv[i], "cmldr=", 6)) {
1342 opt.seg = 0x2000; /* CMLDR wants this address */
1343 opt.loadfile = argv[i] + 6;
1345 opt.sethidden = true;
1346 } else if (!strncmp(argv[i], "freedos=", 8)) {
1347 opt.seg = 0x60; /* FREEDOS wants this address */
1348 opt.loadfile = argv[i] + 8;
1349 opt.sethidden = true;
1350 } else if (!strncmp(argv[i], "msdos=", 6) ||
1351 !strncmp(argv[i], "pcdos=", 6)) {
1352 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
1353 opt.loadfile = argv[i] + 6;
1354 opt.sethidden = true;
1355 } else if (!strncmp(argv[i], "drmk=", 5)) {
1356 opt.seg = 0x70; /* DRMK wants this address */
1357 opt.loadfile = argv[i] + 5;
1358 opt.sethidden = true;
1360 } else if (!strncmp(argv[i], "grub=", 5)) {
1361 opt.seg = 0x800; /* stage2 wants this address */
1362 opt.loadfile = argv[i] + 5;
1364 } else if (!strncmp(argv[i], "grubcfg=", 8)) {
1365 opt.grubcfg = argv[i] + 8;
1366 } else if (!strncmp(argv[i], "grldr=", 6)) {
1367 opt.loadfile = argv[i] + 6;
1369 } else if (!strcmp(argv[i], "swap")) {
1371 } else if (!strcmp(argv[i], "noswap")) {
1373 } else if (!strcmp(argv[i], "hide")) {
1375 } else if (!strcmp(argv[i], "nohide")) {
1377 } else if (!strcmp(argv[i], "keeppxe")) {
1379 } else if (!strcmp(argv[i], "sethidden")) {
1380 opt.sethidden = true;
1381 } else if (!strcmp(argv[i], "nosethidden")) {
1382 opt.sethidden = false;
1383 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1384 && argv[i][1] == 'd')
1385 || !strncmp(argv[i], "mbr:", 4)
1386 || !strncmp(argv[i], "mbr=", 4)
1387 || !strncmp(argv[i], "guid:", 5)
1388 || !strncmp(argv[i], "guid=", 5)
1389 || !strncmp(argv[i], "label:", 6)
1390 || !strncmp(argv[i], "label=", 6)
1391 || !strcmp(argv[i], "boot")
1392 || !strncmp(argv[i], "boot,", 5)
1393 || !strcmp(argv[i], "fs")) {
1394 drivename = argv[i];
1395 p = strchr(drivename, ',');
1399 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1400 && argv[i + 1][0] <= '9') {
1401 partition = argv[++i];
1409 if (opt.grubcfg && !opt.grub) {
1410 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
1415 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1417 regs.ip = regs.esp.l = 0x7c00;
1421 if (!strncmp(drivename, "mbr", 3)) {
1422 drive = find_disk(strtoul(drivename + 4, NULL, 0));
1424 error("Unable to find requested MBR signature\n");
1427 } else if (!strncmp(drivename, "guid", 4)) {
1428 if (str_to_guid(drivename + 5, &gpt_guid))
1430 drive = find_by_guid(&gpt_guid, &cur_part);
1432 error("Unable to find requested GPT disk/partition\n");
1435 } else if (!strncmp(drivename, "label", 5)) {
1436 if (!drivename[6]) {
1437 error("No label specified.\n");
1440 drive = find_by_label(drivename + 6, &cur_part);
1442 error("Unable to find requested partition by label\n");
1445 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1446 drivename[1] == 'd') {
1447 hd = drivename[0] == 'h';
1449 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1450 } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
1451 const union syslinux_derivative_info *sdi;
1453 sdi = syslinux_derivative_info();
1454 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1455 drive = 0x80; /* Boot drive not available */
1457 drive = sdi->disk.drive_number;
1458 if (!strcmp(drivename, "fs")
1459 && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
1460 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
1461 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
1462 /* We should lookup the Syslinux partition number and use it */
1463 fs_lba = *sdi->disk.partoffset;
1465 error("Unparsable drive specification\n");
1469 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1470 regs.ebx.b[0] = regs.edx.b[0] = drive;
1472 /* Get the disk geometry and disk access setup */
1473 if (get_disk_params(drive)) {
1474 error("Cannot get disk parameters\n");
1479 if (!(mbr = read_sectors(0, 1))) {
1480 error("Cannot read Master Boot Record or sector 0\n");
1485 whichpart = strtoul(partition, NULL, 0);
1486 /* "guid:" or "label:" might have specified a partition */
1488 whichpart = cur_part->index;
1490 /* Boot the MBR by default */
1491 if (!cur_part && (whichpart || fs_lba)) {
1492 /* Boot a partition, possibly the Syslinux partition itself */
1493 cur_part = get_first_partition(NULL);
1495 if ((cur_part->index == whichpart)
1496 || (cur_part->lba_data == fs_lba))
1497 /* Found the partition to boot */
1499 cur_part = cur_part->next(cur_part);
1502 error("Requested partition not found!\n");
1505 whichpart = cur_part->index;
1508 if (!(drive & 0x80) && whichpart) {
1509 error("Warning: Partitions of floppy devices may not work\n");
1513 * GRLDR of GRUB4DOS wants the partition number in DH:
1514 * -1: whole drive (default)
1515 * 0-3: primary partitions
1516 * 4-*: logical partitions
1519 regs.edx.b[1] = whichpart - 1;
1522 if (whichpart < 1 || whichpart > 4)
1523 error("WARNING: hide specified without a non-primary partition\n");
1524 if (hide_unhide(mbr, whichpart))
1525 error("WARNING: failed to write MBR for 'hide'\n");
1528 /* Do the actual chainloading */
1529 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1532 fputs("Loading the boot file...\n", stdout);
1533 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1534 error("Failed to load the boot file\n");
1537 data[ndata].base = load_base;
1538 load_base = 0x7c00; /* If we also load a boot sector */
1540 /* Create boot info table: needed when you want to chainload
1541 another version of ISOLINUX (or another bootlaoder that needs
1542 the -boot-info-table switch of mkisofs)
1543 (will only work when run from ISOLINUX) */
1545 const union syslinux_derivative_info *sdi;
1546 sdi = syslinux_derivative_info();
1548 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1549 /* Boot info table info (integers in little endian format)
1551 Offset Name Size Meaning
1552 8 bi_pvd 4 bytes LBA of primary volume descriptor
1553 12 bi_file 4 bytes LBA of boot file
1554 16 bi_length 4 bytes Boot file length in bytes
1555 20 bi_csum 4 bytes 32-bit checksum
1556 24 bi_reserved 40 bytes Reserved
1558 The 32-bit checksum is the sum of all the 32-bit words in the
1559 boot file starting at byte offset 64. All linear block
1560 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1562 LBA of primary volume descriptor should already be set to 16.
1565 isolinux_bin = (unsigned char *)data[ndata].data;
1567 /* Get LBA address of bootfile */
1568 file_lba = get_file_lba(opt.loadfile);
1570 if (file_lba == 0) {
1571 error("Failed to find LBA offset of the boot file\n");
1575 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1577 /* Set boot file length */
1578 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1580 /* Calculate checksum */
1581 checksum = (uint32_t *) & isolinux_bin[20];
1582 chkhead = (uint32_t *) & isolinux_bin[64];
1583 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1585 while (chkhead < chktail)
1586 *checksum += *chkhead++;
1589 * Deal with possible fractional dword at the end;
1590 * this *should* never happen...
1592 if (data[ndata].size & 3) {
1594 memcpy(&xword, chkhead, data[ndata].size & 3);
1599 ("The isolinux= option is only valid when run from ISOLINUX\n");
1605 /* Layout of stage2 file (from byte 0x0 to 0x270) */
1606 struct grub_stage2_patch_area {
1608 char unknown[0x206];
1609 /* 0x206: compatibility version number major */
1610 uint8_t compat_version_major;
1611 /* 0x207: compatibility version number minor */
1612 uint8_t compat_version_minor;
1614 /* 0x208: install_partition variable */
1616 /* 0x208: sub-partition in sub-partition part2 */
1618 /* 0x209: sub-partition in top-level partition */
1620 /* 0x20a: top-level partiton number */
1622 /* 0x20b: BIOS drive number (must be 0) */
1624 } __attribute__ ((packed)) install_partition;
1626 /* 0x20c: deprecated (historical reason only) */
1627 uint32_t saved_entryno;
1628 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1630 /* 0x211: force LBA */
1632 /* 0x212: version string (will probably be 0.97) */
1633 char version_string[5];
1634 /* 0x217: config filename */
1635 char config_file[89];
1636 /* 0x270: start of code (after jump from 0x200) */
1638 } __attribute__ ((packed)) *stage2;
1640 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1642 ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1646 stage2 = data[ndata].data;
1649 * Check the compatibility version number to see if we loaded a real
1650 * stage2 file or a stage2 file that we support.
1652 if (stage2->compat_version_major != 3
1653 || stage2->compat_version_minor != 2) {
1655 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1659 /* jump 0x200 bytes into the loadfile */
1663 * GRUB Legacy wants the partition number in the install_partition
1664 * variable, located at offset 0x208 of stage2.
1665 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1667 * It looks very similar to the "boot information format" of the
1668 * Multiboot specification:
1669 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1671 * 0x208 = part3: sub-partition in sub-partition part2
1672 * 0x209 = part2: sub-partition in top-level partition
1673 * 0x20a = part1: top-level partition number
1674 * 0x20b = drive: BIOS drive number (must be 0)
1676 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1679 * Partition numbers always start from zero.
1680 * Unused partition bytes must be set to 0xFF.
1682 * We only care about top-level partition, so we only need to change
1683 * "part1" to the appropriate value:
1684 * -1: whole drive (default) (-1 = 0xFF)
1685 * 0-3: primary partitions
1686 * 4-*: logical partitions
1688 stage2->install_partition.part1 = whichpart - 1;
1691 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1692 * config filename. The filename passed via grubcfg= will overwrite
1693 * the default config filename "/boot/grub/menu.lst".
1696 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1698 ("The config filename length can't exceed 88 characters.\n");
1702 strcpy((char *)stage2->config_file, opt.grubcfg);
1707 /* DRMK entry is different than MS-DOS/PC-DOS */
1709 * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
1710 * We only really need 4 new, usable bytes at the end.
1712 int tsize = (data[ndata].size + 19) & 0xfffffff0;
1713 const union syslinux_derivative_info *sdi;
1715 sdi = syslinux_derivative_info();
1716 /* We should lookup the Syslinux partition offset and use it */
1717 fs_lba = *sdi->disk.partoffset;
1719 * fs_lba should be verified against the disk as some DRMK
1720 * variants will check and fail if it does not match
1722 dprintf(" fs_lba offset is %d\n", fs_lba);
1723 if (fs_lba > 0xffffffff) {
1724 error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
1726 regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */
1727 if (!realloc(data[ndata].data, tsize)) {
1728 error("Failed to realloc for DRMK\n");
1729 goto bail; /* We'll never make it */
1731 data[ndata].size = tsize;
1732 /* ds:bp is assumed by DRMK to be the boot sector */
1733 /* offset 28 is the FAT HiddenSectors value */
1734 regs.ds = (tsize >> 4) + (opt.seg - 2);
1735 /* "Patch" into tail of the new space */
1736 *(int *)(data[ndata].data + tsize - 4) = (int)(fs_lba & 0xffffffff);
1742 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1743 /* Actually read the boot sector */
1745 data[ndata].data = mbr;
1746 } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
1747 error("Cannot read boot sector\n");
1750 data[ndata].size = SECTOR;
1751 data[ndata].base = load_base;
1753 if (!opt.loadfile) {
1754 const struct mbr *br =
1755 (const struct mbr *)((char *)data[ndata].data +
1756 data[ndata].size - sizeof(struct mbr));
1757 if (br->sig != mbr_sig_magic) {
1759 ("Boot sector signature not found (unbootable disk/partition?)\n");
1764 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1765 * the string "cmdcons\0" to memory location 0000:7C03.
1766 * Memory location 0000:7C00 contains the bootsector of the partition.
1768 if (cur_part && opt.cmldr) {
1769 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1770 sizeof cmldr_signature);
1774 * Modify the hidden sectors (partition offset) copy in memory;
1775 * this modifies the field used by FAT and NTFS filesystems, and
1776 * possibly other boot loaders which use the same format.
1778 if (cur_part && opt.sethidden) {
1779 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1786 if (cur_part->next == next_gpt_part) {
1787 /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1788 struct part_entry *record;
1789 /* Look at the GPT partition */
1790 const struct gpt_part *gp = (const struct gpt_part *)
1792 (cur_part->private.gpt.size * cur_part->private.gpt.index));
1793 /* Note the partition length */
1794 uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1795 /* The length of the hand-over */
1797 sizeof(struct part_entry) + sizeof(uint32_t) +
1798 cur_part->private.gpt.size;
1799 /* Will point to the partition record length in the hand-over */
1802 /* Allocate the hand-over record */
1803 record = malloc(synth_size);
1805 error("Could not build GPT hand-over record!\n");
1808 /* Synthesize the record */
1809 memset(record, 0, synth_size);
1810 record->active_flag = 0x80;
1811 record->ostype = 0xED;
1812 /* All bits set by default */
1813 record->start_lba = ~(uint32_t) 0;
1814 record->length = ~(uint32_t) 0;
1815 /* If these fit the precision, pass them on */
1816 if (cur_part->lba_data < record->start_lba)
1817 record->start_lba = cur_part->lba_data;
1818 if (lba_count < record->length)
1819 record->length = lba_count;
1820 /* Next comes the GPT partition record length */
1821 plen = (uint32_t *) (record + 1);
1822 plen[0] = cur_part->private.gpt.size;
1823 /* Next comes the GPT partition record copy */
1824 memcpy(plen + 1, gp, plen[0]);
1825 cur_part->record = record;
1827 regs.eax.l = 0x54504721; /* '!GPT' */
1828 data[ndata].base = 0x7be;
1829 data[ndata].size = synth_size;
1830 data[ndata].data = (void *)record;
1832 regs.esi.w[0] = 0x7be;
1834 dprintf("GPT handover:\n");
1835 mbr_part_dump(record);
1836 gpt_part_dump((struct gpt_part *)(plen + 1));
1837 } else if (cur_part->record) {
1838 /* MBR handover protocol */
1839 static struct part_entry handover_record;
1841 handover_record = *cur_part->record;
1842 handover_record.start_lba = cur_part->lba_data;
1844 data[ndata].base = 0x7be;
1845 data[ndata].size = sizeof handover_record;
1846 data[ndata].data = &handover_record;
1848 regs.esi.w[0] = 0x7be;
1850 dprintf("MBR handover:\n");
1851 mbr_part_dump(&handover_record);
1855 do_boot(data, ndata, ®s);
1859 free(cur_part->block);
1860 free((void *)cur_part->record);