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 * ----------------------------------------------------------------------- */
17 * Please see doc/chain.txt for detailed documentation.
29 #include <syslinux/loadfile.h>
30 #include <syslinux/bootrm.h>
31 #include <syslinux/config.h>
32 #include <syslinux/disk.h>
33 #include <syslinux/video.h>
36 static struct options {
57 static inline void error(const char *msg)
62 static struct disk_info diskinfo;
64 /* Search for a specific drive, based on the MBR signature; bytes 440-443 */
65 static int find_by_sig(uint32_t mbr_sig)
67 struct part_iter *boot_part = NULL;
70 for (drive = 0x80; drive <= 0xff; drive++) {
71 if (disk_get_params(drive, &diskinfo))
72 continue; /* Drive doesn't exist */
73 /* Check for a MBR disk */
74 boot_part = pi_begin(&diskinfo);
75 if (boot_part->type != typedos) {
79 if (boot_part->sub.dos.disk_sig == mbr_sig) {
90 * Search for a specific drive/partition, based on the GPT GUID.
91 * We return the disk drive number if found, as well as populating the
92 * boot_part pointer with the matching partition, if applicable.
93 * If no matching partition is found or the GUID is a disk GUID,
94 * boot_part will be populated with NULL. If not matching disk is
95 * found, we return -1.
97 static int find_by_guid(const struct guid *gpt_guid,
98 struct part_iter **_boot_part)
100 struct part_iter *boot_part = NULL;
103 for (drive = 0x80; drive <= 0xff; drive++) {
104 if (disk_get_params(drive, &diskinfo))
105 continue; /* Drive doesn't exist */
106 /* Check for a GPT disk */
107 boot_part = pi_begin(&diskinfo);
108 if (boot_part->type != typegpt) {
112 /* Check for a matching GPT disk guid */
113 if(!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
117 /* disk guid doesn't match, maybe partition guid will */
118 while (pi_next(&boot_part)) {
119 if(!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
125 *_boot_part = boot_part;
130 * Search for a specific partition, based on the GPT label.
131 * We return the disk drive number if found, as well as populating the
132 * boot_part pointer with the matching partition, if applicable.
133 * If no matching partition is found, boot_part will be populated with
134 * NULL and we return -1.
136 static int find_by_label(const char *label, struct part_iter **_boot_part)
138 struct part_iter *boot_part = NULL;
141 for (drive = 0x80; drive <= 0xff; drive++) {
142 if (disk_get_params(drive, &diskinfo))
143 continue; /* Drive doesn't exist */
144 /* Check for a GPT disk */
145 boot_part = pi_begin(&diskinfo);
146 if (!(boot_part->type == typegpt)) {
150 /* Check for a matching partition */
151 while (pi_next(&boot_part)) {
152 if (!strcmp(label, boot_part->sub.gpt.part_label))
158 *_boot_part = boot_part;
162 static void do_boot(struct data_area *data, int ndata,
163 struct syslinux_rm_regs *regs)
165 uint16_t *const bios_fbm = (uint16_t *) 0x413;
166 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
167 struct syslinux_memmap *mmap;
168 struct syslinux_movelist *mlist = NULL;
170 uint8_t driveno = regs->edx.b[0];
171 uint8_t swapdrive = driveno & 0x80;
174 mmap = syslinux_memory_map();
177 error("Cannot read system memory map\n");
182 for (i = 0; i < ndata; i++) {
183 if (data[i].base + data[i].size > endimage)
184 endimage = data[i].base + data[i].size;
186 if (endimage > dosmem)
189 for (i = 0; i < ndata; i++) {
190 if (syslinux_add_movelist(&mlist, data[i].base,
191 (addr_t) data[i].data, data[i].size))
195 if (opt.swap && driveno != swapdrive) {
196 static const uint8_t swapstub_master[] = {
197 /* The actual swap code */
198 0x53, /* 00: push bx */
199 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
200 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
201 0x5b, /* 08: pop bx */
202 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
203 0x90, 0x90, /* 0E: nop; nop */
204 /* Code to install this in the right location */
205 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
206 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
207 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
208 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
209 0x4f, /* 1F: dec di */
210 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
211 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
212 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
213 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
214 0x8e, 0xc7, /* 32: mov es,di */
215 0x31, 0xff, /* 34: xor di,di */
216 0xf3, 0x66, 0xa5, /* 36: rep movsd */
217 0xbe, 0, 0, /* 39: mov si,0 */
218 0xbf, 0, 0, /* 3C: mov di,0 */
219 0x8e, 0xde, /* 3F: mov ds,si */
220 0x8e, 0xc7, /* 41: mov es,di */
221 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
222 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
223 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
224 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
225 /* pad out to segment boundary */
226 0x90, 0x90, /* 5A: ... */
227 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
229 static uint8_t swapstub[1024];
232 /* Note: we can't rely on either INT 13h nor the dosmem
233 vector to be correct at this stage, so we have to use an
234 installer stub to put things in the right place.
235 Round the installer location to a 1K boundary so the only
236 possible overlap is the identity mapping. */
237 endimage = (endimage + 1023) & ~1023;
239 /* Create swap stub */
240 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
241 *(uint16_t *) & swapstub[0x3a] = regs->ds;
242 *(uint16_t *) & swapstub[0x3d] = regs->es;
243 *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
244 *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
245 *(uint32_t *) & swapstub[0x51] = regs->edi.l;
246 *(uint16_t *) & swapstub[0x56] = regs->ip;
247 *(uint16_t *) & swapstub[0x58] = regs->cs;
248 p = &swapstub[sizeof swapstub_master];
250 /* Mapping table; start out with identity mapping everything */
251 for (i = 0; i < 256; i++)
254 /* And the actual swap */
255 p[driveno] = swapdrive;
256 p[swapdrive] = driveno;
258 /* Adjust registers */
259 regs->ds = regs->cs = endimage >> 4;
260 regs->es = regs->esi.l = 0;
261 regs->ecx.l = sizeof swapstub >> 2;
262 regs->ip = 0x10; /* Installer offset */
263 regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
265 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
269 endimage += sizeof swapstub;
272 /* Tell the shuffler not to muck with this area... */
273 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
275 /* Force text mode */
276 syslinux_force_text_mode();
278 fputs("Booting...\n", stdout);
279 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
280 error("Chainboot failed!\n");
284 error("Loader file too large\n");
288 error("Out of memory\n");
292 static int hide_unhide(struct disk_dos_mbr *mbr, int part)
295 struct disk_dos_part_entry *pt;
296 const uint16_t mask =
297 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
298 (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
300 bool write_back = false;
302 for (i = 1; i <= 4; i++) {
303 pt = mbr->table + i - 1;
305 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
306 /* It's a hideable partition type */
308 t &= ~0x10; /* unhide */
310 t |= 0x10; /* hide */
312 if (t != pt->ostype) {
319 return disk_write_verify_sector(&diskinfo, 0, mbr);
324 static uint32_t get_file_lba(const char *filename)
329 /* Start with clean registers */
330 memset(&inregs, 0, sizeof(com32sys_t));
332 /* Put the filename in the bounce buffer */
333 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
335 /* Call comapi_open() which returns a structure pointer in SI
336 * to a structure whose first member happens to be the LBA.
338 inregs.eax.w[0] = 0x0006;
339 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
340 inregs.es = SEG(__com32.cs_bounce);
341 __com32.cs_intcall(0x22, &inregs, &inregs);
343 if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
344 return 0; /* Filename not found */
347 /* Since the first member is the LBA, we simply cast */
348 lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
350 /* Clean the registers for the next call */
351 memset(&inregs, 0, sizeof(com32sys_t));
353 /* Put the filename in the bounce buffer */
354 strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
356 /* Call comapi_close() to free the structure */
357 inregs.eax.w[0] = 0x0008;
358 inregs.esi.w[0] = OFFS(__com32.cs_bounce);
359 inregs.es = SEG(__com32.cs_bounce);
360 __com32.cs_intcall(0x22, &inregs, &inregs);
365 static void usage(void)
367 static const char usage[] = "\
368 Usage: chain.c32 [options]\n\
369 chain.c32 hd<disk#> [<partition>] [options]\n\
370 chain.c32 fd<disk#> [options]\n\
371 chain.c32 mbr:<id> [<partition>] [options]\n\
372 chain.c32 guid:<guid> [<partition>] [options]\n\
373 chain.c32 label:<label> [<partition>] [options]\n\
374 chain.c32 boot [<partition>] [options]\n\
375 chain.c32 fs [options]\n\
376 Options: file=<loader> Load and execute file, instead of boot sector\n\
377 isolinux=<loader> Load another version of ISOLINUX\n\
378 ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
379 cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
380 freedos=<loader> Load FreeDOS KERNEL.SYS\n\
381 msdos=<loader> Load MS-DOS IO.SYS\n\
382 pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
383 drmk=<loader> Load DRMK DELLBIO.BIN\n\
384 grub=<loader> Load GRUB Legacy stage2\n\
385 grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
386 grldr=<loader> Load GRUB4DOS grldr\n\
387 seg=<segment> Jump to <seg>:0000, instead of 0000:7C00\n\
388 swap Swap drive numbers, if bootdisk is not fd0/hd0\n\
389 hide Hide primary partitions, except selected partition\n\
390 sethidden Set the FAT/NTFS hidden sectors field\n\
391 keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
392 See syslinux/com32/modules/chain.c for more information\n";
396 int main(int argc, char *argv[])
398 struct disk_dos_mbr *mbr = NULL;
400 struct part_iter *cur_part = NULL;
402 void *sect_area = NULL;
403 void *file_area = NULL;
404 struct disk_dos_part_entry *hand_area = NULL;
406 struct syslinux_rm_regs regs;
407 char *drivename, *partition;
408 int hd, drive, whichpart = 0; /* MBR by default */
410 uint64_t fs_lba = 0; /* Syslinux partition */
411 uint32_t file_lba = 0;
412 struct guid gpt_guid;
413 unsigned char *isolinux_bin;
414 uint32_t *checksum, *chkhead, *chktail;
415 struct data_area data[3];
418 static const char cmldr_signature[8] = "cmdcons";
420 openconsole(&dev_null_r, &dev_stdcon_w);
425 /* Prepare the register set */
426 memset(®s, 0, sizeof regs);
428 for (i = 1; i < argc; i++) {
429 if (!strncmp(argv[i], "file=", 5)) {
430 opt.loadfile = argv[i] + 5;
431 } else if (!strncmp(argv[i], "seg=", 4)) {
432 uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
433 if (segval < 0x50 || segval > 0x9f00) {
434 error("Invalid segment\n");
438 } else if (!strncmp(argv[i], "isolinux=", 9)) {
439 opt.loadfile = argv[i] + 9;
441 } else if (!strncmp(argv[i], "ntldr=", 6)) {
442 opt.seg = 0x2000; /* NTLDR wants this address */
443 opt.loadfile = argv[i] + 6;
444 opt.sethidden = true;
445 } else if (!strncmp(argv[i], "cmldr=", 6)) {
446 opt.seg = 0x2000; /* CMLDR wants this address */
447 opt.loadfile = argv[i] + 6;
449 opt.sethidden = true;
450 } else if (!strncmp(argv[i], "freedos=", 8)) {
451 opt.seg = 0x60; /* FREEDOS wants this address */
452 opt.loadfile = argv[i] + 8;
453 opt.sethidden = true;
454 } else if (!strncmp(argv[i], "msdos=", 6) ||
455 !strncmp(argv[i], "pcdos=", 6)) {
456 opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */
457 opt.loadfile = argv[i] + 6;
458 opt.sethidden = true;
459 } else if (!strncmp(argv[i], "drmk=", 5)) {
460 opt.seg = 0x70; /* DRMK wants this address */
461 opt.loadfile = argv[i] + 5;
462 opt.sethidden = true;
464 } else if (!strncmp(argv[i], "grub=", 5)) {
465 opt.seg = 0x800; /* stage2 wants this address */
466 opt.loadfile = argv[i] + 5;
468 } else if (!strncmp(argv[i], "grubcfg=", 8)) {
469 opt.grubcfg = argv[i] + 8;
470 } else if (!strncmp(argv[i], "grldr=", 6)) {
471 opt.loadfile = argv[i] + 6;
473 } else if (!strcmp(argv[i], "swap")) {
475 } else if (!strcmp(argv[i], "noswap")) {
477 } else if (!strcmp(argv[i], "hide")) {
479 } else if (!strcmp(argv[i], "nohide")) {
481 } else if (!strcmp(argv[i], "keeppxe")) {
483 } else if (!strcmp(argv[i], "sethidden")) {
484 opt.sethidden = true;
485 } else if (!strcmp(argv[i], "nosethidden")) {
486 opt.sethidden = false;
487 } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
488 && argv[i][1] == 'd')
489 || !strncmp(argv[i], "mbr:", 4)
490 || !strncmp(argv[i], "mbr=", 4)
491 || !strncmp(argv[i], "guid:", 5)
492 || !strncmp(argv[i], "guid=", 5)
493 || !strncmp(argv[i], "label:", 6)
494 || !strncmp(argv[i], "label=", 6)
495 || !strcmp(argv[i], "boot")
496 || !strncmp(argv[i], "boot,", 5)
497 || !strcmp(argv[i], "fs")) {
499 p = strchr(drivename, ',');
503 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
504 && argv[i + 1][0] <= '9') {
505 partition = argv[++i];
513 if (opt.grubcfg && !opt.grub) {
514 error("grubcfg=<filename> must be used together with grub=<loader>.\n");
519 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
521 regs.ip = regs.esp.l = 0x7c00;
525 if (!strncmp(drivename, "mbr", 3)) {
526 drive = find_by_sig(strtoul(drivename + 4, NULL, 0));
528 error("Unable to find requested MBR signature\n");
531 } else if (!strncmp(drivename, "guid", 4)) {
532 if (str_to_guid(drivename + 5, &gpt_guid))
534 drive = find_by_guid(&gpt_guid, &cur_part);
536 error("Unable to find requested GPT disk/partition\n");
539 } else if (!strncmp(drivename, "label", 5)) {
541 error("No label specified.\n");
544 drive = find_by_label(drivename + 6, &cur_part);
546 error("Unable to find requested partition by label\n");
549 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
550 drivename[1] == 'd') {
551 hd = drivename[0] == 'h';
553 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
554 } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
555 const union syslinux_derivative_info *sdi;
557 sdi = syslinux_derivative_info();
558 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
559 drive = 0x80; /* Boot drive not available */
561 drive = sdi->disk.drive_number;
562 if (!strcmp(drivename, "fs")
563 && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
564 || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
565 || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
566 /* We should lookup the Syslinux partition number and use it */
567 fs_lba = *sdi->disk.partoffset;
569 error("Unparsable drive specification\n");
573 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
574 regs.ebx.b[0] = regs.edx.b[0] = drive;
576 /* Get the disk geometry and disk access setup */
577 if (disk_get_params(drive, &diskinfo)) {
578 error("Cannot get disk parameters\n");
583 if (!(mbr = disk_read_sectors(&diskinfo, 0, 1))) {
584 error("Cannot read Master Boot Record or sector 0\n");
589 whichpart = strtoul(partition, NULL, 0);
591 /* "guid:" or "label:" might have specified a partition. In such case,
592 * this overrides explicit partition number specification. cur-part->index
593 * can't be 0 at this stage as find_by* won't export iterator at such
597 whichpart = cur_part->index;
599 /* If nothing was found, try fs/boot first */
600 if (!cur_part && fs_lba) {
601 cur_part = pi_begin(&diskinfo);
602 /* search for matching fs_lba, must be partition */
603 while (pi_next(&cur_part)) {
604 if (cur_part->start_lba == fs_lba)
608 /* If still nothing found, do standard search */
610 cur_part = pi_begin(&diskinfo);
611 /* search for matching part#, including disk */
613 if (cur_part->index == whichpart)
615 } while (pi_next(&cur_part));
618 error("Requested disk / partition not found!\n");
622 whichpart = cur_part->index;
624 if (!(drive & 0x80) && whichpart) {
625 error("Warning: Partitions of floppy devices may not work\n");
629 * GRLDR of GRUB4DOS wants the partition number in DH:
630 * -1: whole drive (default)
631 * 0-3: primary partitions
632 * 4-*: logical partitions
635 regs.edx.b[1] = whichpart - 1;
638 if (whichpart < 1 || whichpart > 4)
639 error("WARNING: hide specified without a non-primary partition\n");
640 if (hide_unhide(mbr, whichpart))
641 error("WARNING: failed to write MBR for 'hide'\n");
644 /* Do the actual chainloading */
645 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
648 fputs("Loading the boot file...\n", stdout);
649 if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
650 error("Failed to load the boot file\n");
653 data[ndata].base = load_base;
654 file_area = (void *)data[ndata].data;
655 load_base = 0x7c00; /* If we also load a boot sector */
657 /* Create boot info table: needed when you want to chainload
658 another version of ISOLINUX (or another bootlaoder that needs
659 the -boot-info-table switch of mkisofs)
660 (will only work when run from ISOLINUX) */
662 const union syslinux_derivative_info *sdi;
663 sdi = syslinux_derivative_info();
665 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
666 /* Boot info table info (integers in little endian format)
668 Offset Name Size Meaning
669 8 bi_pvd 4 bytes LBA of primary volume descriptor
670 12 bi_file 4 bytes LBA of boot file
671 16 bi_length 4 bytes Boot file length in bytes
672 20 bi_csum 4 bytes 32-bit checksum
673 24 bi_reserved 40 bytes Reserved
675 The 32-bit checksum is the sum of all the 32-bit words in the
676 boot file starting at byte offset 64. All linear block
677 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
679 LBA of primary volume descriptor should already be set to 16.
682 isolinux_bin = (unsigned char *)data[ndata].data;
684 /* Get LBA address of bootfile */
685 file_lba = get_file_lba(opt.loadfile);
688 error("Failed to find LBA offset of the boot file\n");
692 *((uint32_t *) & isolinux_bin[12]) = file_lba;
694 /* Set boot file length */
695 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
697 /* Calculate checksum */
698 checksum = (uint32_t *) & isolinux_bin[20];
699 chkhead = (uint32_t *) & isolinux_bin[64];
700 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
702 while (chkhead < chktail)
703 *checksum += *chkhead++;
706 * Deal with possible fractional dword at the end;
707 * this *should* never happen...
709 if (data[ndata].size & 3) {
711 memcpy(&xword, chkhead, data[ndata].size & 3);
716 ("The isolinux= option is only valid when run from ISOLINUX\n");
722 /* Layout of stage2 file (from byte 0x0 to 0x270) */
723 struct grub_stage2_patch_area {
726 /* 0x206: compatibility version number major */
727 uint8_t compat_version_major;
728 /* 0x207: compatibility version number minor */
729 uint8_t compat_version_minor;
731 /* 0x208: install_partition variable */
733 /* 0x208: sub-partition in sub-partition part2 */
735 /* 0x209: sub-partition in top-level partition */
737 /* 0x20a: top-level partiton number */
739 /* 0x20b: BIOS drive number (must be 0) */
741 } __attribute__ ((packed)) install_partition;
743 /* 0x20c: deprecated (historical reason only) */
744 uint32_t saved_entryno;
745 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
747 /* 0x211: force LBA */
749 /* 0x212: version string (will probably be 0.97) */
750 char version_string[5];
751 /* 0x217: config filename */
752 char config_file[89];
753 /* 0x270: start of code (after jump from 0x200) */
755 } __attribute__ ((packed)) *stage2;
757 if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
759 ("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
763 stage2 = data[ndata].data;
766 * Check the compatibility version number to see if we loaded a real
767 * stage2 file or a stage2 file that we support.
769 if (stage2->compat_version_major != 3
770 || stage2->compat_version_minor != 2) {
772 ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
776 /* jump 0x200 bytes into the loadfile */
780 * GRUB Legacy wants the partition number in the install_partition
781 * variable, located at offset 0x208 of stage2.
782 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
784 * It looks very similar to the "boot information format" of the
785 * Multiboot specification:
786 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
788 * 0x208 = part3: sub-partition in sub-partition part2
789 * 0x209 = part2: sub-partition in top-level partition
790 * 0x20a = part1: top-level partition number
791 * 0x20b = drive: BIOS drive number (must be 0)
793 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
796 * Partition numbers always start from zero.
797 * Unused partition bytes must be set to 0xFF.
799 * We only care about top-level partition, so we only need to change
800 * "part1" to the appropriate value:
801 * -1: whole drive (default) (-1 = 0xFF)
802 * 0-3: primary partitions
803 * 4-*: logical partitions
805 stage2->install_partition.part1 = whichpart - 1;
808 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
809 * config filename. The filename passed via grubcfg= will overwrite
810 * the default config filename "/boot/grub/menu.lst".
813 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
815 ("The config filename length can't exceed 88 characters.\n");
819 strcpy((char *)stage2->config_file, opt.grubcfg);
824 /* DRMK entry is different than MS-DOS/PC-DOS */
826 * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
827 * We only really need 4 new, usable bytes at the end.
829 int tsize = (data[ndata].size + 19) & 0xfffffff0;
830 regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */
831 if (!realloc(data[ndata].data, tsize)) {
832 error("Failed to realloc for DRMK\n");
835 data[ndata].size = tsize;
836 /* ds:[bp+28] must be 0x0000003f */
837 regs.ds = (tsize >> 4) + (opt.seg - 2);
838 /* "Patch" into tail of the new space */
839 *(int *)(data[ndata].data + tsize - 4) = 0x0000003f;
845 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
846 /* Actually read the boot sector */
847 if (!cur_part->index) {
848 data[ndata].data = mbr;
850 if (!(data[ndata].data =
851 disk_read_sectors(&diskinfo, cur_part->start_lba, 1))) {
852 error("Cannot read boot sector\n");
855 sect_area = (void *)data[ndata].data;
856 data[ndata].size = SECTOR;
857 data[ndata].base = load_base;
860 const struct disk_dos_mbr *br =
861 (const struct disk_dos_mbr *)((char *)data[ndata].data +
863 sizeof(struct disk_dos_mbr));
864 if (br->sig != disk_mbr_sig_magic) {
866 ("Boot sector signature not found (unbootable disk/partition?)\n");
871 * To boot the Recovery Console of Windows NT/2K/XP we need to write
872 * the string "cmdcons\0" to memory location 0000:7C03.
873 * Memory location 0000:7C00 contains the bootsector of the partition.
875 if (cur_part->index && opt.cmldr) {
876 memcpy((char *)data[ndata].data + 3, cmldr_signature,
877 sizeof cmldr_signature);
881 * Modify the hidden sectors (partition offset) copy in memory;
882 * this modifies the field used by FAT and NTFS filesystems, and
883 * possibly other boot loaders which use the same format.
885 if (cur_part->index && opt.sethidden) {
886 *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->start_lba;
892 if (cur_part->index) {
893 if (cur_part->type == typegpt) {
894 /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
895 /* Look at the GPT partition */
896 const struct disk_gpt_part_entry *gp =
897 (const struct disk_gpt_part_entry *)cur_part->record;
898 /* Note the partition length */
899 uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
900 /* The length of the hand-over */
901 uint32_t synth_size =
902 sizeof(struct disk_dos_part_entry) + sizeof(uint32_t) +
903 cur_part->sub.gpt.pe_size;
904 /* Will point to the partition record length in the hand-over */
907 /* Allocate the hand-over record */
908 hand_area = malloc(synth_size);
910 error("Could not build GPT hand-over record!\n");
913 /* Synthesize the record */
914 memset(hand_area, 0, synth_size);
915 hand_area->active_flag = 0x80;
916 hand_area->ostype = 0xED;
917 /* All bits set by default */
918 hand_area->start_lba = ~(uint32_t) 0;
919 hand_area->length = ~(uint32_t) 0;
920 /* If these fit the precision, pass them on */
921 if (cur_part->start_lba < hand_area->start_lba)
922 hand_area->start_lba = cur_part->start_lba;
923 if (lba_count < hand_area->length)
924 hand_area->length = lba_count;
925 /* Next comes the GPT partition record length */
926 plen = (uint32_t *) (hand_area + 1);
927 plen[0] = cur_part->sub.gpt.pe_size;
928 /* Next comes the GPT partition record copy */
929 memcpy(plen + 1, gp, plen[0]);
931 regs.eax.l = 0x54504721; /* '!GPT' */
932 data[ndata].base = 0x7be;
933 data[ndata].size = synth_size;
934 data[ndata].data = (void *)hand_area;
936 regs.esi.w[0] = 0x7be;
938 dprintf("GPT handover:\n");
939 disk_dos_part_dump(hand_area);
940 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
943 /* MBR handover protocol */
944 /* Allocate the hand-over record */
945 hand_area = malloc(sizeof(struct disk_dos_part_entry));
947 error("Could not build MBR hand-over record!\n");
951 memcpy(hand_area, cur_part->record, sizeof(struct disk_dos_part_entry));
952 hand_area->start_lba = cur_part->start_lba;
954 data[ndata].base = 0x7be;
955 data[ndata].size = sizeof(struct disk_dos_part_entry);
956 data[ndata].data = (void *)hand_area;
958 regs.esi.w[0] = 0x7be;
960 dprintf("MBR handover:\n");
961 disk_dos_part_dump(hand_area);
966 do_boot(data, ndata, ®s);
970 /* Free allocated areas */
978 /* vim: set ts=8 sts=4 sw=4 noet: */