summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToomas Soome <tsoome@me.com>2017-06-03 01:58:24 +0300
committerDan McDonald <danmcd@joyent.com>2018-11-02 10:25:31 -0400
commitb75eb7e6b5e51c1d1a8023c478487c4f4542768f (patch)
treef384c4d1b6ad95d438a2afe090ad9eb4e69cb282
parent3602e97d70b31e6f0ae7e1d6fdcd52f4c0e0fbaf (diff)
downloadillumos-gate-b75eb7e6b5e51c1d1a8023c478487c4f4542768f.tar.gz
9840 loader: do not repeat int13 calls when we have no media
9841 loader: issue edd probe before legacy ah=08 Reviewed by: Yuri Pankov <yuripv@yuripv.net> Reviewed by: Andy Fiddaman <andy@omniosce.org> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/biosdisk.c278
1 files changed, 203 insertions, 75 deletions
diff --git a/usr/src/boot/sys/boot/i386/libi386/biosdisk.c b/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
index e286a7e29f..3db2144763 100644
--- a/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
+++ b/usr/src/boot/sys/boot/i386/libi386/biosdisk.c
@@ -79,8 +79,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 */
@@ -188,60 +190,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);
+}
+
+/*
+ * 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;
- /* 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 */
-
- /* 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;
@@ -249,36 +274,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;
+
+ return (0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ int edd;
+ int ret;
- ret = 1;
+ 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);
}
/*
@@ -300,14 +409,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;
@@ -350,6 +464,13 @@ bd_open(struct open_file *f, ...)
if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
return (EIO);
+
+ 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);
+ }
BD(dev).bd_open++;
if (BD(dev).bd_bcache == NULL)
BD(dev).bd_bcache = bcache_allocate();
@@ -454,6 +575,9 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
caddr_t bbuf;
int rc;
+ 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
@@ -684,29 +808,33 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
if (dowrite == BD_RD && dblk >= 0x100000000)
bd_io_workaround(dev);
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)
+ 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);
- if (result == 0)
+ if (result == 0) {
+ if (BD(dev).bd_flags & BD_NO_MEDIA)
+ BD(dev).bd_flags &= ~BD_NO_MEDIA;
+ break;
+ }
+
+ bd_reset_disk(BD(dev).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(dev).bd_flags |= BD_NO_MEDIA;
break;
+ }
}
- /*
- * 0x20 - Controller failure. This is common error when the
- * media is not present.
- */
- if (result != 0 && result != 0x20) {
+ 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,