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;
150 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
151 static int find_disk(uint32_t mbr_sig)
155 struct disk_dos_mbr *mbr;
157 for (drive = 0x80; drive <= 0xff; drive++) {
158 if (disk_get_params(drive, &diskinfo))
159 continue; /* Drive doesn't exist */
160 if (!(mbr = disk_read_sectors(&diskinfo, 0, 1)))
161 continue; /* Cannot read sector */
162 is_me = (mbr->disk_sig == mbr_sig);
170 /* Forward declaration */
171 struct disk_part_iter;
173 /* Partition-/scheme-specific routine returning the next partition */
174 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
177 /* Contains details for a partition under examination */
178 struct disk_part_iter {
179 /* The block holding the table we are part of */
181 /* The LBA for the beginning of data */
183 /* The partition number, as determined by our heuristic */
185 /* The DOS partition record to pass, if applicable */
186 const struct disk_dos_part_entry *record;
187 /* Function returning the next available partition */
188 disk_part_iter_func next;
189 /* Partition-/scheme-specific details */
195 /* The first extended partition's start LBA */
196 uint64_t lba_extended;
197 /* Any applicable parent, or NULL */
198 struct disk_part_iter *parent;
199 /* The parent extended partition index */
204 /* Real (not effective) index in the partition table */
206 /* Current partition GUID */
207 const struct guid *part_guid;
208 /* Current partition label */
209 const char *part_label;
210 /* Count of entries in GPT */
212 /* Partition record size */
218 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
220 const struct disk_dos_part_entry *ebr_table;
221 const struct disk_dos_part_entry *parent_table =
222 ((const struct disk_dos_mbr *)part->private.ebr.parent->block)->table;
223 static const struct disk_dos_part_entry phony = {.start_lba = 0 };
226 /* Don't look for a "next EBR" the first time around */
227 if (part->private.ebr.parent_index >= 0)
228 /* Look at the linked list */
229 ebr_table = ((const struct disk_dos_mbr *)part->block)->table + 1;
230 /* Do we need to look for an extended partition? */
231 if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
232 /* Start looking for an extended partition in the MBR */
233 while (++part->private.ebr.parent_index < 4) {
234 uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
236 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
239 if (part->private.ebr.parent_index == 4)
240 /* No extended partitions found */
242 part->private.ebr.lba_extended =
243 parent_table[part->private.ebr.parent_index].start_lba;
247 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
249 part->block = disk_read_sectors(&diskinfo, ebr_lba, 1);
251 error("Could not load EBR!\n");
254 ebr_table = ((const struct disk_dos_mbr *)part->block)->table;
255 dprintf("next_ebr_part:\n");
256 disk_dos_part_dump(ebr_table);
259 * Sanity check entry: must not extend outside the
260 * extended partition. This is necessary since some OSes
261 * put crap in some entries.
264 const struct disk_dos_mbr *mbr =
265 (const struct disk_dos_mbr *)part->private.ebr.parent->block;
266 const struct disk_dos_part_entry *extended =
267 mbr->table + part->private.ebr.parent_index;
269 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
270 dprintf("Insane logical partition!\n");
275 part->lba_data = ebr_table[0].start_lba + ebr_lba;
276 dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
278 part->record = ebr_table;
288 free(part->private.ebr.parent->block);
289 free(part->private.ebr.parent);
295 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
297 struct disk_part_iter *ebr_part;
298 /* Look at the partition table */
299 struct disk_dos_part_entry *table =
300 ((struct disk_dos_mbr *)part->block)->table;
302 /* Look for data partitions */
303 while (++part->private.mbr_index < 4) {
304 uint8_t type = table[part->private.mbr_index].ostype;
306 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
307 /* Skip empty or extended partitions */
309 if (!table[part->private.mbr_index].length)
314 /* If we're currently the last partition, it's time for EBR processing */
315 if (part->private.mbr_index == 4) {
316 /* Allocate another iterator for extended partitions */
317 ebr_part = malloc(sizeof(*ebr_part));
319 error("Could not allocate extended partition iterator!\n");
322 /* Setup EBR iterator parameters */
323 ebr_part->block = NULL;
325 ebr_part->record = NULL;
326 ebr_part->next = next_ebr_part;
327 ebr_part->private.ebr.parent = part;
328 /* Trigger an initial EBR load */
329 ebr_part->private.ebr.parent_index = -1;
330 /* The EBR iterator is responsible for freeing us */
331 return next_ebr_part(ebr_part);
333 dprintf("next_mbr_part:\n");
334 disk_dos_part_dump(table + part->private.mbr_index);
336 /* Update parameters to reflect this new partition. Re-use iterator */
337 part->lba_data = table[part->private.mbr_index].start_lba;
338 dprintf("Partition %d primary lba %u\n", part->index, part->lba_data);
340 part->record = table + part->private.mbr_index;
352 static void gpt_dump(const struct disk_gpt_header *gpt)
356 printf("GPT sig ______________ : '%8.8s'\n"
357 "GPT major revision ___ : 0x%.4x\n"
358 "GPT minor revision ___ : 0x%.4x\n"
359 "GPT header size ______ : 0x%.8x\n"
360 "GPT header checksum __ : 0x%.8x\n"
361 "GPT reserved _________ : '%4.4s'\n"
362 "GPT LBA current ______ : 0x%.16llx\n"
363 "GPT LBA alternative __ : 0x%.16llx\n"
364 "GPT LBA first usable _ : 0x%.16llx\n"
365 "GPT LBA last usable __ : 0x%.16llx\n"
366 "GPT LBA part. table __ : 0x%.16llx\n"
367 "GPT partition count __ : 0x%.8x\n"
368 "GPT partition size ___ : 0x%.8x\n"
369 "GPT part. table chksum : 0x%.8x\n",
371 gpt->rev.fields.major,
372 gpt->rev.fields.minor,
378 gpt->lba_first_usable,
379 gpt->lba_last_usable,
380 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
381 guid_to_str(guid_text, &gpt->disk_guid);
382 printf("GPT disk GUID ________ : {%s}\n", guid_text);
386 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
388 const struct disk_gpt_part_entry *gpt_part = NULL;
390 while (++part->private.gpt.index < part->private.gpt.parts) {
392 (const struct disk_gpt_part_entry *)(part->block +
393 (part->private.gpt.index *
394 part->private.gpt.size));
395 if (!gpt_part->lba_first)
399 /* Were we the last partition? */
400 if (part->private.gpt.index == part->private.gpt.parts) {
403 part->lba_data = gpt_part->lba_first;
404 part->private.gpt.part_guid = &gpt_part->uid;
405 part->private.gpt.part_label = gpt_part->name;
406 /* Update our index */
409 disk_gpt_part_dump(gpt_part);
412 /* In a GPT scheme, we re-use the iterator */
422 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
424 const struct disk_gpt_header *gpt_candidate;
427 * Ignore any passed partition iterator. The caller should
428 * have passed NULL. Allocate a new partition iterator
430 part = malloc(sizeof(*part));
432 error("Count not allocate partition iterator!\n");
436 part->block = disk_read_sectors(&diskinfo, 0, 2);
438 error("Could not read two sectors!\n");
441 /* Check for an MBR */
442 if (((struct disk_dos_mbr *)part->block)->sig != disk_mbr_sig_magic) {
443 error("No MBR magic!\n");
446 /* Establish a pseudo-partition for the MBR (index 0) */
449 part->private.mbr_index = -1;
450 part->next = next_mbr_part;
451 /* Check for a GPT disk */
452 gpt_candidate = (const struct disk_gpt_header *)(part->block + SECTOR);
454 (gpt_candidate->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
455 /* LBA for partition table */
458 /* It looks like one */
459 /* TODO: Check checksum. Possibly try alternative GPT */
461 puts("Looks like a GPT disk.");
462 gpt_dump(gpt_candidate);
464 /* TODO: Check table checksum (maybe) */
465 /* Note relevant GPT details */
466 part->next = next_gpt_part;
467 part->private.gpt.index = -1;
468 part->private.gpt.parts = gpt_candidate->part_count;
469 part->private.gpt.size = gpt_candidate->part_size;
470 lba_table = gpt_candidate->lba_table;
471 gpt_candidate = NULL;
472 /* Load the partition table */
475 disk_read_sectors(&diskinfo, lba_table,
476 ((part->private.gpt.size *
477 part->private.gpt.parts) + SECTOR -
480 error("Could not read GPT partition list!\n");
484 /* Return the pseudo-partition's next partition, which is real */
485 return part->next(part);
502 * Search for a specific drive/partition, based on the GPT GUID.
503 * We return the disk drive number if found, as well as populating the
504 * boot_part pointer with the matching partition, if applicable.
505 * If no matching partition is found or the GUID is a disk GUID,
506 * boot_part will be populated with NULL. If not matching disk is
507 * found, we return -1.
509 static int find_by_guid(const struct guid *gpt_guid,
510 struct disk_part_iter **boot_part)
514 struct disk_gpt_header *header;
516 for (drive = 0x80; drive <= 0xff; drive++) {
517 if (disk_get_params(drive, &diskinfo))
518 continue; /* Drive doesn't exist */
519 if (!(header = disk_read_sectors(&diskinfo, 1, 1)))
520 continue; /* Cannot read sector */
522 (&header->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
530 is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
533 /* Check for a matching partition */
534 boot_part[0] = get_first_partition(NULL);
535 while (boot_part[0]) {
537 !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
541 boot_part[0] = boot_part[0]->next(boot_part[0]);
552 * Search for a specific partition, based on the GPT label.
553 * We return the disk drive number if found, as well as populating the
554 * boot_part pointer with the matching partition, if applicable.
555 * If no matching partition is found, boot_part will be populated with
556 * NULL and we return -1.
558 static int find_by_label(const char *label, struct disk_part_iter **boot_part)
563 for (drive = 0x80; drive <= 0xff; drive++) {
564 if (disk_get_params(drive, &diskinfo))
565 continue; /* Drive doesn't exist */
566 /* Check for a GPT disk */
567 boot_part[0] = get_first_partition(NULL);
568 if (!(boot_part[0]->next == next_gpt_part)) {
570 while (boot_part[0]) {
571 /* Run through until the end */
572 boot_part[0] = boot_part[0]->next(boot_part[0]);
576 /* Check for a matching partition */
577 while (boot_part[0]) {
578 char gpt_label[sizeof(((struct disk_gpt_part_entry *) NULL)->name)];
579 const char *gpt_label_scanner =
580 boot_part[0]->private.gpt.part_label;
583 /* Re-write the GPT partition label as ASCII */
584 while (gpt_label_scanner <
585 boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
586 if ((gpt_label[j] = *gpt_label_scanner))
590 if ((is_me = !strcmp(label, gpt_label)))
592 boot_part[0] = boot_part[0]->next(boot_part[0]);
601 static void do_boot(struct data_area *data, int ndata,
602 struct syslinux_rm_regs *regs)
604 uint16_t *const bios_fbm = (uint16_t *) 0x413;
605 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
606 struct syslinux_memmap *mmap;
607 struct syslinux_movelist *mlist = NULL;
609 uint8_t driveno = regs->edx.b[0];
610 uint8_t swapdrive = driveno & 0x80;
613 mmap = syslinux_memory_map();
616 error("Cannot read system memory map\n");
621 for (i = 0; i < ndata; i++) {
622 if (data[i].base + data[i].size > endimage)
623 endimage = data[i].base + data[i].size;
625 if (endimage > dosmem)
628 for (i = 0; i < ndata; i++) {
629 if (syslinux_add_movelist(&mlist, data[i].base,
630 (addr_t) data[i].data, data[i].size))
634 if (opt.swap && driveno != swapdrive) {
635 static const uint8_t swapstub_master[] = {
636 /* The actual swap code */
637 0x53, /* 00: push bx */
638 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
639 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
640 0x5b, /* 08: pop bx */
641 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
642 0x90, 0x90, /* 0E: nop; nop */
643 /* Code to install this in the right location */
644 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
645 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
646 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
647 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
648 0x4f, /* 1F: dec di */
649 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
650 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
651 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
652 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
653 0x8e, 0xc7, /* 32: mov es,di */
654 0x31, 0xff, /* 34: xor di,di */
655 0xf3, 0x66, 0xa5, /* 36: rep movsd */
656 0xbe, 0, 0, /* 39: mov si,0 */
657 0xbf, 0, 0, /* 3C: mov di,0 */
658 0x8e, 0xde, /* 3F: mov ds,si */
659 0x8e, 0xc7, /* 41: mov es,di */
660 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
661 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
662 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
663 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
664 /* pad out to segment boundary */
665 0x90, 0x90, /* 5A: ... */
666 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
668 static uint8_t swapstub[1024];
671 /* Note: we can't rely on either INT 13h nor the dosmem
672 vector to be correct at this stage, so we have to use an
673 installer stub to put things in the right place.
674 Round the installer location to a 1K boundary so the only
675 possible overlap is the identity mapping. */
676 endimage = (endimage + 1023) & ~1023;
678 /* Create swap stub */
679 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
680 *(uint16_t *) & swapstub[0x3a] = regs->ds;
681 *(uint16_t *) & swapstub[0x3d] = regs->es;
682 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
683 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
684 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
685 *(uint16_t *) & swapstub[0x56] = regs->ip;
686 *(uint16_t *) & swapstub[0x58] = regs->cs;
687 p = &swapstub[sizeof swapstub_master];
689 /* Mapping table; start out with identity mapping everything */
690 for (i = 0; i < 256; i++)
693 /* And the actual swap */
694 p[driveno] = swapdrive;
695 p[swapdrive] = driveno;
697 /* Adjust registers */
698 regs->ds = regs->cs = endimage >> 4;
699 regs->es = regs->esi.l = 0;
700 regs->ecx.l = sizeof swapstub >> 2;
701 regs->ip = 0x10; /* Installer offset */
702 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
704 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
708 endimage += sizeof swapstub;
711 /* Tell the shuffler not to muck with this area... */
712 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
714 /* Force text mode */
715 syslinux_force_text_mode();
717 fputs("Booting...\n", stdout);
718 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
719 error("Chainboot failed!\n");
723 error("Loader file too large\n");
727 error("Out of memory\n");
731 static int hide_unhide(struct disk_dos_mbr *mbr, int part)
734 struct disk_dos_part_entry *pt;
735 const uint16_t mask =
736 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
741 bool write_back = false;
743 for (i = 1; i <= 4; i++) {
744 pt = mbr->table + i - 1;
746 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
747 /* It's a hideable partition type */
749 t &= ~0x10; /* unhide */
751 t |= 0x10; /* hide */
753 if (t != pt->ostype) {
760 return disk_write_verify_sector(&diskinfo, 0, mbr);
765 static uint32_t get_file_lba(const char *filename)
770 /* Start with clean registers */
771 memset(&inregs, 0, sizeof(com32sys_t));
773 /* Put the filename in the bounce buffer */
774 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
776 /* Call comapi_open() which returns a structure pointer in SI
777 * to a structure whose first member happens to be the LBA.
779 inregs.eax.w[0] = 0x0006;
780 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
781 inregs.es = SEG(__com32.cs_bounce);
782 __com32.cs_intcall(0x22, &inregs, &inregs);
784 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
785 return 0; /* Filename not found */
788 /* Since the first member is the LBA, we simply cast */
789 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
791 /* Clean the registers for the next call */
792 memset(&inregs, 0, sizeof(com32sys_t));
794 /* Put the filename in the bounce buffer */
795 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
797 /* Call comapi_close() to free the structure */
798 inregs.eax.w[0] = 0x0008;
799 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
800 inregs.es = SEG(__com32.cs_bounce);
801 __com32.cs_intcall(0x22, &inregs, &inregs);
806 static void usage(void)
808 static const char usage[] = "\
809 Usage: chain.c32 [options]\n\
810 chain.c32 hd<disk#> [<partition>] [options]\n\
811 chain.c32 fd<disk#> [options]\n\
812 chain.c32 mbr:<id> [<partition>] [options]\n\
813 chain.c32 guid:<guid> [<partition>] [options]\n\
814 chain.c32 label:<label> [<partition>] [options]\n\
815 chain.c32 boot [<partition>] [options]\n\
816 chain.c32 fs [options]\n\
817 Options: file=<loader> Load and execute file, instead of boot sector\n\
818 isolinux=<loader> Load another version of ISOLINUX\n\
819 ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
820 cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
821 freedos=<loader> Load FreeDOS KERNEL.SYS\n\
822 msdos=<loader> Load MS-DOS IO.SYS\n\
823 pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
824 grub=<loader> Load GRUB Legacy stage2\n\
825 grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
826 grldr=<loader> Load GRUB4DOS grldr\n\
827 seg=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\
828 swap Swap drive numbers, if bootdisk is not fd0/hd0\n\
829 hide Hide primary partitions, except selected partition\n\
830 sethidden Set the FAT/NTFS hidden sectors field\n\
831 keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
832 See syslinux/com32/modules/chain.c for more information\n";
836 int main(int argc, char *argv[])
838 struct disk_dos_mbr *mbr = NULL;
840 struct disk_part_iter *cur_part = NULL;
841 struct syslinux_rm_regs regs;
842 char *drivename, *partition;
843 int hd, drive, whichpart = 0; /* MBR by default */
845 uint64_t fs_lba = 0; /* Syslinux partition */
846 uint32_t file_lba = 0;
847 struct guid gpt_guid;
848 unsigned char *isolinux_bin;
849 uint32_t *checksum, *chkhead, *chktail;
850 struct data_area data[3];
853 static const char cmldr_signature[8] = "cmdcons";
855 openconsole(&dev_null_r, &dev_stdcon_w);
860 /* Prepare the register set */
861 memset(®s, 0, sizeof regs);
863 for (i = 1; i < argc; i++) {
864 if (!strncmp(argv[i], "file=", 5)) {
865 opt.loadfile = argv[i] + 5;
866 } else if (!strncmp(argv[i], "seg=", 4)) {
867 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
868 if (segval < 0x50 || segval > 0x9f000) {
869 error("Invalid segment\n");
873 } else if (!strncmp(argv[i], "isolinux=", 9)) {
874 opt.loadfile = argv[i] + 9;
876 } else if (!strncmp(argv[i], "ntldr=", 6)) {
877 opt.seg = 0x2000; /* NTLDR wants this address */
878 opt.loadfile = argv[i] + 6;
879 opt.sethidden = true;
880 } else if (!strncmp(argv[i], "cmldr=", 6)) {
881 opt.seg = 0x2000; /* CMLDR wants this address */
882 opt.loadfile = argv[i] + 6;
884 opt.sethidden = true;
885 } else if (!strncmp(argv[i], "freedos=", 8)) {
886 opt.seg = 0x60; /* FREEDOS wants this address */
887 opt.loadfile = argv[i] + 8;
888 opt.sethidden = true;
889 } else if (!strncmp(argv[i], "msdos=", 6) ||
890 !strncmp(argv[i], "pcdos=", 6)) {
891 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
892 opt.loadfile = argv[i] + 6;
893 opt.sethidden = true;
894 } else if (!strncmp(argv[i], "grub=", 5)) {
895 opt.seg = 0x800; /* stage2 wants this address */
896 opt.loadfile = argv[i] + 5;
898 } else if (!strncmp(argv[i], "grubcfg=", 8)) {
899 opt.grubcfg = argv[i] + 8;
900 } else if (!strncmp(argv[i], "grldr=", 6)) {
901 opt.loadfile = argv[i] + 6;
903 } else if (!strcmp(argv[i], "swap")) {
905 } else if (!strcmp(argv[i], "noswap")) {
907 } else if (!strcmp(argv[i], "hide")) {
909 } else if (!strcmp(argv[i], "nohide")) {
911 } else if (!strcmp(argv[i], "keeppxe")) {
913 } else if (!strcmp(argv[i], "sethidden")) {
914 opt.sethidden = true;
915 } else if (!strcmp(argv[i], "nosethidden")) {
916 opt.sethidden = false;
917 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
918 && argv[i][1] == 'd')
919 || !strncmp(argv[i], "mbr:", 4)
920 || !strncmp(argv[i], "mbr=", 4)
921 || !strncmp(argv[i], "guid:", 5)
922 || !strncmp(argv[i], "guid=", 5)
923 || !strncmp(argv[i], "label:", 6)
924 || !strncmp(argv[i], "label=", 6)
925 || !strcmp(argv[i], "boot")
926 || !strncmp(argv[i], "boot,", 5)
927 || !strcmp(argv[i], "fs")) {
929 p = strchr(drivename, ',');
933 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
934 && argv[i + 1][0] <= '9') {
935 partition = argv[++i];
943 if (opt.grubcfg && !opt.grub) {
944 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
949 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
951 regs.ip = regs.esp.l = 0x7c00;
955 if (!strncmp(drivename, "mbr", 3)) {
956 drive = find_disk(strtoul(drivename + 4, NULL, 0));
958 error("Unable to find requested MBR signature\n");
961 } else if (!strncmp(drivename, "guid", 4)) {
962 if (str_to_guid(drivename + 5, &gpt_guid))
964 drive = find_by_guid(&gpt_guid, &cur_part);
966 error("Unable to find requested GPT disk/partition\n");
969 } else if (!strncmp(drivename, "label", 5)) {
971 error("No label specified.\n");
974 drive = find_by_label(drivename + 6, &cur_part);
976 error("Unable to find requested partition by label\n");
979 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
980 drivename[1] == 'd') {
981 hd = drivename[0] == 'h';
983 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
984 } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
985 const union syslinux_derivative_info *sdi;
987 sdi = syslinux_derivative_info();
988 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
989 drive = 0x80; /* Boot drive not available */
991 drive = sdi->disk.drive_number;
992 if (!strcmp(drivename, "fs")
993 && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
994 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
995 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
996 /* We should lookup the Syslinux partition number and use it */
997 fs_lba = *sdi->disk.partoffset;
999 error("Unparsable drive specification\n");
1003 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1004 regs.ebx.b[0] = regs.edx.b[0] = drive;
1006 /* Get the disk geometry and disk access setup */
1007 if (disk_get_params(drive, &diskinfo)) {
1008 error("Cannot get disk parameters\n");
1013 if (!(mbr = disk_read_sectors(&diskinfo, 0, 1))) {
1014 error("Cannot read Master Boot Record or sector 0\n");
1019 whichpart = strtoul(partition, NULL, 0);
1020 /* "guid:" or "label:" might have specified a partition */
1022 whichpart = cur_part->index;
1024 /* Boot the MBR by default */
1025 if (!cur_part && (whichpart || fs_lba)) {
1026 /* Boot a partition, possibly the Syslinux partition itself */
1027 cur_part = get_first_partition(NULL);
1029 if ((cur_part->index == whichpart)
1030 || (cur_part->lba_data == fs_lba))
1031 /* Found the partition to boot */
1033 cur_part = cur_part->next(cur_part);
1036 error("Requested partition not found!\n");
1039 whichpart = cur_part->index;
1042 if (!(drive & 0x80) && whichpart) {
1043 error("Warning: Partitions of floppy devices may not work\n");
1047 * GRLDR of GRUB4DOS wants the partition number in DH:
1048 * -1: whole drive (default)
1049 * 0-3: primary partitions
1050 * 4-*: logical partitions
1053 regs.edx.b[1] = whichpart - 1;
1056 if (whichpart < 1 || whichpart > 4)
1057 error("WARNING: hide specified without a non-primary partition\n");
1058 if (hide_unhide(mbr, whichpart))
1059 error("WARNING: failed to write MBR for 'hide'\n");
1062 /* Do the actual chainloading */
1063 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1066 fputs("Loading the boot file...\n", stdout);
1067 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1068 error("Failed to load the boot file\n");
1071 data[ndata].base = load_base;
1072 load_base = 0x7c00; /* If we also load a boot sector */
1074 /* Create boot info table: needed when you want to chainload
1075 another version of ISOLINUX (or another bootlaoder that needs
1076 the -boot-info-table switch of mkisofs)
1077 (will only work when run from ISOLINUX) */
1079 const union syslinux_derivative_info *sdi;
1080 sdi = syslinux_derivative_info();
1082 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1083 /* Boot info table info (integers in little endian format)
1085 Offset Name Size Meaning
1086 8 bi_pvd 4 bytes LBA of primary volume descriptor
1087 12 bi_file 4 bytes LBA of boot file
1088 16 bi_length 4 bytes Boot file length in bytes
1089 20 bi_csum 4 bytes 32-bit checksum
1090 24 bi_reserved 40 bytes Reserved
1092 The 32-bit checksum is the sum of all the 32-bit words in the
1093 boot file starting at byte offset 64. All linear block
1094 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1096 LBA of primary volume descriptor should already be set to 16.
1099 isolinux_bin = (unsigned char *)data[ndata].data;
1101 /* Get LBA address of bootfile */
1102 file_lba = get_file_lba(opt.loadfile);
1104 if (file_lba == 0) {
1105 error("Failed to find LBA offset of the boot file\n");
1109 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1111 /* Set boot file length */
1112 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1114 /* Calculate checksum */
1115 checksum = (uint32_t *) & isolinux_bin[20];
1116 chkhead = (uint32_t *) & isolinux_bin[64];
1117 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1119 while (chkhead < chktail)
1120 *checksum += *chkhead++;
1123 * Deal with possible fractional dword at the end;
1124 * this *should* never happen...
1126 if (data[ndata].size & 3) {
1128 memcpy(&xword, chkhead, data[ndata].size & 3);
1133 ("The isolinux= option is only valid when run from ISOLINUX\n");
1139 /* Layout of stage2 file (from byte 0x0 to 0x270) */
1140 struct grub_stage2_patch_area {
1142 char unknown[0x206];
1143 /* 0x206: compatibility version number major */
1144 uint8_t compat_version_major;
1145 /* 0x207: compatibility version number minor */
1146 uint8_t compat_version_minor;
1148 /* 0x208: install_partition variable */
1150 /* 0x208: sub-partition in sub-partition part2 */
1152 /* 0x209: sub-partition in top-level partition */
1154 /* 0x20a: top-level partiton number */
1156 /* 0x20b: BIOS drive number (must be 0) */
1158 } __attribute__ ((packed)) install_partition;
1160 /* 0x20c: deprecated (historical reason only) */
1161 uint32_t saved_entryno;
1162 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
1164 /* 0x211: force LBA */
1166 /* 0x212: version string (will probably be 0.97) */
1167 char version_string[5];
1168 /* 0x217: config filename */
1169 char config_file[89];
1170 /* 0x270: start of code (after jump from 0x200) */
1172 } __attribute__ ((packed)) *stage2;
1174 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
1176 ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
1180 stage2 = data[ndata].data;
1183 * Check the compatibility version number to see if we loaded a real
1184 * stage2 file or a stage2 file that we support.
1186 if (stage2->compat_version_major != 3
1187 || stage2->compat_version_minor != 2) {
1189 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
1193 /* jump 0x200 bytes into the loadfile */
1197 * GRUB Legacy wants the partition number in the install_partition
1198 * variable, located at offset 0x208 of stage2.
1199 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
1201 * It looks very similar to the "boot information format" of the
1202 * Multiboot specification:
1203 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
1205 * 0x208 = part3: sub-partition in sub-partition part2
1206 * 0x209 = part2: sub-partition in top-level partition
1207 * 0x20a = part1: top-level partition number
1208 * 0x20b = drive: BIOS drive number (must be 0)
1210 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
1213 * Partition numbers always start from zero.
1214 * Unused partition bytes must be set to 0xFF.
1216 * We only care about top-level partition, so we only need to change
1217 * "part1" to the appropriate value:
1218 * -1: whole drive (default) (-1 = 0xFF)
1219 * 0-3: primary partitions
1220 * 4-*: logical partitions
1222 stage2->install_partition.part1 = whichpart - 1;
1225 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
1226 * config filename. The filename passed via grubcfg= will overwrite
1227 * the default config filename "/boot/grub/menu.lst".
1230 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
1232 ("The config filename length can't exceed 88 characters.\n");
1236 strcpy((char *)stage2->config_file, opt.grubcfg);
1243 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1244 /* Actually read the boot sector */
1246 data[ndata].data = mbr;
1250 disk_read_sectors(&diskinfo, cur_part->lba_data, 1))) {
1251 error("Cannot read boot sector\n");
1254 data[ndata].size = SECTOR;
1255 data[ndata].base = load_base;
1257 if (!opt.loadfile) {
1258 const struct disk_dos_mbr *br =
1259 (const struct disk_dos_mbr *)((char *)data[ndata].data +
1261 sizeof(struct disk_dos_mbr));
1262 if (br->sig != disk_mbr_sig_magic) {
1264 ("Boot sector signature not found (unbootable disk/partition?)\n");
1269 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1270 * the string "cmdcons\0" to memory location 0000:7C03.
1271 * Memory location 0000:7C00 contains the bootsector of the partition.
1273 if (cur_part && opt.cmldr) {
1274 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1275 sizeof cmldr_signature);
1279 * Modify the hidden sectors (partition offset) copy in memory;
1280 * this modifies the field used by FAT and NTFS filesystems, and
1281 * possibly other boot loaders which use the same format.
1283 if (cur_part && opt.sethidden) {
1284 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1291 if (cur_part->next == next_gpt_part) {
1292 /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
1293 struct disk_dos_part_entry *record;
1294 /* Look at the GPT partition */
1295 const struct disk_gpt_part_entry *gp =
1296 (const struct disk_gpt_part_entry *)
1298 (cur_part->private.gpt.size * cur_part->private.gpt.index));
1299 /* Note the partition length */
1300 uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
1301 /* The length of the hand-over */
1303 sizeof(struct disk_dos_part_entry) + sizeof(uint32_t) +
1304 cur_part->private.gpt.size;
1305 /* Will point to the partition record length in the hand-over */
1308 /* Allocate the hand-over record */
1309 record = malloc(synth_size);
1311 error("Could not build GPT hand-over record!\n");
1314 /* Synthesize the record */
1315 memset(record, 0, synth_size);
1316 record->active_flag = 0x80;
1317 record->ostype = 0xED;
1318 /* All bits set by default */
1319 record->start_lba = ~(uint32_t) 0;
1320 record->length = ~(uint32_t) 0;
1321 /* If these fit the precision, pass them on */
1322 if (cur_part->lba_data < record->start_lba)
1323 record->start_lba = cur_part->lba_data;
1324 if (lba_count < record->length)
1325 record->length = lba_count;
1326 /* Next comes the GPT partition record length */
1327 plen = (uint32_t *) (record + 1);
1328 plen[0] = cur_part->private.gpt.size;
1329 /* Next comes the GPT partition record copy */
1330 memcpy(plen + 1, gp, plen[0]);
1331 cur_part->record = record;
1333 regs.eax.l = 0x54504721; /* '!GPT' */
1334 data[ndata].base = 0x7be;
1335 data[ndata].size = synth_size;
1336 data[ndata].data = (void *)record;
1338 regs.esi.w[0] = 0x7be;
1340 dprintf("GPT handover:\n");
1341 disk_dos_part_dump(record);
1343 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
1345 } else if (cur_part->record) {
1346 /* MBR handover protocol */
1347 static struct disk_dos_part_entry handover_record;
1349 handover_record = *cur_part->record;
1350 handover_record.start_lba = cur_part->lba_data;
1352 data[ndata].base = 0x7be;
1353 data[ndata].size = sizeof handover_record;
1354 data[ndata].data = &handover_record;
1356 regs.esi.w[0] = 0x7be;
1358 dprintf("MBR handover:\n");
1359 disk_dos_part_dump(&handover_record);
1363 do_boot(data, ndata, ®s);
1367 free(cur_part->block);
1368 free((void *)cur_part->record);