Home | History | Annotate | Download | only in memdisk
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
      5  *   Portions copyright 2009-2010 Shao Miller
      6  *				  [El Torito code, mBFT, "safe hook"]
      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 #include <stdint.h>
     17 #include <minmax.h>
     18 #include <suffix_number.h>
     19 #include "bda.h"
     20 #include "dskprobe.h"
     21 #include "e820.h"
     22 #include "conio.h"
     23 #include "version.h"
     24 #include "memdisk.h"
     25 #include <version.h>
     26 
     27 const char memdisk_version[] = "MEMDISK " VERSION_STR " " DATE;
     28 const char copyright[] =
     29     "Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin et al";
     30 
     31 extern const char _binary_memdisk_chs_512_bin_start[];
     32 extern const char _binary_memdisk_chs_512_bin_end[];
     33 extern const char _binary_memdisk_chs_512_bin_size[];
     34 extern const char _binary_memdisk_edd_512_bin_start[];
     35 extern const char _binary_memdisk_edd_512_bin_end[];
     36 extern const char _binary_memdisk_edd_512_bin_size[];
     37 extern const char _binary_memdisk_iso_512_bin_start[];
     38 extern const char _binary_memdisk_iso_512_bin_end[];
     39 extern const char _binary_memdisk_iso_512_bin_size[];
     40 extern const char _binary_memdisk_iso_2048_bin_start[];
     41 extern const char _binary_memdisk_iso_2048_bin_end[];
     42 extern const char _binary_memdisk_iso_2048_bin_size[];
     43 
     44 /* Pull in structures common to MEMDISK and MDISKCHK.COM */
     45 #include "mstructs.h"
     46 
     47 /* An EDD disk packet */
     48 struct edd_dsk_pkt {
     49     uint8_t size;		/* Packet size        */
     50     uint8_t res1;		/* Reserved           */
     51     uint16_t count;		/* Count to transfer  */
     52     uint32_t buf;		/* Buffer pointer     */
     53     uint64_t start;		/* LBA to start from  */
     54     uint64_t buf64;		/* 64-bit buf pointer */
     55 } __attribute__ ((packed));
     56 
     57 /* Change to 1 for El Torito debugging */
     58 #define DBG_ELTORITO 0
     59 
     60 #if DBG_ELTORITO
     61 extern void eltorito_dump(uint32_t);
     62 #endif
     63 
     64 /*
     65  * Routine to seek for a command-line item and return a pointer
     66  * to the data portion, if present
     67  */
     68 
     69 /* Magic return values */
     70 #define CMD_NOTFOUND   ((char *)-1)	/* Not found */
     71 #define CMD_BOOL       ((char *)-2)	/* Found boolean option */
     72 #define CMD_HASDATA(X) ((int)(X) >= 0)
     73 
     74 static const char *getcmditem(const char *what)
     75 {
     76     const char *p;
     77     const char *wp = what;
     78     int match = 0;
     79 
     80     for (p = shdr->cmdline; *p; p++) {
     81 	switch (match) {
     82 	case 0:		/* Ground state */
     83 	    if (*p == ' ')
     84 		break;
     85 
     86 	    wp = what;
     87 	    match = 1;
     88 	    /* Fall through */
     89 
     90 	case 1:		/* Matching */
     91 	    if (*wp == '\0') {
     92 		if (*p == '=')
     93 		    return p + 1;
     94 		else if (*p == ' ')
     95 		    return CMD_BOOL;
     96 		else {
     97 		    match = 2;
     98 		    break;
     99 		}
    100 	    }
    101 	    if (*p != *wp++)
    102 		match = 2;
    103 	    break;
    104 
    105 	case 2:		/* Mismatch, skip rest of option */
    106 	    if (*p == ' ')
    107 		match = 0;	/* Next option */
    108 	    break;
    109 	}
    110     }
    111 
    112     /* Check for matching string at end of line */
    113     if (match == 1 && *wp == '\0')
    114 	return CMD_BOOL;
    115 
    116     return CMD_NOTFOUND;
    117 }
    118 
    119 /*
    120  * Check to see if this is a gzip image
    121  */
    122 #define UNZIP_ALIGN 512
    123 
    124 extern const char _end[];		/* Symbol signalling end of data */
    125 
    126 void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
    127 {
    128     uint32_t where = *where_p;
    129     uint32_t size = *size_p;
    130     uint32_t zbytes;
    131     uint32_t startrange, endrange;
    132     uint32_t gzdatasize, gzwhere;
    133     uint32_t orig_crc, offset;
    134     uint32_t target = 0;
    135     int i, okmem;
    136 
    137     /* Is it a gzip image? */
    138     if (check_zip((void *)where, size, &zbytes, &gzdatasize,
    139 		  &orig_crc, &offset) == 0) {
    140 
    141 	if (offset + zbytes > size) {
    142 	    /*
    143 	     * Assertion failure; check_zip is supposed to guarantee this
    144 	     * never happens.
    145 	     */
    146 	    die("internal error: check_zip returned nonsense\n");
    147 	}
    148 
    149 	/*
    150 	 * Find a good place to put it: search memory ranges in descending
    151 	 * order until we find one that is legal and fits
    152 	 */
    153 	okmem = 0;
    154 	for (i = nranges - 1; i >= 0; i--) {
    155 	    /*
    156 	     * We can't use > 4G memory (32 bits only.)  Truncate to 2^32-1
    157 	     * so we don't have to deal with funny wraparound issues.
    158 	     */
    159 
    160 	    /* Must be memory */
    161 	    if (ranges[i].type != 1)
    162 		continue;
    163 
    164 	    /* Range start */
    165 	    if (ranges[i].start >= 0xFFFFFFFF)
    166 		continue;
    167 
    168 	    startrange = (uint32_t) ranges[i].start;
    169 
    170 	    /* Range end (0 for end means 2^64) */
    171 	    endrange = ((ranges[i + 1].start >= 0xFFFFFFFF ||
    172 			 ranges[i + 1].start == 0)
    173 			? 0xFFFFFFFF : (uint32_t) ranges[i + 1].start);
    174 
    175 	    /* Make sure we don't overwrite ourselves */
    176 	    if (startrange < (uint32_t) _end)
    177 		startrange = (uint32_t) _end;
    178 
    179 	    /* Allow for alignment */
    180 	    startrange =
    181 		(ranges[i].start + (UNZIP_ALIGN - 1)) & ~(UNZIP_ALIGN - 1);
    182 
    183 	    /* In case we just killed the whole range... */
    184 	    if (startrange >= endrange)
    185 		continue;
    186 
    187 	    /*
    188 	     * Must be large enough... don't rely on gzwhere for this
    189 	     * (wraparound)
    190 	     */
    191 	    if (endrange - startrange < gzdatasize)
    192 		continue;
    193 
    194 	    /*
    195 	     * This is where the gz image would be put if we put it in this
    196 	     * range...
    197 	     */
    198 	    gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN - 1);
    199 
    200 	    /* Cast to uint64_t just in case we're flush with the top byte */
    201 	    if ((uint64_t) where + size >= gzwhere && where < endrange) {
    202 		/*
    203 		 * Need to move source data to avoid compressed/uncompressed
    204 		 * overlap
    205 		 */
    206 		uint32_t newwhere;
    207 
    208 		if (gzwhere - startrange < size)
    209 		    continue;	/* Can't fit both old and new */
    210 
    211 		newwhere = (gzwhere - size) & ~(UNZIP_ALIGN - 1);
    212 		printf("Moving compressed data from 0x%08x to 0x%08x\n",
    213 		       where, newwhere);
    214 
    215 		memmove((void *)newwhere, (void *)where, size);
    216 		where = newwhere;
    217 	    }
    218 
    219 	    target = gzwhere;
    220 	    okmem = 1;
    221 	    break;
    222 	}
    223 
    224 	if (!okmem)
    225 	    die("Not enough memory to decompress image (need 0x%08x bytes)\n",
    226 		gzdatasize);
    227 
    228 	printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
    229 	       target, gzdatasize);
    230 
    231 	*size_p = gzdatasize;
    232 	*where_p = (uint32_t) unzip((void *)(where + offset), zbytes,
    233 				    gzdatasize, orig_crc, (void *)target);
    234     }
    235 }
    236 
    237 /*
    238  * Figure out the "geometry" of the disk in question
    239  */
    240 struct geometry {
    241     uint32_t sectors;		/* Sector count */
    242     uint32_t c, h, s;		/* C/H/S geometry */
    243     uint32_t offset;		/* Byte offset for disk */
    244     uint32_t boot_lba;		/* LBA of bootstrap code */
    245     uint8_t type;		/* Type byte for INT 13h AH=08h */
    246     uint8_t driveno;		/* Drive no */
    247     uint8_t sector_shift;	/* Sector size as a power of 2 */
    248     const char *hsrc, *ssrc;	/* Origins of H and S geometries */
    249 };
    250 
    251 /* Format of a DOS partition table entry */
    252 struct ptab_entry {
    253     uint8_t active;
    254     uint8_t start_h, start_s, start_c;
    255     uint8_t type;
    256     uint8_t end_h, end_s, end_c;
    257     uint32_t start;
    258     uint32_t size;
    259 } __attribute__ ((packed));
    260 
    261 /* Format of a FAT filesystem superblock */
    262 struct fat_extra {
    263     uint8_t bs_drvnum;
    264     uint8_t bs_resv1;
    265     uint8_t bs_bootsig;
    266     uint32_t bs_volid;
    267     char bs_vollab[11];
    268     char bs_filsystype[8];
    269 } __attribute__ ((packed));
    270 struct fat_super {
    271     uint8_t bs_jmpboot[3];
    272     char bs_oemname[8];
    273     uint16_t bpb_bytspersec;
    274     uint8_t bpb_secperclus;
    275     uint16_t bpb_rsvdseccnt;
    276     uint8_t bpb_numfats;
    277     uint16_t bpb_rootentcnt;
    278     uint16_t bpb_totsec16;
    279     uint8_t bpb_media;
    280     uint16_t bpb_fatsz16;
    281     uint16_t bpb_secpertrk;
    282     uint16_t bpb_numheads;
    283     uint32_t bpb_hiddsec;
    284     uint32_t bpb_totsec32;
    285     union {
    286 	struct {
    287 	    struct fat_extra extra;
    288 	} fat16;
    289 	struct {
    290 	    uint32_t bpb_fatsz32;
    291 	    uint16_t bpb_extflags;
    292 	    uint16_t bpb_fsver;
    293 	    uint32_t bpb_rootclus;
    294 	    uint16_t bpb_fsinfo;
    295 	    uint16_t bpb_bkbootsec;
    296 	    char bpb_reserved[12];
    297 	    /* Clever, eh?  Same fields, different offset... */
    298 	    struct fat_extra extra;
    299 	} fat32 __attribute__ ((packed));
    300     } x;
    301 } __attribute__ ((packed));
    302 
    303 /* Format of a DOSEMU header */
    304 struct dosemu_header {
    305     uint8_t magic[7];		/* DOSEMU\0 */
    306     uint32_t h;
    307     uint32_t s;
    308     uint32_t c;
    309     uint32_t offset;
    310     uint8_t pad[105];
    311 } __attribute__ ((packed));
    312 
    313 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
    314 
    315 static const struct geometry *get_disk_image_geometry(uint32_t where,
    316 						      uint32_t size)
    317 {
    318     static struct geometry hd_geometry;
    319     struct dosemu_header dosemu;
    320     unsigned int sectors, xsectors, v;
    321     unsigned int offset;
    322     int i;
    323     const char *p;
    324 
    325     printf("command line: %s\n", shdr->cmdline);
    326 
    327     hd_geometry.sector_shift = 9;	/* Assume floppy/HDD at first */
    328 
    329     offset = 0;
    330     if (CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)))
    331 	offset = v;
    332 
    333     sectors = xsectors = (size - offset) >> hd_geometry.sector_shift;
    334 
    335     hd_geometry.hsrc = "guess";
    336     hd_geometry.ssrc = "guess";
    337     hd_geometry.sectors = sectors;
    338     hd_geometry.offset = offset;
    339 
    340     if ((p = getcmditem("iso")) != CMD_NOTFOUND) {
    341 #if DBG_ELTORITO
    342 	eltorito_dump(where);
    343 #endif
    344 	struct edd4_bvd *bvd = (struct edd4_bvd *)(where + 17 * 2048);
    345 	/* Tiny sanity check */
    346 	if ((bvd->boot_rec_ind != 0) || (bvd->ver != 1))
    347 	    printf("El Torito BVD sanity check failed.\n");
    348 	struct edd4_bootcat *boot_cat =
    349 	    (struct edd4_bootcat *)(where + bvd->boot_cat * 2048);
    350 	/* Another tiny sanity check */
    351 	if ((boot_cat->validation_entry.platform_id != 0) ||
    352 	    (boot_cat->validation_entry.key55 != 0x55) ||
    353 	    (boot_cat->validation_entry.keyAA != 0xAA))
    354 	    printf("El Torito boot catalog sanity check failed.\n");
    355 	/* If we have an emulation mode, set the offset to the image */
    356 	if (boot_cat->initial_entry.media_type)
    357 	    hd_geometry.offset += boot_cat->initial_entry.load_block * 2048;
    358 	else
    359 	    /* We're a no-emulation mode, so we will boot to an offset */
    360 	    hd_geometry.boot_lba = boot_cat->initial_entry.load_block * 4;
    361 	if (boot_cat->initial_entry.media_type < 4) {
    362 	    /* We're a floppy emulation mode or our params will be
    363 	     * overwritten by the no emulation mode case
    364 	     */
    365 	    hd_geometry.driveno = 0x00;
    366 	    hd_geometry.c = 80;
    367 	    hd_geometry.h = 2;
    368 	}
    369 	switch (boot_cat->initial_entry.media_type) {
    370 	case 0:		/* No emulation   */
    371 	    hd_geometry.driveno = 0xE0;
    372 	    hd_geometry.type = 10;	/* ATAPI removable media device */
    373 	    hd_geometry.c = 65535;
    374 	    hd_geometry.h = 255;
    375 	    hd_geometry.s = 15;
    376 	    /* 2048-byte sectors, so adjust the size and count */
    377 	    hd_geometry.sector_shift = 11;
    378 	    break;
    379 	case 1:		/* 1.2 MB floppy  */
    380 	    hd_geometry.s = 15;
    381 	    hd_geometry.type = 2;
    382 	    sectors = 2400;
    383 	    break;
    384 	case 2:		/* 1.44 MB floppy */
    385 	    hd_geometry.s = 18;
    386 	    hd_geometry.type = 4;
    387 	    sectors = 2880;
    388 	    break;
    389 	case 3:		/* 2.88 MB floppy */
    390 	    hd_geometry.s = 36;
    391 	    hd_geometry.type = 6;
    392 	    sectors = 5760;
    393 	    break;
    394 	case 4:
    395 	    hd_geometry.driveno = 0x80;
    396 	    hd_geometry.type = 0;
    397 	    break;
    398 	}
    399 	sectors = (size - hd_geometry.offset) >> hd_geometry.sector_shift;
    400 
    401 	/* For HDD emulation, we figure out the geometry later. Otherwise: */
    402 	if (hd_geometry.s) {
    403 	    hd_geometry.hsrc = hd_geometry.ssrc = "El Torito";
    404 	}
    405 	hd_geometry.sectors = sectors;
    406     }
    407 
    408     /* Do we have a DOSEMU header? */
    409     memcpy(&dosemu, (char *)where + hd_geometry.offset, sizeof dosemu);
    410     if (!memcmp("DOSEMU", dosemu.magic, 7)) {
    411 	/* Always a hard disk unless overruled by command-line options */
    412 	hd_geometry.driveno = 0x80;
    413 	hd_geometry.type = 0;
    414 	hd_geometry.c = dosemu.c;
    415 	hd_geometry.h = dosemu.h;
    416 	hd_geometry.s = dosemu.s;
    417 	hd_geometry.offset += dosemu.offset;
    418 	sectors = (size - hd_geometry.offset) >> hd_geometry.sector_shift;
    419 
    420 	hd_geometry.hsrc = hd_geometry.ssrc = "DOSEMU";
    421     }
    422 
    423     if (CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)))
    424 	hd_geometry.c = v;
    425     if (CMD_HASDATA(p = getcmditem("h")) && (v = atou(p))) {
    426 	hd_geometry.h = v;
    427 	hd_geometry.hsrc = "cmd";
    428     }
    429     if (CMD_HASDATA(p = getcmditem("s")) && (v = atou(p))) {
    430 	hd_geometry.s = v;
    431 	hd_geometry.ssrc = "cmd";
    432     }
    433 
    434     if (!hd_geometry.h || !hd_geometry.s) {
    435 	int h, s, max_h, max_s;
    436 
    437 	max_h = hd_geometry.h;
    438 	max_s = hd_geometry.s;
    439 
    440 	if (!(max_h | max_s)) {
    441 	    /* Look for a FAT superblock and if we find something that looks
    442 	       enough like one, use geometry from that.  This takes care of
    443 	       megafloppy images and unpartitioned hard disks. */
    444 	    const struct fat_extra *extra = NULL;
    445 	    const struct fat_super *fs = (const struct fat_super *)
    446 		((char *)where + hd_geometry.offset);
    447 
    448 	    if ((fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) &&
    449 		(fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) &&
    450 		fs->bpb_bytspersec == 512 &&
    451 		fs->bpb_numheads >= 1 && fs->bpb_numheads <= 256 &&
    452 		fs->bpb_secpertrk >= 1 && fs->bpb_secpertrk <= 63) {
    453 		extra =
    454 		    fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
    455 		if (!
    456 		    (extra->bs_bootsig == 0x29 && extra->bs_filsystype[0] == 'F'
    457 		     && extra->bs_filsystype[1] == 'A'
    458 		     && extra->bs_filsystype[2] == 'T'))
    459 		    extra = NULL;
    460 	    }
    461 	    if (extra) {
    462 		hd_geometry.driveno = extra->bs_drvnum & 0x80;
    463 		max_h = fs->bpb_numheads;
    464 		max_s = fs->bpb_secpertrk;
    465 		hd_geometry.hsrc = hd_geometry.ssrc = "FAT";
    466 	    }
    467 	}
    468 
    469 	if (!(max_h | max_s)) {
    470 	    /* No FAT filesystem found to steal geometry from... */
    471 	    if ((sectors < 4096 * 2) && (hd_geometry.sector_shift == 9)) {
    472 		int ok = 0;
    473 		unsigned int xsectors = sectors;
    474 
    475 		hd_geometry.driveno = 0;	/* Assume floppy */
    476 
    477 		while (!ok) {
    478 		    /* Assume it's a floppy drive, guess a geometry */
    479 		    unsigned int type, track;
    480 		    int c, h, s = 0;
    481 
    482 		    if (xsectors < 320 * 2) {
    483 			c = 40;
    484 			h = 1;
    485 			type = 1;
    486 		    } else if (xsectors < 640 * 2) {
    487 			c = 40;
    488 			h = 2;
    489 			type = 1;
    490 		    } else if (xsectors < 1200 * 2) {
    491 			c = 80;
    492 			h = 2;
    493 			type = 3;
    494 		    } else if (xsectors < 1440 * 2) {
    495 			c = 80;
    496 			h = 2;
    497 			type = 2;
    498 		    } else if (xsectors < 2880 * 2) {
    499 			c = 80;
    500 			h = 2;
    501 			type = 4;
    502 		    } else {
    503 			c = 80;
    504 			h = 2;
    505 			type = 6;
    506 		    }
    507 		    track = c * h;
    508 		    while (c < 256) {
    509 			s = xsectors / track;
    510 			if (s < 63 && (xsectors % track) == 0) {
    511 			    ok = 1;
    512 			    break;
    513 			}
    514 			c++;
    515 			track += h;
    516 		    }
    517 		    if (ok) {
    518 			max_h = h;
    519 			max_s = s;
    520 			hd_geometry.hsrc = hd_geometry.ssrc = "fd";
    521 		    } else {
    522 			/* No valid floppy geometry, fake it by simulating broken
    523 			   sectors at the end of the image... */
    524 			xsectors++;
    525 		    }
    526 
    527 		    hd_geometry.type = type;
    528 		}
    529 	    } else {
    530 		/* Assume it is a hard disk image and scan for a partition table */
    531 		const struct ptab_entry *ptab = (const struct ptab_entry *)
    532 		    ((char *)where + hd_geometry.offset + (512 - 2 - 4 * 16));
    533 
    534 		/* Assume hard disk */
    535 		if (!hd_geometry.driveno)
    536 		    hd_geometry.driveno = 0x80;
    537 
    538 		if (*(uint16_t *) ((char *)where + hd_geometry.offset + 512 - 2) == 0xaa55) {
    539 		    for (i = 0; i < 4; i++) {
    540 			if (ptab[i].type && !(ptab[i].active & 0x7f)) {
    541 			    s = (ptab[i].start_s & 0x3f);
    542 			    h = ptab[i].start_h + 1;
    543 
    544 			    if (max_h < h)
    545 				max_h = h;
    546 			    if (max_s < s)
    547 				max_s = s;
    548 
    549 			    s = (ptab[i].end_s & 0x3f);
    550 			    h = ptab[i].end_h + 1;
    551 
    552 			    if (max_h < h) {
    553 				max_h = h;
    554 				hd_geometry.hsrc = "MBR";
    555 			    }
    556 			    if (max_s < s) {
    557 				max_s = s;
    558 				hd_geometry.ssrc = "MBR";
    559 			    }
    560 			}
    561 		    }
    562 		}
    563 
    564 		hd_geometry.type = 0;
    565 	    }
    566 	}
    567 
    568 	if (!max_h)
    569 	    max_h = xsectors > 2097152 ? 255 : 64;
    570 	if (!max_s)
    571 	    max_s = xsectors > 2097152 ? 63 : 32;
    572 
    573 	hd_geometry.h    = max_h;
    574 	hd_geometry.s    = max_s;
    575     }
    576 
    577     if (!hd_geometry.c)
    578 	hd_geometry.c = xsectors / (hd_geometry.h * hd_geometry.s);
    579 
    580     if ((p = getcmditem("floppy")) != CMD_NOTFOUND) {
    581 	hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0;
    582     } else if ((p = getcmditem("harddisk")) != CMD_NOTFOUND) {
    583 	hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80;
    584     }
    585 
    586     if (hd_geometry.driveno & 0x80) {
    587 	hd_geometry.type = 0;	/* Type = hard disk */
    588     } else {
    589 	if (hd_geometry.type == 0)
    590 	    hd_geometry.type = 0x10;	/* ATAPI floppy, e.g. LS-120 */
    591     }
    592 
    593     if ((size - hd_geometry.offset) & 0x1ff) {
    594 	puts("MEMDISK: Image has fractional end sector\n");
    595     }
    596     if (sectors % (hd_geometry.h * hd_geometry.s)) {
    597 	puts("MEMDISK: Image seems to have fractional end cylinder\n");
    598     }
    599     if ((hd_geometry.c * hd_geometry.h * hd_geometry.s) > sectors) {
    600 	puts("MEMDISK: Image appears to be truncated\n");
    601     }
    602 
    603     return &hd_geometry;
    604 }
    605 
    606 /*
    607  * Find a $PnP installation check structure; return (ES << 16) + DI value
    608  */
    609 static uint32_t pnp_install_check(void)
    610 {
    611     uint32_t *seg;
    612     unsigned char *p, csum;
    613     int i, len;
    614 
    615     for (seg = (uint32_t *) 0xf0000; seg < (uint32_t *) 0x100000; seg += 4) {
    616 	if (*seg == ('$' + ('P' << 8) + ('n' << 16) + ('P' << 24))) {
    617 	    p = (unsigned char *)seg;
    618 	    len = p[5];
    619 	    if (len < 0x21)
    620 		continue;
    621 	    csum = 0;
    622 	    for (i = len; i; i--)
    623 		csum += *p++;
    624 	    if (csum != 0)
    625 		continue;
    626 
    627 	    return (0xf000 << 16) + (uint16_t) (unsigned long)seg;
    628 	}
    629     }
    630 
    631     return 0;
    632 }
    633 
    634 /*
    635  * Relocate the real-mode code to a new segment
    636  */
    637 struct gdt_ptr {
    638     uint16_t limit;
    639     uint32_t base;
    640 } __attribute__ ((packed));
    641 
    642 static void set_seg_base(uint32_t gdt_base, int seg, uint32_t v)
    643 {
    644     *(uint16_t *) (gdt_base + seg + 2) = v;
    645     *(uint8_t *) (gdt_base + seg + 4) = v >> 16;
    646     *(uint8_t *) (gdt_base + seg + 7) = v >> 24;
    647 }
    648 
    649 static void relocate_rm_code(uint32_t newbase)
    650 {
    651     uint32_t gdt_base;
    652     uint32_t oldbase = rm_args.rm_base;
    653     uint32_t delta = newbase - oldbase;
    654 
    655     cli();
    656     memmove((void *)newbase, (void *)oldbase, rm_args.rm_size);
    657 
    658     rm_args.rm_return += delta;
    659     rm_args.rm_intcall += delta;
    660     rm_args.rm_bounce += delta;
    661     rm_args.rm_base += delta;
    662     rm_args.rm_gdt += delta;
    663     rm_args.rm_pmjmp += delta;
    664     rm_args.rm_rmjmp += delta;
    665 
    666     gdt_base = rm_args.rm_gdt;
    667 
    668     *(uint32_t *) (gdt_base + 2) = gdt_base;	/* GDT self-pointer */
    669 
    670     /* Segments 0x10 and 0x18 are real-mode-based */
    671     set_seg_base(gdt_base, 0x10, rm_args.rm_base);
    672     set_seg_base(gdt_base, 0x18, rm_args.rm_base);
    673 
    674 #if __SIZEOF_POINTER__ == 4
    675     asm volatile ("lgdtl %0"::"m" (*(char *)gdt_base));
    676 #elif __SIZEOF_POINTER__ == 8
    677     asm volatile ("lgdt %0"::"m" (*(char *)gdt_base));
    678 #else
    679 #error "unsupported architecture"
    680 #endif
    681 
    682     *(uint32_t *) rm_args.rm_pmjmp += delta;
    683     *(uint16_t *) rm_args.rm_rmjmp += delta >> 4;
    684 
    685     rm_args.rm_handle_interrupt += delta;
    686 
    687     sti();
    688 }
    689 
    690 static uint8_t checksum_buf(const void *buf, int count)
    691 {
    692     const uint8_t *p = buf;
    693     uint8_t c = 0;
    694 
    695     while (count--)
    696 	c += *p++;
    697 
    698     return c;
    699 }
    700 
    701 static int stack_needed(void)
    702 {
    703   const unsigned int min_stack = 128;	/* Minimum stack size */
    704   const unsigned int def_stack = 512;	/* Default stack size */
    705   unsigned int v = 0;
    706   const char *p;
    707 
    708   if (CMD_HASDATA(p = getcmditem("stack")))
    709     v = atou(p);
    710 
    711   if (!v)
    712     v = def_stack;
    713 
    714   if (v < min_stack)
    715     v = min_stack;
    716 
    717   return v;
    718 }
    719 
    720 /*
    721  * Set max memory by reservation
    722  * Adds reservations to data in INT15h to prevent access to the top of RAM
    723  * if there's any above the point specified.
    724  */
    725 void setmaxmem(unsigned long long restop_ull)
    726 {
    727     uint32_t restop;
    728     struct e820range *ep;
    729     const int int15restype = 2;
    730 
    731     /* insertrange() works on uint32_t */
    732     restop = min(restop_ull, UINT32_MAX);
    733     /* printf("  setmaxmem  '%08x%08x'  => %08x\n",
    734 	(unsigned int)(restop_ull>>32), (unsigned int)restop_ull, restop); */
    735 
    736     for (ep = ranges; ep->type != -1U; ep++) {
    737 	if (ep->type == 1) {	/* Only if available */
    738 	    if (ep->start >= restop) {
    739 		/* printf("  %08x -> 2\n", ep->start); */
    740 		ep->type = int15restype;
    741 	    } else if (ep[1].start > restop) {
    742 		/* printf("  +%08x =2; cut %08x\n", restop, ep->start); */
    743 		insertrange(restop, (ep[1].start - restop), int15restype);
    744 	    }
    745 	}
    746     }
    747     parse_mem();
    748 }
    749 
    750 struct real_mode_args rm_args;
    751 
    752 /*
    753  * Actual setup routine
    754  * Returns the drive number (which is then passed in %dl to the
    755  * called routine.)
    756  */
    757 void setup(const struct real_mode_args *rm_args_ptr)
    758 {
    759     unsigned int bin_size;
    760     char *memdisk_hook;
    761     struct memdisk_header *hptr;
    762     struct patch_area *pptr;
    763     struct mBFT *mbft;
    764     uint16_t driverseg;
    765     uint32_t driverptr, driveraddr;
    766     uint16_t dosmem_k;
    767     uint32_t stddosmem;
    768     const struct geometry *geometry;
    769     unsigned int total_size;
    770     unsigned int cmdline_len, stack_len, e820_len;
    771     const struct edd4_bvd *bvd;
    772     const struct edd4_bootcat *boot_cat = 0;
    773     com32sys_t regs;
    774     uint32_t ramdisk_image, ramdisk_size;
    775     uint32_t boot_base, rm_base;
    776     int bios_drives;
    777     int do_edd = 1;		/* 0 = no, 1 = yes, default is yes */
    778     int do_eltorito = 0;	/* default is no */
    779     int no_bpt;			/* No valid BPT presented */
    780     uint32_t boot_seg = 0;	/* Meaning 0000:7C00 */
    781     uint32_t boot_len = 512;	/* One sector */
    782     const char *p;
    783 
    784     /* We need to copy the rm_args into their proper place */
    785     memcpy(&rm_args, rm_args_ptr, sizeof rm_args);
    786     sti();			/* ... then interrupts are safe */
    787 
    788     /* Show signs of life */
    789     printf("%s  %s\n", memdisk_version, copyright);
    790 
    791     if (!shdr->ramdisk_image || !shdr->ramdisk_size)
    792 	die("MEMDISK: No ramdisk image specified!\n");
    793 
    794     ramdisk_image = shdr->ramdisk_image;
    795     ramdisk_size = shdr->ramdisk_size;
    796 
    797     e820map_init();		/* Initialize memory data structure */
    798     get_mem();			/* Query BIOS for memory map */
    799     parse_mem();		/* Parse memory map */
    800 
    801     printf("Ramdisk at 0x%08x, length 0x%08x\n", ramdisk_image, ramdisk_size);
    802 
    803     unzip_if_needed(&ramdisk_image, &ramdisk_size);
    804 
    805     geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
    806 
    807     if (getcmditem("edd") != CMD_NOTFOUND ||
    808 	getcmditem("ebios") != CMD_NOTFOUND)
    809 	do_edd = 1;
    810     else if (getcmditem("noedd") != CMD_NOTFOUND ||
    811 	     getcmditem("noebios") != CMD_NOTFOUND ||
    812 	     getcmditem("cbios") != CMD_NOTFOUND)
    813 	do_edd = 0;
    814     else
    815 	do_edd = (geometry->driveno & 0x80) ? 1 : 0;
    816 
    817     if (getcmditem("iso") != CMD_NOTFOUND) {
    818 	do_eltorito = 1;
    819 	do_edd = 1;		/* Mandatory */
    820     }
    821 
    822     /* Choose the appropriate installable memdisk hook */
    823     if (do_eltorito) {
    824 	if (geometry->sector_shift == 11) {
    825 	    bin_size = (int)&_binary_memdisk_iso_2048_bin_size;
    826 	    memdisk_hook = (char *)&_binary_memdisk_iso_2048_bin_start;
    827 	} else {
    828 	    bin_size = (int)&_binary_memdisk_iso_512_bin_size;
    829 	    memdisk_hook = (char *)&_binary_memdisk_iso_512_bin_start;
    830 	}
    831     } else {
    832 	if (do_edd) {
    833 	    bin_size = (int)&_binary_memdisk_edd_512_bin_size;
    834 	    memdisk_hook = (char *)&_binary_memdisk_edd_512_bin_start;
    835 	} else {
    836 	    bin_size = (int)&_binary_memdisk_chs_512_bin_size;
    837 	    memdisk_hook = (char *)&_binary_memdisk_chs_512_bin_start;
    838 	}
    839     }
    840 
    841     /* Reserve the ramdisk memory */
    842     insertrange(ramdisk_image, ramdisk_size, 2);
    843     parse_mem();		/* Recompute variables */
    844 
    845     /* Figure out where it needs to go */
    846     hptr = (struct memdisk_header *)memdisk_hook;
    847     pptr = (struct patch_area *)(memdisk_hook + hptr->patch_offs);
    848 
    849     dosmem_k = rdz_16(BIOS_BASEMEM);
    850     pptr->mdi.olddosmem = dosmem_k;
    851     stddosmem = dosmem_k << 10;
    852     /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
    853     if (stddosmem > dos_mem)
    854 	stddosmem = dos_mem;
    855 
    856     pptr->driveno = geometry->driveno;
    857     pptr->drivetype = geometry->type;
    858     pptr->cylinders = geometry->c;	/* Possible precision loss */
    859     pptr->heads = geometry->h;
    860     pptr->sectors = geometry->s;
    861     pptr->mdi.disksize = geometry->sectors;
    862     pptr->mdi.diskbuf = ramdisk_image + geometry->offset;
    863     pptr->mdi.sector_shift = geometry->sector_shift;
    864     pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
    865 
    866     pptr->mdi.bootloaderid = shdr->type_of_loader;
    867 
    868     pptr->configflags = CONFIG_SAFEINT;	/* Default */
    869     /* Set config flags */
    870     if (getcmditem("ro") != CMD_NOTFOUND) {
    871 	pptr->configflags |= CONFIG_READONLY;
    872     }
    873     if (getcmditem("raw") != CMD_NOTFOUND) {
    874 	pptr->configflags &= ~CONFIG_MODEMASK;
    875 	pptr->configflags |= CONFIG_RAW;
    876     }
    877     if (getcmditem("bigraw") != CMD_NOTFOUND) {
    878 	pptr->configflags &= ~CONFIG_MODEMASK;
    879 	pptr->configflags |= CONFIG_BIGRAW | CONFIG_RAW;
    880     }
    881     if (getcmditem("int") != CMD_NOTFOUND) {
    882 	pptr->configflags &= ~CONFIG_MODEMASK;
    883 	/* pptr->configflags |= 0; */
    884     }
    885     if (getcmditem("safeint") != CMD_NOTFOUND) {
    886 	pptr->configflags &= ~CONFIG_MODEMASK;
    887 	pptr->configflags |= CONFIG_SAFEINT;
    888     }
    889 
    890     printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n",
    891 	   (geometry->driveno & 0x80) ? "hd" : "fd",
    892 	   geometry->driveno & 0x7f,
    893 	   geometry->sectors >> 1,
    894 	   (geometry->sectors & 1) ? ".5" : "",
    895 	   geometry->c, geometry->h, geometry->s,
    896 	   geometry->hsrc, geometry->ssrc,
    897 	   do_edd ? "on" : "off",
    898 	   pptr->configflags & CONFIG_READONLY ? "ro" : "rw");
    899 
    900     puts("Using ");
    901     switch (pptr->configflags & CONFIG_MODEMASK) {
    902     case 0:
    903 	puts("standard INT 15h");
    904 	break;
    905     case CONFIG_SAFEINT:
    906 	puts("safe INT 15h");
    907 	break;
    908     case CONFIG_RAW:
    909 	puts("raw");
    910 	break;
    911     case CONFIG_RAW | CONFIG_BIGRAW:
    912 	puts("big real mode raw");
    913 	break;
    914     default:
    915 	printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK);
    916 	break;
    917     }
    918     puts(" access to high memory\n");
    919 
    920     /* Set up a drive parameter table */
    921     if (geometry->driveno & 0x80) {
    922 	/* Hard disk */
    923 	pptr->dpt.hd.max_cyl = geometry->c - 1;
    924 	pptr->dpt.hd.max_head = geometry->h - 1;
    925 	pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08 : 0;
    926     } else {
    927 	/* Floppy - most of these fields are bogus and mimic
    928 	   a 1.44 MB floppy drive */
    929 	pptr->dpt.fd.specify1 = 0xdf;
    930 	pptr->dpt.fd.specify2 = 0x02;
    931 	pptr->dpt.fd.delay = 0x25;
    932 	pptr->dpt.fd.sectors = geometry->s;
    933 	pptr->dpt.fd.bps = 0x02;
    934 	pptr->dpt.fd.isgap = 0x12;
    935 	pptr->dpt.fd.dlen = 0xff;
    936 	pptr->dpt.fd.fgap = 0x6c;
    937 	pptr->dpt.fd.ffill = 0xf6;
    938 	pptr->dpt.fd.settle = 0x0f;
    939 	pptr->dpt.fd.mstart = 0x05;
    940 	pptr->dpt.fd.maxtrack = geometry->c - 1;
    941 	pptr->dpt.fd.cmos = geometry->type > 5 ? 5 : geometry->type;
    942 
    943 	pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
    944     }
    945 
    946     /* Set up an EDD drive parameter table */
    947     if (do_edd) {
    948 	pptr->edd_dpt.sectors = geometry->sectors;
    949 	/* The EDD spec has this as <= 15482880  sectors (1024x240x63);
    950 	   this seems to make very little sense.  Try for something saner. */
    951 	if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) {
    952 	    pptr->edd_dpt.c = geometry->c;
    953 	    pptr->edd_dpt.h = geometry->h;
    954 	    pptr->edd_dpt.s = geometry->s;
    955 	    /* EDD-4 states that invalid geometry should be returned
    956 	     * for INT 0x13, AH=0x48 "EDD Get Disk Parameters" call on an
    957 	     * El Torito ODD.  Check for 2048-byte sector size
    958 	     */
    959 	    if (geometry->sector_shift != 11)
    960 		pptr->edd_dpt.flags |= 0x0002;	/* Geometry valid */
    961 	}
    962 	if (!(geometry->driveno & 0x80)) {
    963 	    /* Floppy drive.  Mark it as a removable device with
    964 	       media change notification; media is present. */
    965 	    pptr->edd_dpt.flags |= 0x0014;
    966 	}
    967 
    968 	pptr->edd_dpt.devpath[0] = pptr->mdi.diskbuf;
    969 	pptr->edd_dpt.chksum = -checksum_buf(&pptr->edd_dpt.dpikey, 73 - 30);
    970     }
    971 
    972     if (do_eltorito) {
    973 	bvd = (struct edd4_bvd *)(ramdisk_image + 17 * 2048);
    974 	boot_cat =
    975 	    (struct edd4_bootcat *)(ramdisk_image + bvd->boot_cat * 2048);
    976 	pptr->cd_pkt.type = boot_cat->initial_entry.media_type;	/* Cheat */
    977 	pptr->cd_pkt.driveno = geometry->driveno;
    978 	pptr->cd_pkt.start = boot_cat->initial_entry.load_block;
    979 	boot_seg = pptr->cd_pkt.load_seg = boot_cat->initial_entry.load_seg;
    980 	pptr->cd_pkt.sect_count = boot_cat->initial_entry.sect_count;
    981 	boot_len = pptr->cd_pkt.sect_count * 512;
    982 	pptr->cd_pkt.geom1 = (uint8_t)(pptr->cylinders) & 0xFF;
    983 	pptr->cd_pkt.geom2 =
    984 	    (uint8_t)(pptr->sectors) | (uint8_t)((pptr->cylinders >> 2) & 0xC0);
    985 	pptr->cd_pkt.geom3 = (uint8_t)(pptr->heads);
    986     }
    987 
    988     if ((p = getcmditem("mem")) != CMD_NOTFOUND) {
    989 	setmaxmem(suffix_number(p));
    990     }
    991 
    992     /* The size is given by hptr->total_size plus the size of the E820
    993        map -- 12 bytes per range; we may need as many as 2 additional
    994        ranges (each insertrange() can worst-case turn 1 area into 3)
    995        plus the terminating range, over what nranges currently show. */
    996     total_size = hptr->total_size;	/* Actual memdisk code */
    997     e820_len = (nranges + 3) * sizeof(ranges[0]);
    998     total_size += e820_len;		/* E820 memory ranges */
    999     cmdline_len = strlen(shdr->cmdline) + 1;
   1000     total_size += cmdline_len;		/* Command line */
   1001     stack_len = stack_needed();
   1002     total_size += stack_len;		/* Stack */
   1003     printf("Code %u, meminfo %u, cmdline %u, stack %u\n",
   1004 	   hptr->total_size, e820_len, cmdline_len, stack_len);
   1005     printf("Total size needed = %u bytes, allocating %uK\n",
   1006 	   total_size, (total_size + 0x3ff) >> 10);
   1007 
   1008     if (total_size > dos_mem)
   1009 	die("MEMDISK: Insufficient low memory\n");
   1010 
   1011     driveraddr = stddosmem - total_size;
   1012     driveraddr &= ~0x3FF;
   1013 
   1014     printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
   1015 	   stddosmem, dos_mem, driveraddr);
   1016 
   1017     /* Reserve this range of memory */
   1018     wrz_16(BIOS_BASEMEM, driveraddr >> 10);
   1019     insertrange(driveraddr, dos_mem - driveraddr, 2);
   1020     parse_mem();
   1021 
   1022     pptr->mem1mb = low_mem >> 10;
   1023     pptr->mem16mb = high_mem >> 16;
   1024     if (low_mem == (15 << 20)) {
   1025 	/* lowmem maxed out */
   1026 	uint32_t int1588mem = (high_mem >> 10) + (low_mem >> 10);
   1027 	pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
   1028     } else {
   1029 	pptr->memint1588 = low_mem >> 10;
   1030     }
   1031 
   1032     printf("1588: 0x%04x  15E801: 0x%04x 0x%04x\n",
   1033 	   pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
   1034 
   1035     driverseg = driveraddr >> 4;
   1036     driverptr = driverseg << 16;
   1037 
   1038     /* Anything beyond the end is for the stack */
   1039     pptr->mystack = (uint16_t) (stddosmem - driveraddr);
   1040 
   1041     pptr->mdi.oldint13.uint32 = rdz_32(BIOS_INT13);
   1042     pptr->mdi.oldint15.uint32 = rdz_32(BIOS_INT15);
   1043 
   1044     /* Adjust the E820 table: if there are null ranges (type 0)
   1045        at the end, change them to type end of list (-1).
   1046        This is necessary for the driver to be able to report end
   1047        of list correctly. */
   1048     while (nranges && ranges[nranges - 1].type == 0) {
   1049 	ranges[--nranges].type = -1;
   1050     }
   1051 
   1052     if (getcmditem("nopassany") != CMD_NOTFOUND) {
   1053 	printf("nopassany specified - we're the only drive of any kind\n");
   1054 	bios_drives = 0;
   1055 	pptr->drivecnt = 0;
   1056 	no_bpt = 1;
   1057 	pptr->mdi.oldint13.uint32 = driverptr + hptr->iret_offs;
   1058 	wrz_8(BIOS_EQUIP, rdz_8(BIOS_EQUIP) & ~0xc1);
   1059 	wrz_8(BIOS_HD_COUNT, 0);
   1060     } else if (getcmditem("nopass") != CMD_NOTFOUND) {
   1061 	printf("nopass specified - we're the only drive\n");
   1062 	bios_drives = 0;
   1063 	pptr->drivecnt = 0;
   1064 	no_bpt = 1;
   1065     } else {
   1066 	/* Query drive parameters of this type */
   1067 	memset(&regs, 0, sizeof regs);
   1068 	regs.es = 0;
   1069 	regs.eax.b[1] = 0x08;
   1070 	regs.edx.b[0] = geometry->driveno & 0x80;
   1071 	intcall(0x13, &regs, &regs);
   1072 
   1073 	/* Note: per suggestion from the Interrupt List, consider
   1074 	   INT 13 08 to have failed if the sector count in CL is zero. */
   1075 	if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) {
   1076 	    printf("INT 13 08: Failure, assuming this is the only drive\n");
   1077 	    pptr->drivecnt = 0;
   1078 	    no_bpt = 1;
   1079 	} else {
   1080 	    printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
   1081 		   regs.edx.b[0], regs.es, regs.edi.w[0]);
   1082 	    pptr->drivecnt = regs.edx.b[0];
   1083 	    no_bpt = !(regs.es | regs.edi.w[0]);
   1084 	}
   1085 
   1086 	/* Compare what INT 13h returned with the appropriate equipment byte */
   1087 	if (geometry->driveno & 0x80) {
   1088 	    bios_drives = rdz_8(BIOS_HD_COUNT);
   1089 	} else {
   1090 	    uint8_t equip = rdz_8(BIOS_EQUIP);
   1091 
   1092 	    if (equip & 1)
   1093 		bios_drives = (equip >> 6) + 1;
   1094 	    else
   1095 		bios_drives = 0;
   1096 	}
   1097 
   1098 	if (pptr->drivecnt > bios_drives) {
   1099 	    printf("BIOS equipment byte says count = %d, go with that\n",
   1100 		   bios_drives);
   1101 	    pptr->drivecnt = bios_drives;
   1102 	}
   1103     }
   1104 
   1105     /* Add ourselves to the drive count */
   1106     pptr->drivecnt++;
   1107 
   1108     /* Discontiguous drive space.  There is no really good solution for this. */
   1109     if (pptr->drivecnt <= (geometry->driveno & 0x7f))
   1110 	pptr->drivecnt = (geometry->driveno & 0x7f) + 1;
   1111 
   1112     /* Probe for contiguous range of BIOS drives starting with driveno */
   1113     pptr->driveshiftlimit = probe_drive_range(geometry->driveno) + 1;
   1114     if ((pptr->driveshiftlimit & 0x80) != (geometry->driveno & 0x80))
   1115 	printf("We lost the last drive in our class of drives.\n");
   1116     printf("Drive probing gives drive shift limit: 0x%02x\n",
   1117 	pptr->driveshiftlimit);
   1118 
   1119     /* Pointer to the command line */
   1120     pptr->mdi.cmdline.seg_off.offset = bin_size + (nranges + 1) * sizeof(ranges[0]);
   1121     pptr->mdi.cmdline.seg_off.segment = driverseg;
   1122 
   1123     /* Copy driver followed by E820 table followed by command line */
   1124     {
   1125 	unsigned char *dpp = (unsigned char *)(driverseg << 4);
   1126 
   1127 	/* Adjust these pointers to point to the installed image */
   1128 	/* Careful about the order here... the image isn't copied yet! */
   1129 	pptr = (struct patch_area *)(dpp + hptr->patch_offs);
   1130 	hptr = (struct memdisk_header *)dpp;
   1131 
   1132 	/* Actually copy to low memory */
   1133 	dpp = mempcpy(dpp, memdisk_hook, bin_size);
   1134 	dpp = mempcpy(dpp, ranges, (nranges + 1) * sizeof(ranges[0]));
   1135 	dpp = mempcpy(dpp, shdr->cmdline, cmdline_len);
   1136     }
   1137 
   1138     /* Note the previous INT 13h hook in the "safe hook" structure */
   1139     hptr->safe_hook.old_hook.uint32 = pptr->mdi.oldint13.uint32;
   1140 
   1141     /* Re-fill the "safe hook" mBFT field with the physical address */
   1142     mbft = (struct mBFT *)(((const char *)hptr) + hptr->safe_hook.mbft);
   1143     hptr->safe_hook.mbft = (size_t)mbft;
   1144 
   1145     /* Update various BIOS magic data areas (gotta love this shit) */
   1146 
   1147     if (geometry->driveno & 0x80) {
   1148 	/* Update BIOS hard disk count */
   1149 	uint8_t nhd = pptr->drivecnt;
   1150 
   1151 	if (nhd > 128)
   1152 	    nhd = 128;
   1153 
   1154 	if (!do_eltorito)
   1155 	    wrz_8(BIOS_HD_COUNT, nhd);
   1156     } else {
   1157 	/* Update BIOS floppy disk count */
   1158 	uint8_t equip = rdz_8(BIOS_EQUIP);
   1159 	uint8_t nflop = pptr->drivecnt;
   1160 
   1161 	if (nflop > 4)		/* Limit of equipment byte */
   1162 	    nflop = 4;
   1163 
   1164 	equip &= 0x3E;
   1165 	if (nflop)
   1166 	    equip |= ((nflop - 1) << 6) | 0x01;
   1167 
   1168 	wrz_8(BIOS_EQUIP, equip);
   1169 
   1170 	/* Install DPT pointer if this was the only floppy */
   1171 	if (getcmditem("dpt") != CMD_NOTFOUND ||
   1172 	    ((nflop == 1 || no_bpt) && getcmditem("nodpt") == CMD_NOTFOUND)) {
   1173 	    /* Do install a replacement DPT into INT 1Eh */
   1174 	    pptr->mdi.dpt_ptr =
   1175 		hptr->patch_offs + offsetof(struct patch_area, dpt);
   1176 	}
   1177     }
   1178 
   1179     /* Complete the mBFT */
   1180     mbft->acpi.signature[0] = 'm';	/* "mBFT" */
   1181     mbft->acpi.signature[1] = 'B';
   1182     mbft->acpi.signature[2] = 'F';
   1183     mbft->acpi.signature[3] = 'T';
   1184     mbft->safe_hook = (size_t)&hptr->safe_hook;
   1185     mbft->acpi.checksum = -checksum_buf(mbft, mbft->acpi.length);
   1186 
   1187     /* Install the interrupt handlers */
   1188     printf("old: int13 = %08x  int15 = %08x  int1e = %08x\n",
   1189 	   rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
   1190 
   1191     wrz_32(BIOS_INT13, driverptr + hptr->int13_offs);
   1192     wrz_32(BIOS_INT15, driverptr + hptr->int15_offs);
   1193     if (pptr->mdi.dpt_ptr)
   1194 	wrz_32(BIOS_INT1E, driverptr + pptr->mdi.dpt_ptr);
   1195 
   1196     printf("new: int13 = %08x  int15 = %08x  int1e = %08x\n",
   1197 	   rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
   1198 
   1199     /* Figure out entry point */
   1200     if (!boot_seg) {
   1201 	boot_base = 0x7c00;
   1202 	shdr->sssp = 0x7c00;
   1203 	shdr->csip = 0x7c00;
   1204     } else {
   1205 	boot_base = boot_seg << 4;
   1206 	shdr->sssp = boot_seg << 16;
   1207 	shdr->csip = boot_seg << 16;
   1208     }
   1209 
   1210     /* Relocate the real-mode code to below the stub */
   1211     rm_base = (driveraddr - rm_args.rm_size) & ~15;
   1212     if (rm_base < boot_base + boot_len)
   1213 	die("MEMDISK: bootstrap too large to load\n");
   1214 
   1215     relocate_rm_code(rm_base);
   1216 
   1217     /* Reboot into the new "disk" */
   1218     puts("Loading boot sector... ");
   1219 
   1220     memcpy((void *)boot_base,
   1221 	   (char *)pptr->mdi.diskbuf + geometry->boot_lba * 512,
   1222 	   boot_len);
   1223 
   1224     if (getcmditem("pause") != CMD_NOTFOUND) {
   1225 	puts("press any key to boot... ");
   1226 	memset(&regs, 0, sizeof regs);
   1227 	regs.eax.w[0] = 0;
   1228 	intcall(0x16, &regs, NULL);
   1229     }
   1230 
   1231     puts("booting...\n");
   1232 
   1233     /* On return the assembly code will jump to the boot vector */
   1234     shdr->esdi = pnp_install_check();
   1235     shdr->edx = geometry->driveno;
   1236 }
   1237 
   1238