Add EFI ZFS boot support
This builds on the modular EFI loader support added r294060 adding a module to provide ZFS boot support on EFI systems. It should be noted that EFI uses a fixed size memory block for all allocations performed by the loader so it may be necessary to tune this size. For example when building an image which uses mfs_root e.g. mfsbsd, adding the following to /etc/make.conf would be needed to prevent EFI from running out of memory when loading the mfs_root image. EFI_STAGING_SIZE=128 Submitted by: Eric McCorkle MFC after: 2 weeks X-MFC-With: r293268 Sponsored by: Multiplay
This commit is contained in:
parent
6ca07079af
commit
7bd249ecf0
@ -10,8 +10,22 @@ PROG= boot1.sym
|
||||
INTERNALPROG=
|
||||
WARNS?= 6
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
# Disable warnings that are currently incompatible with the zfs boot code
|
||||
CWARNFLAGS.zfs_module.c += -Wno-array-bounds
|
||||
CWARNFLAGS.zfs_module.c += -Wno-cast-align
|
||||
CWARNFLAGS.zfs_module.c += -Wno-cast-qual
|
||||
CWARNFLAGS.zfs_module.c += -Wno-missing-prototypes
|
||||
CWARNFLAGS.zfs_module.c += -Wno-sign-compare
|
||||
CWARNFLAGS.zfs_module.c += -Wno-unused-parameter
|
||||
CWARNFLAGS.zfs_module.c += -Wno-unused-function
|
||||
.endif
|
||||
|
||||
# architecture-specific loader code
|
||||
SRCS= boot1.c self_reloc.c start.S ufs_module.c
|
||||
.if ${MK_ZFS} != "no"
|
||||
SRCS+= zfs_module.c
|
||||
.endif
|
||||
|
||||
CFLAGS+= -I.
|
||||
CFLAGS+= -I${.CURDIR}/../include
|
||||
@ -20,6 +34,12 @@ CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include
|
||||
CFLAGS+= -I${.CURDIR}/../../..
|
||||
CFLAGS+= -DEFI_UFS_BOOT
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
CFLAGS+= -I${.CURDIR}/../../zfs/
|
||||
CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs/
|
||||
CFLAGS+= -DEFI_ZFS_BOOT
|
||||
.endif
|
||||
|
||||
# Always add MI sources and REGULAR efi loader bits
|
||||
.PATH: ${.CURDIR}/../loader/arch/${MACHINE}
|
||||
.PATH: ${.CURDIR}/../loader
|
||||
|
@ -36,6 +36,9 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
static const boot_module_t *boot_modules[] =
|
||||
{
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
&zfs_module,
|
||||
#endif
|
||||
#ifdef EFI_UFS_BOOT
|
||||
&ufs_module
|
||||
#endif
|
||||
|
@ -97,6 +97,9 @@ typedef struct boot_module_t
|
||||
#ifdef EFI_UFS_BOOT
|
||||
extern const boot_module_t ufs_module;
|
||||
#endif
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
extern const boot_module_t zfs_module;
|
||||
#endif
|
||||
|
||||
/* Functions available to modules. */
|
||||
extern void add_device(dev_info_t **devinfop, dev_info_t *devinfo);
|
||||
|
199
sys/boot/efi/boot1/zfs_module.c
Normal file
199
sys/boot/efi/boot1/zfs_module.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Eric McCorkle
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <efi.h>
|
||||
|
||||
#include "boot_module.h"
|
||||
|
||||
#include "libzfs.h"
|
||||
#include "zfsimpl.c"
|
||||
|
||||
static dev_info_t *devices;
|
||||
|
||||
static int
|
||||
vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
|
||||
{
|
||||
dev_info_t *devinfo;
|
||||
off_t lba;
|
||||
EFI_STATUS status;
|
||||
|
||||
devinfo = (dev_info_t *)priv;
|
||||
lba = off / devinfo->dev->Media->BlockSize;
|
||||
|
||||
status = devinfo->dev->ReadBlocks(devinfo->dev,
|
||||
devinfo->dev->Media->MediaId, lba, bytes, buf);
|
||||
if (status != EFI_SUCCESS) {
|
||||
DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %lu, size: %d,"
|
||||
" status: %lu\n", devinfo->dev,
|
||||
devinfo->dev->Media->MediaId, lba, size,
|
||||
EFI_ERROR_CODE(status));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
probe(dev_info_t *dev)
|
||||
{
|
||||
spa_t *spa;
|
||||
dev_info_t *tdev;
|
||||
EFI_STATUS status;
|
||||
|
||||
/* ZFS consumes the dev on success so we need a copy. */
|
||||
if ((status = bs->AllocatePool(EfiLoaderData, sizeof(*dev),
|
||||
(void**)&tdev)) != EFI_SUCCESS) {
|
||||
DPRINTF("Failed to allocate tdev (%lu)\n",
|
||||
EFI_ERROR_CODE(status));
|
||||
return (status);
|
||||
}
|
||||
memcpy(tdev, dev, sizeof(*dev));
|
||||
|
||||
if (vdev_probe(vdev_read, tdev, &spa) != 0) {
|
||||
(void)bs->FreePool(tdev);
|
||||
return (EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
dev->devdata = spa;
|
||||
add_device(&devices, dev);
|
||||
|
||||
return (EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
try_load(dev_info_t *devinfo, const char *loader_path, void **bufp, size_t *bufsize)
|
||||
{
|
||||
spa_t *spa;
|
||||
struct zfsmount zfsmount;
|
||||
dnode_phys_t dn;
|
||||
struct stat st;
|
||||
int err;
|
||||
void *buf;
|
||||
EFI_STATUS status;
|
||||
|
||||
spa = devinfo->devdata;
|
||||
if (zfs_spa_init(spa) != 0) {
|
||||
/* Init failed, don't report this loudly. */
|
||||
return (EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (zfs_mount(spa, 0, &zfsmount) != 0) {
|
||||
/* Mount failed, don't report this loudly. */
|
||||
return (EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ((err = zfs_lookup(&zfsmount, loader_path, &dn)) != 0) {
|
||||
printf("Failed to lookup %s on pool %s (%d)\n", loader_path,
|
||||
spa->spa_name, err);
|
||||
return (EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
|
||||
printf("Failed to lookup %s on pool %s (%d)\n", loader_path,
|
||||
spa->spa_name, err);
|
||||
return (EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if ((status = bs->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf))
|
||||
!= EFI_SUCCESS) {
|
||||
printf("Failed to allocate load buffer for pool %s (%lu)\n",
|
||||
spa->spa_name, EFI_ERROR_CODE(status));
|
||||
return (EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) {
|
||||
printf("Failed to read node from %s (%d)\n", spa->spa_name,
|
||||
err);
|
||||
(void)bs->FreePool(buf);
|
||||
return (EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
*bufsize = st.st_size;
|
||||
*bufp = buf;
|
||||
|
||||
return (EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
load(const char *loader_path, dev_info_t **devinfop, void **bufp,
|
||||
size_t *bufsize)
|
||||
{
|
||||
dev_info_t *devinfo;
|
||||
EFI_STATUS status;
|
||||
|
||||
for (devinfo = devices; devinfo != NULL; devinfo = devinfo->next) {
|
||||
status = try_load(devinfo, loader_path, bufp, bufsize);
|
||||
if (status == EFI_SUCCESS) {
|
||||
*devinfop = devinfo;
|
||||
return (EFI_SUCCESS);
|
||||
} else if (status != EFI_NOT_FOUND) {
|
||||
return (status);
|
||||
}
|
||||
}
|
||||
|
||||
return (EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static void
|
||||
status()
|
||||
{
|
||||
spa_t *spa;
|
||||
|
||||
spa = STAILQ_FIRST(&zfs_pools);
|
||||
if (spa == NULL) {
|
||||
printf("%s found no pools\n", zfs_module.name);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s found the following pools:", zfs_module.name);
|
||||
STAILQ_FOREACH(spa, &zfs_pools, spa_link)
|
||||
printf(" %s", spa->spa_name);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
init()
|
||||
{
|
||||
|
||||
zfs_init();
|
||||
}
|
||||
|
||||
const boot_module_t zfs_module =
|
||||
{
|
||||
.name = "ZFS",
|
||||
.init = init,
|
||||
.probe = probe,
|
||||
.load = load,
|
||||
.status = status
|
||||
};
|
@ -42,7 +42,8 @@ void *efi_get_table(EFI_GUID *tbl);
|
||||
|
||||
int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int);
|
||||
EFI_HANDLE efi_find_handle(struct devsw *, int);
|
||||
int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *);
|
||||
int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *);
|
||||
int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t);
|
||||
|
||||
int efi_status_to_errno(EFI_STATUS);
|
||||
time_t efi_time(EFI_TIME *);
|
||||
|
@ -35,6 +35,7 @@ struct entry {
|
||||
EFI_HANDLE alias;
|
||||
struct devsw *dev;
|
||||
int unit;
|
||||
uint64_t extra;
|
||||
};
|
||||
|
||||
struct entry *entry;
|
||||
@ -79,7 +80,7 @@ efi_find_handle(struct devsw *dev, int unit)
|
||||
}
|
||||
|
||||
int
|
||||
efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit)
|
||||
efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit, uint64_t *extra)
|
||||
{
|
||||
int idx;
|
||||
|
||||
@ -90,7 +91,28 @@ efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit)
|
||||
*dev = entry[idx].dev;
|
||||
if (unit != NULL)
|
||||
*unit = entry[idx].unit;
|
||||
if (extra != NULL)
|
||||
*extra = entry[idx].extra;
|
||||
return (0);
|
||||
}
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
int
|
||||
efi_handle_update_dev(EFI_HANDLE h, struct devsw *dev, int unit,
|
||||
uint64_t guid)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < nentries; idx++) {
|
||||
if (entry[idx].handle != h)
|
||||
continue;
|
||||
entry[idx].dev = dev;
|
||||
entry[idx].unit = unit;
|
||||
entry[idx].alias = NULL;
|
||||
entry[idx].extra = guid;
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (ENOENT);
|
||||
}
|
||||
|
@ -21,6 +21,16 @@ SRCS= autoload.c \
|
||||
smbios.c \
|
||||
vers.c
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
SRCS+= zfs.c
|
||||
.PATH: ${.CURDIR}/../../zfs
|
||||
|
||||
# Disable warnings that are currently incompatible with the zfs boot code
|
||||
CWARNFLAGS.zfs.c+= -Wno-sign-compare
|
||||
CWARNFLAGS.zfs.c+= -Wno-array-bounds
|
||||
CWARNFLAGS.zfs.c+= -Wno-missing-prototypes
|
||||
.endif
|
||||
|
||||
.PATH: ${.CURDIR}/arch/${MACHINE}
|
||||
# For smbios.c
|
||||
.PATH: ${.CURDIR}/../../i386/libi386
|
||||
@ -33,6 +43,11 @@ CFLAGS+= -I${.CURDIR}/../include/${MACHINE}
|
||||
CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include
|
||||
CFLAGS+= -I${.CURDIR}/../../..
|
||||
CFLAGS+= -I${.CURDIR}/../../i386/libi386
|
||||
.if ${MK_ZFS} != "no"
|
||||
CFLAGS+= -I${.CURDIR}/../../zfs
|
||||
CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs
|
||||
CFLAGS+= -DEFI_ZFS_BOOT
|
||||
.endif
|
||||
CFLAGS+= -DNO_PCI -DEFI
|
||||
|
||||
# make buildenv doesn't set DESTDIR, this means LIBSTAND
|
||||
|
@ -31,14 +31,23 @@ __FBSDID("$FreeBSD$");
|
||||
#include <bootstrap.h>
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
#include <libzfs.h>
|
||||
#endif
|
||||
|
||||
struct devsw *devsw[] = {
|
||||
&efipart_dev,
|
||||
&efinet_dev,
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
&zfs_dev,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
struct fs_ops *file_system[] = {
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
&zfs_fsops,
|
||||
#endif
|
||||
&dosfs_fsops,
|
||||
&ufs_fsops,
|
||||
&cd9660_fsops,
|
||||
|
@ -33,6 +33,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/disklabel.h>
|
||||
#include <sys/param.h>
|
||||
#include <bootstrap.h>
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
#include <libzfs.h>
|
||||
#endif
|
||||
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
@ -104,6 +107,23 @@ efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
|
||||
|
||||
np = devspec + strlen(dv->dv_name);
|
||||
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
if (dv->dv_type == DEVT_ZFS) {
|
||||
int err;
|
||||
|
||||
idev = malloc(sizeof(struct zfs_devdesc));
|
||||
if (idev == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
err = zfs_parsedev((struct zfs_devdesc*)idev, np, path);
|
||||
if (err != 0) {
|
||||
free(idev);
|
||||
return (err);
|
||||
}
|
||||
*dev = idev;
|
||||
cp = strchr(np + 1, ':');
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
idev = malloc(sizeof(struct devdesc));
|
||||
if (idev == NULL)
|
||||
@ -143,6 +163,10 @@ efi_fmtdev(void *vdev)
|
||||
static char buf[SPECNAMELEN + 1];
|
||||
|
||||
switch(dev->d_type) {
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
case DEVT_ZFS:
|
||||
return (zfs_fmtdev(dev));
|
||||
#endif
|
||||
case DEVT_NONE:
|
||||
strcpy(buf, "(no device)");
|
||||
break;
|
||||
|
@ -39,6 +39,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <bootstrap.h>
|
||||
#include <smbios.h>
|
||||
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
#include <libzfs.h>
|
||||
#endif
|
||||
|
||||
#include "loader_efi.h"
|
||||
|
||||
extern char bootprog_name[];
|
||||
@ -61,6 +65,10 @@ EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
|
||||
EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
|
||||
EFI_GUID fdtdtb = FDT_TABLE_GUID;
|
||||
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
static void efi_zfs_probe(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Need this because EFI uses UTF-16 unicode string constants, but we
|
||||
* use UTF-8. We can't use printf due to the possiblity of \0 and we
|
||||
@ -83,6 +91,7 @@ main(int argc, CHAR16 *argv[])
|
||||
EFI_GUID *guid;
|
||||
int i, j, vargood, unit;
|
||||
struct devsw *dev;
|
||||
uint64_t pool_guid;
|
||||
UINTN k;
|
||||
|
||||
archsw.arch_autoload = efi_autoload;
|
||||
@ -90,6 +99,10 @@ main(int argc, CHAR16 *argv[])
|
||||
archsw.arch_copyin = efi_copyin;
|
||||
archsw.arch_copyout = efi_copyout;
|
||||
archsw.arch_readin = efi_readin;
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
/* Note this needs to be set before ZFS init. */
|
||||
archsw.arch_zfs_probe = efi_zfs_probe;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX Chicken-and-egg problem; we want to have console output
|
||||
@ -168,10 +181,27 @@ main(int argc, CHAR16 *argv[])
|
||||
*/
|
||||
BS->SetWatchdogTimer(0, 0, 0, NULL);
|
||||
|
||||
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit) != 0)
|
||||
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0)
|
||||
return (EFI_NOT_FOUND);
|
||||
|
||||
switch (dev->dv_type) {
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
case DEVT_ZFS: {
|
||||
struct zfs_devdesc currdev;
|
||||
|
||||
currdev.d_dev = dev;
|
||||
currdev.d_unit = unit;
|
||||
currdev.d_type = currdev.d_dev->dv_type;
|
||||
currdev.d_opendata = NULL;
|
||||
currdev.pool_guid = pool_guid;
|
||||
currdev.root_guid = 0;
|
||||
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
|
||||
efi_setcurrdev, env_nounset);
|
||||
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
|
||||
env_nounset);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
struct devdesc currdev;
|
||||
|
||||
@ -456,6 +486,29 @@ command_nvram(int argc, char *argv[])
|
||||
return (CMD_OK);
|
||||
}
|
||||
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
|
||||
command_lszfs);
|
||||
|
||||
static int
|
||||
command_lszfs(int argc, char *argv[])
|
||||
{
|
||||
int err;
|
||||
|
||||
if (argc != 2) {
|
||||
command_errmsg = "wrong number of arguments";
|
||||
return (CMD_ERROR);
|
||||
}
|
||||
|
||||
err = zfs_list(argv[1]);
|
||||
if (err != 0) {
|
||||
command_errmsg = strerror(err);
|
||||
return (CMD_ERROR);
|
||||
}
|
||||
return (CMD_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LOADER_FDT_SUPPORT
|
||||
extern int command_fdt_internal(int argc, char *argv[]);
|
||||
|
||||
@ -474,3 +527,23 @@ command_fdt(int argc, char *argv[])
|
||||
|
||||
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
|
||||
#endif
|
||||
|
||||
#ifdef EFI_ZFS_BOOT
|
||||
static void
|
||||
efi_zfs_probe(void)
|
||||
{
|
||||
EFI_HANDLE h;
|
||||
u_int unit;
|
||||
int i;
|
||||
char dname[SPECNAMELEN + 1];
|
||||
uint64_t guid;
|
||||
|
||||
unit = 0;
|
||||
h = efi_find_handle(&efipart_dev, 0);
|
||||
for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) {
|
||||
snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i);
|
||||
if (zfs_probe_dev(dname, &guid) == 0)
|
||||
(void)efi_handle_update_dev(h, &zfs_dev, unit++, guid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user