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 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
81 * used with GRUB Legacy stage2 files.
84 * set an alternative config filename in stage2 of Grub Legacy,
85 * only applicable in combination with "grub=<loader>".
88 * pass the partition number to GRUB4DOS,
89 * used with GRUB4DOS' grldr.
92 * if the disk is not fd0/hd0, install a BIOS stub which swaps
96 * change type of primary partitions with IDs 01, 04, 06, 07,
97 * 0b, 0c, or 0e to 1x, except for the selected partition, which
98 * is converted the other way.
101 * update the "hidden sectors" (partition offset) field in a
102 * FAT/NTFS boot sector.
105 * keep the PXE and UNDI stacks in memory (PXELINUX only).
117 #include <syslinux/loadfile.h>
118 #include <syslinux/bootrm.h>
119 #include <syslinux/config.h>
120 #include <syslinux/disk.h>
121 #include <syslinux/video.h>
123 static struct options {
124 const char *loadfile;
143 static inline void error(const char *msg)
148 static struct disk_info diskinfo;
151 * Get a disk block and return a malloc'd buffer.
152 * Uses the disk number and information from diskinfo.
162 /* Read count sectors from drive, starting at lba. Return a new buffer */
163 static void *read_sectors(uint64_t lba, uint8_t count)
166 struct ebios_dapa *dapa = __com32.cs_bounce;
167 void *buf = (char *)__com32.cs_bounce + SECTOR;
174 memset(&inreg, 0, sizeof inreg);
176 if (diskinfo.ebios) {
177 dapa->len = sizeof(*dapa);
179 dapa->off = OFFS(buf);
180 dapa->seg = SEG(buf);
183 inreg.esi.w[0] = OFFS(dapa);
184 inreg.ds = SEG(dapa);
185 inreg.edx.b[0] = diskinfo.disk;
186 inreg.eax.b[1] = 0x42; /* Extended read */
188 unsigned int c, h, s, t;
190 if (!diskinfo.cbios) {
191 /* We failed to get the geometry */
194 return NULL; /* Can only read MBR */
200 s = (lba % diskinfo.sect) + 1;
201 t = lba / diskinfo.sect; /* Track = head*cyl */
202 h = t % diskinfo.head;
203 c = t / diskinfo.head;
206 if (s > 63 || h > 256 || c > 1023)
209 inreg.eax.b[0] = count;
210 inreg.eax.b[1] = 0x02; /* Read */
211 inreg.ecx.b[1] = c & 0xff;
212 inreg.ecx.b[0] = s + (c >> 6);
214 inreg.edx.b[0] = diskinfo.disk;
215 inreg.ebx.w[0] = OFFS(buf);
219 if (disk_int13_retry(&inreg, NULL))
222 data = malloc(count * SECTOR);
224 memcpy(data, buf, count * SECTOR);
228 static int write_sector(unsigned int lba, const void *data)
231 struct ebios_dapa *dapa = __com32.cs_bounce;
232 void *buf = (char *)__com32.cs_bounce + SECTOR;
234 memcpy(buf, data, SECTOR);
235 memset(&inreg, 0, sizeof inreg);
237 if (diskinfo.ebios) {
238 dapa->len = sizeof(*dapa);
239 dapa->count = 1; /* 1 sector */
240 dapa->off = OFFS(buf);
241 dapa->seg = SEG(buf);
244 inreg.esi.w[0] = OFFS(dapa);
245 inreg.ds = SEG(dapa);
246 inreg.edx.b[0] = diskinfo.disk;
247 inreg.eax.w[0] = 0x4300; /* Extended write */
249 unsigned int c, h, s, t;
251 if (!diskinfo.cbios) {
252 /* We failed to get the geometry */
255 return -1; /* Can only write MBR */
261 s = (lba % diskinfo.sect) + 1;
262 t = lba / diskinfo.sect; /* Track = head*cyl */
263 h = t % diskinfo.head;
264 c = t / diskinfo.head;
267 if (s > 63 || h > 256 || c > 1023)
270 inreg.eax.w[0] = 0x0301; /* Write one sector */
271 inreg.ecx.b[1] = c & 0xff;
272 inreg.ecx.b[0] = s + (c >> 6);
274 inreg.edx.b[0] = diskinfo.disk;
275 inreg.ebx.w[0] = OFFS(buf);
279 if (disk_int13_retry(&inreg, NULL))
285 static int write_verify_sector(unsigned int lba, const void *buf)
290 rv = write_sector(lba, buf);
292 return rv; /* Write failure */
293 rb = read_sectors(lba, 1);
295 return -1; /* Readback failure */
296 rv = memcmp(buf, rb, SECTOR);
302 * CHS (cylinder, head, sector) value extraction macros.
303 * Taken from WinVBlock. Does not expand to an lvalue
305 #define chs_head(chs) chs[0]
306 #define chs_sector(chs) (chs[1] & 0x3F)
307 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
308 #define chs_cyl_low(chs) ((uint16_t)chs[2])
309 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
310 typedef uint8_t chs[3];
312 /* A DOS partition table entry */
314 uint8_t active_flag; /* 0x80 if "active" */
320 } __attribute__ ((packed));
322 static void mbr_part_dump(const struct part_entry *part)
325 dprintf("Partition status _____ : 0x%.2x\n"
326 "Partition CHS start\n"
327 " Cylinder ___________ : 0x%.4x (%u)\n"
328 " Head _______________ : 0x%.2x (%u)\n"
329 " Sector _____________ : 0x%.2x (%u)\n"
330 "Partition type _______ : 0x%.2x\n"
331 "Partition CHS end\n"
332 " Cylinder ___________ : 0x%.4x (%u)\n"
333 " Head _______________ : 0x%.2x (%u)\n"
334 " Sector _____________ : 0x%.2x (%u)\n"
335 "Partition LBA start __ : 0x%.8x (%u)\n"
336 "Partition LBA count __ : 0x%.8x (%u)\n"
337 "-------------------------------\n",
339 chs_cylinder(part->start),
340 chs_cylinder(part->start),
341 chs_head(part->start),
342 chs_head(part->start),
343 chs_sector(part->start),
344 chs_sector(part->start),
346 chs_cylinder(part->end),
347 chs_cylinder(part->end),
350 chs_sector(part->end),
351 chs_sector(part->end),
352 part->start_lba, part->start_lba, part->length, part->length);
360 struct part_entry table[4];
362 } __attribute__ ((packed));
363 static const uint16_t mbr_sig_magic = 0xAA55;
365 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
366 static int find_disk(uint32_t mbr_sig)
372 for (drive = 0x80; drive <= 0xff; drive++) {
373 if (disk_get_params(drive, &diskinfo))
374 continue; /* Drive doesn't exist */
375 if (!(mbr = read_sectors(0, 1)))
376 continue; /* Cannot read sector */
377 is_me = (mbr->disk_sig == mbr_sig);
385 /* Forward declaration */
386 struct disk_part_iter;
388 /* Partition-/scheme-specific routine returning the next partition */
389 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
392 /* Contains details for a partition under examination */
393 struct disk_part_iter {
394 /* The block holding the table we are part of */
396 /* The LBA for the beginning of data */
398 /* The partition number, as determined by our heuristic */
400 /* The DOS partition record to pass, if applicable */
401 const struct part_entry *record;
402 /* Function returning the next available partition */
403 disk_part_iter_func next;
404 /* Partition-/scheme-specific details */
410 /* The first extended partition's start LBA */
411 uint64_t lba_extended;
412 /* Any applicable parent, or NULL */
413 struct disk_part_iter *parent;
414 /* The parent extended partition index */
419 /* Real (not effective) index in the partition table */
421 /* Current partition GUID */
422 const struct guid *part_guid;
423 /* Current partition label */
424 const char *part_label;
425 /* Count of entries in GPT */
427 /* Partition record size */
433 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
435 const struct part_entry *ebr_table;
436 const struct part_entry *parent_table =
437 ((const struct mbr *)part->private.ebr.parent->block)->table;
438 static const struct part_entry phony = {.start_lba = 0 };
441 /* Don't look for a "next EBR" the first time around */
442 if (part->private.ebr.parent_index >= 0)
443 /* Look at the linked list */
444 ebr_table = ((const struct mbr *)part->block)->table + 1;
445 /* Do we need to look for an extended partition? */
446 if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
447 /* Start looking for an extended partition in the MBR */
448 while (++part->private.ebr.parent_index < 4) {
449 uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
451 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
454 if (part->private.ebr.parent_index == 4)
455 /* No extended partitions found */
457 part->private.ebr.lba_extended =
458 parent_table[part->private.ebr.parent_index].start_lba;
462 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
464 part->block = read_sectors(ebr_lba, 1);
466 error("Could not load EBR!\n");
469 ebr_table = ((const struct mbr *)part->block)->table;
470 dprintf("next_ebr_part:\n");
471 mbr_part_dump(ebr_table);
474 * Sanity check entry: must not extend outside the
475 * extended partition. This is necessary since some OSes
476 * put crap in some entries.
479 const struct mbr *mbr =
480 (const struct mbr *)part->private.ebr.parent->block;
481 const struct part_entry *extended =
482 mbr->table + part->private.ebr.parent_index;
484 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
485 dprintf("Insane logical partition!\n");
490 part->lba_data = ebr_table[0].start_lba + ebr_lba;
491 dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
493 part->record = ebr_table;
503 free(part->private.ebr.parent->block);
504 free(part->private.ebr.parent);
510 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
512 struct disk_part_iter *ebr_part;
513 /* Look at the partition table */
514 struct part_entry *table = ((struct mbr *)part->block)->table;
516 /* Look for data partitions */
517 while (++part->private.mbr_index < 4) {
518 uint8_t type = table[part->private.mbr_index].ostype;
520 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
521 /* Skip empty or extended partitions */
523 if (!table[part->private.mbr_index].length)
528 /* If we're currently the last partition, it's time for EBR processing */
529 if (part->private.mbr_index == 4) {
530 /* Allocate another iterator for extended partitions */
531 ebr_part = malloc(sizeof(*ebr_part));
533 error("Could not allocate extended partition iterator!\n");
536 /* Setup EBR iterator parameters */
537 ebr_part->block = NULL;
539 ebr_part->record = NULL;
540 ebr_part->next = next_ebr_part;
541 ebr_part->private.ebr.parent = part;
542 /* Trigger an initial EBR load */
543 ebr_part->private.ebr.parent_index = -1;
544 /* The EBR iterator is responsible for freeing us */
545 return next_ebr_part(ebr_part);
547 dprintf("next_mbr_part:\n");
548 mbr_part_dump(table + part->private.mbr_index);
550 /* Update parameters to reflect this new partition. Re-use iterator */
551 part->lba_data = table[part->private.mbr_index].start_lba;
552 dprintf("Partition %d primary lba %u\n", part->index, part->lba_data);
554 part->record = table + part->private.mbr_index;
567 * Be careful with endianness, you must adjust it yourself
568 * iff you are directly using the fourth data chunk
575 } __attribute__ ((packed));
578 * This walk-map effectively reverses the little-endian
579 * portions of the GUID in the output text
581 static const char guid_le_walk_map[] = {
591 * Fill a buffer with a textual GUID representation.
592 * The buffer must be >= char[37] and will be populated
593 * with an ASCII NUL C string terminator.
594 * Example: 11111111-2222-3333-4444-444444444444
595 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
597 static void guid_to_str(char *buf, const struct guid *id)
600 const char *walker = (const char *)id;
602 while (i < sizeof(guid_le_walk_map)) {
603 walker += guid_le_walk_map[i];
604 if (!guid_le_walk_map[i])
607 *buf = ((*walker & 0xF0) >> 4) + '0';
609 *buf += 'A' - '9' - 1;
611 *buf = (*walker & 0x0F) + '0';
613 *buf += 'A' - '9' - 1;
623 * Create a GUID structure from a textual GUID representation.
624 * The input buffer must be >= 32 hexadecimal chars and be
625 * terminated with an ASCII NUL. Returns non-zero on failure.
626 * Example: 11111111-2222-3333-4444-444444444444
627 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
629 static int str_to_guid(const char *buf, struct guid *id)
631 char guid_seq[sizeof(struct guid) * 2];
633 char *walker = (char *)id;
635 while (*buf && i < sizeof(guid_seq)) {
637 /* Skip these three characters */
643 /* Copy something useful to the temp. sequence */
644 if ((*buf >= '0') && (*buf <= '9'))
645 guid_seq[i] = *buf - '0';
646 else if ((*buf >= 'A') && (*buf <= 'F'))
647 guid_seq[i] = *buf - 'A' + 10;
648 else if ((*buf >= 'a') && (*buf <= 'f'))
649 guid_seq[i] = *buf - 'a' + 10;
652 error("Illegal character in GUID!\n");
659 /* Check for insufficient valid characters */
660 if (i < sizeof(guid_seq)) {
661 error("Too few GUID characters!\n");
666 while (i < sizeof(guid_le_walk_map)) {
667 if (!guid_le_walk_map[i])
669 walker += guid_le_walk_map[i];
679 /* A GPT partition */
687 } __attribute__ ((packed));
689 static void gpt_part_dump(const struct gpt_part *gpt_part)
695 dprintf("----------------------------------\n"
696 "GPT part. LBA first __ : 0x%.16llx\n"
697 "GPT part. LBA last ___ : 0x%.16llx\n"
698 "GPT part. attribs ____ : 0x%.16llx\n"
699 "GPT part. name _______ : '",
700 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
701 for (i = 0; i < sizeof(gpt_part->name); i++) {
702 if (gpt_part->name[i])
703 dprintf("%c", gpt_part->name[i]);
706 guid_to_str(guid_text, &gpt_part->type);
707 dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
708 guid_to_str(guid_text, &gpt_part->uid);
709 dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
721 } fields __attribute__ ((packed));
724 } rev __attribute__ ((packed));
730 uint64_t lba_first_usable;
731 uint64_t lba_last_usable;
732 struct guid disk_guid;
736 uint32_t table_chksum;
738 } __attribute__ ((packed));
739 static const char gpt_sig_magic[] = "EFI PART";
742 static void gpt_dump(const struct gpt *gpt)
746 printf("GPT sig ______________ : '%8.8s'\n"
747 "GPT major revision ___ : 0x%.4x\n"
748 "GPT minor revision ___ : 0x%.4x\n"
749 "GPT header size ______ : 0x%.8x\n"
750 "GPT header checksum __ : 0x%.8x\n"
751 "GPT reserved _________ : '%4.4s'\n"
752 "GPT LBA current ______ : 0x%.16llx\n"
753 "GPT LBA alternative __ : 0x%.16llx\n"
754 "GPT LBA first usable _ : 0x%.16llx\n"
755 "GPT LBA last usable __ : 0x%.16llx\n"
756 "GPT LBA part. table __ : 0x%.16llx\n"
757 "GPT partition count __ : 0x%.8x\n"
758 "GPT partition size ___ : 0x%.8x\n"
759 "GPT part. table chksum : 0x%.8x\n",
761 gpt->rev.fields.major,
762 gpt->rev.fields.minor,
768 gpt->lba_first_usable,
769 gpt->lba_last_usable,
770 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
771 guid_to_str(guid_text, &gpt->disk_guid);
772 printf("GPT disk GUID ________ : {%s}\n", guid_text);
776 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
778 const struct gpt_part *gpt_part = NULL;
780 while (++part->private.gpt.index < part->private.gpt.parts) {
782 (const struct gpt_part *)(part->block +
783 (part->private.gpt.index *
784 part->private.gpt.size));
785 if (!gpt_part->lba_first)
789 /* Were we the last partition? */
790 if (part->private.gpt.index == part->private.gpt.parts) {
793 part->lba_data = gpt_part->lba_first;
794 part->private.gpt.part_guid = &gpt_part->uid;
795 part->private.gpt.part_label = gpt_part->name;
796 /* Update our index */
798 gpt_part_dump(gpt_part);
800 /* In a GPT scheme, we re-use the iterator */
810 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
812 const struct gpt *gpt_candidate;
815 * Ignore any passed partition iterator. The caller should
816 * have passed NULL. Allocate a new partition iterator
818 part = malloc(sizeof(*part));
820 error("Count not allocate partition iterator!\n");
824 part->block = read_sectors(0, 2);
826 error("Could not read two sectors!\n");
829 /* Check for an MBR */
830 if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
831 error("No MBR magic!\n");
834 /* Establish a pseudo-partition for the MBR (index 0) */
837 part->private.mbr_index = -1;
838 part->next = next_mbr_part;
839 /* Check for a GPT disk */
840 gpt_candidate = (const struct gpt *)(part->block + SECTOR);
841 if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
842 /* LBA for partition table */
845 /* It looks like one */
846 /* TODO: Check checksum. Possibly try alternative GPT */
848 puts("Looks like a GPT disk.");
849 gpt_dump(gpt_candidate);
851 /* TODO: Check table checksum (maybe) */
852 /* Note relevant GPT details */
853 part->next = next_gpt_part;
854 part->private.gpt.index = -1;
855 part->private.gpt.parts = gpt_candidate->part_count;
856 part->private.gpt.size = gpt_candidate->part_size;
857 lba_table = gpt_candidate->lba_table;
858 gpt_candidate = NULL;
859 /* Load the partition table */
862 read_sectors(lba_table,
863 ((part->private.gpt.size * part->private.gpt.parts) +
864 SECTOR - 1) / SECTOR);
866 error("Could not read GPT partition list!\n");
870 /* Return the pseudo-partition's next partition, which is real */
871 return part->next(part);
888 * Search for a specific drive/partition, based on the GPT GUID.
889 * We return the disk drive number if found, as well as populating the
890 * boot_part pointer with the matching partition, if applicable.
891 * If no matching partition is found or the GUID is a disk GUID,
892 * boot_part will be populated with NULL. If not matching disk is
893 * found, we return -1.
895 static int find_by_guid(const struct guid *gpt_guid,
896 struct disk_part_iter **boot_part)
902 for (drive = 0x80; drive <= 0xff; drive++) {
903 if (disk_get_params(drive, &diskinfo))
904 continue; /* Drive doesn't exist */
905 if (!(header = read_sectors(1, 1)))
906 continue; /* Cannot read sector */
907 if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
915 is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
918 /* Check for a matching partition */
919 boot_part[0] = get_first_partition(NULL);
920 while (boot_part[0]) {
922 !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
926 boot_part[0] = boot_part[0]->next(boot_part[0]);
937 * Search for a specific partition, based on the GPT label.
938 * We return the disk drive number if found, as well as populating the
939 * boot_part pointer with the matching partition, if applicable.
940 * If no matching partition is found, boot_part will be populated with
941 * NULL and we return -1.
943 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
948 for (drive = 0x80; drive <= 0xff; drive++) {
949 if (disk_get_params(drive, &diskinfo))
950 continue; /* Drive doesn't exist */
951 /* Check for a GPT disk */
952 boot_part[0] = get_first_partition(NULL);
953 if (!(boot_part[0]->next == next_gpt_part)) {
955 while (boot_part[0]) {
956 /* Run through until the end */
957 boot_part[0] = boot_part[0]->next(boot_part[0]);
961 /* Check for a matching partition */
962 while (boot_part[0]) {
963 char gpt_label[sizeof(((struct gpt_part *) NULL)->name)];
964 const char *gpt_label_scanner =
965 boot_part[0]->private.gpt.part_label;
968 /* Re-write the GPT partition label as ASCII */
969 while (gpt_label_scanner <
970 boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
971 if ((gpt_label[j] = *gpt_label_scanner))
975 if ((is_me = !strcmp(label, gpt_label)))
977 boot_part[0] = boot_part[0]->next(boot_part[0]);
986 static void do_boot(struct data_area *data, int ndata,
987 struct syslinux_rm_regs *regs)
989 uint16_t *const bios_fbm = (uint16_t *) 0x413;
990 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
991 struct syslinux_memmap *mmap;
992 struct syslinux_movelist *mlist = NULL;
994 uint8_t driveno = regs->edx.b[0];
995 uint8_t swapdrive = driveno & 0x80;
998 mmap = syslinux_memory_map();
1001 error("Cannot read system memory map\n");
1006 for (i = 0; i < ndata; i++) {
1007 if (data[i].base + data[i].size > endimage)
1008 endimage = data[i].base + data[i].size;
1010 if (endimage > dosmem)
1013 for (i = 0; i < ndata; i++) {
1014 if (syslinux_add_movelist(&mlist, data[i].base,
1015 (addr_t) data[i].data, data[i].size))
1019 if (opt.swap && driveno != swapdrive) {
1020 static const uint8_t swapstub_master[] = {
1021 /* The actual swap code */
1022 0x53, /* 00: push bx */
1023 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
1024 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
1025 0x5b, /* 08: pop bx */
1026 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
1027 0x90, 0x90, /* 0E: nop; nop */
1028 /* Code to install this in the right location */
1029 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
1030 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
1031 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
1032 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
1033 0x4f, /* 1F: dec di */
1034 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
1035 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
1036 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
1037 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
1038 0x8e, 0xc7, /* 32: mov es,di */
1039 0x31, 0xff, /* 34: xor di,di */
1040 0xf3, 0x66, 0xa5, /* 36: rep movsd */
1041 0xbe, 0, 0, /* 39: mov si,0 */
1042 0xbf, 0, 0, /* 3C: mov di,0 */
1043 0x8e, 0xde, /* 3F: mov ds,si */
1044 0x8e, 0xc7, /* 41: mov es,di */
1045 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
1046 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
1047 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
1048 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
1049 /* pad out to segment boundary */
1050 0x90, 0x90, /* 5A: ... */
1051 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
1053 static uint8_t swapstub[1024];
1056 /* Note: we can't rely on either INT 13h nor the dosmem
1057 vector to be correct at this stage, so we have to use an
1058 installer stub to put things in the right place.
1059 Round the installer location to a 1K boundary so the only
1060 possible overlap is the identity mapping. */
1061 endimage = (endimage + 1023) & ~1023;
1063 /* Create swap stub */
1064 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
1065 *(uint16_t *) & swapstub[0x3a] = regs->ds;
1066 *(uint16_t *) & swapstub[0x3d] = regs->es;
1067 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
1068 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
1069 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
1070 *(uint16_t *) & swapstub[0x56] = regs->ip;
1071 *(uint16_t *) & swapstub[0x58] = regs->cs;
1072 p = &swapstub[sizeof swapstub_master];
1074 /* Mapping table; start out with identity mapping everything */
1075 for (i = 0; i < 256; i++)
1078 /* And the actual swap */
1079 p[driveno] = swapdrive;
1080 p[swapdrive] = driveno;
1082 /* Adjust registers */
1083 regs->ds = regs->cs = endimage >> 4;
1084 regs->es = regs->esi.l = 0;
1085 regs->ecx.l = sizeof swapstub >> 2;
1086 regs->ip = 0x10; /* Installer offset */
1087 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
1089 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
1093 endimage += sizeof swapstub;
1096 /* Tell the shuffler not to muck with this area... */
1097 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
1099 /* Force text mode */
1100 syslinux_force_text_mode();
1102 fputs("Booting...\n", stdout);
1103 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
1104 error("Chainboot failed!\n");
1108 error("Loader file too large\n");
1112 error("Out of memory\n");
1116 static int hide_unhide(struct mbr *mbr, int part)
1119 struct part_entry *pt;
1120 const uint16_t mask =
1121 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1126 bool write_back = false;
1128 for (i = 1; i <= 4; i++) {
1129 pt = mbr->table + i - 1;
1131 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1132 /* It's a hideable partition type */
1134 t &= ~0x10; /* unhide */
1136 t |= 0x10; /* hide */
1138 if (t != pt->ostype) {
1145 return write_verify_sector(0, mbr);
1150 static uint32_t get_file_lba(const char *filename)
1155 /* Start with clean registers */
1156 memset(&inregs, 0, sizeof(com32sys_t));
1158 /* Put the filename in the bounce buffer */
1159 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1161 /* Call comapi_open() which returns a structure pointer in SI
1162 * to a structure whose first member happens to be the LBA.
1164 inregs.eax.w[0] = 0x0006;
1165 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1166 inregs.es = SEG(__com32.cs_bounce);
1167 __com32.cs_intcall(0x22, &inregs, &inregs);
1169 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1170 return 0; /* Filename not found */
1173 /* Since the first member is the LBA, we simply cast */
1174 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1176 /* Clean the registers for the next call */
1177 memset(&inregs, 0, sizeof(com32sys_t));
1179 /* Put the filename in the bounce buffer */
1180 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1182 /* Call comapi_close() to free the structure */
1183 inregs.eax.w[0] = 0x0008;
1184 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1185 inregs.es = SEG(__com32.cs_bounce);
1186 __com32.cs_intcall(0x22, &inregs, &inregs);
1191 static void usage(void)
1193 static const char usage[] = "\
1194 Usage: chain.c32 [options]\n\
1195 chain.c32 hd<disk#> [<partition>] [options]\n\
1196 chain.c32 fd<disk#> [options]\n\
1197 chain.c32 mbr:<id> [<partition>] [options]\n\
1198 chain.c32 guid:<guid> [<partition>] [options]\n\
1199 chain.c32 label:<label> [<partition>] [options]\n\
1200 chain.c32 boot [<partition>] [options]\n\
1201 chain.c32 fs [options]\n\
1202 Options: file=<loader> Load and execute file, instead of boot sector\n\
1203 isolinux=<loader> Load another version of ISOLINUX\n\
1204 ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
1205 cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
1206 freedos=<loader> Load FreeDOS KERNEL.SYS\n\
1207 msdos=<loader> Load MS-DOS IO.SYS\n\
1208 pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
1209 grub=<loader> Load GRUB Legacy stage2\n\
1210 grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
1211 grldr=<loader> Load GRUB4DOS grldr\n\
1212 seg=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\
1213 swap Swap drive numbers, if bootdisk is not fd0/hd0\n\
1214 hide Hide primary partitions, except selected partition\n\
1215 sethidden Set the FAT/NTFS hidden sectors field\n\
1216 keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
1217 See syslinux/com32/modules/chain.c for more information\n";
1221 int main(int argc, char *argv[])
1223 struct mbr *mbr = NULL;
1225 struct disk_part_iter *cur_part = NULL;
1226 struct syslinux_rm_regs regs;
1227 char *drivename, *partition;
1228 int hd, drive, whichpart = 0; /* MBR by default */
1230 uint64_t fs_lba = 0; /* Syslinux partition */
1231 uint32_t file_lba = 0;
1232 struct guid gpt_guid;
1233 unsigned char *isolinux_bin;
1234 uint32_t *checksum, *chkhead, *chktail;
1235 struct data_area data[3];
1238 static const char cmldr_signature[8] = "cmdcons";
1240 openconsole(&dev_null_r, &dev_stdcon_w);
1245 /* Prepare the register set */
1246 memset(®s, 0, sizeof regs);
1248 for (i = 1; i < argc; i++) {
1249 if (!strncmp(argv[i], "file=", 5)) {
1250 opt.loadfile = argv[i] + 5;
1251 } else if (!strncmp(argv[i], "seg=", 4)) {
1252 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1253 if (segval < 0x50 || segval > 0x9f000) {
1254 error("Invalid segment\n");
1258 } else if (!strncmp(argv[i], "isolinux=", 9)) {
1259 opt.loadfile = argv[i] + 9;
1260 opt.isolinux = true;
1261 } else if (!strncmp(argv[i], "ntldr=", 6)) {
1262 opt.seg = 0x2000; /* NTLDR wants this address */
1263 opt.loadfile = argv[i] + 6;
1264 opt.sethidden = true;
1265 } else if (!strncmp(argv[i], "cmldr=", 6)) {
1266 opt.seg = 0x2000; /* CMLDR wants this address */
1267 opt.loadfile = argv[i] + 6;
1269 opt.sethidden = true;
1270 } else if (!strncmp(argv[i], "freedos=", 8)) {
1271 opt.seg = 0x60; /* FREEDOS wants this address */
1272 opt.loadfile = argv[i] + 8;
1273 opt.sethidden = true;
1274 } else if (!strncmp(argv[i], "msdos=", 6) ||
1275 !strncmp(argv[i], "pcdos=", 6)) {
1276 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
1277 opt.loadfile = argv[i] + 6;
1278 opt.sethidden = true;
1279 } else if (!strncmp(argv[i], "grub=", 5)) {
1280 opt.seg = 0x800; /* stage2 wants this address */
1281 opt.loadfile = argv[i] + 5;
1283 } else if (!strncmp(argv[i], "grubcfg=", 8)) {
1284 opt.grubcfg = argv[i] + 8;
1285 } else if (!strncmp(argv[i], "grldr=", 6)) {
1286 opt.loadfile = argv[i] + 6;
1288 } else if (!strcmp(argv[i], "swap")) {
1290 } else if (!strcmp(argv[i], "noswap")) {
1292 } else if (!strcmp(argv[i], "hide")) {
1294 } else if (!strcmp(argv[i], "nohide")) {
1296 } else if (!strcmp(argv[i], "keeppxe")) {
1298 } else if (!strcmp(argv[i], "sethidden")) {
1299 opt.sethidden = true;
1300 } else if (!strcmp(argv[i], "nosethidden")) {
1301 opt.sethidden = false;
1302 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1303 && argv[i][1] == 'd')
1304 || !strncmp(argv[i], "mbr:", 4)
1305 || !strncmp(argv[i], "mbr=", 4)
1306 || !strncmp(argv[i], "guid:", 5)
1307 || !strncmp(argv[i], "guid=", 5)
1308 || !strncmp(argv[i], "label:", 6)
1309 || !strncmp(argv[i], "label=", 6)
1310 || !strcmp(argv[i], "boot")
1311 || !strncmp(argv[i], "boot,", 5)
1312 || !strcmp(argv[i], "fs")) {
1313 drivename = argv[i];
1314 p = strchr(drivename, ',');
1318 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1319 && argv[i + 1][0] <= '9') {
1320 partition = argv[++i];
1328 if (opt.grubcfg && !opt.grub) {
1329 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
1334 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1336 regs.ip = regs.esp.l = 0x7c00;
1340 if (!strncmp(drivename, "mbr", 3)) {
1341 drive = find_disk(strtoul(drivename + 4, NULL, 0));
1343 error("Unable to find requested MBR signature\n");
1346 } else if (!strncmp(drivename, "guid", 4)) {
1347 if (str_to_guid(drivename + 5, &gpt_guid))
1349 drive = find_by_guid(&gpt_guid, &cur_part);
1351 error("Unable to find requested GPT disk/partition\n");
1354 } else if (!strncmp(drivename, "label", 5)) {
1355 if (!drivename[6]) {
1356 error("No label specified.\n");
1359 drive = find_by_label(drivename + 6, &cur_part);
1361 error("Unable to find requested partition by label\n");
1364 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1365 drivename[1] == 'd') {
1366 hd = drivename[0] == 'h';
1368 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1369 } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
1370 const union syslinux_derivative_info *sdi;
1372 sdi = syslinux_derivative_info();
1373 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1374 drive = 0x80; /* Boot drive not available */
1376 drive = sdi->disk.drive_number;
1377 if (!strcmp(drivename, "fs")
1378 && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
1379 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
1380 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
1381 /* We should lookup the Syslinux partition number and use it */
1382 fs_lba = *sdi->disk.partoffset;
1384 error("Unparsable drive specification\n");
1388 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1389 regs.ebx.b[0] = regs.edx.b[0] = drive;
1391 /* Get the disk geometry and disk access setup */
1392 if (disk_get_params(drive, &diskinfo)) {
1393 error("Cannot get disk parameters\n");
1398 if (!(mbr = read_sectors(0, 1))) {
1399 error("Cannot read Master Boot Record or sector 0\n");
1404 whichpart = strtoul(partition, NULL, 0);
1405 /* "guid:" or "label:" might have specified a partition */
1407 whichpart = cur_part->index;
1409 /* Boot the MBR by default */
1410 if (!cur_part && (whichpart || fs_lba)) {
1411 /* Boot a partition, possibly the Syslinux partition itself */
1412 cur_part = get_first_partition(NULL);
1414 if ((cur_part->index == whichpart)
1415 || (cur_part->lba_data == fs_lba))
1416 /* Found the partition to boot */
1418 cur_part = cur_part->next(cur_part);
1421 error("Requested partition not found!\n");
1424 whichpart = cur_part->index;
1427 if (!(drive & 0x80) && whichpart) {
1428 error("Warning: Partitions of floppy devices may not work\n");
1432 * GRLDR of GRUB4DOS wants the partition number in DH:
1433 * -1: whole drive (default)
1434 * 0-3: primary partitions
1435 * 4-*: logical partitions
1438 regs.edx.b[1] = whichpart - 1;
1441 if (whichpart < 1 || whichpart > 4)
1442 error("WARNING: hide specified without a non-primary partition\n");
1443 if (hide_unhide(mbr, whichpart))
1444 error("WARNING: failed to write MBR for 'hide'\n");
1447 /* Do the actual chainloading */
1448 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1451 fputs("Loading the boot file...\n", stdout);
1452 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1453 error("Failed to load the boot file\n");
1456 data[ndata].base = load_base;
1457 load_base = 0x7c00; /* If we also load a boot sector */
1459 /* Create boot info table: needed when you want to chainload
1460 another version of ISOLINUX (or another bootlaoder that needs
1461 the -boot-info-table switch of mkisofs)
1462 (will only work when run from ISOLINUX) */
1464 const union syslinux_derivative_info *sdi;
1465 sdi = syslinux_derivative_info();
1467 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1468 /* Boot info table info (integers in little endian format)
1470 Offset Name Size Meaning
1471 8 bi_pvd 4 bytes LBA of primary volume descriptor
1472 12 bi_file 4 bytes LBA of boot file
1473 16 bi_length 4 bytes Boot file length in bytes
1474 20 bi_csum 4 bytes 32-bit checksum
1475 24 bi_reserved 40 bytes Reserved
1477 The 32-bit checksum is the sum of all the 32-bit words in the
1478 boot file starting at byte offset 64. All linear block
1479 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1481 LBA of primary volume descriptor should already be set to 16.
1484 isolinux_bin = (unsigned char *)data[ndata].data;
1486 /* Get LBA address of bootfile */
1487 file_lba = get_file_lba(opt.loadfile);
1489 if (file_lba == 0) {
1490 error("Failed to find LBA offset of the boot file\n");
1494 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1496 /* Set boot file length */
1497 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1499 /* Calculate checksum */
1500 checksum = (uint32_t *) & isolinux_bin[20];
1501 chkhead = (uint32_t *) & isolinux_bin[64];
1502 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1504 while (chkhead < chktail)
1505 *checksum += *chkhead++;
1508 * Deal with possible fractional dword at the end;
1509 * this *should* never happen...
1511 if (data[ndata].size & 3) {
1513 memcpy(&xword, chkhead, data[ndata].size & 3);
1518 ("The isolinux= option is only valid when run from ISOLINUX\n");
1524 /* Layout of stage2 file (from byte 0x0 to 0x270) */
1525 struct grub_stage2_patch_area {
1527 char unknown[0x206];
1528 /* 0x206: compatibility version number major */
1529 uint8_t compat_version_major;
1530 /* 0x207: compatibility version number minor */
1531 uint8_t compat_version_minor;
1533 /* 0x208: install_partition variable */
1535 /* 0x208: sub-partition in sub-partition part2 */
1537 /* 0x209: sub-partition in top-level partition */
1539 /* 0x20a: top-level partiton number */
1541 /* 0x20b: BIOS drive number (must be 0) */
1543 } __attribute__ ((packed)) install_partition;
1545 /* 0x20c: deprecated (historical reason only) */
1546 uint32_t saved_entryno;
1547 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1549 /* 0x211: force LBA */
1551 /* 0x212: version string (will probably be 0.97) */
1552 char version_string[5];
1553 /* 0x217: config filename */
1554 char config_file[89];
1555 /* 0x270: start of code (after jump from 0x200) */
1557 } __attribute__ ((packed)) *stage2;
1559 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1561 ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1565 stage2 = data[ndata].data;
1568 * Check the compatibility version number to see if we loaded a real
1569 * stage2 file or a stage2 file that we support.
1571 if (stage2->compat_version_major != 3
1572 || stage2->compat_version_minor != 2) {
1574 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1578 /* jump 0x200 bytes into the loadfile */
1582 * GRUB Legacy wants the partition number in the install_partition
1583 * variable, located at offset 0x208 of stage2.
1584 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1586 * It looks very similar to the "boot information format" of the
1587 * Multiboot specification:
1588 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1590 * 0x208 = part3: sub-partition in sub-partition part2
1591 * 0x209 = part2: sub-partition in top-level partition
1592 * 0x20a = part1: top-level partition number
1593 * 0x20b = drive: BIOS drive number (must be 0)
1595 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1598 * Partition numbers always start from zero.
1599 * Unused partition bytes must be set to 0xFF.
1601 * We only care about top-level partition, so we only need to change
1602 * "part1" to the appropriate value:
1603 * -1: whole drive (default) (-1 = 0xFF)
1604 * 0-3: primary partitions
1605 * 4-*: logical partitions
1607 stage2->install_partition.part1 = whichpart - 1;
1610 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1611 * config filename. The filename passed via grubcfg= will overwrite
1612 * the default config filename "/boot/grub/menu.lst".
1615 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1617 ("The config filename length can't exceed 88 characters.\n");
1621 strcpy((char *)stage2->config_file, opt.grubcfg);
1628 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1629 /* Actually read the boot sector */
1631 data[ndata].data = mbr;
1632 } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
1633 error("Cannot read boot sector\n");
1636 data[ndata].size = SECTOR;
1637 data[ndata].base = load_base;
1639 if (!opt.loadfile) {
1640 const struct mbr *br =
1641 (const struct mbr *)((char *)data[ndata].data +
1642 data[ndata].size - sizeof(struct mbr));
1643 if (br->sig != mbr_sig_magic) {
1645 ("Boot sector signature not found (unbootable disk/partition?)\n");
1650 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1651 * the string "cmdcons\0" to memory location 0000:7C03.
1652 * Memory location 0000:7C00 contains the bootsector of the partition.
1654 if (cur_part && opt.cmldr) {
1655 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1656 sizeof cmldr_signature);
1660 * Modify the hidden sectors (partition offset) copy in memory;
1661 * this modifies the field used by FAT and NTFS filesystems, and
1662 * possibly other boot loaders which use the same format.
1664 if (cur_part && opt.sethidden) {
1665 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1672 if (cur_part->next == next_gpt_part) {
1673 /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1674 struct part_entry *record;
1675 /* Look at the GPT partition */
1676 const struct gpt_part *gp = (const struct gpt_part *)
1678 (cur_part->private.gpt.size * cur_part->private.gpt.index));
1679 /* Note the partition length */
1680 uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1681 /* The length of the hand-over */
1683 sizeof(struct part_entry) + sizeof(uint32_t) +
1684 cur_part->private.gpt.size;
1685 /* Will point to the partition record length in the hand-over */
1688 /* Allocate the hand-over record */
1689 record = malloc(synth_size);
1691 error("Could not build GPT hand-over record!\n");
1694 /* Synthesize the record */
1695 memset(record, 0, synth_size);
1696 record->active_flag = 0x80;
1697 record->ostype = 0xED;
1698 /* All bits set by default */
1699 record->start_lba = ~(uint32_t) 0;
1700 record->length = ~(uint32_t) 0;
1701 /* If these fit the precision, pass them on */
1702 if (cur_part->lba_data < record->start_lba)
1703 record->start_lba = cur_part->lba_data;
1704 if (lba_count < record->length)
1705 record->length = lba_count;
1706 /* Next comes the GPT partition record length */
1707 plen = (uint32_t *) (record + 1);
1708 plen[0] = cur_part->private.gpt.size;
1709 /* Next comes the GPT partition record copy */
1710 memcpy(plen + 1, gp, plen[0]);
1711 cur_part->record = record;
1713 regs.eax.l = 0x54504721; /* '!GPT' */
1714 data[ndata].base = 0x7be;
1715 data[ndata].size = synth_size;
1716 data[ndata].data = (void *)record;
1718 regs.esi.w[0] = 0x7be;
1720 dprintf("GPT handover:\n");
1721 mbr_part_dump(record);
1722 gpt_part_dump((struct gpt_part *)(plen + 1));
1723 } else if (cur_part->record) {
1724 /* MBR handover protocol */
1725 static struct part_entry handover_record;
1727 handover_record = *cur_part->record;
1728 handover_record.start_lba = cur_part->lba_data;
1730 data[ndata].base = 0x7be;
1731 data[ndata].size = sizeof handover_record;
1732 data[ndata].data = &handover_record;
1734 regs.esi.w[0] = 0x7be;
1736 dprintf("MBR handover:\n");
1737 mbr_part_dump(&handover_record);
1741 do_boot(data, ndata, ®s);
1745 free(cur_part->block);
1746 free((void *)cur_part->record);