From f6bd3d520f078d20e0ed562aaaece3bc89c2058c Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Thu, 4 May 2017 05:26:37 +0000 Subject: [PATCH] zfsboot: drvsize() may be unusable on some systems From user report, the errors are seen: error 1 error 1 gptzfsboot: error 1 lba 4294967288 gptzfsboot: error 1 lba 1 gptzfsboot: no ZFS pools located, can't boot The first two errors above are from issuing INT13 EAX=0x4800, meaning we need to check if EDD is available and use EAX=0x800 if not. For an workaround I'm using the similar idea as in biosdisk.c - first probe ah=8h, then check if we have EDD. Note we would like to see the correct disk size info, but we *may* get away with anything >64MB, so we could at least test 2 zfs pool labels on whole disk setup and not to freak out the INT13 interface. If we get away with initial disk probing, then we have partition sizes from the partition table and we should be able to complete the disk probing. Note: this update does not provide full fix to all errors, but we get the drvsize() errors removed. Reported by: Michael W. Lucas Reviewed by: julian Differential Revision: https://reviews.freebsd.org/D10591 --- sys/boot/i386/zfsboot/zfsboot.c | 56 +++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index e76e58fc80e..88b5231efe4 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include "lib.h" #include "rbx.h" #include "drv.h" +#include "edd.h" #include "util.h" #include "cons.h" #include "bootargs.h" @@ -125,6 +126,7 @@ static int parse_cmd(void); static void bios_getmem(void); void *malloc(size_t n); void free(void *ptr); +int main(void); void * malloc(size_t n) @@ -468,6 +470,56 @@ copy_dsk(struct dsk *dsk) return (newdsk); } +/* + * Get disk size from eax=0x800 and 0x4800. We need to probe both + * because 0x4800 may not be available and we would like to get more + * or less correct disk size - if it is possible at all. + * Note we do not really want to touch drv.c because that code is shared + * with boot2 and we can not afford to grow that code. + */ +static uint64_t +drvsize_ext(struct dsk *dskp) +{ + uint64_t size, tmp; + int cyl, hds, sec; + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = dskp->drive; + 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)(dskp->drive & 0x7f)) /* unit # bad */ + return (0); + + cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; + /* Convert max head # -> # of heads */ + hds = ((v86.edx & 0xff00) >> 8) + 1; + sec = v86.ecx & 0x3f; + + size = (uint64_t)cyl * hds * sec; + + /* Determine if we can use EDD with this device. */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4100; + v86.edx = dskp->drive; + v86.ebx = 0x55aa; + v86int(); + if (V86_CY(v86.efl) || /* carry set */ + (v86.ebx & 0xffff) != 0xaa55 || /* signature */ + (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) + return (size); + + tmp = drvsize(dskp); + if (tmp > size) + size = tmp; + + return (size); +} + /* * The "layered" ioctl to read disk/partition size. Unfortunately * the zfsboot case is hardest, because we do not have full software @@ -480,7 +532,7 @@ ldi_get_size(void *priv) uint64_t size = dskp->size; if (dskp->start == 0) - size = drvsize(dskp); + size = drvsize_ext(dskp); return (size * DEV_BSIZE); } @@ -515,7 +567,7 @@ probe_drive(struct dsk *dsk) * out the partition table and probe each slice/partition * in turn for a vdev or GELI encrypted vdev. */ - elba = drvsize(dsk); + elba = drvsize_ext(dsk); if (elba > 0) { elba--; }