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.\n");
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.\n");
406 ((miter->index < 1 && opt.hide < 4) || /* try to hide a disk */
407 (miter->index > 4 && opt.hide == 1))) /* try to hide a part when limited to pri */
408 error("WARNING: It's impossible to hide the selected partition (or you selected a disk).\n");
410 if (!(iter = pi_begin(&miter->di, 1))) /* turn on stepall */
413 while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
414 ridx = iter->rawindex;
415 dp = (struct disk_dos_part_entry *)iter->record;
418 if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
419 wb |= pem_sethide(miter, iter);
422 wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
424 wb |= pem_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
428 if (ridx >= 4 && wb && !werr) {
429 werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
435 werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
440 error("WARNING: failed to write E/MBR for partition\n"
441 "mangling options ('[un]hide[all]', 'mbrchs').\n");
445 int find_dp(struct part_iter **_iter)
447 struct part_iter *iter = NULL;
448 struct disk_info diskinfo;
449 struct guid gpt_guid;
451 int drive, hd, partition;
452 const union syslinux_derivative_info *sdi;
454 sdi = syslinux_derivative_info();
456 if (!strncmp(opt.drivename, "mbr", 3)) {
457 if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
458 error("Unable to find requested MBR signature.\n");
461 } else if (!strncmp(opt.drivename, "guid", 4)) {
462 if (str_to_guid(opt.drivename + 5, &gpt_guid))
464 if (find_by_guid(&gpt_guid, &iter) < 0) {
465 error("Unable to find requested GPT disk or partition by guid.\n");
468 } else if (!strncmp(opt.drivename, "label", 5)) {
469 if (!opt.drivename[6]) {
470 error("No label specified.\n");
473 if (find_by_label(opt.drivename + 6, &iter) < 0) {
474 error("Unable to find requested GPT partition by label.\n");
477 } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
478 opt.drivename[1] == 'd') {
479 hd = opt.drivename[0] == 'h' ? 0x80 : 0;
481 drive = hd | strtol(opt.drivename, NULL, 0);
483 if (disk_get_params(drive, &diskinfo))
485 /* this will start iteration over FDD, possibly raw */
486 if (!(iter = pi_begin(&diskinfo, 0)))
489 } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
490 if (!is_phys(sdi->c.filesystem)) {
491 error("When syslinux is not booted from physical disk (or its emulation),\n"
492 "'boot' and 'fs' are meaningless.\n");
495 /* offsets match, but in case it changes in the future */
496 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
497 drive = sdi->iso.drive_number;
498 fs_lba = *sdi->iso.partoffset;
500 drive = sdi->disk.drive_number;
501 fs_lba = *sdi->disk.partoffset;
503 if (disk_get_params(drive, &diskinfo))
505 /* this will start iteration over disk emulation, possibly raw */
506 if (!(iter = pi_begin(&diskinfo, 0)))
509 /* 'fs' => we should lookup the syslinux partition number and use it */
510 if (!strcmp(opt.drivename, "fs")) {
511 while (!pi_next(&iter)) {
512 if (iter->start_lba == fs_lba)
515 /* broken part structure or other problems */
517 error("Can't find myself on the drive I booted from.\n");
522 error("Unparsable drive specification.\n");
525 /* main options done - only thing left is explicit partition specification,
526 * if we're still at the disk stage with the iterator AND user supplied
527 * partition number (including disk pseudo-partition).
529 if (!iter->index && opt.partition) {
530 partition = strtol(opt.partition, NULL, 0);
531 /* search for matching part#, including disk */
533 if (iter->index == partition)
535 } while (!pi_next(&iter));
537 error("Requested disk / partition combination not found.\n");
542 if (!(iter->di.disk & 0x80) && iter->index) {
543 error("WARNING: Partitions on floppy devices may not work.\n");
555 static int setup_handover(const struct part_iter *iter,
556 struct data_area *data)
558 const struct disk_dos_part_entry *dp;
559 const struct disk_gpt_part_entry *gp;
560 struct disk_dos_part_entry *ha;
565 if (iter->type == typegpt) {
566 /* GPT handover protocol */
567 gp = (const struct disk_gpt_part_entry *)iter->record;
568 lba_count = gp->lba_last - gp->lba_first + 1;
569 synth_size = sizeof(struct disk_dos_part_entry) +
570 sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
572 ha = malloc(synth_size);
574 error("Could not build GPT hand-over record!\n");
577 *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first, l2c_cadd);
578 *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last, l2c_cadd);
579 ha->active_flag = 0x80;
581 /* All bits set by default */
584 /* If these fit the precision, pass them on */
585 if (iter->start_lba < ha->start_lba)
586 ha->start_lba = (uint32_t)iter->start_lba;
587 if (lba_count < ha->length)
588 ha->length = (uint32_t)lba_count;
589 /* Next comes the GPT partition record length */
590 plen = (uint32_t *) (ha + 1);
591 plen[0] = (uint32_t)iter->sub.gpt.pe_size;
592 /* Next comes the GPT partition record copy */
593 memcpy(plen + 1, gp, plen[0]);
595 dprintf("GPT handover:\n");
596 disk_dos_part_dump(ha);
597 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
599 } else if (iter->type == typedos || iter->type == typeraw) {
600 /* MBR / RAW handover protocol */
601 synth_size = sizeof(struct disk_dos_part_entry);
602 ha = malloc(synth_size);
604 error("Could not build MBR / RAW hand-over record!\n");
609 if (iter->di.lbacnt < len)
610 len = (uint32_t)iter->di.lbacnt;
611 *(uint32_t *)ha->start = lba2chs(&iter->di, 0, l2c_cadd);
612 *(uint32_t *)ha->end = lba2chs(&iter->di, len - 1, l2c_cadd);
613 ha->active_flag = 0x80;
614 ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
617 } else if (iter->type == typedos) {
618 dp = (const struct disk_dos_part_entry *)iter->record;
620 *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba, l2c_cadd);
621 *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1, l2c_cadd);
622 ha->active_flag = dp->active_flag;
623 ha->ostype = dp->ostype;
624 ha->start_lba = (uint32_t)iter->start_lba; /* fine, we iterate over legacy scheme */
625 ha->length = dp->length;
627 dprintf("MBR handover:\n");
628 disk_dos_part_dump(ha);
635 /* shouldn't ever happen */
641 data->size = synth_size;
642 data->data = (void *)ha;
649 int main(int argc, char *argv[])
651 struct part_iter *iter = NULL;
654 struct data_area fdat, hdat, sdat, data[3];
658 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
660 /* Prepare and set defaults */
661 memset(&fdat, 0, sizeof(fdat));
662 memset(&hdat, 0, sizeof(hdat));
663 memset(&sdat, 0, sizeof(sdat));
664 memset(&opt, 0, sizeof(opt));
665 opt.sect = true; /* by def. load sector */
666 opt.maps = true; /* by def. map sector */
667 opt.hand = true; /* by def. prepare handover */
668 opt.chain = true; /* by def. do chainload */
669 opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
670 opt.drivename = "boot";
675 /* Parse arguments */
676 if (parse_args(argc, argv))
679 /* Get max fixed disk number */
680 fixed_cnt = *(uint8_t *)(0x475);
683 * hmm, looks like we can't do that
684 * any better options than hardcoded 0x80 - 0xFF ?
688 /* Get disk/part iterator matching user supplied options */
692 /* Perform initial partition entry mangling */
693 if (opt.hide || opt.mbrchs)
695 /* hide_unhide(iter);*/
697 /* Load the boot file */
699 fdat.base = (opt.fseg << 4) + opt.foff;
701 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
702 error("Couldn't read the boot file.\n");
705 if (fdat.base + fdat.size - 1 > ADDRMAX) {
706 error("The boot file is too big to load at this address.\n");
711 /* Load the sector */
714 sdat.base = (opt.sseg << 4) + opt.soff;
716 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
717 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
720 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
721 error("Couldn't read the sector.\n");
725 if (!(sbck = malloc(SECTOR))) {
726 error("Couldn't allocate cmp-buf for option 'save'.\n");
729 memcpy(sbck, sdat.data, sdat.size);
734 /* Prep the handover */
736 if (setup_handover(iter, &hdat))
738 /* Verify possible conflicts */
739 if ( ( opt.file && overlap(&fdat, &hdat)) ||
740 ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
741 error("WARNING: Handover area won't be prepared,\n"
742 "as it would conflict with the boot file and/or the sector.\n");
747 /* Adjust registers */
749 mangler_common(iter);
750 mangler_handover(iter, &hdat);
753 /* Patching functions */
755 if (manglef_isolinux(&fdat))
758 if (manglef_grub(iter, &fdat))
761 if (manglef_drmk(&fdat))
764 if (manglef_bpb(iter, &fdat))
767 if (mangles_bpb(iter, &sdat))
770 if (mangles_save(iter, &sdat, sbck))
773 if (manglesf_bss(&sdat, &fdat))
776 /* This *must* be after BPB saving or copying */
777 if (mangles_cmldr(&sdat))
781 * Prepare boot-time mmap data. We should to it here, as manglers could
782 * potentially alter some of the data.
786 memcpy(data + ndata++, &fdat, sizeof(fdat));
787 if (opt.sect && opt.maps)
788 memcpy(data + ndata++, &sdat, sizeof(sdat));
790 memcpy(data + ndata++, &hdat, sizeof(hdat));
793 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
794 "iter->di C, H, S: %u, %u, %u\n",
795 iter->di.disk, iter->di.bps,
796 iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
797 iter->di.cyl, iter->di.head, iter->di.spt);
798 printf("iter idx: %d\n", iter->index);
799 printf("iter lba: %llu\n", iter->start_lba);
801 printf("hand lba: %u\n",
802 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
806 puts("Press any key to continue booting...");
810 if (ndata && opt.chain) /* boot only if we actually chainload */
811 do_boot(data, ndata);
813 error("Service-only run completed, exiting.\n");
816 /* Free allocated areas */
824 /* vim: set ts=8 sts=4 sw=4 noet: */