1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 * Copyright 2010 Shao Miller
6 * Copyright 2010 Michal Soltys
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 the detailed documentation.
32 #include <syslinux/loadfile.h>
33 #include <syslinux/bootrm.h>
34 #include <syslinux/config.h>
35 #include <syslinux/disk.h>
36 #include <syslinux/video.h>
46 static int fixed_cnt = 128; /* see comments in main() */
48 static int overlap(const struct data_area *a, const struct data_area *b)
51 a->base + a->size > b->base &&
52 b->base + b->size > a->base;
55 static int is_phys(uint8_t sdifs)
58 sdifs == SYSLINUX_FS_SYSLINUX ||
59 sdifs == SYSLINUX_FS_EXTLINUX ||
60 sdifs == SYSLINUX_FS_ISOLINUX;
64 * Search for a specific drive, based on the MBR signature.
65 * Return drive and iterator at 0th position.
67 static int find_by_sig(uint32_t mbr_sig,
68 struct part_iter **_boot_part)
70 struct part_iter *boot_part = NULL;
71 struct disk_info diskinfo;
74 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
75 if (disk_get_params(drive, &diskinfo))
76 continue; /* Drive doesn't exist */
77 if (!(boot_part = pi_begin(&diskinfo, 0)))
79 /* Check for a MBR disk */
80 if (boot_part->type != typedos) {
84 if (boot_part->sub.dos.disk_sig == mbr_sig) {
90 *_boot_part = boot_part;
95 * Search for a specific drive/partition, based on the GPT GUID.
96 * Return drive and iterator at proper position.
98 static int find_by_guid(const struct guid *gpt_guid,
99 struct part_iter **_boot_part)
101 struct part_iter *boot_part = NULL;
102 struct disk_info diskinfo;
105 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
106 if (disk_get_params(drive, &diskinfo))
107 continue; /* Drive doesn't exist */
108 if (!(boot_part = pi_begin(&diskinfo, 0)))
110 /* Check for a GPT disk */
111 if (boot_part->type != typegpt) {
115 /* Check for a matching GPT disk guid */
116 if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
119 /* disk guid doesn't match, maybe partition guid will */
120 while (!pi_next(&boot_part)) {
121 if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
127 *_boot_part = boot_part;
132 * Search for a specific drive/partition, based on the GPT label.
133 * Return drive and iterator at proper position.
135 static int find_by_label(const char *label, struct part_iter **_boot_part)
137 struct part_iter *boot_part = NULL;
138 struct disk_info diskinfo;
141 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
142 if (disk_get_params(drive, &diskinfo))
143 continue; /* Drive doesn't exist */
144 if (!(boot_part = pi_begin(&diskinfo, 0)))
146 /* Check for a GPT disk */
147 if (!(boot_part->type == typegpt)) {
151 /* Check for a matching partition */
152 while (!pi_next(&boot_part)) {
153 if (!strcmp(label, boot_part->sub.gpt.part_label))
159 *_boot_part = boot_part;
163 static void do_boot(struct data_area *data, int ndata)
165 uint16_t *const bios_fbm = (uint16_t *) 0x413;
166 addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
167 struct syslinux_memmap *mmap;
168 struct syslinux_movelist *mlist = NULL;
170 uint8_t driveno = opt.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 + 1023u) & ~1023u;
239 /* Create swap stub */
240 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
241 *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
242 *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
243 *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
244 *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
245 *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
246 *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
247 *(uint16_t *) & swapstub[0x58] = opt.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 opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
260 opt.regs.esi.l = opt.regs.es = 0;
261 opt.regs.ecx.l = sizeof swapstub >> 2;
262 opt.regs.ip = 0x10; /* Installer offset */
263 opt.regs.ebx.b[0] = opt.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, &opt.regs);
280 error("Chainboot failed!\n");
284 error("Loader file too large\n");
288 error("Out of memory\n");
292 static void hide_unhide(const struct part_iter *_iter)
295 struct disk_dos_mbr *mbr = NULL;
296 struct disk_dos_part_entry *pt;
297 const uint16_t mask =
298 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
299 (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
301 bool write_back = false;
303 if (_iter->type != typedos) {
304 error("Option 'hide' is only meaningful for legacy partition scheme.");
307 if (!(mbr = disk_read_sectors(&_iter->di, 0, 1))) {
308 error("WARNING: Couldn't read MBR to hide/unhide partitions.\n");
312 if (_iter->index < 1 || _iter->index > 4)
313 error("WARNING: option 'hide' specified with a non-primary partition.\n");
315 for (i = 1; i <= 4; i++) {
316 pt = mbr->table + i - 1;
318 if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
319 /* It's a hideable partition type */
320 if (i == _iter->index)
321 t &= (uint8_t)(~0x10u); /* unhide */
323 t |= 0x10u; /* hide */
325 if (t != pt->ostype) {
330 if (write_back && disk_write_verify_sector(&_iter->di, 0, mbr))
331 error("WARNING: failed to write MBR for option 'hide'\n");
338 static int pem_sethide(struct part_iter *miter, struct part_iter *iter)
340 struct disk_dos_part_entry *mdp, *dp;
341 static const uint16_t mask =
342 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
343 (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
346 mdp = (struct disk_dos_part_entry *)miter->record;
347 dp = (struct disk_dos_part_entry *)iter->record;
350 if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
351 /* It's a hideable partition type */
352 if (miter->index == iter->index || opt.hide & 4)
353 t &= (uint8_t)(~0x10u); /* unhide */
355 t |= 0x10u; /* hide */
357 if (dp->ostype != t) {
360 * the type of the partition being booted has to be adjusted in
361 * the match iterator (miter) as well
363 if (miter->index == iter->index) {
371 static int pem_setchs(const struct disk_info *di,
372 struct disk_dos_part_entry *dp,
375 uint32_t ochs1, ochs2;
377 ochs1 = *(uint32_t *)dp->start;
378 ochs2 = *(uint32_t *)dp->end;
380 *(uint32_t *)dp->start =
381 lba2chs(di, lba1, l2c_cadd) |
382 (*(uint32_t *)dp->start & 0xFF000000);
384 *(uint32_t *)dp->end =
385 lba2chs(di, lba1 + dp->length - 1, l2c_cadd) |
386 (*(uint32_t *)dp->end & 0xFF000000);
389 *(uint32_t *)dp->start != ochs1 ||
390 *(uint32_t *)dp->end != ochs2;
393 static int pentry_mangle(struct part_iter *miter)
395 int wb = 0, werr = 0;
396 struct part_iter *iter = NULL;
397 struct disk_dos_part_entry *dp;
400 if (miter->type != typedos) {
401 error("Partition entry mangling ('[un]hide[all]', 'mbrchs')\n"
402 "is meaningful only for legacy partition scheme.");
405 if ((miter->index < 1 || miter->index > 4) && opt.hide & 1)
406 error("WARNING: option '[un]hide' specified with a non-primary partition.\n");
408 if (!(iter = pi_begin(&miter->di, 1))) /* turn on stepall */
411 while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
412 ridx = iter->rawindex;
413 dp = (struct disk_dos_part_entry *)iter->record;
416 if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
417 wb |= pem_sethide(miter, iter);
420 wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
422 wb |= pem_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
426 if (ridx >= 4 && wb && !werr) {
427 werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
433 werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
438 error("WARNING: failed to write E/MBR for partition\n"
439 "mangling options ('[un]hide[all]', 'mbrchs').\n");
443 int find_dp(struct part_iter **_iter)
445 struct part_iter *iter = NULL;
446 struct disk_info diskinfo;
447 struct guid gpt_guid;
449 int drive, hd, partition;
450 const union syslinux_derivative_info *sdi;
452 sdi = syslinux_derivative_info();
454 if (!strncmp(opt.drivename, "mbr", 3)) {
455 if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
456 error("Unable to find requested MBR signature.\n");
459 } else if (!strncmp(opt.drivename, "guid", 4)) {
460 if (str_to_guid(opt.drivename + 5, &gpt_guid))
462 if (find_by_guid(&gpt_guid, &iter) < 0) {
463 error("Unable to find requested GPT disk or partition by guid.\n");
466 } else if (!strncmp(opt.drivename, "label", 5)) {
467 if (!opt.drivename[6]) {
468 error("No label specified.\n");
471 if (find_by_label(opt.drivename + 6, &iter) < 0) {
472 error("Unable to find requested GPT partition by label.\n");
475 } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
476 opt.drivename[1] == 'd') {
477 hd = opt.drivename[0] == 'h' ? 0x80 : 0;
479 drive = hd | strtol(opt.drivename, NULL, 0);
481 if (disk_get_params(drive, &diskinfo))
483 /* this will start iteration over FDD, possibly raw */
484 if (!(iter = pi_begin(&diskinfo, 0)))
487 } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
488 if (!is_phys(sdi->c.filesystem)) {
489 error("When syslinux is not booted from physical disk (or its emulation),\n"
490 "'boot' and 'fs' are meaningless.\n");
493 /* offsets match, but in case it changes in the future */
494 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
495 drive = sdi->iso.drive_number;
496 fs_lba = *sdi->iso.partoffset;
498 drive = sdi->disk.drive_number;
499 fs_lba = *sdi->disk.partoffset;
501 if (disk_get_params(drive, &diskinfo))
503 /* this will start iteration over disk emulation, possibly raw */
504 if (!(iter = pi_begin(&diskinfo, 0)))
507 /* 'fs' => we should lookup the syslinux partition number and use it */
508 if (!strcmp(opt.drivename, "fs")) {
509 while (!pi_next(&iter)) {
510 if (iter->start_lba == fs_lba)
513 /* broken part structure or other problems */
515 error("Can't find myself on the drive I booted from.\n");
520 error("Unparsable drive specification.\n");
523 /* main options done - only thing left is explicit partition specification,
524 * if we're still at the disk stage with the iterator AND user supplied
525 * partition number (including disk pseudo-partition).
527 if (!iter->index && opt.partition) {
528 partition = strtol(opt.partition, NULL, 0);
529 /* search for matching part#, including disk */
531 if (iter->index == partition)
533 } while (!pi_next(&iter));
535 error("Requested disk / partition combination not found.\n");
540 if (!(iter->di.disk & 0x80) && iter->index) {
541 error("WARNING: Partitions on floppy devices may not work.\n");
553 static int setup_handover(const struct part_iter *iter,
554 struct data_area *data)
556 const struct disk_dos_part_entry *dp;
557 const struct disk_gpt_part_entry *gp;
558 struct disk_dos_part_entry *ha;
563 if (iter->type == typegpt) {
564 /* GPT handover protocol */
565 gp = (const struct disk_gpt_part_entry *)iter->record;
566 lba_count = gp->lba_last - gp->lba_first + 1;
567 synth_size = sizeof(struct disk_dos_part_entry) +
568 sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
570 ha = malloc(synth_size);
572 error("Could not build GPT hand-over record!\n");
575 *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first, l2c_cadd);
576 *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last, l2c_cadd);
577 ha->active_flag = 0x80;
579 /* All bits set by default */
582 /* If these fit the precision, pass them on */
583 if (iter->start_lba < ha->start_lba)
584 ha->start_lba = (uint32_t)iter->start_lba;
585 if (lba_count < ha->length)
586 ha->length = (uint32_t)lba_count;
587 /* Next comes the GPT partition record length */
588 plen = (uint32_t *) (ha + 1);
589 plen[0] = (uint32_t)iter->sub.gpt.pe_size;
590 /* Next comes the GPT partition record copy */
591 memcpy(plen + 1, gp, plen[0]);
593 dprintf("GPT handover:\n");
594 disk_dos_part_dump(ha);
595 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
597 } else if (iter->type == typedos || iter->type == typeraw) {
598 /* MBR / RAW handover protocol */
599 synth_size = sizeof(struct disk_dos_part_entry);
600 ha = malloc(synth_size);
602 error("Could not build MBR / RAW hand-over record!\n");
606 *(uint32_t *)ha->start = lba2chs(&iter->di, 0, l2c_cadd);
607 *(uint32_t *)ha->end = lba2chs(&iter->di, 2879, l2c_cadd);
608 ha->active_flag = 0x80;
612 } else if (iter->type == typedos) {
613 dp = (const struct disk_dos_part_entry *)iter->record;
615 *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba, l2c_cadd);
616 *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1, l2c_cadd);
617 ha->active_flag = dp->active_flag;
618 ha->ostype = dp->ostype;
619 ha->start_lba = (uint32_t)iter->start_lba; /* fine, we iterate over legacy scheme */
620 ha->length = dp->length;
622 dprintf("MBR handover:\n");
623 disk_dos_part_dump(ha);
630 /* shouldn't ever happen */
636 data->size = synth_size;
637 data->data = (void *)ha;
644 int main(int argc, char *argv[])
646 struct part_iter *iter = NULL;
649 struct data_area fdat, hdat, sdat, data[3];
653 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
655 /* Prepare and set defaults */
656 memset(&fdat, 0, sizeof(fdat));
657 memset(&hdat, 0, sizeof(hdat));
658 memset(&sdat, 0, sizeof(sdat));
659 memset(&opt, 0, sizeof(opt));
660 opt.sect = true; /* by def load sector */
661 opt.maps = true; /* by def map sector */
662 opt.hand = true; /* by def prepare handover */
663 opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
664 opt.drivename = "boot";
669 /* Parse arguments */
670 if (parse_args(argc, argv))
673 /* Get max fixed disk number */
674 fixed_cnt = *(uint8_t *)(0x475);
677 * hmm, looks like we can't do that
678 * any better options than hardcoded 0x80 - 0xFF ?
682 /* Get disk/part iterator matching user supplied options */
686 /* Perform initial partition entry mangling */
687 if (opt.hide || opt.mbrchs)
689 /* hide_unhide(iter);*/
691 /* Load the boot file */
693 fdat.base = (opt.fseg << 4) + opt.foff;
695 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
696 error("Couldn't read the boot file.\n");
699 if (fdat.base + fdat.size - 1 > ADDRMAX) {
700 error("The boot file is too big to load at this address.\n");
705 /* Load the sector */
708 sdat.base = (opt.sseg << 4) + opt.soff;
710 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
711 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
714 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
715 error("Couldn't read the sector.\n");
719 if (!(sbck = malloc(SECTOR))) {
720 error("Couldn't allocate cmp-buf for option 'save'.\n");
723 memcpy(sbck, sdat.data, sdat.size);
728 /* Prep the handover */
730 if (setup_handover(iter, &hdat))
732 /* Verify possible conflicts */
733 if ( ( opt.file && overlap(&fdat, &hdat)) ||
734 ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
735 error("WARNING: Handover area won't be prepared,\n"
736 "as it would conflict with the boot file and/or the sector.\n");
741 /* Adjust registers */
743 mangler_common(iter);
744 mangler_handover(iter, &hdat);
747 /* Patching functions */
749 if (manglef_isolinux(&fdat))
752 if (manglef_grub(iter, &fdat))
755 if (manglef_drmk(&fdat))
758 if (manglef_bpb(iter, &fdat))
761 if (mangles_bpb(iter, &sdat))
764 if (mangles_save(iter, &sdat, sbck))
767 if (manglesf_bss(&sdat, &fdat))
770 /* This *must* be after BPB saving or copying */
771 if (mangles_cmldr(&sdat))
775 * Prepare boot-time mmap data We should to it here, as manglers could
776 * potentially alter some of the data.
780 memcpy(data + ndata++, &fdat, sizeof(fdat));
781 if (opt.sect && opt.maps)
782 memcpy(data + ndata++, &sdat, sizeof(sdat));
784 memcpy(data + ndata++, &hdat, sizeof(hdat));
787 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
788 "iter->di C, H, S: %u, %u, %u\n",
789 iter->di.disk, iter->di.bps,
790 iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
791 iter->di.cyl, iter->di.head, iter->di.spt);
792 printf("iter idx: %d\n", iter->index);
793 printf("iter lba: %llu\n", iter->start_lba);
795 printf("hand lba: %u\n",
796 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
800 puts("Press any key to continue booting...");
804 do_boot(data, ndata);
807 /* Free allocated areas */
815 /* vim: set ts=8 sts=4 sw=4 noet: */