diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c
index 3539d206d011..1b6134ab2e6b 100644
--- a/stand/i386/libi386/biosdisk.c
+++ b/stand/i386/libi386/biosdisk.c
@@ -1,1399 +1,1400 @@
 /*-
  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 /*
  * BIOS disk device handling.
  *
  * Ideas and algorithms from:
  *
  * - NetBSD libi386/biosdisk.c
  * - FreeBSD biosboot/disk.c
  *
  */
 
 #include <sys/disk.h>
 #include <sys/limits.h>
 #include <sys/queue.h>
 #include <stand.h>
 #include <machine/bootinfo.h>
 #include <stdarg.h>
 #include <stdbool.h>
 
 #include <bootstrap.h>
 #include <btxv86.h>
 #include <edd.h>
 #include "disk.h"
 #include "libi386.h"
 
 #define	BIOS_NUMDRIVES		0x475
 #define	BIOSDISK_SECSIZE	512
 #define	BUFSIZE			(1 * BIOSDISK_SECSIZE)
 
 #define	DT_ATAPI	0x10	/* disk type for ATAPI floppies */
 #define	WDMAJOR		0	/* major numbers for devices we frontend for */
 #define	WFDMAJOR	1
 #define	FDMAJOR		2
 #define	DAMAJOR		4
 #define	ACDMAJOR	117
 #define	CDMAJOR		15
 
 /*
  * INT13 commands
  */
 #define	CMD_RESET	0x0000
 #define	CMD_READ_CHS	0x0200
 #define	CMD_WRITE_CHS	0x0300
 #define	CMD_READ_PARAM	0x0800
 #define	CMD_DRIVE_TYPE	0x1500
 #define	CMD_CHECK_EDD	0x4100
 #define	CMD_READ_LBA	0x4200
 #define	CMD_WRITE_LBA	0x4300
 #define	CMD_EXT_PARAM	0x4800
 #define	CMD_CD_GET_STATUS 0x4b01
 
 #define	DISK_BIOS	0x13
 
 #ifdef DISK_DEBUG
 #define	DPRINTF(fmt, args...)	printf("%s: " fmt "\n", __func__, ## args)
 #else
 #define	DPRINTF(fmt, args...)	((void)0)
 #endif
 
 struct specification_packet {
 	uint8_t		sp_size;
 	uint8_t		sp_bootmedia;
 	uint8_t		sp_drive;
 	uint8_t		sp_controller;
 	uint32_t	sp_lba;
 	uint16_t	sp_devicespec;
 	uint16_t	sp_buffersegment;
 	uint16_t	sp_loadsegment;
 	uint16_t	sp_sectorcount;
 	uint16_t	sp_cylsec;
 	uint8_t		sp_head;
 	uint8_t		sp_dummy[16];	/* Avoid memory corruption */
 };
 
 /*
  * List of BIOS devices, translation from disk unit number to
  * BIOS unit number.
  */
 typedef struct bdinfo
 {
 	STAILQ_ENTRY(bdinfo)	bd_link;	/* link in device list */
 	int		bd_unit;	/* BIOS unit number */
 	int		bd_cyl;		/* BIOS geometry */
 	int		bd_hds;
 	int		bd_sec;
 	int		bd_flags;
 #define	BD_MODEINT13	0x0000
 #define	BD_MODEEDD1	0x0001
 #define	BD_MODEEDD3	0x0002
 #define	BD_MODEEDD	(BD_MODEEDD1 | BD_MODEEDD3)
 #define	BD_MODEMASK	0x0003
 #define	BD_FLOPPY	0x0004
 #define	BD_CDROM	0x0008
 #define	BD_NO_MEDIA	0x0010
 	int		bd_type;	/* BIOS 'drive type' (floppy only) */
 	uint16_t	bd_sectorsize;	/* Sector size */
 	uint64_t	bd_sectors;	/* Disk size */
 	int		bd_open;	/* reference counter */
 	void		*bd_bcache;	/* buffer cache data */
 } bdinfo_t;
 
 #define	BD_RD		0
 #define	BD_WR		1
 
 typedef STAILQ_HEAD(bdinfo_list, bdinfo) bdinfo_list_t;
 static bdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
 static bdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
 static bdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
 
 static void bd_io_workaround(bdinfo_t *);
 static int bd_io(struct disk_devdesc *, bdinfo_t *, daddr_t, int, caddr_t, int);
 static bool bd_int13probe(bdinfo_t *);
 
 static int bd_init(void);
 static int cd_init(void);
 static int fd_init(void);
 static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
     char *buf, size_t *rsize);
 static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
     char *buf, size_t *rsize);
 static int bd_open(struct open_file *f, ...);
 static int bd_close(struct open_file *f);
 static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
 static int bd_print(int verbose);
 static int cd_print(int verbose);
 static int fd_print(int verbose);
 static void bd_reset_disk(int);
 static int bd_get_diskinfo_std(struct bdinfo *);
 
 struct devsw biosfd = {
 	.dv_name = "fd",
 	.dv_type = DEVT_FD,
 	.dv_init = fd_init,
 	.dv_strategy = bd_strategy,
 	.dv_open = bd_open,
 	.dv_close = bd_close,
 	.dv_ioctl = bd_ioctl,
 	.dv_print = fd_print,
 	.dv_cleanup = nullsys,
 };
 
 struct devsw bioscd = {
 	.dv_name = "cd",
 	.dv_type = DEVT_CD,
 	.dv_init = cd_init,
 	.dv_strategy = bd_strategy,
 	.dv_open = bd_open,
 	.dv_close = bd_close,
 	.dv_ioctl = bd_ioctl,
 	.dv_print = cd_print,
 	.dv_cleanup = nullsys,
 };
 
 struct devsw bioshd = {
 	.dv_name = "disk",
 	.dv_type = DEVT_DISK,
 	.dv_init = bd_init,
 	.dv_strategy = bd_strategy,
 	.dv_open = bd_open,
 	.dv_close = bd_close,
 	.dv_ioctl = bd_ioctl,
 	.dv_print = bd_print,
 	.dv_cleanup = nullsys,
 	.dv_fmtdev = disk_fmtdev,
 };
 
 static bdinfo_list_t *
 bd_get_bdinfo_list(struct devsw *dev)
 {
 	if (dev->dv_type == DEVT_DISK)
 		return (&hdinfo);
 	if (dev->dv_type == DEVT_CD)
 		return (&cdinfo);
 	if (dev->dv_type == DEVT_FD)
 		return (&fdinfo);
 	return (NULL);
 }
 
 /* XXX this gets called way way too often, investigate */
 static bdinfo_t *
 bd_get_bdinfo(struct devdesc *dev)
 {
 	bdinfo_list_t *bdi;
 	bdinfo_t *bd = NULL;
 	int unit;
 
 	bdi = bd_get_bdinfo_list(dev->d_dev);
 	if (bdi == NULL)
 		return (bd);
 
 	unit = 0;
 	STAILQ_FOREACH(bd, bdi, bd_link) {
 		if (unit == dev->d_unit)
 			return (bd);
 		unit++;
 	}
 	return (bd);
 }
 
 /*
  * Translate between BIOS device numbers and our private unit numbers.
  */
 int
 bd_bios2unit(int biosdev)
 {
 	bdinfo_list_t *bdi[] = { &fdinfo, &cdinfo, &hdinfo, NULL };
 	bdinfo_t *bd;
 	int i, unit;
 
 	DPRINTF("looking for bios device 0x%x", biosdev);
 	for (i = 0; bdi[i] != NULL; i++) {
 		unit = 0;
 		STAILQ_FOREACH(bd, bdi[i], bd_link) {
 			if (bd->bd_unit == biosdev) {
 				DPRINTF("bd unit %d is BIOS device 0x%x", unit,
 				    bd->bd_unit);
 				return (unit);
 			}
 			unit++;
 		}
 	}
 	return (-1);
 }
 
 int
 bd_unit2bios(struct i386_devdesc *dev)
 {
 	bdinfo_list_t *bdi;
 	bdinfo_t *bd;
 	int unit;
 
 	bdi = bd_get_bdinfo_list(dev->dd.d_dev);
 	if (bdi == NULL)
 		return (-1);
 
 	unit = 0;
 	STAILQ_FOREACH(bd, bdi, bd_link) {
 		if (unit == dev->dd.d_unit)
 			return (bd->bd_unit);
 		unit++;
 	}
 	return (-1);
 }
 
 /*
  * Use INT13 AH=15 - Read Drive Type.
  */
 static int
 fd_count(void)
 {
 	int drive;
 
 	for (drive = 0; drive < MAXBDDEV; drive++) {
 		bd_reset_disk(drive);
 
 		v86.ctl = V86_FLAGS;
 		v86.addr = DISK_BIOS;
 		v86.eax = CMD_DRIVE_TYPE;
 		v86.edx = drive;
 		v86int();
 
 		if (V86_CY(v86.efl))
 			break;
 
 		if ((v86.eax & 0x300) == 0)
 			break;
 	}
 
 	return (drive);
 }
 
 /*
  * Quiz the BIOS for disk devices, save a little info about them.
  */
 static int
 fd_init(void)
 {
 	int unit, numfd;
 	bdinfo_t *bd;
 
 	numfd = fd_count();
 	for (unit = 0; unit < numfd; unit++) {
 		if ((bd = calloc(1, sizeof(*bd))) == NULL)
 			break;
 
 		bd->bd_sectorsize = BIOSDISK_SECSIZE;
 		bd->bd_flags = BD_FLOPPY;
 		bd->bd_unit = unit;
 
 		/* Use std diskinfo for floppy drive */
 		if (bd_get_diskinfo_std(bd) != 0) {
 			free(bd);
 			break;
 		}
 		if (bd->bd_sectors == 0)
 			bd->bd_flags |= BD_NO_MEDIA;
 
 		printf("BIOS drive %c: is %s%d\n", ('A' + unit),
 		    biosfd.dv_name, unit);
 
 		STAILQ_INSERT_TAIL(&fdinfo, bd, bd_link);
 	}
 
 	bcache_add_dev(unit);
 	return (0);
 }
 
 static int
 bd_init(void)
 {
 	int base, unit;
 	bdinfo_t *bd;
 
 	TSENTER();
 
 	base = 0x80;
 	for (unit = 0; unit < *(unsigned char *)PTOV(BIOS_NUMDRIVES); unit++) {
 		/*
 		 * Check the BIOS equipment list for number of fixed disks.
 		 */
 		if ((bd = calloc(1, sizeof(*bd))) == NULL)
 			break;
 		bd->bd_unit = base + unit;
 		if (!bd_int13probe(bd)) {
 			free(bd);
 			break;
 		}
 
 		printf("BIOS drive %c: is %s%d\n", ('C' + unit),
 		    bioshd.dv_name, unit);
 
 		STAILQ_INSERT_TAIL(&hdinfo, bd, bd_link);
 	}
 	bcache_add_dev(unit);
 	TSEXIT();
 	return (0);
 }
 
 /*
  * We can't quiz, we have to be told what device to use, so this function
  * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
  * device number to add.
  */
 static int
 cd_init(void)
 {
 
 	return (0);
 }
 
 /*
  * Information from bootable CD-ROM.
  */
 static int
 bd_get_diskinfo_cd(struct bdinfo *bd)
 {
 	struct specification_packet bc_sp;
 	int ret = -1;
 
 	(void) memset(&bc_sp, 0, sizeof (bc_sp));
 	/* Set sp_size as per specification. */
 	bc_sp.sp_size = sizeof (bc_sp) - sizeof (bc_sp.sp_dummy);
 
         v86.ctl = V86_FLAGS;
         v86.addr = DISK_BIOS;
         v86.eax = CMD_CD_GET_STATUS;
         v86.edx = bd->bd_unit;
         v86.ds = VTOPSEG(&bc_sp);
         v86.esi = VTOPOFF(&bc_sp);
         v86int();
 
 	if ((v86.eax & 0xff00) == 0 &&
 	    bc_sp.sp_drive == bd->bd_unit) {
 		bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) +
 		    ((bc_sp.sp_cylsec & 0xff00) >> 8) + 1;
 		bd->bd_sec = bc_sp.sp_cylsec & 0x3f;
 		bd->bd_hds = bc_sp.sp_head + 1;
 		bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
 
 		if (bc_sp.sp_bootmedia & 0x0F) {
 			/* Floppy or hard-disk emulation */
 			bd->bd_sectorsize = BIOSDISK_SECSIZE;
 			return (-1);
 		} else {
 			bd->bd_sectorsize = 2048;
 			bd->bd_flags = BD_MODEEDD | BD_CDROM;
 			ret = 0;
 		}
 	}
 
 	/*
 	 * If this is the boot_drive, default to non-emulation bootable CD-ROM.
 	 */
 	if (ret != 0 && bd->bd_unit >= 0x88) {
 		bd->bd_cyl = 0;
 		bd->bd_hds = 1;
 		bd->bd_sec = 15;
 		bd->bd_sectorsize = 2048;
 		bd->bd_flags = BD_MODEEDD | BD_CDROM;
 		bd->bd_sectors = 0;
 		ret = 0;
 	}
 
 	/*
 	 * Note we can not use bd_get_diskinfo_ext() nor bd_get_diskinfo_std()
 	 * here - some systems do get hung with those.
 	 */
 	/*
 	 * Still no size? use 7.961GB. The size does not really matter
 	 * as long as it is reasonably large to make our reads to pass
 	 * the sector count check.
 	 */
 	if (bd->bd_sectors == 0)
 		bd->bd_sectors = 4173824;
  
 	return (ret);
 }
 
 int
 bc_add(int biosdev)
 {
 	bdinfo_t *bd;
 	int nbcinfo = 0;
 
 	if (!STAILQ_EMPTY(&cdinfo))
                 return (-1);
 
 	if ((bd = calloc(1, sizeof(*bd))) == NULL)
 		return (-1);
 
 	bd->bd_unit = biosdev;
 	if (bd_get_diskinfo_cd(bd) < 0) {
 		free(bd);
 		return (-1);
 	}
 
 	STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link);
         printf("BIOS CD is cd%d\n", nbcinfo);
         nbcinfo++;
         bcache_add_dev(nbcinfo);        /* register cd device in bcache */
         return(0);
 }
 
 /*
  * Return EDD version or 0 if EDD is not supported on this drive.
  */
 static int
 bd_check_extensions(int unit)
 {
 	/* do not use ext calls for floppy devices */
 	if (unit < 0x80)
 		return (0);
 
 	/* Determine if we can use EDD with this device. */
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	v86.eax = CMD_CHECK_EDD;
 	v86.edx = unit;
 	v86.ebx = EDD_QUERY_MAGIC;
 	v86int();
 
 	if (V86_CY(v86.efl) ||			/* carry set */
 	    (v86.ebx & 0xffff) != EDD_INSTALLED) /* signature */
 		return (0);
 
 	/* extended disk access functions (AH=42h-44h,47h,48h) supported */
 	if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
 		return (0);
 
 	return ((v86.eax >> 8) & 0xff);
 }
 
 static void
 bd_reset_disk(int unit)
 {
 	/* reset disk */
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	v86.eax = CMD_RESET;
 	v86.edx = unit;
 	v86int();
 }
 
 /*
  * Read CHS info. Return 0 on success, error otherwise.
  */
 static int
 bd_get_diskinfo_std(struct bdinfo *bd)
 {
 	bzero(&v86, sizeof(v86));
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	v86.eax = CMD_READ_PARAM;
 	v86.edx = bd->bd_unit;
 	v86int();
 
 	if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
 		return ((v86.eax & 0xff00) >> 8);
 
 	/* return custom error on absurd sector number */
 	if ((v86.ecx & 0x3f) == 0)
 		return (0x60);
 
 	bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
 	/* Convert max head # -> # of heads */
 	bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
 	bd->bd_sec = v86.ecx & 0x3f;
 	bd->bd_type = v86.ebx;
 	bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
 
 	return (0);
 }
 
 /*
  * Read EDD info. Return 0 on success, error otherwise.
  *
  * Avoid stack corruption on some systems by adding extra bytes to
  * params block.
  */
 static int
 bd_get_diskinfo_ext(struct bdinfo *bd)
 {
 	struct disk_params {
 		struct edd_params head;
 		struct edd_device_path_v3 device_path;
 		uint8_t dummy[16];
 	} __packed dparams;
 	struct edd_params *params;
 	uint64_t total;
 
 	params = &dparams.head;
 
 	/* Get disk params */
 	bzero(&dparams, sizeof(dparams));
 	params->len = sizeof(struct edd_params_v3);
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	v86.eax = CMD_EXT_PARAM;
 	v86.edx = bd->bd_unit;
 	v86.ds = VTOPSEG(&dparams);
 	v86.esi = VTOPOFF(&dparams);
 	v86int();
 
 	if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
 		return ((v86.eax & 0xff00) >> 8);
 
 	/*
 	 * Sector size must be a multiple of 512 bytes.
 	 * An alternate test would be to check power of 2,
 	 * powerof2(params.sector_size).
 	 * 16K is largest read buffer we can use at this time.
 	 */
 	if (params->sector_size >= 512 &&
 	    params->sector_size <= 16384 &&
 	    (params->sector_size % BIOSDISK_SECSIZE) == 0)
 		bd->bd_sectorsize = params->sector_size;
 
 	bd->bd_cyl = params->cylinders;
 	bd->bd_hds = params->heads;
 	bd->bd_sec = params->sectors_per_track;
 
 	if (params->sectors != 0) {
 		total = params->sectors;
 	} else {
 		total = (uint64_t)params->cylinders *
 		    params->heads * params->sectors_per_track;
 	}
 	bd->bd_sectors = total;
 
 	return (0);
 }
 
 /*
  * Try to detect a device supported by the legacy int13 BIOS
  */
 static bool
 bd_int13probe(bdinfo_t *bd)
 {
 	int edd, ret;
 
 	bd->bd_flags &= ~BD_NO_MEDIA;
 
 	if ((bd->bd_flags & BD_CDROM) != 0) {
 		return (bd_get_diskinfo_cd(bd) == 0);
 	}
 
 	edd = bd_check_extensions(bd->bd_unit);
 	if (edd == 0)
 		bd->bd_flags |= BD_MODEINT13;
 	else if (edd < 0x30)
 		bd->bd_flags |= BD_MODEEDD1;
 	else
 		bd->bd_flags |= BD_MODEEDD3;
 
 	/* Default sector size */
 	if (bd->bd_sectorsize == 0)
 		bd->bd_sectorsize = BIOSDISK_SECSIZE;
 
 	/*
 	 * Test if the floppy device is present, so we can avoid receiving
 	 * bogus information from bd_get_diskinfo_std().
 	 */
 	if (bd->bd_unit < 0x80) {
 		/* reset disk */
 		bd_reset_disk(bd->bd_unit);
 
 		/* Get disk type */
 		v86.ctl = V86_FLAGS;
 		v86.addr = DISK_BIOS;
 		v86.eax = CMD_DRIVE_TYPE;
 		v86.edx = bd->bd_unit;
 		v86int();
 		if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
 			return (false);
 	}
 
 	ret = 1;
 	if (edd != 0)
 		ret = bd_get_diskinfo_ext(bd);
 	if (ret != 0 || bd->bd_sectors == 0)
 		ret = bd_get_diskinfo_std(bd);
 
 	if (ret != 0 && bd->bd_unit < 0x80) {
 		/* Set defaults for 1.44 floppy */
 		bd->bd_cyl = 80;
 		bd->bd_hds = 2;
 		bd->bd_sec = 18;
 		bd->bd_sectors = 2880;
 		/* Since we are there, there most likely is no media */
 		bd->bd_flags |= BD_NO_MEDIA;
 		ret = 0;
 	}
 
 	if (ret != 0) {
 		if (bd->bd_sectors != 0 && edd != 0) {
 			bd->bd_sec = 63;
 			bd->bd_hds = 255;
 			bd->bd_cyl =
 			    (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
 			    bd->bd_sec * bd->bd_hds;
 		} else {
 			const char *dv_name;
 
 			if ((bd->bd_flags & BD_FLOPPY) != 0)
 				dv_name = biosfd.dv_name;
 			else
 				dv_name = bioshd.dv_name;
 
 			printf("Can not get information about %s unit %#x\n",
 			    dv_name, bd->bd_unit);
 			return (false);
 		}
 	}
 
 	if (bd->bd_sec == 0)
 		bd->bd_sec = 63;
 	if (bd->bd_hds == 0)
 		bd->bd_hds = 255;
 
 	if (bd->bd_sectors == 0)
 		bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
 
 	DPRINTF("unit 0x%x geometry %d/%d/%d\n", bd->bd_unit, bd->bd_cyl,
 	    bd->bd_hds, bd->bd_sec);
 
 	return (true);
 }
 
 static int
 bd_count(bdinfo_list_t *bdi)
 {
 	bdinfo_t *bd;
 	int i;
 
 	i = 0;
 	STAILQ_FOREACH(bd, bdi, bd_link)
 		i++;
 	return (i);
 }
 
 /*
  * Print information about disks
  */
 static int
 bd_print_common(struct devsw *dev, bdinfo_list_t *bdi, int verbose)
 {
 	char line[80];
 	struct disk_devdesc devd;
 	bdinfo_t *bd;
 	int i, ret = 0;
 	char drive;
 
 	if (STAILQ_EMPTY(bdi))
 		return (0);
 
 	printf("%s devices:", dev->dv_name);
 	if ((ret = pager_output("\n")) != 0)
 		return (ret);
 
 	i = -1;
 	STAILQ_FOREACH(bd, bdi, bd_link) {
 		i++;
 
 		switch (dev->dv_type) {
 		case DEVT_FD:
 			drive = 'A';
 			break;
 		case DEVT_CD:
 			drive = 'C' + bd_count(&hdinfo);
 			break;
 		default:
 			drive = 'C';
 			break;
 		}
 
 		snprintf(line, sizeof(line),
 		    "    %s%d:   BIOS drive %c (%s%ju X %u):\n",
 		    dev->dv_name, i, drive + i,
 		    (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
 		    "no media, " : "",
 		    (uintmax_t)bd->bd_sectors,
 		    bd->bd_sectorsize);
 		if ((ret = pager_output(line)) != 0)
 			break;
 
 		if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
 			continue;
 
 		if (dev->dv_type != DEVT_DISK)
 			continue;
 
 		devd.dd.d_dev = dev;
 		devd.dd.d_unit = i;
 		devd.d_slice = D_SLICENONE;
 		devd.d_partition = D_PARTNONE;
 		if (disk_open(&devd,
 		    bd->bd_sectorsize * bd->bd_sectors,
 		    bd->bd_sectorsize) == 0) {
 			snprintf(line, sizeof(line), "    %s%d",
 			    dev->dv_name, i);
 			ret = disk_print(&devd, line, verbose);
 			disk_close(&devd);
 			if (ret != 0)
 				break;
 		}
 	}
 	return (ret);
 }
 
 static int
 fd_print(int verbose)
 {
 	return (bd_print_common(&biosfd, &fdinfo, verbose));
 }
 
 static int
 bd_print(int verbose)
 {
 	return (bd_print_common(&bioshd, &hdinfo, verbose));
 }
 
 static int
 cd_print(int verbose)
 {
 	return (bd_print_common(&bioscd, &cdinfo, verbose));
 }
 
 /*
  * Read disk size from partition.
  * This is needed to work around buggy BIOS systems returning
  * wrong (truncated) disk media size.
  * During bd_probe() we tested if the multiplication of bd_sectors
  * would overflow so it should be safe to perform here.
  */
 static uint64_t
 bd_disk_get_sectors(struct disk_devdesc *dev)
 {
 	bdinfo_t *bd;
 	struct disk_devdesc disk;
 	uint64_t size;
 
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (0);
 
 	disk.dd.d_dev = dev->dd.d_dev;
 	disk.dd.d_unit = dev->dd.d_unit;
 	disk.d_slice = D_SLICENONE;
 	disk.d_partition = D_PARTNONE;
 	disk.d_offset = 0;
 
 	size = bd->bd_sectors * bd->bd_sectorsize;
 	if (disk_open(&disk, size, bd->bd_sectorsize) == 0) {
 		(void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size);
 		disk_close(&disk);
 	}
 	return (size / bd->bd_sectorsize);
 }
 
 /*
  * Attempt to open the disk described by (dev) for use by (f).
  *
  * Note that the philosophy here is "give them exactly what
  * they ask for".  This is necessary because being too "smart"
  * about what the user might want leads to complications.
  * (eg. given no slice or partition value, with a disk that is
  *  sliced - are they after the first BSD slice, or the DOS
  *  slice before it?)
  */
 static int
 bd_open(struct open_file *f, ...)
 {
 	bdinfo_t *bd;
 	struct disk_devdesc *dev;
 	va_list ap;
 	int rc;
 
 	TSENTER();
 
 	va_start(ap, f);
 	dev = va_arg(ap, struct disk_devdesc *);
 	va_end(ap);
 
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (EIO);
 
 	if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
 		if (!bd_int13probe(bd))
 			return (EIO);
 		if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
 			return (EIO);
 	}
 	if (bd->bd_bcache == NULL)
 	    bd->bd_bcache = bcache_allocate();
 
 	if (bd->bd_open == 0)
 		bd->bd_sectors = bd_disk_get_sectors(dev);
 	bd->bd_open++;
 
 	rc = 0;
 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
 		rc = disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
 		    bd->bd_sectorsize);
 		if (rc != 0) {
 			bd->bd_open--;
 			if (bd->bd_open == 0) {
 				bcache_free(bd->bd_bcache);
 				bd->bd_bcache = NULL;
 			}
 		}
 	}
 	TSEXIT();
 	return (rc);
 }
 
 static int
 bd_close(struct open_file *f)
 {
 	struct disk_devdesc *dev;
 	bdinfo_t *bd;
 	int rc = 0;
 
 	dev = (struct disk_devdesc *)f->f_devdata;
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (EIO);
 
 	bd->bd_open--;
 	if (bd->bd_open == 0) {
 	    bcache_free(bd->bd_bcache);
 	    bd->bd_bcache = NULL;
 	}
 	if (dev->dd.d_dev->dv_type == DEVT_DISK)
 		rc = disk_close(dev);
 	return (rc);
 }
 
 static int
 bd_ioctl(struct open_file *f, u_long cmd, void *data)
 {
 	bdinfo_t *bd;
 	struct disk_devdesc *dev;
 	int rc;
 
 	dev = (struct disk_devdesc *)f->f_devdata;
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (EIO);
 
 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
 		rc = disk_ioctl(dev, cmd, data);
 		if (rc != ENOTTY)
 			return (rc);
 	}
 
 	switch (cmd) {
 	case DIOCGSECTORSIZE:
 		*(uint32_t *)data = bd->bd_sectorsize;
 		break;
 	case DIOCGMEDIASIZE:
 		*(uint64_t *)data = bd->bd_sectors * bd->bd_sectorsize;
 		break;
 	default:
 		return (ENOTTY);
 	}
 	return (0);
 }
 
 static int
 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
     char *buf, size_t *rsize)
 {
 	bdinfo_t *bd;
 	struct bcache_devdata bcd;
 	struct disk_devdesc *dev;
 	daddr_t offset;
 
 	dev = (struct disk_devdesc *)devdata;
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (EINVAL);
 
 	bcd.dv_strategy = bd_realstrategy;
 	bcd.dv_devdata = devdata;
 	bcd.dv_cache = bd->bd_bcache;
 
 	offset = 0;
 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
 
 		offset = dev->d_offset * bd->bd_sectorsize;
 		offset /= BIOSDISK_SECSIZE;
 	}
 	return (bcache_strategy(&bcd, rw, dblk + offset, size,
 	    buf, rsize));
 }
 
 static int
 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
     char *buf, size_t *rsize)
 {
 	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
 	bdinfo_t *bd;
 	uint64_t disk_blocks, offset, d_offset;
 	size_t blks, blkoff, bsize, bio_size, rest;
 	caddr_t bbuf = NULL;
 	int rc;
 
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL || (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
 		return (EIO);
 
 	/*
 	 * First make sure the IO size is a multiple of 512 bytes. While we do
 	 * process partial reads below, the strategy mechanism is built
 	 * assuming IO is a multiple of 512B blocks. If the request is not
 	 * a multiple of 512B blocks, it has to be some sort of bug.
 	 */
 	if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
 		printf("bd_strategy: %d bytes I/O not multiple of %d\n",
 		    size, BIOSDISK_SECSIZE);
 		return (EIO);
 	}
 
 	DPRINTF("open_disk %p", dev);
 
 	offset = dblk * BIOSDISK_SECSIZE;
 	dblk = offset / bd->bd_sectorsize;
 	blkoff = offset % bd->bd_sectorsize;
 
 	/*
 	 * Check the value of the size argument. We do have quite small
 	 * heap (64MB), but we do not know good upper limit, so we check against
 	 * INT_MAX here. This will also protect us against possible overflows
 	 * while translating block count to bytes.
 	 */
 	if (size > INT_MAX) {
 		DPRINTF("too large I/O: %zu bytes", size);
 		return (EIO);
 	}
 
 	blks = size / bd->bd_sectorsize;
 	if (blks == 0 || (size % bd->bd_sectorsize) != 0)
 		blks++;
 
 	if (dblk > dblk + blks)
 		return (EIO);
 
 	if (rsize)
 		*rsize = 0;
 
 	/*
 	 * Get disk blocks, this value is either for whole disk or for
 	 * partition.
 	 */
 	d_offset = 0;
 	disk_blocks = 0;
 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
 		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
 			/* DIOCGMEDIASIZE does return bytes. */
 			disk_blocks /= bd->bd_sectorsize;
 		}
 		d_offset = dev->d_offset;
 	}
 	if (disk_blocks == 0)
-		disk_blocks = bd->bd_sectors - d_offset;
+		disk_blocks = bd->bd_sectors * (bd->bd_sectorsize /
+		    BIOSDISK_SECSIZE) - d_offset;
 
 	/* Validate source block address. */
 	if (dblk < d_offset || dblk >= d_offset + disk_blocks)
 		return (EIO);
 
 	/*
 	 * Truncate if we are crossing disk or partition end.
 	 */
 	if (dblk + blks >= d_offset + disk_blocks) {
 		blks = d_offset + disk_blocks - dblk;
 		size = blks * bd->bd_sectorsize;
 		DPRINTF("short I/O %d", blks);
 	}
 
 	bio_size = min(BIO_BUFFER_SIZE, size);
 	while (bio_size > bd->bd_sectorsize) {
 		bbuf = bio_alloc(bio_size);
 		if (bbuf != NULL)
 			break;
 		bio_size -= bd->bd_sectorsize;
 	}
 	if (bbuf == NULL) {
 		bio_size = V86_IO_BUFFER_SIZE;
 		if (bio_size / bd->bd_sectorsize == 0)
 			panic("BUG: Real mode buffer is too small");
 
 		/* Use alternate 4k buffer */
 		bbuf = PTOV(V86_IO_BUFFER);
 	}
 	rest = size;
 	rc = 0;
 	while (blks > 0) {
 		int x = min(blks, bio_size / bd->bd_sectorsize);
 
 		switch (rw & F_MASK) {
 		case F_READ:
 			DPRINTF("read %d from %lld to %p", x, dblk, buf);
 			bsize = bd->bd_sectorsize * x - blkoff;
 			if (rest < bsize)
 				bsize = rest;
 
 			if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD)) != 0) {
 				rc = EIO;
 				goto error;
 			}
 
 			bcopy(bbuf + blkoff, buf, bsize);
 			break;
 		case F_WRITE :
 			DPRINTF("write %d from %lld to %p", x, dblk, buf);
 			if (blkoff != 0) {
 				/*
 				 * We got offset to sector, read 1 sector to
 				 * bbuf.
 				 */
 				x = 1;
 				bsize = bd->bd_sectorsize - blkoff;
 				bsize = min(bsize, rest);
 				rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
 			} else if (rest < bd->bd_sectorsize) {
 				/*
 				 * The remaining block is not full
 				 * sector. Read 1 sector to bbuf.
 				 */
 				x = 1;
 				bsize = rest;
 				rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
 			} else {
 				/* We can write full sector(s). */
 				bsize = bd->bd_sectorsize * x;
 			}
 			/*
 			 * Put your Data In, Put your Data out,
 			 * Put your Data In, and shake it all about
 			 */
 			bcopy(buf, bbuf + blkoff, bsize);
 			if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_WR)) != 0) {
 				rc = EIO;
 				goto error;
 			}
 
 			break;
 		default:
 			/* DO NOTHING */
 			rc = EROFS;
 			goto error;
 		}
 
 		blkoff = 0;
 		buf += bsize;
 		rest -= bsize;
 		blks -= x;
 		dblk += x;
 	}
 
 	if (rsize != NULL)
 		*rsize = size;
 error:
 	if (bbuf != PTOV(V86_IO_BUFFER))
 		bio_free(bbuf, bio_size);
 	return (rc);
 }
 
 static int
 bd_edd_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
     int dowrite)
 {
 	static struct edd_packet packet;
 
 	TSENTER();
 
 	packet.len = sizeof(struct edd_packet);
 	packet.count = blks;
 	packet.off = VTOPOFF(dest);
 	packet.seg = VTOPSEG(dest);
 	packet.lba = dblk;
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	if (dowrite == BD_WR)
 		v86.eax = CMD_WRITE_LBA; /* maybe Write with verify 0x4302? */
 	else
 		v86.eax = CMD_READ_LBA;
 	v86.edx = bd->bd_unit;
 	v86.ds = VTOPSEG(&packet);
 	v86.esi = VTOPOFF(&packet);
 	v86int();
 	if (V86_CY(v86.efl))
 		return (v86.eax >> 8);
 
 	TSEXIT();
 	return (0);
 }
 
 static int
 bd_chs_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
     int dowrite)
 {
 	uint32_t x, bpc, cyl, hd, sec;
 
 	TSENTER();
 
 	bpc = bd->bd_sec * bd->bd_hds;	/* blocks per cylinder */
 	x = dblk;
 	cyl = x / bpc;			/* block # / blocks per cylinder */
 	x %= bpc;				/* block offset into cylinder */
 	hd = x / bd->bd_sec;		/* offset / blocks per track */
 	sec = x % bd->bd_sec;		/* offset into track */
 
 	/* correct sector number for 1-based BIOS numbering */
 	sec++;
 
 	if (cyl > 1023) {
 		/* CHS doesn't support cylinders > 1023. */
 		return (1);
 	}
 
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	if (dowrite == BD_WR)
 		v86.eax = CMD_WRITE_CHS | blks;
 	else
 		v86.eax = CMD_READ_CHS | blks;
 	v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
 	v86.edx = (hd << 8) | bd->bd_unit;
 	v86.es = VTOPSEG(dest);
 	v86.ebx = VTOPOFF(dest);
 	v86int();
 	if (V86_CY(v86.efl))
 		return (v86.eax >> 8);
 	TSEXIT();
 	return (0);
 }
 
 static void
 bd_io_workaround(bdinfo_t *bd)
 {
 	uint8_t buf[8 * 1024];
 
 	bd_edd_io(bd, 0xffffffff, 1, (caddr_t)buf, BD_RD);
 }
 
 static int
 bd_io(struct disk_devdesc *dev, bdinfo_t *bd, daddr_t dblk, int blks,
     caddr_t dest, int dowrite)
 {
 	int result, retry;
 
 	TSENTER();
 
 	/* Just in case some idiot actually tries to read/write -1 blocks... */
 	if (blks < 0)
 		return (-1);
 
 	/*
 	 * Workaround for a problem with some HP ProLiant BIOS failing to work
 	 * out the boot disk after installation. hrs and kuriyama discovered
 	 * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
 	 * discovered that an int13h call seems to cause a buffer overrun in
 	 * the bios. The problem is alleviated by doing an extra read before
 	 * the buggy read. It is not immediately known whether other models
 	 * are similarly affected.
 	 * Loop retrying the operation a couple of times.  The BIOS
 	 * may also retry.
 	 */
 	if (dowrite == BD_RD && dblk >= 0x100000000)
 		bd_io_workaround(bd);
 	for (retry = 0; retry < 3; retry++) {
 		if (bd->bd_flags & BD_MODEEDD)
 			result = bd_edd_io(bd, dblk, blks, dest, dowrite);
 		else
 			result = bd_chs_io(bd, dblk, blks, dest, dowrite);
 
 		if (result == 0) {
 			if (bd->bd_flags & BD_NO_MEDIA)
 				bd->bd_flags &= ~BD_NO_MEDIA;
 			break;
 		}
 
 		bd_reset_disk(bd->bd_unit);
 
 		/*
 		 * Error codes:
 		 * 20h	controller failure
 		 * 31h	no media in drive (IBM/MS INT 13 extensions)
 		 * 80h	no media in drive, VMWare (Fusion)
 		 * There is no reason to repeat the IO with errors above.
 		 */
 		if (result == 0x20 || result == 0x31 || result == 0x80) {
 			bd->bd_flags |= BD_NO_MEDIA;
 			break;
 		}
 	}
 
 	if (result != 0 && (bd->bd_flags & BD_NO_MEDIA) == 0) {
 		if (dowrite == BD_WR) {
 			printf("%s%d: Write %d sector(s) from %p (0x%x) "
 			    "to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
 			    dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
 			    result);
 		} else {
 			printf("%s%d: Read %d sector(s) from %lld to %p "
 			    "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
 			    dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
 			    result);
 		}
 	}
 
 	TSEXIT();
 
 	return (result);
 }
 
 /*
  * Return the BIOS geometry of a given "fixed drive" in a format
  * suitable for the legacy bootinfo structure.  Since the kernel is
  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
  * prefer to get the information directly, rather than rely on being
  * able to put it together from information already maintained for
  * different purposes and for a probably different number of drives.
  *
  * For valid drives, the geometry is expected in the format (31..0)
  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
  * indicated by returning the geometry of a "1.2M" PC-format floppy
  * disk.  And, incidentally, what is returned is not the geometry as
  * such but the highest valid cylinder, head, and sector numbers.
  */
 uint32_t
 bd_getbigeom(int bunit)
 {
 
 	v86.ctl = V86_FLAGS;
 	v86.addr = DISK_BIOS;
 	v86.eax = CMD_READ_PARAM;
 	v86.edx = 0x80 + bunit;
 	v86int();
 	if (V86_CY(v86.efl))
 		return (0x4f010f);
 	return (((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
 	    (v86.edx & 0xff00) | (v86.ecx & 0x3f));
 }
 
 /*
  * Return a suitable dev_t value for (dev).
  *
  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
  */
 int
 bd_getdev(struct i386_devdesc *d)
 {
 	struct disk_devdesc *dev;
 	bdinfo_t *bd;
 	int	biosdev;
 	int	major;
 	int	rootdev;
 	char	*nip, *cp;
 	int	i, unit, slice, partition;
 
 	/* XXX: Assume partition 'a'. */
 	slice = 0;
 	partition = 0;
 
 	dev = (struct disk_devdesc *)d;
 	bd = bd_get_bdinfo(&dev->dd);
 	if (bd == NULL)
 		return (-1);
 
 	biosdev = bd_unit2bios(d);
 	DPRINTF("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
 	if (biosdev == -1)			/* not a BIOS device */
 		return (-1);
 
 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
 		if (disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
 		    bd->bd_sectorsize) != 0)	/* oops, not a viable device */
 			return (-1);
 		else
 			disk_close(dev);
 		slice = dev->d_slice + 1;
 		partition = dev->d_partition;
 	}
 
 	if (biosdev < 0x80) {
 		/* floppy (or emulated floppy) or ATAPI device */
 		if (bd->bd_type == DT_ATAPI) {
 			/* is an ATAPI disk */
 			major = WFDMAJOR;
 		} else {
 			/* is a floppy disk */
 			major = FDMAJOR;
 		}
 	} else {
 		/* assume an IDE disk */
 		major = WDMAJOR;
 	}
 	/* default root disk unit number */
 	unit = biosdev & 0x7f;
 
 	if (dev->dd.d_dev->dv_type == DEVT_CD) {
 		/*
 		 * XXX: Need to examine device spec here to figure out if
 		 * SCSI or ATAPI.  No idea on how to figure out device number.
 		 * All we can really pass to the kernel is what bus and device
 		 * on which bus we were booted from, which dev_t isn't well
 		 * suited to since those number don't match to unit numbers
 		 * very well.  We may just need to engage in a hack where
 		 * we pass -C to the boot args if we are the boot device.
 		 */
 		major = ACDMAJOR;
 		unit = 0;       /* XXX */
 	}
 
 	/* XXX a better kludge to set the root disk unit number */
 	if ((nip = getenv("root_disk_unit")) != NULL) {
 		i = strtol(nip, &cp, 0);
 		/* check for parse error */
 		if ((cp != nip) && (*cp == 0))
 			unit = i;
 	}
 
 	rootdev = MAKEBOOTDEV(major, slice, unit, partition);
 	DPRINTF("dev is 0x%x\n", rootdev);
 	return (rootdev);
 }