1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Chainload a hard disk (currently rather braindead.)
19 * Usage: chain hd<disk#> [<partition>] [options]
20 * chain fd<disk#> [options]
21 * chain mbr:<id> [<partition>] [options]
22 * chain boot [<partition>] [options]
24 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
28 * The mbr: syntax means search all the hard disks until one with a
29 * specific MBR serial number (bytes 440-443) is found.
31 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
36 * loads the file <loader> **from the SYSLINUX filesystem**
37 * instead of loading the boot sector.
40 * loads at and jumps to <seg>:0000 instead of 0000:7C00.
43 * chainload another version/build of the ISOLINUX bootloader and patch
44 * the loader with appropriate parameters in memory.
45 * This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
46 * when you want more than one ISOLINUX per CD/DVD.
49 * equivalent to seg=0x2000 file=<loader> sethidden,
50 * used with WinNT's loaders
53 * used with Recovery Console of Windows NT/2K/XP.
54 * same as ntldr=<loader> & "cmdcons\0" written to
55 * the system name field in the bootsector
58 * equivalent to seg=0x60 file=<loader> sethidden,
59 * used with FreeDOS kernel.sys.
63 * equivalent to seg=0x70 file=<loader> sethidden,
64 * used with DOS' io.sys.
67 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
68 * used with GRUB stage2 files.
71 * if the disk is not fd0/hd0, install a BIOS stub which swaps
75 * change type of primary partitions with IDs 01, 04, 06, 07,
76 * 0b, 0c, or 0e to 1x, except for the selected partition, which
77 * is converted the other way.
80 * update the "hidden sectors" (partition offset) field in a
81 * FAT/NTFS boot sector.
92 #include <syslinux/loadfile.h>
93 #include <syslinux/bootrm.h>
94 #include <syslinux/config.h>
95 #include <syslinux/video.h>
97 #define SECTOR 512 /* bytes/sector */
99 static struct options {
100 const char *loadfile;
117 static inline void error(const char *msg)
123 * Call int 13h, but with retry on failure. Especially floppies need this.
125 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
127 int retry = 6; /* Number of retries */
134 __intcall(0x13, inreg, outreg);
135 if (!(outreg->eflags.l & EFLAGS_CF))
136 return 0; /* CF=0, OK */
139 return -1; /* Error */
143 * Query disk parameters and EBIOS availability for a particular disk.
147 int ebios; /* EBIOS supported on this disk */
148 int cbios; /* CHS geometry is valid */
153 static int get_disk_params(int disk)
155 static com32sys_t getparm, parm, getebios, ebios;
157 disk_info.disk = disk;
158 disk_info.ebios = disk_info.cbios = 0;
160 /* Get EBIOS support */
161 getebios.eax.w[0] = 0x4100;
162 getebios.ebx.w[0] = 0x55aa;
163 getebios.edx.b[0] = disk;
164 getebios.eflags.b[0] = 0x3; /* CF set */
166 __intcall(0x13, &getebios, &ebios);
168 if (!(ebios.eflags.l & EFLAGS_CF) &&
169 ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
173 /* Get disk parameters -- really only useful for
174 hard disks, but if we have a partitioned floppy
175 it's actually our best chance... */
176 getparm.eax.b[1] = 0x08;
177 getparm.edx.b[0] = disk;
179 __intcall(0x13, &getparm, &parm);
181 if (parm.eflags.l & EFLAGS_CF)
182 return disk_info.ebios ? 0 : -1;
184 disk_info.head = parm.edx.b[1] + 1;
185 disk_info.sect = parm.ecx.b[0] & 0x3f;
186 if (disk_info.sect == 0) {
189 disk_info.cbios = 1; /* Valid geometry */
196 * Get a disk block and return a malloc'd buffer.
197 * Uses the disk number and information from disk_info.
207 /* Read count sectors from drive, starting at lba. Return a new buffer */
208 static void *read_sectors(uint64_t lba, uint8_t count)
211 struct ebios_dapa *dapa = __com32.cs_bounce;
212 void *buf = (char *)__com32.cs_bounce + SECTOR;
219 memset(&inreg, 0, sizeof inreg);
221 if (disk_info.ebios) {
222 dapa->len = sizeof(*dapa);
224 dapa->off = OFFS(buf);
225 dapa->seg = SEG(buf);
228 inreg.esi.w[0] = OFFS(dapa);
229 inreg.ds = SEG(dapa);
230 inreg.edx.b[0] = disk_info.disk;
231 inreg.eax.b[1] = 0x42; /* Extended read */
233 unsigned int c, h, s, t;
235 if (!disk_info.cbios) {
236 /* We failed to get the geometry */
239 return NULL; /* Can only read MBR */
245 s = (lba % disk_info.sect) + 1;
246 t = lba / disk_info.sect; /* Track = head*cyl */
247 h = t % disk_info.head;
248 c = t / disk_info.head;
251 if (s > 63 || h > 256 || c > 1023)
254 inreg.eax.b[0] = count;
255 inreg.eax.b[1] = 0x02; /* Read */
256 inreg.ecx.b[1] = c & 0xff;
257 inreg.ecx.b[0] = s + (c >> 6);
259 inreg.edx.b[0] = disk_info.disk;
260 inreg.ebx.w[0] = OFFS(buf);
264 if (int13_retry(&inreg, NULL))
267 data = malloc(SECTOR);
269 memcpy(data, buf, count * SECTOR);
273 static int write_sector(unsigned int lba, const void *data)
276 struct ebios_dapa *dapa = __com32.cs_bounce;
277 void *buf = (char *)__com32.cs_bounce + SECTOR;
279 memcpy(buf, data, SECTOR);
280 memset(&inreg, 0, sizeof inreg);
282 if (disk_info.ebios) {
283 dapa->len = sizeof(*dapa);
284 dapa->count = 1; /* 1 sector */
285 dapa->off = OFFS(buf);
286 dapa->seg = SEG(buf);
289 inreg.esi.w[0] = OFFS(dapa);
290 inreg.ds = SEG(dapa);
291 inreg.edx.b[0] = disk_info.disk;
292 inreg.eax.w[0] = 0x4300; /* Extended write */
294 unsigned int c, h, s, t;
296 if (!disk_info.cbios) {
297 /* We failed to get the geometry */
300 return -1; /* Can only write MBR */
306 s = (lba % disk_info.sect) + 1;
307 t = lba / disk_info.sect; /* Track = head*cyl */
308 h = t % disk_info.head;
309 c = t / disk_info.head;
312 if (s > 63 || h > 256 || c > 1023)
315 inreg.eax.w[0] = 0x0301; /* Write one sector */
316 inreg.ecx.b[1] = c & 0xff;
317 inreg.ecx.b[0] = s + (c >> 6);
319 inreg.edx.b[0] = disk_info.disk;
320 inreg.ebx.w[0] = OFFS(buf);
324 if (int13_retry(&inreg, NULL))
330 static int write_verify_sector(unsigned int lba, const void *buf)
335 rv = write_sector(lba, buf);
337 return rv; /* Write failure */
338 rb = read_sectors(lba, 1);
340 return -1; /* Readback failure */
341 rv = memcmp(buf, rb, SECTOR);
347 * CHS (cylinder, head, sector) value extraction macros.
348 * Taken from WinVBlock. Does not expand to an lvalue
350 #define chs_head(chs) chs[0]
351 #define chs_sector(chs) (chs[1] & 0x3F)
352 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
353 #define chs_cyl_low(chs) ((uint16_t)chs[2])
354 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
355 typedef uint8_t chs[3];
357 /* A DOS partition table entry */
359 uint8_t active_flag; /* 0x80 if "active" */
365 } __attribute__ ((packed));
372 struct part_entry table[4];
374 } __attribute__ ((packed));
375 static const uint16_t mbr_sig_magic = 0xAA55;
377 /* Search for a specific drive, based on the MBR signature; bytes
379 static int find_disk(uint32_t mbr_sig)
385 for (drive = 0x80; drive <= 0xff; drive++) {
386 if (get_disk_params(drive))
387 continue; /* Drive doesn't exist */
388 if (!(mbr = read_sectors(0, 1)))
389 continue; /* Cannot read sector */
390 is_me = (mbr->disk_sig == mbr_sig);
398 /* Search for a logical partition. Logical partitions are actually implemented
399 as recursive partition tables; theoretically they're supposed to form a
400 linked list, but other structures have been seen.
402 To make things extra confusing: data partition offsets are relative to where
403 the data partition record is stored, whereas extended partition offsets
404 are relative to the beginning of the extended partition all the way back
405 at the MBR... but still not absolute! */
407 int nextpart; /* Number of the next logical partition */
409 static struct part_entry *find_logical_partition(int whichpart, struct mbr *br,
410 struct part_entry *self,
411 struct part_entry *root)
413 static struct part_entry ltab_entry;
414 struct part_entry *ptab = br->table;
415 struct part_entry *found;
420 if (br->sig != mbr_sig_magic)
421 return NULL; /* Signature missing */
423 /* We are assumed to already having enumerated all the data partitions
424 in this table if this is the MBR. For MBR, self == NULL. */
427 /* Scan the data partitions. */
429 for (i = 0; i < 4; i++) {
430 if (ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
431 ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85)
432 continue; /* Skip empty or extended partitions */
437 /* Adjust the offset to account for the extended partition itself */
438 ptab[i].start_lba += self->start_lba;
441 * Sanity check entry: must not extend outside the
442 * extended partition. This is necessary since some OSes
443 * put crap in some entries. Note that root is non-NULL here.
445 if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
446 ptab[i].start_lba >= root->start_lba + root->length)
449 /* OK, it's a data partition. Is it the one we're looking for? */
450 if (nextpart++ == whichpart) {
451 memcpy(<ab_entry, &ptab[i], sizeof ltab_entry);
457 /* Scan the extended partitions. */
458 for (i = 0; i < 4; i++) {
459 if (ptab[i].ostype != 0x05 &&
460 ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85)
461 continue; /* Skip empty or data partitions */
466 /* Adjust the offset to account for the extended partition itself */
468 ptab[i].start_lba += root->start_lba;
470 /* Sanity check entry: must not extend outside the extended partition.
471 This is necessary since some OSes put crap in some entries. */
473 if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
474 ptab[i].start_lba >= root->start_lba + root->length)
477 /* Process this partition */
478 if (!(ebr = read_sectors(ptab[i].start_lba, 1)))
479 continue; /* Read error, must be invalid */
481 found = find_logical_partition(whichpart, ebr, &ptab[i],
482 root ? root : &ptab[i]);
488 /* If we get here, there ain't nothing... */
492 static void do_boot(struct data_area *data, int ndata,
493 struct syslinux_rm_regs *regs)
495 uint16_t *const bios_fbm = (uint16_t *) 0x413;
496 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
497 struct syslinux_memmap *mmap;
498 struct syslinux_movelist *mlist = NULL;
500 uint8_t driveno = regs->edx.b[0];
501 uint8_t swapdrive = driveno & 0x80;
504 mmap = syslinux_memory_map();
507 error("Cannot read system memory map\n");
512 for (i = 0; i < ndata; i++) {
513 if (data[i].base + data[i].size > endimage)
514 endimage = data[i].base + data[i].size;
516 if (endimage > dosmem)
519 for (i = 0; i < ndata; i++) {
520 if (syslinux_add_movelist(&mlist, data[i].base,
521 (addr_t) data[i].data, data[i].size))
525 if (opt.swap && driveno != swapdrive) {
526 static const uint8_t swapstub_master[] = {
527 /* The actual swap code */
528 0x53, /* 00: push bx */
529 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
530 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
531 0x5b, /* 08: pop bx */
532 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
533 0x90, 0x90, /* 0E: nop; nop */
534 /* Code to install this in the right location */
535 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
536 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
537 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
538 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
539 0x4f, /* 1F: dec di */
540 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
541 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
542 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
543 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
544 0x8e, 0xc7, /* 32: mov es,di */
545 0x31, 0xff, /* 34: xor di,di */
546 0xf3, 0x66, 0xa5, /* 36: rep movsd */
547 0xbe, 0, 0, /* 39: mov si,0 */
548 0xbf, 0, 0, /* 3C: mov di,0 */
549 0x8e, 0xde, /* 3F: mov ds,si */
550 0x8e, 0xc7, /* 41: mov es,di */
551 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
552 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
553 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
554 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
555 /* pad out to segment boundary */
556 0x90, 0x90, /* 5A: ... */
557 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
559 static uint8_t swapstub[1024];
562 /* Note: we can't rely on either INT 13h nor the dosmem
563 vector to be correct at this stage, so we have to use an
564 installer stub to put things in the right place.
565 Round the installer location to a 1K boundary so the only
566 possible overlap is the identity mapping. */
567 endimage = (endimage + 1023) & ~1023;
569 /* Create swap stub */
570 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
571 *(uint16_t *) & swapstub[0x3a] = regs->ds;
572 *(uint16_t *) & swapstub[0x3d] = regs->es;
573 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
574 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
575 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
576 *(uint16_t *) & swapstub[0x56] = regs->ip;
577 *(uint16_t *) & swapstub[0x58] = regs->cs;
578 p = &swapstub[sizeof swapstub_master];
580 /* Mapping table; start out with identity mapping everything */
581 for (i = 0; i < 256; i++)
584 /* And the actual swap */
585 p[driveno] = swapdrive;
586 p[swapdrive] = driveno;
588 /* Adjust registers */
589 regs->ds = regs->cs = endimage >> 4;
590 regs->es = regs->esi.l = 0;
591 regs->ecx.l = sizeof swapstub >> 2;
592 regs->ip = 0x10; /* Installer offset */
593 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
595 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
599 endimage += sizeof swapstub;
602 /* Tell the shuffler not to muck with this area... */
603 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
605 /* Force text mode */
606 syslinux_force_text_mode();
608 fputs("Booting...\n", stdout);
609 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
610 error("Chainboot failed!\n");
614 error("Loader file too large\n");
618 error("Out of memory\n");
622 static int hide_unhide(struct mbr *mbr, int part)
625 struct part_entry *pt;
626 const uint16_t mask =
627 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
632 bool write_back = false;
634 for (i = 1; i <= 4; i++) {
635 pt = mbr->table + i - 1;
637 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
638 /* It's a hideable partition type */
640 t &= ~0x10; /* unhide */
642 t |= 0x10; /* hide */
644 if (t != pt->ostype) {
651 return write_verify_sector(0, mbr);
656 static uint32_t get_file_lba(const char *filename)
661 /* Start with clean registers */
662 memset(&inregs, 0, sizeof(com32sys_t));
664 /* Put the filename in the bounce buffer */
665 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
667 /* Call comapi_open() which returns a structure pointer in SI
668 * to a structure whose first member happens to be the LBA.
670 inregs.eax.w[0] = 0x0006;
671 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
672 inregs.es = SEG(__com32.cs_bounce);
673 __com32.cs_intcall(0x22, &inregs, &inregs);
675 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
676 return 0; /* Filename not found */
679 /* Since the first member is the LBA, we simply cast */
680 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
682 /* Clean the registers for the next call */
683 memset(&inregs, 0, sizeof(com32sys_t));
685 /* Put the filename in the bounce buffer */
686 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
688 /* Call comapi_close() to free the structure */
689 inregs.eax.w[0] = 0x0008;
690 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
691 inregs.es = SEG(__com32.cs_bounce);
692 __com32.cs_intcall(0x22, &inregs, &inregs);
697 static void usage(void)
699 error("Usage: chain.c32 hd<disk#> [<partition>] [options]\n"
700 " chain.c32 fd<disk#> [options]\n"
701 " chain.c32 mbr:<id> [<partition>] [options]\n"
702 " chain.c32 boot [<partition>] [options]\n"
703 "Options: file=<loader> load file, instead of boot sector\n"
704 " isolinux=<loader> load another version of ISOLINUX\n"
705 " ntldr=<loader> load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n"
706 " cmldr=<loader> load Recovery Console of Windows NT/2K/XP\n"
707 " freedos=<loader> load FreeDOS kernel.sys\n"
708 " msdos=<loader> load MS-DOS io.sys\n"
709 " pcdos=<loader> load PC-DOS ibmbio.com\n"
710 " grub=<loader> load GRUB stage2\n"
711 " seg=<segment> jump to <seg>:0000 instead of 0000:7C00\n"
712 " swap swap drive numbers, if bootdisk is not fd0/hd0\n"
713 " hide hide primary partitions, except selected partition\n"
714 " sethidden set the FAT/NTFS hidden sectors field\n");
717 int main(int argc, char *argv[])
721 struct part_entry *partinfo;
722 struct syslinux_rm_regs regs;
723 char *drivename, *partition;
724 int hd, drive, whichpart;
726 uint32_t file_lba = 0;
727 unsigned char *isolinux_bin;
728 uint32_t *checksum, *chkhead, *chktail;
729 struct data_area data[3];
732 static const char cmldr_signature[8] = "cmdcons";
734 openconsole(&dev_null_r, &dev_stdcon_w);
739 /* Prepare the register set */
740 memset(®s, 0, sizeof regs);
742 for (i = 1; i < argc; i++) {
743 if (!strncmp(argv[i], "file=", 5)) {
744 opt.loadfile = argv[i] + 5;
745 } else if (!strncmp(argv[i], "seg=", 4)) {
746 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
747 if (segval < 0x50 || segval > 0x9f000) {
748 error("Invalid segment\n");
752 } else if (!strncmp(argv[i], "isolinux=", 9)) {
753 opt.loadfile = argv[i] + 9;
755 } else if (!strncmp(argv[i], "ntldr=", 6)) {
756 opt.seg = 0x2000; /* NTLDR wants this address */
757 opt.loadfile = argv[i] + 6;
758 opt.sethidden = true;
759 } else if (!strncmp(argv[i], "cmldr=", 6)) {
760 opt.seg = 0x2000; /* CMLDR wants this address */
761 opt.loadfile = argv[i] + 6;
763 opt.sethidden = true;
764 } else if (!strncmp(argv[i], "freedos=", 8)) {
765 opt.seg = 0x60; /* FREEDOS wants this address */
766 opt.loadfile = argv[i] + 8;
767 opt.sethidden = true;
768 } else if (!strncmp(argv[i], "msdos=", 6) ||
769 !strncmp(argv[i], "pcdos=", 6)) {
770 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
771 opt.loadfile = argv[i] + 6;
772 opt.sethidden = true;
773 } else if (!strncmp(argv[i], "grub=", 5)) {
774 opt.seg = 0x800; /* stage2 wants this address */
775 opt.loadfile = argv[i] + 5;
777 } else if (!strcmp(argv[i], "swap")) {
779 } else if (!strcmp(argv[i], "noswap")) {
781 } else if (!strcmp(argv[i], "hide")) {
783 } else if (!strcmp(argv[i], "nohide")) {
785 } else if (!strcmp(argv[i], "keeppxe")) {
787 } else if (!strcmp(argv[i], "sethidden")) {
788 opt.sethidden = true;
789 } else if (!strcmp(argv[i], "nosethidden")) {
790 opt.sethidden = false;
791 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
792 && argv[i][1] == 'd')
793 || !strncmp(argv[i], "mbr:", 4)
794 || !strncmp(argv[i], "mbr=", 4)
795 || !strcmp(argv[i], "boot")
796 || !strncmp(argv[i], "boot,", 5)) {
798 p = strchr(drivename, ',');
802 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
803 && argv[i + 1][0] <= '9') {
804 partition = argv[++i];
813 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
815 regs.ip = regs.esp.l = 0x7c00;
819 if (!strncmp(drivename, "mbr", 3)) {
820 drive = find_disk(strtoul(drivename + 4, NULL, 0));
822 error("Unable to find requested MBR signature\n");
825 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
826 drivename[1] == 'd') {
827 hd = drivename[0] == 'h';
829 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
830 } else if (!strcmp(drivename, "boot")) {
831 const union syslinux_derivative_info *sdi;
832 sdi = syslinux_derivative_info();
833 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
834 drive = 0x80; /* Boot drive not available */
836 drive = sdi->disk.drive_number;
838 error("Unparsable drive specification\n");
842 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
843 regs.ebx.b[0] = regs.edx.b[0] = drive;
845 whichpart = 0; /* Default */
847 whichpart = strtoul(partition, NULL, 0);
849 if (!(drive & 0x80) && whichpart) {
850 error("Warning: Partitions of floppy devices may not work\n");
854 * grldr of Grub4dos wants the partition number in DH:
855 * -1: whole drive (default)
856 * 0-3: primary partitions
857 * 4-*: logical partitions
859 regs.edx.b[1] = whichpart - 1;
861 /* Get the disk geometry and disk access setup */
862 if (get_disk_params(drive)) {
863 error("Cannot get disk parameters\n");
868 if (!(mbr = read_sectors(0, 1))) {
869 error("Cannot read Master Boot Record\n");
874 if (whichpart < 1 || whichpart > 4)
875 error("WARNING: hide specified without a non-primary partition\n");
876 if (hide_unhide(mbr, whichpart))
877 error("WARNING: failed to write MBR for 'hide'\n");
880 if (whichpart == 0) {
884 } else if (whichpart <= 4) {
885 /* Boot a primary partition */
887 partinfo = mbr->table + whichpart - 1;
888 if (partinfo->ostype == 0) {
889 error("Invalid primary partition\n");
893 /* Boot a logical partition */
896 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
898 if (!partinfo || partinfo->ostype == 0) {
899 error("Requested logical partition not found\n");
904 /* Do the actual chainloading */
905 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
908 fputs("Loading the boot file...\n", stdout);
909 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
910 error("Failed to load the boot file\n");
913 data[ndata].base = load_base;
914 load_base = 0x7c00; /* If we also load a boot sector */
916 /* Create boot info table: needed when you want to chainload
917 another version of ISOLINUX (or another bootlaoder that needs
918 the -boot-info-table switch of mkisofs)
919 (will only work when run from ISOLINUX) */
921 const union syslinux_derivative_info *sdi;
922 sdi = syslinux_derivative_info();
924 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
925 /* Boot info table info (integers in little endian format)
927 Offset Name Size Meaning
928 8 bi_pvd 4 bytes LBA of primary volume descriptor
929 12 bi_file 4 bytes LBA of boot file
930 16 bi_length 4 bytes Boot file length in bytes
931 20 bi_csum 4 bytes 32-bit checksum
932 24 bi_reserved 40 bytes Reserved
934 The 32-bit checksum is the sum of all the 32-bit words in the
935 boot file starting at byte offset 64. All linear block
936 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
938 LBA of primary volume descriptor should already be set to 16.
941 isolinux_bin = (unsigned char *)data[ndata].data;
943 /* Get LBA address of bootfile */
944 file_lba = get_file_lba(opt.loadfile);
947 error("Failed to find LBA offset of the boot file\n");
951 *((uint32_t *) & isolinux_bin[12]) = file_lba;
953 /* Set boot file length */
954 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
956 /* Calculate checksum */
957 checksum = (uint32_t *) & isolinux_bin[20];
958 chkhead = (uint32_t *) & isolinux_bin[64];
959 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
961 while (chkhead < chktail)
962 *checksum += *chkhead++;
965 * Deal with possible fractional dword at the end;
966 * this *should* never happen...
968 if (data[ndata].size & 3) {
970 memcpy(&xword, chkhead, data[ndata].size & 3);
975 ("The isolinux= option is only valid when run from ISOLINUX\n");
981 regs.ip = 0x200; /* jump 0x200 bytes into the loadfile */
983 /* 0xffffff00 seems to be GRUB ways to record that it's
984 "root" is the whole disk (and not a partition). */
985 *(uint32_t *) ((unsigned char *)data[ndata].data + 0x208) =
992 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
993 /* Actually read the boot sector */
995 data[ndata].data = mbr;
996 } else if (!(data[ndata].data = read_sectors(partinfo->start_lba, 1))) {
997 error("Cannot read boot sector\n");
1000 data[ndata].size = SECTOR;
1001 data[ndata].base = load_base;
1003 if (!opt.loadfile) {
1004 const struct mbr *br =
1005 (const struct mbr *)((char *)data[ndata].data +
1006 data[ndata].size - sizeof(struct mbr));
1007 if (br->sig != mbr_sig_magic) {
1009 ("Boot sector signature not found (unbootable disk/partition?)\n");
1014 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1015 * the string "cmdcons\0" to memory location 0000:7C03.
1016 * Memory location 0000:7C00 contains the bootsector of the partition.
1018 if (partinfo && opt.cmldr) {
1019 memcpy((char *)data[ndata].data + 3, cmldr_signature,
1020 sizeof cmldr_signature);
1024 * Modify the hidden sectors (partition offset) copy in memory;
1025 * this modifies the field used by FAT and NTFS filesystems, and
1026 * possibly other boot loaders which use the same format.
1028 if (partinfo && opt.sethidden) {
1029 *(uint32_t *) ((char *)data[ndata].data + 28) = partinfo->start_lba;
1036 /* 0x7BE is the canonical place for the first partition entry. */
1037 data[ndata].data = partinfo;
1038 data[ndata].base = 0x7be;
1039 data[ndata].size = sizeof *partinfo;
1041 regs.esi.w[0] = 0x7be;
1044 do_boot(data, ndata, ®s);