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 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>
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");
337 static int pem_sethide(struct disk_dos_part_entry *dp, int midx, int idx)
339 static const uint16_t mask =
340 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
341 (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
345 if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
346 /* It's a hideable partition type */
348 t &= (uint8_t)(~0x10u); /* unhide */
350 t |= 0x10u; /* hide */
352 if (t != dp->ostype) {
359 static int pem_setchs(const struct disk_info *di,
360 struct disk_dos_part_entry *dp,
363 uint32_t ochs1, ochs2;
365 ochs1 = *(uint32_t *)dp->start;
366 ochs2 = *(uint32_t *)dp->end;
368 *(uint32_t *)dp->start =
370 (*(uint32_t *)dp->start & 0xFF000000);
372 *(uint32_t *)dp->end =
373 lba2chs(di, lba1 + dp->length - 1) |
374 (*(uint32_t *)dp->end & 0xFF000000);
377 *(uint32_t *)dp->start != ochs1 ||
378 *(uint32_t *)dp->end != ochs2;
381 static int pentry_mangle(struct part_iter *_iter)
383 int wb = 0, werr = 0;
384 uint32_t cebr_lba = 0;
385 struct part_iter *iter = NULL;
386 struct disk_dos_part_entry *dp;
387 struct disk_dos_mbr mbr;
390 if (_iter->type != typedos) {
391 error("Partition entry mangling ('hide[all]', 'mbrchs')\n"
392 "is meaningful only for legacy partition scheme.");
395 if ((_iter->index < 1 || _iter->index > 4) && opt.hide == 1)
396 error("WARNING: option 'hide' specified with a non-primary partition.\n");
398 if (!(iter = pi_begin(&_iter->di, 1))) /* turn on stepall */
401 memcpy(&mbr, iter->data, sizeof(struct disk_dos_mbr));
403 while (pi_next(&iter) && !werr) {
404 ridx = iter->rawindex;
406 if (opt.hide < 2 && !opt.mbrchs)
407 break; /* don't walk unnecessarily */
409 werr |= disk_write_sector(&iter->di, cebr_lba, &mbr);
412 memcpy(&mbr, iter->data, sizeof(struct disk_dos_mbr));
413 cebr_lba = iter->sub.dos.cebr_lba;
416 dp = mbr.table + ridx - 1;
418 (opt.hide == 1 && ridx <= 4)) {
419 wb |= pem_sethide(dp, _iter->index, iter->index);
420 if (_iter->index == iter->index) {
421 ((struct disk_dos_part_entry *)_iter->record)->ostype =
426 wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
428 wb |= pem_setchs(&iter->di, mbr.table + 1, iter->sub.dos.ebr_lba);
433 werr |= disk_write_sector(&_iter->di, cebr_lba, &mbr);
438 error("WARNING: failed to write E/MBR for partition\n"
439 "mangling options ('hide[all]', 'mbrchs').\n");
443 int find_dp(struct part_iter **_iter)
445 struct part_iter *iter;
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)) {
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)) {
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)) {
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");
552 static int setup_handover(const struct part_iter *iter,
553 struct data_area *data)
555 const struct disk_dos_part_entry *dp;
556 const struct disk_gpt_part_entry *gp;
557 struct disk_dos_part_entry *ha;
562 if (iter->type == typegpt) {
563 /* GPT handover protocol */
564 gp = (const struct disk_gpt_part_entry *)iter->record;
565 lba_count = gp->lba_last - gp->lba_first + 1;
566 synth_size = sizeof(struct disk_dos_part_entry) +
567 sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
569 ha = malloc(synth_size);
571 error("Could not build GPT hand-over record!\n");
574 memset(ha, 0, synth_size);
575 *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first);
576 *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last);
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) {
598 /* MBR handover protocol */
599 dp = (const struct disk_dos_part_entry *)iter->record;
600 synth_size = sizeof(struct disk_dos_part_entry);
601 ha = malloc(synth_size);
603 error("Could not build MBR hand-over record!\n");
607 *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba);
608 *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1);
609 ha->active_flag = dp->active_flag;
610 ha->ostype = dp->ostype;
611 ha->start_lba = (uint32_t)iter->start_lba; /* fine, we iterate over legacy scheme */
612 ha->length = dp->length;
615 dprintf("MBR handover:\n");
616 disk_dos_part_dump(ha);
619 /* shouldn't ever happen */
624 data->size = synth_size;
625 data->data = (void *)ha;
632 int main(int argc, char *argv[])
634 struct part_iter *iter = NULL;
637 struct data_area fdat, hdat, sdat, data[3];
641 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
643 /* Prepare and set defaults */
644 memset(&fdat, 0, sizeof(fdat));
645 memset(&hdat, 0, sizeof(hdat));
646 memset(&sdat, 0, sizeof(sdat));
647 memset(&opt, 0, sizeof(opt));
648 opt.sect = true; /* by def load sector */
649 opt.maps = true; /* by def map sector */
650 opt.hand = true; /* by def prepare handover */
651 opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
652 opt.drivename = "boot";
657 /* Parse arguments */
658 if (parse_args(argc, argv))
661 /* Get max fixed disk number */
662 fixed_cnt = *(uint8_t *)(0x475);
664 /* Get disk/part iterator matching user supplied options */
668 /* Perform initial partition entry mangling */
669 if (opt.hide || opt.mbrchs)
671 /* hide_unhide(iter);*/
673 /* Load the boot file */
675 fdat.base = (opt.fseg << 4) + opt.foff;
677 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
678 error("Couldn't read the boot file.\n");
681 if (fdat.base + fdat.size - 1 > ADDRMAX) {
682 error("The boot file is too big to load at this address.\n");
687 /* Load the sector */
690 sdat.base = (opt.sseg << 4) + opt.soff;
692 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
693 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
696 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
697 error("Couldn't read the sector.\n");
701 if (!(sbck = malloc(SECTOR))) {
702 error("Couldn't allocate cmp-buf for option 'save'.\n");
705 memcpy(sbck, sdat.data, sdat.size);
710 /* Prep the handover */
713 } else if (opt.hand) {
714 if (setup_handover(iter, &hdat))
716 /* Verify possible conflicts */
717 if ( ( opt.file && overlap(&fdat, &hdat)) ||
718 ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
719 error("WARNING: Handover area won't be prepared,\n"
720 "as it would conflict with the boot file and/or the sector.\n");
725 /* Adjust registers */
726 mangler_common(iter);
727 mangler_handover(iter, &hdat);
732 * opt.* are tested inside
735 if (manglef_isolinux(&fdat))
738 if (manglef_grub(iter, &fdat))
741 if (manglef_drmk(&fdat))
744 if (manglef_bpb(iter, &fdat))
747 if (mangles_bpb(iter, &sdat))
750 if (mangles_save(iter, &sdat, sbck))
753 if (manglesf_bss(&sdat, &fdat))
756 /* This *must* be after BPB saving or copying */
757 if (mangles_cmldr(&sdat))
760 /* Prepare boot-time mmap data */
763 memcpy(data + ndata++, &fdat, sizeof(fdat));
764 if (opt.sect && opt.maps)
765 memcpy(data + ndata++, &sdat, sizeof(sdat));
767 memcpy(data + ndata++, &hdat, sizeof(hdat));
770 printf("iter dsk: %d\n", iter->di.disk);
771 printf("iter idx: %d\n", iter->index);
772 printf("iter lba: %llu\n", iter->start_lba);
774 printf("hand lba: %u\n", ((disk_dos_part_entry *)hdat.data)->start_lba);
778 puts("Press any key to continue booting...");
782 do_boot(data, ndata);
785 /* Free allocated areas */
793 /* vim: set ts=8 sts=4 sw=4 noet: */