svn commit: r294068 - in head/sys/boot/efi: boot1 include libefi loader

Steven Hartland smh at FreeBSD.org
Fri Jan 15 02:33:50 UTC 2016


Author: smh
Date: Fri Jan 15 02:33:47 2016
New Revision: 294068
URL: https://svnweb.freebsd.org/changeset/base/294068

Log:
  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

Added:
  head/sys/boot/efi/boot1/zfs_module.c   (contents, props changed)
Modified:
  head/sys/boot/efi/boot1/Makefile
  head/sys/boot/efi/boot1/boot1.c
  head/sys/boot/efi/boot1/boot_module.h
  head/sys/boot/efi/include/efilib.h
  head/sys/boot/efi/libefi/handles.c
  head/sys/boot/efi/loader/Makefile
  head/sys/boot/efi/loader/conf.c
  head/sys/boot/efi/loader/devicename.c
  head/sys/boot/efi/loader/main.c

Modified: head/sys/boot/efi/boot1/Makefile
==============================================================================
--- head/sys/boot/efi/boot1/Makefile	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/boot1/Makefile	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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/d
 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

Modified: head/sys/boot/efi/boot1/boot1.c
==============================================================================
--- head/sys/boot/efi/boot1/boot1.c	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/boot1/boot1.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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

Modified: head/sys/boot/efi/boot1/boot_module.h
==============================================================================
--- head/sys/boot/efi/boot1/boot_module.h	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/boot1/boot_module.h	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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);

Added: head/sys/boot/efi/boot1/zfs_module.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/boot/efi/boot1/zfs_module.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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
+};

Modified: head/sys/boot/efi/include/efilib.h
==============================================================================
--- head/sys/boot/efi/include/efilib.h	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/include/efilib.h	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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 *);

Modified: head/sys/boot/efi/libefi/handles.c
==============================================================================
--- head/sys/boot/efi/libefi/handles.c	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/libefi/handles.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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 u
 }
 
 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 d
 			*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);
+}

Modified: head/sys/boot/efi/loader/Makefile
==============================================================================
--- head/sys/boot/efi/loader/Makefile	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/loader/Makefile	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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/${MACHI
 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

Modified: head/sys/boot/efi/loader/conf.c
==============================================================================
--- head/sys/boot/efi/loader/conf.c	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/loader/conf.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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,

Modified: head/sys/boot/efi/loader/devicename.c
==============================================================================
--- head/sys/boot/efi/loader/devicename.c	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/loader/devicename.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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
 
 	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;

Modified: head/sys/boot/efi/loader/main.c
==============================================================================
--- head/sys/boot/efi/loader/main.c	Fri Jan 15 02:32:57 2016	(r294067)
+++ head/sys/boot/efi/loader/main.c	Fri Jan 15 02:33:47 2016	(r294068)
@@ -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_INFORMATI
 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


More information about the svn-src-head mailing list