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
This commit is contained in:
Toomas Soome 2017-05-04 05:26:37 +00:00
parent 8066a14a3c
commit f6bd3d520f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=317785

View File

@ -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--;
}