Home | History | Annotate | Download | only in chain
      1 /* ----------------------------------------------------------------------- *
      2  *
      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-2012 Michal Soltys
      7  *
      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.
     13  *
     14  * ----------------------------------------------------------------------- */
     15 
     16 /*
     17  * Please see doc/chain.txt for the detailed documentation.
     18  */
     19 
     20 #include <com32.h>
     21 #include <stdlib.h>
     22 #include <stdio.h>
     23 #include <ctype.h>
     24 #include <string.h>
     25 #include <console.h>
     26 #include <consoles.h>
     27 #include <minmax.h>
     28 #include <stdbool.h>
     29 #include <dprintf.h>
     30 #include <errno.h>
     31 #include <unistd.h>
     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>
     37 #include "chain.h"
     38 #include "utility.h"
     39 #include "options.h"
     40 #include "partiter.h"
     41 #include "mangle.h"
     42 
     43 static int fixed_cnt = 128;   /* see comments in main() */
     44 
     45 static int overlap(const struct data_area *a, const struct data_area *b)
     46 {
     47     return
     48 	a->base + a->size > b->base &&
     49 	b->base + b->size > a->base;
     50 }
     51 
     52 static int is_phys(uint8_t sdifs)
     53 {
     54     return
     55 	sdifs == SYSLINUX_FS_SYSLINUX ||
     56 	sdifs == SYSLINUX_FS_EXTLINUX ||
     57 	sdifs == SYSLINUX_FS_ISOLINUX;
     58 }
     59 
     60 /*
     61  * Search for a specific drive, based on the MBR signature.
     62  * Return drive and iterator at 0th position.
     63  */
     64 static int find_by_sig(uint32_t mbr_sig,
     65 			struct part_iter **_boot_part)
     66 {
     67     struct part_iter *iter = NULL;
     68     struct disk_info diskinfo;
     69     int drive;
     70 
     71     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
     72 	if (disk_get_params(drive, &diskinfo))
     73 	    continue;		/* Drive doesn't exist */
     74 	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
     75 	    continue;
     76 	/* Check for a matching MBR disk */
     77 	if (iter->type == typedos && iter->dos.disk_sig == mbr_sig)
     78 	    goto ok;
     79 	pi_del(&iter);
     80     }
     81     drive = -1;
     82 ok:
     83     *_boot_part = iter;
     84     return drive;
     85 }
     86 
     87 /*
     88  * Search for a specific drive/partition, based on the GPT GUID.
     89  * Return drive and iterator at proper position.
     90  */
     91 static int find_by_guid(const struct guid *gpt_guid,
     92 			struct part_iter **_boot_part)
     93 {
     94     struct part_iter *iter = NULL;
     95     struct disk_info diskinfo;
     96     int drive;
     97 
     98     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
     99 	if (disk_get_params(drive, &diskinfo))
    100 	    continue;		/* Drive doesn't exist */
    101 	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
    102 	    continue;
    103 	/* Check for a matching GPT disk/partition guid */
    104 	if (iter->type == typegpt)
    105 	    do {
    106 		if (!memcmp(&iter->gpt.part_guid, gpt_guid, sizeof *gpt_guid))
    107 		    goto ok;
    108 	    } while (!pi_next(iter));
    109 	pi_del(&iter);
    110     }
    111     drive = -1;
    112 ok:
    113     *_boot_part = iter;
    114     return drive;
    115 }
    116 
    117 /*
    118  * Search for a specific drive/partition, based on the GPT label.
    119  * Return drive and iterator at proper position.
    120  */
    121 static int find_by_label(const char *label, struct part_iter **_boot_part)
    122 {
    123     struct part_iter *iter = NULL;
    124     struct disk_info diskinfo;
    125     int drive;
    126 
    127     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
    128 	if (disk_get_params(drive, &diskinfo))
    129 	    continue;		/* Drive doesn't exist */
    130 	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
    131 	    continue;
    132 	/* Check for a matching GPT partition label */
    133 	if (iter->type == typegpt)
    134 	    while (!pi_next(iter)) {
    135 		if (!strcmp(label, iter->gpt.part_label))
    136 		    goto ok;
    137 	    }
    138 	pi_del(&iter);
    139     }
    140     drive = -1;
    141 ok:
    142     *_boot_part = iter;
    143     return drive;
    144 }
    145 
    146 static void do_boot(struct data_area *data, int ndata)
    147 {
    148     struct syslinux_memmap *mmap;
    149     struct syslinux_movelist *mlist = NULL;
    150     addr_t endimage;
    151     uint8_t driveno = opt.regs.edx.b[0];
    152     uint8_t swapdrive = driveno & 0x80;
    153     int i;
    154 
    155     mmap = syslinux_memory_map();
    156 
    157     if (!mmap) {
    158 	error("Cannot read system memory map.");
    159 	return;
    160     }
    161 
    162     endimage = 0;
    163     for (i = 0; i < ndata; i++) {
    164 	if (data[i].base + data[i].size > endimage)
    165 	    endimage = data[i].base + data[i].size;
    166     }
    167     if (endimage > dosmax)
    168 	goto too_big;
    169 
    170     for (i = 0; i < ndata; i++) {
    171 	if (syslinux_add_movelist(&mlist, data[i].base,
    172 				  (addr_t) data[i].data, data[i].size))
    173 	    goto enomem;
    174     }
    175 
    176     if (opt.swap && driveno != swapdrive) {
    177 	static const uint8_t swapstub_master[] = {
    178 	    /* The actual swap code */
    179 	    0x53,		/* 00: push bx */
    180 	    0x0f, 0xb6, 0xda,	/* 01: movzx bx,dl */
    181 	    0x2e, 0x8a, 0x57, 0x60,	/* 04: mov dl,[cs:bx+0x60] */
    182 	    0x5b,		/* 08: pop bx */
    183 	    0xea, 0, 0, 0, 0,	/* 09: jmp far 0:0 */
    184 	    0x90, 0x90,		/* 0E: nop; nop */
    185 	    /* Code to install this in the right location */
    186 	    /* Entry with DS = CS; ES = SI = 0; CX = 256 */
    187 	    0x26, 0x66, 0x8b, 0x7c, 0x4c,	/* 10: mov edi,[es:si+4*0x13] */
    188 	    0x66, 0x89, 0x3e, 0x0a, 0x00,	/* 15: mov [0x0A],edi */
    189 	    0x26, 0x8b, 0x3e, 0x13, 0x04,	/* 1A: mov di,[es:0x413] */
    190 	    0x4f,		/* 1F: dec di */
    191 	    0x26, 0x89, 0x3e, 0x13, 0x04,	/* 20: mov [es:0x413],di */
    192 	    0x66, 0xc1, 0xe7, 0x16,	/* 25: shl edi,16+6 */
    193 	    0x26, 0x66, 0x89, 0x7c, 0x4c,	/* 29: mov [es:si+4*0x13],edi */
    194 	    0x66, 0xc1, 0xef, 0x10,	/* 2E: shr edi,16 */
    195 	    0x8e, 0xc7,		/* 32: mov es,di */
    196 	    0x31, 0xff,		/* 34: xor di,di */
    197 	    0xf3, 0x66, 0xa5,	/* 36: rep movsd */
    198 	    0xbe, 0, 0,		/* 39: mov si,0 */
    199 	    0xbf, 0, 0,		/* 3C: mov di,0 */
    200 	    0x8e, 0xde,		/* 3F: mov ds,si */
    201 	    0x8e, 0xc7,		/* 41: mov es,di */
    202 	    0x66, 0xb9, 0, 0, 0, 0,	/* 43: mov ecx,0 */
    203 	    0x66, 0xbe, 0, 0, 0, 0,	/* 49: mov esi,0 */
    204 	    0x66, 0xbf, 0, 0, 0, 0,	/* 4F: mov edi,0 */
    205 	    0xea, 0, 0, 0, 0,	/* 55: jmp 0:0 */
    206 	    /* pad out to segment boundary */
    207 	    0x90, 0x90,		/* 5A: ... */
    208 	    0x90, 0x90, 0x90, 0x90,	/* 5C: ... */
    209 	};
    210 	static uint8_t swapstub[1024];
    211 	uint8_t *p;
    212 
    213 	/* Note: we can't rely on either INT 13h nor the dosmax
    214 	   vector to be correct at this stage, so we have to use an
    215 	   installer stub to put things in the right place.
    216 	   Round the installer location to a 1K boundary so the only
    217 	   possible overlap is the identity mapping. */
    218 	endimage = (endimage + 1023u) & ~1023u;
    219 
    220 	/* Create swap stub */
    221 	memcpy(swapstub, swapstub_master, sizeof swapstub_master);
    222 	*(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
    223 	*(uint16_t *) & swapstub[0x3d] = opt.regs.es;
    224 	*(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
    225 	*(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
    226 	*(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
    227 	*(uint16_t *) & swapstub[0x56] = opt.regs.ip;
    228 	*(uint16_t *) & swapstub[0x58] = opt.regs.cs;
    229 	p = &swapstub[sizeof swapstub_master];
    230 
    231 	/* Mapping table; start out with identity mapping everything */
    232 	for (i = 0; i < 256; i++)
    233 	    p[i] = i;
    234 
    235 	/* And the actual swap */
    236 	p[driveno] = swapdrive;
    237 	p[swapdrive] = driveno;
    238 
    239 	/* Adjust registers */
    240 	opt.regs.ds = opt.regs.cs = endimage >> 4;
    241 	opt.regs.esi.l = opt.regs.es = 0;
    242 	opt.regs.ecx.l = sizeof swapstub >> 2;
    243 	opt.regs.ip = 0x10;	/* Installer offset */
    244 	opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
    245 
    246 	if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
    247 				  sizeof swapstub))
    248 	    goto enomem;
    249 
    250 	endimage += sizeof swapstub;
    251     }
    252 
    253     /* Tell the shuffler not to muck with this area... */
    254     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
    255 
    256     /* Force text mode */
    257     syslinux_force_text_mode();
    258 
    259     puts("Booting...");
    260     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
    261     error("Chainboot failed !");
    262     return;
    263 
    264 too_big:
    265     error("Loader file too large.");
    266     return;
    267 
    268 enomem:
    269     error("Out of memory.");
    270     return;
    271 }
    272 
    273 int find_dp(struct part_iter **_iter)
    274 {
    275     struct part_iter *iter = NULL;
    276     struct disk_info diskinfo;
    277     struct guid gpt_guid;
    278     uint64_t fs_lba;
    279     int drive, hd, partition;
    280     const union syslinux_derivative_info *sdi;
    281 
    282     sdi = syslinux_derivative_info();
    283 
    284     if (!strncmp(opt.drivename, "mbr", 3)) {
    285 	if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
    286 	    error("Unable to find requested MBR signature.");
    287 	    goto bail;
    288 	}
    289     } else if (!strncmp(opt.drivename, "guid", 4)) {
    290 	if (str_to_guid(opt.drivename + 5, &gpt_guid))
    291 	    goto bail;
    292 	if (find_by_guid(&gpt_guid, &iter) < 0) {
    293 	    error("Unable to find requested GPT disk or partition by guid.");
    294 	    goto bail;
    295 	}
    296     } else if (!strncmp(opt.drivename, "label", 5)) {
    297 	if (!opt.drivename[6]) {
    298 	    error("No label specified.");
    299 	    goto bail;
    300 	}
    301 	if (find_by_label(opt.drivename + 6, &iter) < 0) {
    302 	    error("Unable to find requested GPT partition by label.");
    303 	    goto bail;
    304 	}
    305     } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
    306 	       opt.drivename[1] == 'd') {
    307 	hd = opt.drivename[0] == 'h' ? 0x80 : 0;
    308 	opt.drivename += 2;
    309 	drive = hd | strtol(opt.drivename, NULL, 0);
    310 
    311 	if (disk_get_params(drive, &diskinfo))
    312 	    goto bail;
    313 	/* this will start iteration over FDD, possibly raw */
    314 	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
    315 	    goto bail;
    316 
    317     } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
    318 	if (!is_phys(sdi->c.filesystem)) {
    319 	    error("When syslinux is not booted from physical disk (or its emulation),\n"
    320 		   "'boot' and 'fs' are meaningless.");
    321 	    goto bail;
    322 	}
    323 	/* offsets match, but in case it changes in the future */
    324 	if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
    325 	    drive = sdi->iso.drive_number;
    326 	    fs_lba = *sdi->iso.partoffset;
    327 	} else {
    328 	    drive = sdi->disk.drive_number;
    329 	    fs_lba = *sdi->disk.partoffset;
    330 	}
    331 	if (disk_get_params(drive, &diskinfo))
    332 	    goto bail;
    333 	/* this will start iteration over disk emulation, possibly raw */
    334 	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
    335 	    goto bail;
    336 
    337 	/* 'fs' => we should lookup the syslinux partition number and use it */
    338 	if (!strcmp(opt.drivename, "fs")) {
    339 	    do {
    340 		if (iter->abs_lba == fs_lba)
    341 		    break;
    342 	    } while (!pi_next(iter));
    343 	    /* broken part structure or other problems */
    344 	    if (iter->status) {
    345 		error("Unable to find partition with syslinux (fs).");
    346 		goto bail;
    347 	    }
    348 	}
    349     } else {
    350 	error("Unparsable drive specification.");
    351 	goto bail;
    352     }
    353     /* main options done - only thing left is explicit partition specification,
    354      * if we're still at the disk stage with the iterator AND user supplied
    355      * partition number (including disk pseudo-partition).
    356      */
    357     if (!iter->index && opt.partition) {
    358 	partition = strtol(opt.partition, NULL, 0);
    359 	/* search for matching part#, including disk */
    360 	do {
    361 	    if (iter->index == partition)
    362 		break;
    363 	} while (!pi_next(iter));
    364 	if (iter->status) {
    365 	    error("Unable to find requested disk / partition combination.");
    366 	    goto bail;
    367 	}
    368     }
    369 
    370     if (!(iter->di.disk & 0x80) && iter->index) {
    371 	warn("Partitions on floppy devices may not work.");
    372     }
    373 
    374     *_iter = iter;
    375 
    376     return 0;
    377 
    378 bail:
    379     pi_del(&iter);
    380     return -1;
    381 }
    382 
    383 static int setup_handover(const struct part_iter *iter,
    384 		   struct data_area *data)
    385 {
    386     struct disk_dos_part_entry *ha;
    387     uint32_t synth_size = sizeof *ha;
    388 
    389     /*
    390      * we have to cover both non-iterated but otherwise properly detected
    391      * gpt/dos schemes as well as raw disks; checking index for 0 covers both
    392      */
    393     if (iter->index == 0) {
    394 	uint32_t len;
    395 	/* RAW handover protocol */
    396 	ha = malloc(synth_size);
    397 	if (!ha) {
    398 	    critm();
    399 	    goto bail;
    400 	}
    401 	len = ~0u;
    402 	if (iter->length < len)
    403 	    len = iter->length;
    404 	lba2chs(&ha->start, &iter->di, 0, L2C_CADD);
    405 	lba2chs(&ha->end, &iter->di, len - 1, L2C_CADD);
    406 	ha->active_flag = 0x80;
    407 	ha->ostype = 0xDA;	/* "Non-FS Data", anything is good here though ... */
    408 	ha->start_lba = 0;
    409 	ha->length = len;
    410     } else if (iter->type == typegpt) {
    411 	uint32_t *plen;
    412 	/* GPT handover protocol */
    413 	synth_size += sizeof *plen + iter->gpt.pe_size;
    414 	ha = malloc(synth_size);
    415 	if (!ha) {
    416 	    critm();
    417 	    goto bail;
    418 	}
    419 	lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
    420 	lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
    421 	ha->active_flag = 0x80;
    422 	ha->ostype = 0xED;
    423 	/* All bits set by default */
    424 	ha->start_lba = ~0u;
    425 	ha->length = ~0u;
    426 	/* If these fit the precision, pass them on */
    427 	if (iter->abs_lba < ha->start_lba)
    428 	    ha->start_lba = iter->abs_lba;
    429 	if (iter->length < ha->length)
    430 	    ha->length = iter->length;
    431 	/* Next comes the GPT partition record length */
    432 	plen = (uint32_t *)(ha + 1);
    433 	plen[0] = iter->gpt.pe_size;
    434 	/* Next comes the GPT partition record copy */
    435 	memcpy(plen + 1, iter->record, plen[0]);
    436 #ifdef DEBUG
    437 	dprintf("GPT handover:\n");
    438 	disk_dos_part_dump(ha);
    439 	disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
    440 #endif
    441     /* the only possible case left is dos scheme */
    442     } else if (iter->type == typedos) {
    443 	/* MBR handover protocol */
    444 	ha = malloc(synth_size);
    445 	if (!ha) {
    446 	    critm();
    447 	    goto bail;
    448 	}
    449 	memcpy(ha, iter->record, synth_size);
    450 	/* make sure these match bios imaginations and are ebr agnostic */
    451 	lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
    452 	lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
    453 	ha->start_lba = iter->abs_lba;
    454 	ha->length = iter->length;
    455 
    456 #ifdef DEBUG
    457 	dprintf("MBR handover:\n");
    458 	disk_dos_part_dump(ha);
    459 #endif
    460     } else {
    461 	/* shouldn't ever happen */
    462 	goto bail;
    463     }
    464 
    465     data->base = 0x7be;
    466     data->size = synth_size;
    467     data->data = (void *)ha;
    468 
    469     return 0;
    470 bail:
    471     return -1;
    472 }
    473 
    474 int main(int argc, char *argv[])
    475 {
    476     struct part_iter *iter = NULL;
    477     void *sbck = NULL;
    478     struct data_area fdat, hdat, sdat, data[3];
    479     int ndata = 0;
    480 
    481     console_ansi_raw();
    482 
    483     memset(&fdat, 0, sizeof fdat);
    484     memset(&hdat, 0, sizeof hdat);
    485     memset(&sdat, 0, sizeof sdat);
    486 
    487     opt_set_defs();
    488     if (opt_parse_args(argc, argv))
    489 	goto bail;
    490 
    491 #if 0
    492     /* Get max fixed disk number */
    493     fixed_cnt = *(uint8_t *)(0x475);
    494 
    495     /*
    496      * hmm, looks like we can't do that -
    497      * some bioses/vms just set it to 1
    498      * and go on living happily
    499      * any better options than hardcoded 0x80 - 0xFF ?
    500      */
    501 #endif
    502 
    503     /* Get disk/part iterator matching user supplied options */
    504     if (find_dp(&iter))
    505 	goto bail;
    506 
    507     /* Perform initial partition entry mangling */
    508     if (manglepe_fixchs(iter))
    509 	goto bail;
    510     if (manglepe_hide(iter))
    511 	goto bail;
    512 
    513     /* Load the boot file */
    514     if (opt.file) {
    515 	fdat.base = (opt.fseg << 4) + opt.foff;
    516 
    517 	if (loadfile(opt.file, &fdat.data, &fdat.size)) {
    518 	    error("Couldn't read the boot file.");
    519 	    goto bail;
    520 	}
    521 	if (fdat.base + fdat.size > dosmax) {
    522 	    error("The boot file is too big to load at this address.");
    523 	    goto bail;
    524 	}
    525     }
    526 
    527     /* Load the sector */
    528     if (opt.sect) {
    529 	sdat.base = (opt.sseg << 4) + opt.soff;
    530 	sdat.size = iter->di.bps;
    531 
    532 	if (sdat.base + sdat.size > dosmax) {
    533 	    error("The sector cannot be loaded at such high address.");
    534 	    goto bail;
    535 	}
    536 	if (!(sdat.data = disk_read_sectors(&iter->di, iter->abs_lba, 1))) {
    537 	    error("Couldn't read the sector.");
    538 	    goto bail;
    539 	}
    540 	if (opt.save) {
    541 	    if (!(sbck = malloc(sdat.size))) {
    542 		critm();
    543 		goto bail;
    544 	    }
    545 	    memcpy(sbck, sdat.data, sdat.size);
    546 	}
    547 	if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
    548 	    warn("The sector won't be mmapped, as it would conflict with the boot file.");
    549 	    opt.maps = false;
    550 	}
    551     }
    552 
    553     /* Prep the handover */
    554     if (opt.hand) {
    555 	if (setup_handover(iter, &hdat))
    556 	    goto bail;
    557 	/* Verify possible conflicts */
    558 	if ( ( opt.file && overlap(&fdat, &hdat)) ||
    559 	     ( opt.maps && overlap(&sdat, &hdat)) ) {
    560 	    warn("Handover area won't be prepared,\n"
    561 		  "as it would conflict with the boot file and/or the sector.");
    562 	    opt.hand = false;
    563 	}
    564     }
    565 
    566     /* Adjust registers */
    567 
    568     mangler_init(iter);
    569     mangler_handover(iter, &hdat);
    570     mangler_grldr(iter);
    571 
    572     /* Patching functions */
    573 
    574     if (manglef_isolinux(&fdat))
    575 	goto bail;
    576 
    577     if (manglef_grub(iter, &fdat))
    578 	goto bail;
    579 #if 0
    580     if (manglef_drmk(&fdat))
    581 	goto bail;
    582 #endif
    583     if (manglef_bpb(iter, &fdat))
    584 	goto bail;
    585 
    586     if (mangles_bpb(iter, &sdat))
    587 	goto bail;
    588 
    589     if (mangles_save(iter, &sdat, sbck))
    590 	goto bail;
    591 
    592     if (manglesf_bss(&sdat, &fdat))
    593 	goto bail;
    594 
    595     /* This *must* be after BPB saving or copying */
    596     if (mangles_cmldr(&sdat))
    597 	goto bail;
    598 
    599     /*
    600      * Prepare boot-time mmap data. We should to it here, as manglers could
    601      * potentially alter some of the data.
    602      */
    603 
    604     if (opt.file)
    605 	memcpy(data + ndata++, &fdat, sizeof fdat);
    606     if (opt.maps)
    607 	memcpy(data + ndata++, &sdat, sizeof sdat);
    608     if (opt.hand)
    609 	memcpy(data + ndata++, &hdat, sizeof hdat);
    610 
    611 #ifdef DEBUG
    612     dprintf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
    613 	   "iter->di C, H, S: %u, %u, %u\n",
    614 	iter->di.disk, iter->di.bps,
    615 	iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
    616 	iter->di.cyl, iter->di.head, iter->di.spt);
    617     dprintf("iter idx: %d\n", iter->index);
    618     dprintf("iter lba: %"PRIu64"\n", iter->abs_lba);
    619     if (opt.hand)
    620 	dprintf("hand lba: %u\n",
    621 		((struct disk_dos_part_entry *)hdat.data)->start_lba);
    622 #endif
    623 
    624     if (opt.warn) {
    625 	puts("Press any key to continue booting...");
    626 	wait_key();
    627     }
    628 
    629     if (ndata && !opt.brkchain) /* boot only if we actually chainload */
    630 	do_boot(data, ndata);
    631     else
    632 	puts("Service-only run completed, exiting.");
    633 bail:
    634     pi_del(&iter);
    635     /* Free allocated areas */
    636     free(fdat.data);
    637     free(sdat.data);
    638     free(hdat.data);
    639     free(sbck);
    640     return 255;
    641 }
    642 
    643 /* vim: set ts=8 sts=4 sw=4 noet: */
    644