Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 21 Apr 2019 03:30:47 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r346475 - in stable/11/stand: common i386/libi386 i386/loader
Message-ID:  <201904210330.x3L3UlHt015693@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Sun Apr 21 03:30:47 2019
New Revision: 346475
URL: https://svnweb.freebsd.org/changeset/base/346475

Log:
  MFC r339658, r339959, r340047, r340049, r340215
  
  r339658:
  loader: biosdisk interface should be able to cope with 4k sectors
  
  The 4kn support in current bios specific biosdisk.c is broken, as the code
  is only implementing the support for the 512B sector size.
  
  This work is building the support for custom size sectors, we still do assume
  the requested data to be multiple of 512B blocks and we only do address the
  biosdisk.c interface here.
  
  For reference, see also:
  https://www.illumos.org/issues/8303
  https://www.illumos.org/rb/r/547
  
  As the GELI is moved above biosdisk "layer", the GELI should just work
  
  r339959:
  loader: issue edd probe before legacy ah=08 and detect no media
  
  while probing for drives, use int13 extended info before standard one and
  provide workaround for case we are not getting needed information in case
  of floppy drive.
  
  In case of INT13 errors, there are (at least) 3 error codes appearing in case
  of missin media - 20h, 31h and 80h. Flag the no media and do not print an
  error.
  
  r340047:
  loader: do not probe floppy devices for zfs
  
  The subject is telling it all.
  
  r340049:
  loader: biosdisk should check if the media is present
  
  The bd_print/bd_open/bd_strategy need to make sure the device does have
  media, before getting into performing IO operations. Some systems can
  hung if the device without a media is accessed.
  
  r340215:
  loader: always set media size from partition.
  
  The disk access is validated by using partition table definitions, therefore
  we have no need for if statements, just set the disk size.
  
  Of course the partition table itself may be incorrect/inconsistent, but if
  so, we are in trouble anyhow.

Modified:
  stable/11/stand/common/disk.c
  stable/11/stand/common/part.c
  stable/11/stand/i386/libi386/biosdisk.c
  stable/11/stand/i386/loader/main.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/stand/common/disk.c
==============================================================================
--- stable/11/stand/common/disk.c	Sun Apr 21 03:27:12 2019	(r346474)
+++ stable/11/stand/common/disk.c	Sun Apr 21 03:30:47 2019	(r346475)
@@ -265,9 +265,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize
 		rc = ENXIO;
 		goto out;
 	}
-	if (mediasize > od->mediasize) {
-		od->mediasize = mediasize;
-	}
+	od->mediasize = mediasize;
 
 	if (ptable_gettype(od->table) == PTABLE_BSD &&
 	    partition >= 0) {

Modified: stable/11/stand/common/part.c
==============================================================================
--- stable/11/stand/common/part.c	Sun Apr 21 03:27:12 2019	(r346474)
+++ stable/11/stand/common/part.c	Sun Apr 21 03:30:47 2019	(r346475)
@@ -323,8 +323,7 @@ ptable_gptread(struct ptable *table, void *dev, diskre
 	 * Note, this is still not a foolproof way to get disk's size. For
 	 * example, an image file can be truncated when copied to smaller media.
 	 */
-	if (hdr.hdr_lba_alt + 1 > table->sectors)
-		table->sectors = hdr.hdr_lba_alt + 1;
+	table->sectors = hdr.hdr_lba_alt + 1;
 
 	for (i = 0; i < size / hdr.hdr_entsz; i++) {
 		ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);

Modified: stable/11/stand/i386/libi386/biosdisk.c
==============================================================================
--- stable/11/stand/i386/libi386/biosdisk.c	Sun Apr 21 03:27:12 2019	(r346474)
+++ stable/11/stand/i386/libi386/biosdisk.c	Sun Apr 21 03:30:47 2019	(r346475)
@@ -80,8 +80,10 @@ static struct bdinfo
 #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_NO_MEDIA	0x0008
 	int		bd_type;	/* BIOS 'drive type' (floppy only) */
 	uint16_t	bd_sectorsize;	/* Sector size */
 	uint64_t	bd_sectors;	/* Disk size */
@@ -187,60 +189,83 @@ bd_init(void)
 }
 
 /*
- * Try to detect a device supported by the legacy int13 BIOS
+ * Return EDD version or 0 if EDD is not supported on this drive.
  */
 static int
-bd_int13probe(struct bdinfo *bd)
+bd_check_extensions(int unit)
 {
-	struct edd_params params;
-	int ret = 1;	/* assume success */
+	/* Determine if we can use EDD with this device. */
+	v86.ctl = V86_FLAGS;
+	v86.addr = 0x13;
+	v86.eax = 0x4100;
+	v86.edx = unit;
+	v86.ebx = 0x55aa;
+	v86int();
 
+	if (V86_CY(v86.efl) ||			/* carry set */
+	    (v86.ebx & 0xffff) != 0xaa55)	/* 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 = 0x13;
+	v86.eax = 0;
+	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 = 0x13;
 	v86.eax = 0x800;
 	v86.edx = bd->bd_unit;
 	v86int();
 
-	/* Don't error out if we get bad sector number, try EDD as well */
-	if (V86_CY(v86.efl) ||	/* carry set */
-	    (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f))	/* unit # bad */
-		return (0);	/* skip device */
+	if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
+		return ((v86.eax & 0xff00) >> 8);
 
-	if ((v86.ecx & 0x3f) == 0)	/* absurd sector number */
-		ret = 0;	/* set error */
+	/* return custom error on absurd sector number */
+	if ((v86.ecx & 0x3f) == 0)
+		return (0x60);
 
-	/* Convert max cyl # -> # of cylinders */
 	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 & 0xff;
-	bd->bd_flags |= BD_MODEINT13;
+	bd->bd_type = v86.ebx;
+	bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
 
-	/* Calculate sectors count from the geometry */
-	bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec;
-	bd->bd_sectorsize = BIOSDISK_SECSIZE;
-	DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
-	    bd->bd_hds, bd->bd_sec);
+	return (0);
+}
 
-	/* Determine if we can use EDD with this device. */
-	v86.ctl = V86_FLAGS;
-	v86.addr = 0x13;
-	v86.eax = 0x4100;
-	v86.edx = bd->bd_unit;
-	v86.ebx = 0x55aa;
-	v86int();
-	if (V86_CY(v86.efl) ||	/* carry set */
-	    (v86.ebx & 0xffff) != 0xaa55 || /* signature */
-	    (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
-		return (ret);	/* return code from int13 AH=08 */
+/*
+ * Read EDD info. Return 0 on success, error otherwise.
+ */
+static int
+bd_get_diskinfo_ext(struct bdinfo *bd)
+{
+	struct edd_params params;
+	uint64_t total;
 
-	/* EDD supported */
-	bd->bd_flags |= BD_MODEEDD1;
-	if ((v86.eax & 0xff00) >= 0x3000)
-		bd->bd_flags |= BD_MODEEDD3;
 	/* Get disk params */
-	params.len = sizeof(struct edd_params);
+	bzero(&params, sizeof(params));
+	params.len = sizeof(params);
 	v86.ctl = V86_FLAGS;
 	v86.addr = 0x13;
 	v86.eax = 0x4800;
@@ -248,36 +273,120 @@ bd_int13probe(struct bdinfo *bd)
 	v86.ds = VTOPSEG(&params);
 	v86.esi = VTOPOFF(&params);
 	v86int();
-	if (!V86_CY(v86.efl)) {
-		uint64_t total;
 
-		/*
-		 * Sector size must be a multiple of 512 bytes.
-		 * An alternate test would be to check power of 2,
-		 * powerof2(params.sector_size).
-		 */
-		if (params.sector_size % BIOSDISK_SECSIZE)
-			bd->bd_sectorsize = BIOSDISK_SECSIZE;
-		else
-			bd->bd_sectorsize = params.sector_size;
+	if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
+		return ((v86.eax & 0xff00) >> 8);
 
-		total = bd->bd_sectorsize * params.sectors;
-		if (params.sectors != 0) {
-			/* Only update if we did not overflow. */
-			if (total > params.sectors)
-				bd->bd_sectors = params.sectors;
-		}
+	/*
+	 * Sector size must be a multiple of 512 bytes.
+	 * An alternate test would be to check power of 2,
+	 * powerof2(params.sector_size).
+	 * 4K is largest read buffer we can use at this time.
+	 */
+	if (params.sector_size >= 512 &&
+	    params.sector_size <= 4096 &&
+	    (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;
-		if (total > 0 && bd->bd_sectors > total)
-			bd->bd_sectors = total;
+	}
+	bd->bd_sectors = total;
 
-		ret = 1;
+	return (0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+	int edd;
+	int ret;
+
+	bd->bd_flags &= ~BD_NO_MEDIA;
+
+	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 */
+	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 = 0x13;
+		v86.eax = 0x1500;
+		v86.edx = bd->bd_unit;
+		v86int();
+		if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
+			return (0);
 	}
-	DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u",
-	    bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize);
-	return (ret);
+
+	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_type = 4;
+		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 {
+			printf("Can not get information about %s unit %#x\n",
+			    biosdisk.dv_name, bd->bd_unit);
+			return (0);
+		}
+	}
+
+	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;
+
+	DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
+	    bd->bd_hds, bd->bd_sec);
+
+	return (1);
 }
 
 /*
@@ -299,14 +408,19 @@ bd_print(int verbose)
 
 	for (i = 0; i < nbdinfo; i++) {
 		snprintf(line, sizeof(line),
-		    "    disk%d:   BIOS drive %c (%ju X %u):\n", i,
+		    "    disk%d:   BIOS drive %c (%s%ju X %u):\n", i,
 		    (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
 		    ('C' + bdinfo[i].bd_unit - 0x80),
+		    (bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
+		    "no media, " : "",
 		    (uintmax_t)bdinfo[i].bd_sectors,
 		    bdinfo[i].bd_sectorsize);
 		if ((ret = pager_output(line)) != 0)
 			break;
 
+		if ((bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
+			continue;
+
 		dev.dd.d_dev = &biosdisk;
 		dev.dd.d_unit = i;
 		dev.d_slice = -1;
@@ -325,6 +439,33 @@ bd_print(int 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)
+{
+	struct disk_devdesc disk;
+	uint64_t size;
+
+	disk.dd.d_dev = dev->dd.d_dev;
+	disk.dd.d_unit = dev->dd.d_unit;
+	disk.d_slice = -1;
+	disk.d_partition = -1;
+	disk.d_offset = 0;
+
+	size = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
+	if (disk_open(&disk, size, BD(dev).bd_sectorsize) == 0) {
+		(void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size);
+		disk_close(&disk);
+	}
+	return (size / BD(dev).bd_sectorsize);
+}
+
+/*
  * Attempt to open the disk described by (dev) for use by (f).
  *
  * Note that the philosophy here is "give them exactly what
@@ -338,9 +479,7 @@ static int
 bd_open(struct open_file *f, ...)
 {
 	struct disk_devdesc *dev;
-	struct disk_devdesc disk;
 	va_list ap;
-	uint64_t size;
 	int rc;
 
 	va_start(ap, f);
@@ -349,34 +488,20 @@ bd_open(struct open_file *f, ...)
 
 	if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
 		return (EIO);
-	BD(dev).bd_open++;
+
+	if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
+		if (!bd_int13probe(&BD(dev)))
+			return (EIO);
+		if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
+			return (EIO);
+	}
 	if (BD(dev).bd_bcache == NULL)
 	    BD(dev).bd_bcache = bcache_allocate();
 
-	/*
-	 * 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 mulitplication of bd_sectors
-	 * would overflow so it should be safe to perform here.
-	 */
-	disk.dd.d_dev = dev->dd.d_dev;
-	disk.dd.d_unit = dev->dd.d_unit;
-	disk.d_slice = -1;
-	disk.d_partition = -1;
-	disk.d_offset = 0;
+	if (BD(dev).bd_open == 0)
+		BD(dev).bd_sectors = bd_disk_get_sectors(dev);
+	BD(dev).bd_open++;
 
-	if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
-	    BD(dev).bd_sectorsize) == 0) {
-
-		if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
-			size /= BD(dev).bd_sectorsize;
-			if (size > BD(dev).bd_sectors)
-				BD(dev).bd_sectors = size;
-		}
-		disk_close(&disk);
-	}
-
 	rc = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
 	    BD(dev).bd_sectorsize);
 	if (rc != 0) {
@@ -448,16 +573,32 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, s
     char *buf, size_t *rsize)
 {
 	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
-	uint64_t		disk_blocks;
-	int			blks, rc;
+	uint64_t disk_blocks, offset;
+	size_t blks, blkoff, bsize, rest;
+	caddr_t bbuf;
+	int rc;
 
-	if (size % BD(dev).bd_sectorsize) {
-		panic("bd_strategy: %d bytes I/O not multiple of block size",
-		    size);
+	if ((BD(dev).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);
 	}
 
 	DEBUG("open_disk %p", dev);
 
+	offset = dblk * BIOSDISK_SECSIZE;
+	dblk = offset / BD(dev).bd_sectorsize;
+	blkoff = offset % BD(dev).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
@@ -465,11 +606,14 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, s
 	 * while translating block count to bytes.
 	 */
 	if (size > INT_MAX) {
-		DEBUG("too large read: %zu bytes", size);
+		DEBUG("too large I/O: %zu bytes", size);
 		return (EIO);
 	}
 
 	blks = size / BD(dev).bd_sectorsize;
+	if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0)
+		blks++;
+
 	if (dblk > dblk + blks)
 		return (EIO);
 
@@ -481,7 +625,7 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, s
 	 * partition.
 	 */
 	if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
-		/* DIOCGMEDIASIZE returns bytes. */
+		/* DIOCGMEDIASIZE does return bytes. */
 		disk_blocks /= BD(dev).bd_sectorsize;
 	} else {
 		/* We should not get here. Just try to survive. */
@@ -498,36 +642,75 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, s
 	if (dblk + blks >= dev->d_offset + disk_blocks) {
 		blks = dev->d_offset + disk_blocks - dblk;
 		size = blks * BD(dev).bd_sectorsize;
-		DEBUG("short read %d", blks);
+		DEBUG("short I/O %d", blks);
 	}
 
-	switch (rw & F_MASK) {
-	case F_READ:
-		DEBUG("read %d from %lld to %p", blks, dblk, buf);
+	if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0)
+		panic("BUG: Real mode buffer is too small\n");
 
-		if (blks && (rc = bd_io(dev, dblk, blks, buf, BD_RD))) {
-			/* Filter out floppy controller errors */
-			if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
-				printf("read %d from %lld to %p, error: 0x%x\n",
-				    blks, dblk, buf, rc);
+	bbuf = PTOV(V86_IO_BUFFER);
+	rest = size;
+
+	while (blks > 0) {
+		int x = min(blks, V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize);
+
+		switch (rw & F_MASK) {
+		case F_READ:
+			DEBUG("read %d from %lld to %p", x, dblk, buf);
+			bsize = BD(dev).bd_sectorsize * x - blkoff;
+			if (rest < bsize)
+				bsize = rest;
+
+			if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0)
+				return (EIO);
+
+			bcopy(bbuf + blkoff, buf, bsize);
+			break;
+		case F_WRITE :
+			DEBUG("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(dev).bd_sectorsize - blkoff;
+				bsize = min(bsize, rest);
+				rc = bd_io(dev, dblk, x, bbuf, BD_RD);
+			} else if (rest < BD(dev).bd_sectorsize) {
+				/*
+				 * The remaining block is not full
+				 * sector. Read 1 sector to bbuf.
+				 */
+				x = 1;
+				bsize = rest;
+				rc = bd_io(dev, dblk, x, bbuf, BD_RD);
+			} else {
+				/* We can write full sector(s). */
+				bsize = BD(dev).bd_sectorsize * x;
 			}
-			return (EIO);
-		}
-		break;
-	case F_WRITE :
-		DEBUG("write %d from %lld to %p", blks, dblk, buf);
+			/*
+			 * 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, dblk, x, bbuf, BD_WR)) != 0)
+				return (EIO);
 
-		if (blks && bd_io(dev, dblk, blks, buf, BD_WR)) {
-			DEBUG("write error");
-			return (EIO);
+			break;
+		default:
+			/* DO NOTHING */
+			return (EROFS);
 		}
-		break;
-	default:
-		/* DO NOTHING */
-		return (EROFS);
+
+		blkoff = 0;
+		buf += bsize;
+		rest -= bsize;
+		blks -= x;
+		dblk += x;
 	}
 
-	if (rsize)
+	if (rsize != NULL)
 		*rsize = size;
 	return (0);
 }
@@ -604,21 +787,16 @@ bd_io_workaround(struct disk_devdesc *dev)
 	bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD);
 }
 
-
 static int
 bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
     int dowrite)
 {
-	u_int	x, sec, result, resid, retry, maxfer;
-	caddr_t	p, xp, bbuf;
-    
+	int result, retry;
+
 	/* Just in case some idiot actually tries to read/write -1 blocks... */
 	if (blks < 0)
 		return (-1);
 
-	resid = blks;
-	p = dest;
-
 	/*
 	 * Workaround for a problem with some HP ProLiant BIOS failing to work
 	 * out the boot disk after installation. hrs and kuriyama discovered
@@ -627,91 +805,53 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks
 	 * 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(dev);
+	for (retry = 0; retry < 3; retry++) {
+		if (BD(dev).bd_flags & BD_MODEEDD)
+			result = bd_edd_io(dev, dblk, blks, dest, dowrite);
+		else
+			result = bd_chs_io(dev, dblk, blks, dest, dowrite);
 
-	/* Decide whether we have to bounce */
-	if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
-	    (VTOP(dest) >> 16) !=
-	    (VTOP(dest + blks * BD(dev).bd_sectorsize) >> 16))) {
+		if (result == 0) {
+			if (BD(dev).bd_flags & BD_NO_MEDIA)
+				BD(dev).bd_flags &= ~BD_NO_MEDIA;
+			break;
+		}
 
-		/* 
-		 * There is a 64k physical boundary somewhere in the
-		 * destination buffer, or the destination buffer is above
-		 * first 1MB of physical memory so we have to arrange a
-		 * suitable bounce buffer.  Allocate a buffer twice as large
-		 * as we need to.  Use the bottom half unless there is a break
-		 * there, in which case we use the top half.
-		 */
-		x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
-		x = min(x, (unsigned)blks);
-		bbuf = PTOV(V86_IO_BUFFER);
-		maxfer = x;	/* limit transfers to bounce region size */
-	} else {
-		bbuf = NULL;
-		maxfer = 0;
-	}
-    
-	while (resid > 0) {
-		/*
-		 * Play it safe and don't cross track boundaries.
-		 * (XXX this is probably unnecessary)
-		 */
-		sec = dblk % BD(dev).bd_sec;	/* offset into track */
-		x = min(BD(dev).bd_sec - sec, resid);
-		if (maxfer > 0)
-			x = min(x, maxfer);		/* fit bounce buffer */
+		bd_reset_disk(BD(dev).bd_unit);
 
-		/* where do we transfer to? */
-		xp = bbuf == NULL ? p : bbuf;
-
 		/*
-		 * Put your Data In, Put your Data out,
-		 * Put your Data In, and shake it all about 
+		 * 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 (dowrite == BD_WR && bbuf != NULL)
-			bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
-
-		/*
-		 * Loop retrying the operation a couple of times.  The BIOS
-		 * may also retry.
-		 */
-		for (retry = 0; retry < 3; retry++) {
-			/* if retrying, reset the drive */
-			if (retry > 0) {
-				v86.ctl = V86_FLAGS;
-				v86.addr = 0x13;
-				v86.eax = 0;
-				v86.edx = BD(dev).bd_unit;
-				v86int();
-			}
-
-			if (BD(dev).bd_flags & BD_MODEEDD1)
-				result = bd_edd_io(dev, dblk, x, xp, dowrite);
-			else
-				result = bd_chs_io(dev, dblk, x, xp, dowrite);
-			if (result == 0)
-				break;
+		if (result == 0x20 || result == 0x31 || result == 0x80) {
+			BD(dev).bd_flags |= BD_NO_MEDIA;
+			break;
 		}
+	}
 
-		if (dowrite == BD_WR)
-			DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
-			    p, VTOP(p), dblk, result ? "failed" : "ok");
-		else
-			DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
-			    dblk, p, VTOP(p), result ? "failed" : "ok");
-		if (result) {
-			return (result);
+	if (result != 0 && (BD(dev).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);
 		}
-		if (dowrite == BD_RD && bbuf != NULL)
-			bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
-		p += (x * BD(dev).bd_sectorsize);
-		dblk += x;
-		resid -= x;
 	}
 
-	return (0);
+	return (result);
 }
 
 /*

Modified: stable/11/stand/i386/loader/main.c
==============================================================================
--- stable/11/stand/i386/loader/main.c	Sun Apr 21 03:27:12 2019	(r346474)
+++ stable/11/stand/i386/loader/main.c	Sun Apr 21 03:30:47 2019	(r346475)
@@ -408,6 +408,8 @@ i386_zfs_probe(void)
     for (unit = 0; unit < MAXBDDEV; unit++) {
 	if (bd_unit2bios(unit) == -1)
 	    break;
+	if (bd_unit2bios(unit) < 0x80)
+	    continue;
 	sprintf(devname, "disk%d:", unit);
 	zfs_probe_dev(devname, NULL);
     }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904210330.x3L3UlHt015693>