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]
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 hd<disk#> [<partition>] [options]
22 * chain fd<disk#> [options]
23 * chain mbr:<id> [<partition>] [options]
24 * chain boot [<partition>] [options]
26 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
30 * The mbr: syntax means search all the hard disks until one with a
31 * specific MBR serial number (bytes 440-443) is found.
33 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
38 * loads the file <loader> **from the SYSLINUX filesystem**
39 * instead of loading the boot sector.
42 * loads at and jumps to <seg>:0000 instead of 0000:7C00.
45 * chainload another version/build of the ISOLINUX bootloader and patch
46 * the loader with appropriate parameters in memory.
47 * This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
48 * when you want more than one ISOLINUX per CD/DVD.
51 * equivalent to seg=0x2000 file=<loader> sethidden,
52 * used with WinNT's loaders
55 * used with Recovery Console of Windows NT/2K/XP.
56 * same as ntldr=<loader> & "cmdcons\0" written to
57 * the system name field in the bootsector
60 * equivalent to seg=0x60 file=<loader> sethidden,
61 * used with FreeDOS kernel.sys.
65 * equivalent to seg=0x70 file=<loader> sethidden,
66 * used with DOS' io.sys.
69 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
70 * used with GRUB stage2 files.
73 * if the disk is not fd0/hd0, install a BIOS stub which swaps
77 * change type of primary partitions with IDs 01, 04, 06, 07,
78 * 0b, 0c, or 0e to 1x, except for the selected partition, which
79 * is converted the other way.
82 * update the "hidden sectors" (partition offset) field in a
83 * FAT/NTFS boot sector.
86 #define DEBUG 0 /* 1 to enable */
96 #include <syslinux/loadfile.h>
97 #include <syslinux/bootrm.h>
98 #include <syslinux/config.h>
99 #include <syslinux/video.h>
101 #define SECTOR 512 /* bytes/sector */
103 static struct options {
104 const char *loadfile;
121 static inline void error(const char *msg)
127 * Call int 13h, but with retry on failure. Especially floppies need this.
129 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
131 int retry = 6; /* Number of retries */
138 __intcall(0x13, inreg, outreg);
139 if (!(outreg->eflags.l & EFLAGS_CF))
140 return 0; /* CF=0, OK */
143 return -1; /* Error */
147 * Query disk parameters and EBIOS availability for a particular disk.
151 int ebios; /* EBIOS supported on this disk */
152 int cbios; /* CHS geometry is valid */
157 static int get_disk_params(int disk)
159 static com32sys_t getparm, parm, getebios, ebios;
161 disk_info.disk = disk;
162 disk_info.ebios = disk_info.cbios = 0;
164 /* Get EBIOS support */
165 getebios.eax.w[0] = 0x4100;
166 getebios.ebx.w[0] = 0x55aa;
167 getebios.edx.b[0] = disk;
168 getebios.eflags.b[0] = 0x3; /* CF set */
170 __intcall(0x13, &getebios, &ebios);
172 if (!(ebios.eflags.l & EFLAGS_CF) &&
173 ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
177 /* Get disk parameters -- really only useful for
178 hard disks, but if we have a partitioned floppy
179 it's actually our best chance... */
180 getparm.eax.b[1] = 0x08;
181 getparm.edx.b[0] = disk;
183 __intcall(0x13, &getparm, &parm);
185 if (parm.eflags.l & EFLAGS_CF)
186 return disk_info.ebios ? 0 : -1;
188 disk_info.head = parm.edx.b[1] + 1;
189 disk_info.sect = parm.ecx.b[0] & 0x3f;
190 if (disk_info.sect == 0) {
193 disk_info.cbios = 1; /* Valid geometry */
200 * Get a disk block and return a malloc'd buffer.
201 * Uses the disk number and information from disk_info.
211 /* Read count sectors from drive, starting at lba. Return a new buffer */
212 static void *read_sectors(uint64_t lba, uint8_t count)
215 struct ebios_dapa *dapa = __com32.cs_bounce;
216 void *buf = (char *)__com32.cs_bounce + SECTOR;
223 memset(&inreg, 0, sizeof inreg);
225 if (disk_info.ebios) {
226 dapa->len = sizeof(*dapa);
228 dapa->off = OFFS(buf);
229 dapa->seg = SEG(buf);
232 inreg.esi.w[0] = OFFS(dapa);
233 inreg.ds = SEG(dapa);
234 inreg.edx.b[0] = disk_info.disk;
235 inreg.eax.b[1] = 0x42; /* Extended read */
237 unsigned int c, h, s, t;
239 if (!disk_info.cbios) {
240 /* We failed to get the geometry */
243 return NULL; /* Can only read MBR */
249 s = (lba % disk_info.sect) + 1;
250 t = lba / disk_info.sect; /* Track = head*cyl */
251 h = t % disk_info.head;
252 c = t / disk_info.head;
255 if (s > 63 || h > 256 || c > 1023)
258 inreg.eax.b[0] = count;
259 inreg.eax.b[1] = 0x02; /* Read */
260 inreg.ecx.b[1] = c & 0xff;
261 inreg.ecx.b[0] = s + (c >> 6);
263 inreg.edx.b[0] = disk_info.disk;
264 inreg.ebx.w[0] = OFFS(buf);
268 if (int13_retry(&inreg, NULL))
271 data = malloc(count * SECTOR);
273 memcpy(data, buf, count * SECTOR);
277 static int write_sector(unsigned int lba, const void *data)
280 struct ebios_dapa *dapa = __com32.cs_bounce;
281 void *buf = (char *)__com32.cs_bounce + SECTOR;
283 memcpy(buf, data, SECTOR);
284 memset(&inreg, 0, sizeof inreg);
286 if (disk_info.ebios) {
287 dapa->len = sizeof(*dapa);
288 dapa->count = 1; /* 1 sector */
289 dapa->off = OFFS(buf);
290 dapa->seg = SEG(buf);
293 inreg.esi.w[0] = OFFS(dapa);
294 inreg.ds = SEG(dapa);
295 inreg.edx.b[0] = disk_info.disk;
296 inreg.eax.w[0] = 0x4300; /* Extended write */
298 unsigned int c, h, s, t;
300 if (!disk_info.cbios) {
301 /* We failed to get the geometry */
304 return -1; /* Can only write MBR */
310 s = (lba % disk_info.sect) + 1;
311 t = lba / disk_info.sect; /* Track = head*cyl */
312 h = t % disk_info.head;
313 c = t / disk_info.head;
316 if (s > 63 || h > 256 || c > 1023)
319 inreg.eax.w[0] = 0x0301; /* Write one sector */
320 inreg.ecx.b[1] = c & 0xff;
321 inreg.ecx.b[0] = s + (c >> 6);
323 inreg.edx.b[0] = disk_info.disk;
324 inreg.ebx.w[0] = OFFS(buf);
328 if (int13_retry(&inreg, NULL))
334 static int write_verify_sector(unsigned int lba, const void *buf)
339 rv = write_sector(lba, buf);
341 return rv; /* Write failure */
342 rb = read_sectors(lba, 1);
344 return -1; /* Readback failure */
345 rv = memcmp(buf, rb, SECTOR);
351 * CHS (cylinder, head, sector) value extraction macros.
352 * Taken from WinVBlock. Does not expand to an lvalue
354 #define chs_head(chs) chs[0]
355 #define chs_sector(chs) (chs[1] & 0x3F)
356 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
357 #define chs_cyl_low(chs) ((uint16_t)chs[2])
358 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
359 typedef uint8_t chs[3];
361 /* A DOS partition table entry */
363 uint8_t active_flag; /* 0x80 if "active" */
369 } __attribute__ ((packed));
372 static void mbr_part_dump(const struct part_entry *part)
374 printf("-------------------------------\n"
375 "Partition status _____ : 0x%.2x\n"
376 "Partition CHS start\n"
377 " Cylinder ___________ : 0x%.4x\n"
378 " Head _______________ : 0x%.2x\n"
379 " Sector _____________ : 0x%.2x\n"
380 "Partition type _______ : 0x%.2x\n"
381 "Partition CHS end\n"
382 " Cylinder ___________ : 0x%.4x\n"
383 " Head _______________ : 0x%.2x\n"
384 " Sector _____________ : 0x%.2x\n"
385 "Partition LBA start __ : 0x%.16x\n"
386 "Partition LBA count __ : 0x%.16x\n",
388 chs_cylinder(part->start),
389 chs_head(part->start),
390 chs_sector(part->start),
392 chs_cylinder(part->end),
394 chs_sector(part->end), part->start_lba, part->length);
403 struct part_entry table[4];
405 } __attribute__ ((packed));
406 static const uint16_t mbr_sig_magic = 0xAA55;
408 /* Search for a specific drive, based on the MBR signature; bytes
410 static int find_disk(uint32_t mbr_sig)
416 for (drive = 0x80; drive <= 0xff; drive++) {
417 if (get_disk_params(drive))
418 continue; /* Drive doesn't exist */
419 if (!(mbr = read_sectors(0, 1)))
420 continue; /* Cannot read sector */
421 is_me = (mbr->disk_sig == mbr_sig);
429 /* Forward declaration */
430 struct disk_part_iter;
432 /* Partition-/scheme-specific routine returning the next partition */
433 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
436 /* Contains details for a partition under examination */
437 struct disk_part_iter {
438 /* The block holding the table we are part of */
440 /* The LBA for the beginning of data */
442 /* The partition number, as determined by our heuristic */
444 /* The DOS partition record to pass, if applicable */
445 const struct part_entry *record;
446 /* Function returning the next available partition */
447 disk_part_iter_func next;
448 /* Partition-/scheme-specific details */
454 /* The first extended partition's start LBA */
455 uint64_t lba_extended;
456 /* Any applicable parent, or NULL */
457 struct disk_part_iter *parent;
458 /* The parent extended partition index */
463 /* Real (not effective) index in the partition table */
465 /* Count of entries in GPT */
467 /* Partition record size */
473 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
475 const struct part_entry *ebr_table;
476 const struct part_entry *parent_table =
477 ((const struct mbr *)part->private.ebr.parent->block)->table;
478 static const struct part_entry phony = {.start_lba = 0 };
481 /* Don't look for a "next EBR" the first time around */
482 if (part->private.ebr.parent_index >= 0)
483 /* Look at the linked list */
484 ebr_table = ((const struct mbr *)part->block)->table + 1;
485 /* Do we need to look for an extended partition? */
486 if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
487 /* Start looking for an extended partition in the MBR */
488 while (++part->private.ebr.parent_index < 4) {
489 uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
491 if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
494 if (part->private.ebr.parent_index == 4)
495 /* No extended partitions found */
497 part->private.ebr.lba_extended =
498 parent_table[part->private.ebr.parent_index].start_lba;
502 ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
504 part->block = read_sectors(ebr_lba, 1);
506 error("Could not load EBR!\n");
509 ebr_table = ((const struct mbr *)part->block)->table;
511 mbr_part_dump(ebr_table);
514 * Sanity check entry: must not extend outside the
515 * extended partition. This is necessary since some OSes
516 * put crap in some entries.
519 const struct mbr *mbr =
520 (const struct mbr *)part->private.ebr.parent->block;
521 const struct part_entry *extended =
522 mbr->table + part->private.ebr.parent_index;
524 if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
525 error("Insane logical partition!\n");
530 part->lba_data = ebr_table[0].start_lba + ebr_lba;
532 part->record = ebr_table;
542 free(part->private.ebr.parent->block);
543 free(part->private.ebr.parent);
549 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
551 struct disk_part_iter *ebr_part;
552 /* Look at the partition table */
553 struct part_entry *table = ((struct mbr *)part->block)->table;
555 /* Look for data partitions */
556 while (++part->private.mbr_index < 4) {
557 uint8_t type = table[part->private.mbr_index].ostype;
559 if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
560 /* Skip empty or extended partitions */
562 if (!table[part->private.mbr_index].length)
567 /* If we're currently the last partition, it's time for EBR processing */
568 if (part->private.mbr_index == 4) {
569 /* Allocate another iterator for extended partitions */
570 ebr_part = malloc(sizeof(*ebr_part));
572 error("Could not allocate extended partition iterator!\n");
575 /* Setup EBR iterator parameters */
576 ebr_part->block = NULL;
578 ebr_part->record = NULL;
579 ebr_part->next = next_ebr_part;
580 ebr_part->private.ebr.parent = part;
581 /* Trigger an initial EBR load */
582 ebr_part->private.ebr.parent_index = -1;
583 /* The EBR iterator is responsible for freeing us */
584 return next_ebr_part(ebr_part);
587 mbr_part_dump(table + part->private.mbr_index);
589 /* Update parameters to reflect this new partition. Re-use iterator */
590 part->lba_data = table[part->private.mbr_index].start_lba;
592 part->record = table + part->private.mbr_index;
605 * Be careful with endianness, you must adjust it yourself
606 * iff you are directly using the fourth data chunk
613 } __attribute__ ((packed));
617 * Fill a buffer with a textual GUID representation.
618 * The buffer must be >= char[37] and will be populated
619 * with an ASCII NUL C string terminator.
620 * Example: 11111111-2222-3333-4444-444444444444
621 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
623 static void guid_to_str(char *buf, const struct guid *id)
626 * This walk-map effectively reverses the little-endian
627 * portions of the GUID in the output text
629 static const char le_walk_map[] = {
637 const char *walker = (const char *)id;
639 while (i < sizeof(le_walk_map)) {
640 walker += le_walk_map[i];
644 *buf = ((*walker & 0xF0) >> 4) + '0';
646 *buf += 'A' - '9' - 1;
648 *buf = (*walker & 0x0F) + '0';
650 *buf += 'A' - '9' - 1;
659 /* A GPT partition */
667 } __attribute__ ((packed));
670 static void gpt_part_dump(const struct gpt_part *gpt_part)
675 printf("----------------------------------\n"
676 "GPT part. LBA first __ : 0x%.16llx\n"
677 "GPT part. LBA last ___ : 0x%.16llx\n"
678 "GPT part. attribs ____ : 0x%.16llx\n"
679 "GPT part. name _______ : '",
680 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
681 for (i = 0; i < sizeof(gpt_part->name); i++) {
682 if (gpt_part->name[i])
683 printf("%c", gpt_part->name[i]);
686 guid_to_str(guid_text, &gpt_part->type);
687 printf("GPT part. type GUID __ : {%s}\n", guid_text);
688 guid_to_str(guid_text, &gpt_part->uid);
689 printf("GPT part. unique ID __ : {%s}\n", guid_text);
700 } fields __attribute__ ((packed));
703 } rev __attribute__ ((packed));
709 uint64_t lba_first_usable;
710 uint64_t lba_last_usable;
711 struct guid disk_guid;
715 uint32_t table_chksum;
717 } __attribute__ ((packed));
718 static const char gpt_sig_magic[] = "EFI PART";
721 static void gpt_dump(const struct gpt *gpt)
725 printf("GPT sig ______________ : '%8.8s'\n"
726 "GPT major revision ___ : 0x%.4x\n"
727 "GPT minor revision ___ : 0x%.4x\n"
728 "GPT header size ______ : 0x%.8x\n"
729 "GPT header checksum __ : 0x%.8x\n"
730 "GPT reserved _________ : '%4.4s'\n"
731 "GPT LBA current ______ : 0x%.16llx\n"
732 "GPT LBA alternative __ : 0x%.16llx\n"
733 "GPT LBA first usable _ : 0x%.16llx\n"
734 "GPT LBA last usable __ : 0x%.16llx\n"
735 "GPT LBA part. table __ : 0x%.16llx\n"
736 "GPT partition count __ : 0x%.8x\n"
737 "GPT partition size ___ : 0x%.8x\n"
738 "GPT part. table chksum : 0x%.8x\n",
740 gpt->rev.fields.major,
741 gpt->rev.fields.minor,
747 gpt->lba_first_usable,
748 gpt->lba_last_usable,
749 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
750 guid_to_str(guid_text, &gpt->disk_guid);
751 printf("GPT disk GUID ________ : {%s}\n", guid_text);
755 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
757 const struct gpt_part *gpt_part = NULL;
759 while (++part->private.gpt.index < part->private.gpt.parts) {
761 (const struct gpt_part *)(part->block +
762 (part->private.gpt.index *
763 part->private.gpt.size));
764 if (!gpt_part->lba_first)
768 /* Were we the last partition? */
769 if (part->private.gpt.index == part->private.gpt.parts) {
772 part->lba_data = gpt_part->lba_first;
773 /* Update our index */
776 gpt_part_dump(gpt_part);
778 /* In a GPT scheme, we re-use the iterator */
788 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
790 const struct gpt *gpt_candidate;
793 * Ignore any passed partition iterator. The caller should
794 * have passed NULL. Allocate a new partition iterator
796 part = malloc(sizeof(*part));
798 error("Count not allocate partition iterator!\n");
802 part->block = read_sectors(0, 2);
804 error("Could not read two sectors!\n");
807 /* Check for an MBR */
808 if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
809 error("No MBR magic!\n");
812 /* Establish a pseudo-partition for the MBR (index 0) */
815 part->private.mbr_index = -1;
816 part->next = next_mbr_part;
817 /* Check for a GPT disk */
818 gpt_candidate = (const struct gpt *)(part->block + SECTOR);
819 if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
820 /* LBA for partition table */
823 /* It looks like one */
824 /* TODO: Check checksum. Possibly try alternative GPT */
826 puts("Looks like a GPT disk.");
827 gpt_dump(gpt_candidate);
829 /* TODO: Check table checksum (maybe) */
830 /* Note relevant GPT details */
831 part->next = next_gpt_part;
832 part->private.gpt.index = -1;
833 part->private.gpt.parts = gpt_candidate->part_count;
834 part->private.gpt.size = gpt_candidate->part_size;
835 lba_table = gpt_candidate->lba_table;
836 gpt_candidate = NULL;
837 /* Load the partition table */
840 read_sectors(lba_table,
841 ((part->private.gpt.size * part->private.gpt.parts) +
842 SECTOR - 1) / SECTOR);
844 error("Could not read GPT partition list!\n");
848 /* Return the pseudo-partition's next partition, which is real */
849 return part->next(part);
865 static void do_boot(struct data_area *data, int ndata,
866 struct syslinux_rm_regs *regs)
868 uint16_t *const bios_fbm = (uint16_t *) 0x413;
869 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
870 struct syslinux_memmap *mmap;
871 struct syslinux_movelist *mlist = NULL;
873 uint8_t driveno = regs->edx.b[0];
874 uint8_t swapdrive = driveno & 0x80;
877 mmap = syslinux_memory_map();
880 error("Cannot read system memory map\n");
885 for (i = 0; i < ndata; i++) {
886 if (data[i].base + data[i].size > endimage)
887 endimage = data[i].base + data[i].size;
889 if (endimage > dosmem)
892 for (i = 0; i < ndata; i++) {
893 if (syslinux_add_movelist(&mlist, data[i].base,
894 (addr_t) data[i].data, data[i].size))
898 if (opt.swap && driveno != swapdrive) {
899 static const uint8_t swapstub_master[] = {
900 /* The actual swap code */
901 0x53, /* 00: push bx */
902 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
903 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
904 0x5b, /* 08: pop bx */
905 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
906 0x90, 0x90, /* 0E: nop; nop */
907 /* Code to install this in the right location */
908 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
909 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
910 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
911 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
912 0x4f, /* 1F: dec di */
913 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
914 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
915 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
916 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
917 0x8e, 0xc7, /* 32: mov es,di */
918 0x31, 0xff, /* 34: xor di,di */
919 0xf3, 0x66, 0xa5, /* 36: rep movsd */
920 0xbe, 0, 0, /* 39: mov si,0 */
921 0xbf, 0, 0, /* 3C: mov di,0 */
922 0x8e, 0xde, /* 3F: mov ds,si */
923 0x8e, 0xc7, /* 41: mov es,di */
924 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
925 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
926 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
927 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
928 /* pad out to segment boundary */
929 0x90, 0x90, /* 5A: ... */
930 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
932 static uint8_t swapstub[1024];
935 /* Note: we can't rely on either INT 13h nor the dosmem
936 vector to be correct at this stage, so we have to use an
937 installer stub to put things in the right place.
938 Round the installer location to a 1K boundary so the only
939 possible overlap is the identity mapping. */
940 endimage = (endimage + 1023) & ~1023;
942 /* Create swap stub */
943 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
944 *(uint16_t *) & swapstub[0x3a] = regs->ds;
945 *(uint16_t *) & swapstub[0x3d] = regs->es;
946 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
947 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
948 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
949 *(uint16_t *) & swapstub[0x56] = regs->ip;
950 *(uint16_t *) & swapstub[0x58] = regs->cs;
951 p = &swapstub[sizeof swapstub_master];
953 /* Mapping table; start out with identity mapping everything */
954 for (i = 0; i < 256; i++)
957 /* And the actual swap */
958 p[driveno] = swapdrive;
959 p[swapdrive] = driveno;
961 /* Adjust registers */
962 regs->ds = regs->cs = endimage >> 4;
963 regs->es = regs->esi.l = 0;
964 regs->ecx.l = sizeof swapstub >> 2;
965 regs->ip = 0x10; /* Installer offset */
966 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
968 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
972 endimage += sizeof swapstub;
975 /* Tell the shuffler not to muck with this area... */
976 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
978 /* Force text mode */
979 syslinux_force_text_mode();
981 fputs("Booting...\n", stdout);
982 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
983 error("Chainboot failed!\n");
987 error("Loader file too large\n");
991 error("Out of memory\n");
995 static int hide_unhide(struct mbr *mbr, int part)
998 struct part_entry *pt;
999 const uint16_t mask =
1000 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1005 bool write_back = false;
1007 for (i = 1; i <= 4; i++) {
1008 pt = mbr->table + i - 1;
1010 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1011 /* It's a hideable partition type */
1013 t &= ~0x10; /* unhide */
1015 t |= 0x10; /* hide */
1017 if (t != pt->ostype) {
1024 return write_verify_sector(0, mbr);
1029 static uint32_t get_file_lba(const char *filename)
1034 /* Start with clean registers */
1035 memset(&inregs, 0, sizeof(com32sys_t));
1037 /* Put the filename in the bounce buffer */
1038 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1040 /* Call comapi_open() which returns a structure pointer in SI
1041 * to a structure whose first member happens to be the LBA.
1043 inregs.eax.w[0] = 0x0006;
1044 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1045 inregs.es = SEG(__com32.cs_bounce);
1046 __com32.cs_intcall(0x22, &inregs, &inregs);
1048 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1049 return 0; /* Filename not found */
1052 /* Since the first member is the LBA, we simply cast */
1053 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1055 /* Clean the registers for the next call */
1056 memset(&inregs, 0, sizeof(com32sys_t));
1058 /* Put the filename in the bounce buffer */
1059 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1061 /* Call comapi_close() to free the structure */
1062 inregs.eax.w[0] = 0x0008;
1063 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1064 inregs.es = SEG(__com32.cs_bounce);
1065 __com32.cs_intcall(0x22, &inregs, &inregs);
1070 static void usage(void)
1072 error("Usage: chain.c32 hd<disk#> [<partition>] [options]\n"
1073 " chain.c32 fd<disk#> [options]\n"
1074 " chain.c32 mbr:<id> [<partition>] [options]\n"
1075 " chain.c32 boot [<partition>] [options]\n"
1076 "Options: file=<loader> load file, instead of boot sector\n"
1077 " isolinux=<loader> load another version of ISOLINUX\n"
1078 " ntldr=<loader> load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n"
1079 " cmldr=<loader> load Recovery Console of Windows NT/2K/XP\n"
1080 " freedos=<loader> load FreeDOS kernel.sys\n"
1081 " msdos=<loader> load MS-DOS io.sys\n"
1082 " pcdos=<loader> load PC-DOS ibmbio.com\n"
1083 " grub=<loader> load GRUB stage2\n"
1084 " seg=<segment> jump to <seg>:0000 instead of 0000:7C00\n"
1085 " swap swap drive numbers, if bootdisk is not fd0/hd0\n"
1086 " hide hide primary partitions, except selected partition\n"
1087 " sethidden set the FAT/NTFS hidden sectors field\n");
1090 int main(int argc, char *argv[])
1092 struct mbr *mbr = NULL;
1094 struct disk_part_iter *cur_part = NULL;
1095 struct syslinux_rm_regs regs;
1096 char *drivename, *partition;
1097 int hd, drive, whichpart;
1099 uint32_t file_lba = 0;
1100 unsigned char *isolinux_bin;
1101 uint32_t *checksum, *chkhead, *chktail;
1102 struct data_area data[3];
1105 static const char cmldr_signature[8] = "cmdcons";
1107 openconsole(&dev_null_r, &dev_stdcon_w);
1112 /* Prepare the register set */
1113 memset(®s, 0, sizeof regs);
1115 for (i = 1; i < argc; i++) {
1116 if (!strncmp(argv[i], "file=", 5)) {
1117 opt.loadfile = argv[i] + 5;
1118 } else if (!strncmp(argv[i], "seg=", 4)) {
1119 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1120 if (segval < 0x50 || segval > 0x9f000) {
1121 error("Invalid segment\n");
1125 } else if (!strncmp(argv[i], "isolinux=", 9)) {
1126 opt.loadfile = argv[i] + 9;
1127 opt.isolinux = true;
1128 } else if (!strncmp(argv[i], "ntldr=", 6)) {
1129 opt.seg = 0x2000; /* NTLDR wants this address */
1130 opt.loadfile = argv[i] + 6;
1131 opt.sethidden = true;
1132 } else if (!strncmp(argv[i], "cmldr=", 6)) {
1133 opt.seg = 0x2000; /* CMLDR wants this address */
1134 opt.loadfile = argv[i] + 6;
1136 opt.sethidden = true;
1137 } else if (!strncmp(argv[i], "freedos=", 8)) {
1138 opt.seg = 0x60; /* FREEDOS wants this address */
1139 opt.loadfile = argv[i] + 8;
1140 opt.sethidden = true;
1141 } else if (!strncmp(argv[i], "msdos=", 6) ||
1142 !strncmp(argv[i], "pcdos=", 6)) {
1143 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
1144 opt.loadfile = argv[i] + 6;
1145 opt.sethidden = true;
1146 } else if (!strncmp(argv[i], "grub=", 5)) {
1147 opt.seg = 0x800; /* stage2 wants this address */
1148 opt.loadfile = argv[i] + 5;
1150 } else if (!strcmp(argv[i], "swap")) {
1152 } else if (!strcmp(argv[i], "noswap")) {
1154 } else if (!strcmp(argv[i], "hide")) {
1156 } else if (!strcmp(argv[i], "nohide")) {
1158 } else if (!strcmp(argv[i], "keeppxe")) {
1160 } else if (!strcmp(argv[i], "sethidden")) {
1161 opt.sethidden = true;
1162 } else if (!strcmp(argv[i], "nosethidden")) {
1163 opt.sethidden = false;
1164 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1165 && argv[i][1] == 'd')
1166 || !strncmp(argv[i], "mbr:", 4)
1167 || !strncmp(argv[i], "mbr=", 4)
1168 || !strcmp(argv[i], "boot")
1169 || !strncmp(argv[i], "boot,", 5)) {
1170 drivename = argv[i];
1171 p = strchr(drivename, ',');
1175 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1176 && argv[i + 1][0] <= '9') {
1177 partition = argv[++i];
1186 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1188 regs.ip = regs.esp.l = 0x7c00;
1192 if (!strncmp(drivename, "mbr", 3)) {
1193 drive = find_disk(strtoul(drivename + 4, NULL, 0));
1195 error("Unable to find requested MBR signature\n");
1198 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1199 drivename[1] == 'd') {
1200 hd = drivename[0] == 'h';
1202 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1203 } else if (!strcmp(drivename, "boot")) {
1204 const union syslinux_derivative_info *sdi;
1205 sdi = syslinux_derivative_info();
1206 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1207 drive = 0x80; /* Boot drive not available */
1209 drive = sdi->disk.drive_number;
1211 error("Unparsable drive specification\n");
1215 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
1216 regs.ebx.b[0] = regs.edx.b[0] = drive;
1218 whichpart = 0; /* Default */
1220 whichpart = strtoul(partition, NULL, 0);
1222 if (!(drive & 0x80) && whichpart) {
1223 error("Warning: Partitions of floppy devices may not work\n");
1227 * grldr of Grub4dos wants the partition number in DH:
1228 * -1: whole drive (default)
1229 * 0-3: primary partitions
1230 * 4-*: logical partitions
1232 regs.edx.b[1] = whichpart - 1;
1234 /* Get the disk geometry and disk access setup */
1235 if (get_disk_params(drive)) {
1236 error("Cannot get disk parameters\n");
1241 if (!(mbr = read_sectors(0, 1))) {
1242 error("Cannot read Master Boot Record\n");
1247 if (whichpart < 1 || whichpart > 4)
1248 error("WARNING: hide specified without a non-primary partition\n");
1249 if (hide_unhide(mbr, whichpart))
1250 error("WARNING: failed to write MBR for 'hide'\n");
1253 /* Boot the MBR by default */
1255 /* Boot a partition */
1256 cur_part = get_first_partition(NULL);
1258 if (cur_part->index == whichpart)
1259 /* Found the partition to boot */
1261 cur_part = cur_part->next(cur_part);
1264 error("Requested partition not found!\n");
1269 /* Do the actual chainloading */
1270 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1273 fputs("Loading the boot file...\n", stdout);
1274 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1275 error("Failed to load the boot file\n");
1278 data[ndata].base = load_base;
1279 load_base = 0x7c00; /* If we also load a boot sector */
1281 /* Create boot info table: needed when you want to chainload
1282 another version of ISOLINUX (or another bootlaoder that needs
1283 the -boot-info-table switch of mkisofs)
1284 (will only work when run from ISOLINUX) */
1286 const union syslinux_derivative_info *sdi;
1287 sdi = syslinux_derivative_info();
1289 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1290 /* Boot info table info (integers in little endian format)
1292 Offset Name Size Meaning
1293 8 bi_pvd 4 bytes LBA of primary volume descriptor
1294 12 bi_file 4 bytes LBA of boot file
1295 16 bi_length 4 bytes Boot file length in bytes
1296 20 bi_csum 4 bytes 32-bit checksum
1297 24 bi_reserved 40 bytes Reserved
1299 The 32-bit checksum is the sum of all the 32-bit words in the
1300 boot file starting at byte offset 64. All linear block
1301 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1303 LBA of primary volume descriptor should already be set to 16.
1306 isolinux_bin = (unsigned char *)data[ndata].data;
1308 /* Get LBA address of bootfile */
1309 file_lba = get_file_lba(opt.loadfile);
1311 if (file_lba == 0) {
1312 error("Failed to find LBA offset of the boot file\n");
1316 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1318 /* Set boot file length */
1319 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1321 /* Calculate checksum */
1322 checksum = (uint32_t *) & isolinux_bin[20];
1323 chkhead = (uint32_t *) & isolinux_bin[64];
1324 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1326 while (chkhead < chktail)
1327 *checksum += *chkhead++;
1330 * Deal with possible fractional dword at the end;
1331 * this *should* never happen...
1333 if (data[ndata].size & 3) {
1335 memcpy(&xword, chkhead, data[ndata].size & 3);
1340 ("The isolinux= option is only valid when run from ISOLINUX\n");
1346 regs.ip = 0x200; /* jump 0x200 bytes into the loadfile */
1348 /* 0xffffff00 seems to be GRUB ways to record that it's
1349 "root" is the whole disk (and not a partition). */
1350 *(uint32_t *) ((unsigned char *)data[ndata].data + 0x208) =
1357 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1358 /* Actually read the boot sector */
1360 data[ndata].data = mbr;
1361 } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
1362 error("Cannot read boot sector\n");
1365 data[ndata].size = SECTOR;
1366 data[ndata].base = load_base;
1368 if (!opt.loadfile) {
1369 const struct mbr *br =
1370 (const struct mbr *)((char *)data[ndata].data +
1371 data[ndata].size - sizeof(struct mbr));
1372 if (br->sig != mbr_sig_magic) {
1374 ("Boot sector signature not found (unbootable disk/partition?)\n");
1379 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1380 * the string "cmdcons\0" to memory location 0000:7C03.
1381 * Memory location 0000:7C00 contains the bootsector of the partition.
1383 if (cur_part && opt.cmldr) {
1384 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1385 sizeof cmldr_signature);
1389 * Modify the hidden sectors (partition offset) copy in memory;
1390 * this modifies the field used by FAT and NTFS filesystems, and
1391 * possibly other boot loaders which use the same format.
1393 if (cur_part && opt.sethidden) {
1394 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1400 if (cur_part && cur_part->record) {
1401 /* 0x7BE is the canonical place for the first partition entry. */
1402 data[ndata].data = (void *)cur_part->record;
1403 data[ndata].base = 0x7be;
1404 data[ndata].size = sizeof(*cur_part->record);
1406 regs.esi.w[0] = 0x7be;
1409 do_boot(data, ndata, ®s);
1413 free(cur_part->block);