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 setdrv_auto(const struct part_iter *iter)
637 if (!(buf = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
638 error("Couldn't read a sector to detect 'setdrv' offset.\n");
642 a = strncmp(buf + 0x36, "FAT", 3);
643 b = strncmp(buf + 0x52, "FAT", 3);
645 if ((!a && b && (buf[0x26] & 0xFE) == 0x28) || *((uint8_t*)buf + 0x26) == 0x80) {
647 } else if (a && !b && (buf[0x42] & 0xFE) == 0x28) {
650 error("WARNING: Couldn't autodetect 'setdrv' offset - turning option off.\n");
659 int main(int argc, char *argv[])
661 struct part_iter *iter = NULL;
663 void *file_area = NULL;
664 void *sect_area = NULL;
665 void *sbck_area = NULL;
666 struct disk_dos_part_entry *hand_area = NULL;
668 struct data_area data[3], bdata[3];
669 int ndata = 0, fidx = -1, sidx = -1, hidx = -1;
672 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
674 /* Prepare and set defaults */
675 memset(&opt, 0, sizeof(opt));
676 opt.sect = true; /* by def load sector */
677 opt.maps = true; /* by def map sector */
678 opt.hand = true; /* by def prepare handover */
679 opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
680 opt.drivename = "boot";
685 /* Parse arguments */
686 if (parse_args(argc, argv))
689 /* Set initial registry values */
691 opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
692 opt.regs.ip = (uint16_t)opt.fip;
694 opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
695 opt.regs.ip = (uint16_t)opt.sip;
698 if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
699 opt.regs.esp.l = 0x7C00;
701 /* Get max fixed disk number */
702 fixed_cnt = *(uint8_t *)(0x475);
704 /* Get disk/part iterator matching user supplied options */
708 /* Try to autodetect setdrv offest */
709 if (opt.setdrv && opt.drvoff == ~0u && setdrv_auto(iter))
712 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
713 opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
715 /* Perform initial partition entry mangling */
716 if (opt.hide || opt.mbrchs)
718 /* hide_unhide(iter);*/
720 /* Load the boot file */
722 data[ndata].base = (opt.fseg << 4) + opt.foff;
724 if (loadfile(opt.file, &data[ndata].data, &data[ndata].size)) {
725 error("Couldn't read the boot file.\n");
728 file_area = (void *)data[ndata].data;
730 if (data[ndata].base + data[ndata].size - 1 > ADDRMAX) {
731 error("The boot file is too big to load at this address.\n");
739 /* Load the sector */
741 data[ndata].size = SECTOR;
742 data[ndata].base = (opt.sseg << 4) + opt.soff;
744 if (opt.file && opt.maps && overlap(data + fidx, data + ndata)) {
745 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
747 if (!(data[ndata].data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
748 error("Couldn't read the sector.\n");
751 sect_area = (void *)data[ndata].data;
754 if (!(sbck_area = malloc(SECTOR))) {
755 error("Couldn't allocate cmp-buf for option 'save'.\n");
758 memcpy(sbck_area, data->data, data->size);
766 /* Prep the handover */
767 if (opt.hand && iter->index) {
768 if (setup_handover(iter, data + ndata))
770 hand_area = (void *)data[ndata].data;
772 /* Verify possible conflicts */
773 if ( ( fidx >= 0 && overlap(data + fidx, data + ndata)) ||
774 ( sidx >= 0 && opt.maps && overlap(data + sidx, data + ndata)) ) {
775 error("WARNING: Handover area won't be prepared,\n"
776 "as it would conflict with the boot file and/or the sector.\n");
784 * Adjust registers - ds:si & ds:bp
785 * We do it here, as they might get further
786 * overriden during mangling.
789 if (sidx >= 0 && fidx >= 0 && opt.maps && !opt.hptr) {
790 opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
791 opt.regs.ds = (uint16_t)opt.sseg;
793 } else if (hidx >= 0) {
794 opt.regs.esi.l = opt.regs.ebp.l = data[hidx].base;
796 if (iter->type == typegpt)
797 opt.regs.eax.l = 0x54504721; /* '!GPT' */
802 /* Do file related stuff */
805 if (manglef_isolinux(data + fidx))
808 if (manglef_grldr(iter))
811 if (manglef_grub(iter, data + fidx))
814 if (manglef_drmk(data + fidx))
817 if (manglef_bpb(iter, data + fidx))
821 /* Do sector related stuff */
824 if (mangles_bpb(iter, data + sidx))
827 if (mangles_save(iter, data + sidx, sbck_area))
830 /* This *must* be after last BPB saving */
831 if (mangles_cmldr(data + sidx))
835 /* Prepare boot-time mmap data */
839 memcpy(bdata + ndata++, data + sidx, sizeof(struct data_area));
841 memcpy(bdata + ndata++, data + fidx, sizeof(struct data_area));
843 memcpy(bdata + ndata++, data + hidx, sizeof(struct data_area));
846 printf("iter dsk: %d\n", iter->di.disk);
847 printf("iter idx: %d\n", iter->index);
848 printf("iter lba: %llu\n", iter->start_lba);
850 printf("hand lba: %u\n", hand_area->start_lba);
854 puts("Press any key to continue booting...");
858 do_boot(bdata, ndata);
861 /* Free allocated areas */
869 /* vim: set ts=8 sts=4 sw=4 noet: */