git: cc82c650a72f - main - kboot: Add ZFS support to hostdisk

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 13 Jan 2023 21:24:49 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=cc82c650a72fcdde255b023703a9b214a81254ec

commit cc82c650a72fcdde255b023703a9b214a81254ec
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2023-01-13 21:20:30 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-01-13 21:22:38 +0000

    kboot: Add ZFS support to hostdisk
    
    Add helper function to walk through the disk drives we've found to look
    for zpools. main.c will still need to call this because the loader
    hasn't implemented a good way to 'taste' drives for zpools and/or GELI
    partitions (mostly because there's no generic list of candidate
    devices).
    
    Sponsored by:           Netflix
    Reviewed by:            kevans
    Differential Revision:  https://reviews.freebsd.org/D38007
---
 stand/kboot/hostdisk.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
 stand/kboot/kboot.h    |   7 ++-
 2 files changed, 127 insertions(+), 2 deletions(-)

diff --git a/stand/kboot/hostdisk.c b/stand/kboot/hostdisk.c
index cbe8faabca96..a96c38c21182 100644
--- a/stand/kboot/hostdisk.c
+++ b/stand/kboot/hostdisk.c
@@ -30,8 +30,14 @@ __FBSDID("$FreeBSD$");
 #include <sys/types.h>
 #include <sys/disk.h>
 #include <stdarg.h>
+#include <paths.h>
 #include "host_syscall.h"
 #include "kboot.h"
+#include "bootstrap.h"
+#ifdef LOADER_ZFS_SUPPORT
+#include "libzfs.h"
+#include <sys/zfs_bootenv.h>
+#endif
 
 static int hostdisk_init(void);
 static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk,
@@ -75,6 +81,8 @@ typedef struct hdinfo {
 	uint64_t	hd_sectors;
 	uint64_t	hd_sectorsize;
 	int		hd_flags;
+#define HDF_HAS_ZPOOL	1			/* We found a zpool here and uuid valid */
+	uint64_t	hd_zfs_uuid;
 } hdinfo_t;
 
 #define dev2hd(d) ((hdinfo_t *)d->d_opendata)
@@ -416,3 +424,117 @@ hostdisk_parsedev(struct devdesc **idev, const char *devspec, const char **path)
 	*idev = dev;
 	return (0);
 }
+
+#ifdef LOADER_ZFS_SUPPORT
+static bool
+hostdisk_zfs_check_one(hdinfo_t *hd)
+{
+	char *fn;
+	bool found = false;
+	uint64_t pool_uuid;
+
+	if (asprintf(&fn, "%s:", hd->hd_dev) == -1)
+		return (false);
+	pool_uuid = 0;
+	zfs_probe_dev(fn, &pool_uuid, false);
+	if (pool_uuid != 0) {
+		found = true;
+		hd->hd_flags |= HDF_HAS_ZPOOL;
+		hd->hd_zfs_uuid = pool_uuid;
+	}
+	free(fn);
+
+	return (found);
+}
+
+void
+hostdisk_zfs_probe(void)
+{
+	hdinfo_t *hd, *md;
+
+	STAILQ_FOREACH(hd, &hdinfo, hd_link) {
+		if (hostdisk_zfs_check_one(hd))
+			continue;
+		STAILQ_FOREACH(md, &hd->hd_children, hd_link) {
+			hostdisk_zfs_check_one(md);
+		}
+	}
+}
+
+/* XXX refactor */
+static bool
+sanity_check_currdev(void)
+{
+	struct stat st;
+
+	return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 ||
+#ifdef PATH_BOOTABLE_TOKEN
+	    stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */
+#endif
+	    stat(PATH_KERNEL, &st) == 0);
+}
+
+/* This likely shoud move to libsa/zfs/zfs.c and be used by at least EFI booting */
+static bool
+probe_zfs_currdev(uint64_t pool_guid, uint64_t root_guid, bool setcurrdev)
+{
+	char *devname;
+	struct zfs_devdesc currdev;
+	char *buf = NULL;
+	bool bootable;
+
+	currdev.dd.d_dev = &zfs_dev;
+	currdev.dd.d_unit = 0;
+	currdev.pool_guid = pool_guid;
+	currdev.root_guid = root_guid;
+	devname = devformat(&currdev.dd);
+	if (setcurrdev)
+		set_currdev(devname);
+
+	bootable = sanity_check_currdev();
+	if (bootable) {
+		buf = malloc(VDEV_PAD_SIZE);
+		if (buf != NULL) {
+			if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf,
+			    VDEV_PAD_SIZE) == 0) {
+				printf("zfs bootonce: %s\n", buf);
+				if (setcurrdev)
+					set_currdev(buf);
+				setenv("zfs-bootonce", buf, 1);
+			}
+			free(buf);
+			(void)zfs_attach_nvstore(&currdev);
+		}
+		init_zfs_boot_options(devname);
+	}
+	return (bootable);
+}
+
+static bool
+hostdisk_zfs_try_default(hdinfo_t *hd)
+{
+	return (probe_zfs_currdev(hd->hd_zfs_uuid, 0, true));
+}
+
+bool
+hostdisk_zfs_find_default(void)
+{
+	hdinfo_t *hd, *md;
+
+	STAILQ_FOREACH(hd, &hdinfo, hd_link) {
+		if (hd->hd_flags & HDF_HAS_ZPOOL) {
+			if (hostdisk_zfs_try_default(hd))
+				return (true);
+			continue;
+		}
+		STAILQ_FOREACH(md, &hd->hd_children, hd_link) {
+			if (md->hd_flags & HDF_HAS_ZPOOL) {
+				if (hostdisk_zfs_try_default(md))
+					return (true);
+			}
+		}
+	}
+	return (false);
+}
+
+#endif
diff --git a/stand/kboot/kboot.h b/stand/kboot/kboot.h
index 72be7299995b..58cbedff67a1 100644
--- a/stand/kboot/kboot.h
+++ b/stand/kboot/kboot.h
@@ -14,14 +14,17 @@ vm_offset_t acpi_rsdp(void);
 
 void do_init(void);
 
-extern const char *hostfs_root;
-
 /* Per-platform fdt fixup */
 void fdt_arch_fixups(void *fdtp);
 
 uint64_t kboot_get_phys_load_segment(void);
 uint8_t kboot_get_kernel_machine_bits(void);
 
+/* hostdisk.c */
+extern const char *hostfs_root;
+void hostdisk_zfs_probe(void);
+bool hostdisk_zfs_find_default(void);
+
 /* util.c */
 bool file2str(const char *fn, char *buffer, size_t buflen);
 bool file2u64(const char *fn, uint64_t *val);