CFT EFI Boot Refactoring

Ben Woods woodsb02 at gmail.com
Sat Dec 3 06:40:20 UTC 2016


Hi Eric,

Thanks for your work on this.

I just applied your diff to my subversion repository, and tried to
buildworld, but the build failed with the following error:

make[6]: make[6]: don't know how to make efipart.c. Stop

make[6]: stopped in /usr/src/sys/boot/efi/drivers
*** [all_subdir_sys/boot/efi/drivers] Error code 2


Does it build ok for you?

Because I use subversion, and I wanted to build it from my main tree, I had
to regenerate your patch using "git diff --no-prefix
master..origin/efize_new > /tmp/efize_new.diff".
I could then apply this cleanly with "svn patch /tmp/efize_new.diff".
I checked the difference between your diff and the diff I generated, and
the only differences were in the file headers. Therefore I don't think the
patch change should make any difference. I have attached my version of the
patch for reference.

For example:

diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile
index 0ebcaf1..3b608c5 100644
--- a/lib/libstand/Makefile
+++ b/lib/libstand/Makefile

Became:

diff --git lib/libstand/Makefile lib/libstand/Makefile
index 0ebcaf1ccfd..3b608c5bc92 100644
--- lib/libstand/Makefile
+++ lib/libstand/Makefile


To show the differences with your patch applied:
$ svn status
M       lib/libstand/Makefile
M       lib/libstand/stand.h
M       sys/boot/efi/Makefile
M       sys/boot/efi/boot1/Makefile
M       sys/boot/efi/boot1/Makefile.fat
M       sys/boot/efi/boot1/boot1.c
D       sys/boot/efi/boot1/boot_module.h
M       sys/boot/efi/boot1/fat-amd64.tmpl.bz2.uu
M       sys/boot/efi/boot1/fat-arm.tmpl.bz2.uu
M       sys/boot/efi/boot1/fat-arm64.tmpl.bz2.uu
M       sys/boot/efi/boot1/fat-i386.tmpl.bz2.uu
M       sys/boot/efi/boot1/generate-fat.sh
D       sys/boot/efi/boot1/ufs_module.c
D       sys/boot/efi/boot1/zfs_module.c
A       sys/boot/efi/drivers
A       sys/boot/efi/drivers/Makefile
A       sys/boot/efi/drivers/efi_drivers.h
A       sys/boot/efi/drivers/fs_driver.c
M       sys/boot/efi/include/efilib.h
M       sys/boot/efi/include/efiprot.h
A       sys/boot/efi/include/string16.h
M       sys/boot/efi/libefi/Makefile
A       sys/boot/efi/libefi/efifs.c
M       sys/boot/efi/libefi/efipart.c
M       sys/boot/efi/libefi/errno.c
M       sys/boot/efi/libefi/handles.c
A       sys/boot/efi/libefi/string16.c
M       sys/boot/efi/libefi/time.c
M       sys/boot/efi/loader/Makefile
M       sys/boot/efi/loader/conf.c
M       sys/boot/efi/loader/loader_efi.h
M       sys/boot/efi/loader/main.c


Regards,
Ben


--
From: Benjamin Woods
woodsb02 at gmail.com

On 3 December 2016 at 01:02, Eric McCorkle <eric at metricspace.net> wrote:

> Hello everyone,
>
> My work to refactor the EFI boot loader has been in review for some time
> now.  This work is a behavior-neutral refactoring which eliminates
> duplicated code in boot1, provides better integration of boot1 and
> loader with the EFI API, and moves towards better compliance with the
> recommendations of the UEFI driver writer's guide.  This work also
> serves as a precursor to more work, such as GELI, hot-plugging, and
> other things.
>
> One of the reviewers was able to trigger a hang on his setup; however,
> it's not clear whether this is a problem in the refactoring, or whether
> it's due to a related bug:
>
> https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=214423
>
> Therefore, I would like to issue a CFT for this changeset.  We need
> people using the boot1/loader EFI boot setup to test their setup using
> boot1 and loader as built with this patch applied.
>
> You can also get the source tree directly from my github
> (https://github.com/emc2/freebsd.git).  Use the efize_new branch to get
> this changeset.  Note that I am maintaining the state of this branch in
> a single change at this point using rebase -i, so there *will* be forced
> pushes to this branch.
>
>
> Here are some notes on testing the changeset:
>
> * To test it, just do a buildworld, then copy loader.efi in place and
> copy boot1.efi to /efi/BOOT/BOOTX64.EFI on your ESP.  If your system
> boots, then the test was successful (there are no new features in this
> changeset).
>
> * The output of boot1 is slightly different, so you'll be able to tell
> if you installed it correctly.
>
> * I recommend keeping a copy of the basic boot1 around on your ESP, just
> in case something goes wrong.  On my setup, I have a backup at
> /efi/BOOT/BOOTX64.BAK (with the main program at /efi/BOOT/BOOTX64.EFI,
> of course)
>
> * I have been using this on a machine with two disks, a ZFS pool
> spanning both disks, and a dummy UFS filesystem for months now, so it
> can be considered relatively safe.
>
> * This has also been tested on basic setups without incident, so
> priority is on complex or odd setups.
>
> * If something goes wrong, you will most likely get a boot-hang.  If
> this happens, please contact me directly with the details, and I'll
> coordinate on diagnosis.
>
-------------- next part --------------
diff --git lib/libstand/Makefile lib/libstand/Makefile
index 0ebcaf1ccfd..3b608c5bc92 100644
--- lib/libstand/Makefile
+++ lib/libstand/Makefile
@@ -41,7 +41,7 @@ SRCS+= ntoh.c
 .PATH: ${LIBC_SRC}/string
 SRCS+=	bcmp.c bcopy.c bzero.c ffs.c fls.c \
 	memccpy.c memchr.c memcmp.c memcpy.c memmove.c memset.c \
-	qdivrem.c strcat.c strchr.c strcmp.c strcpy.c \
+	qdivrem.c strcat.c strchr.c strcmp.c strcpy.c stpcpy.c stpncpy.c \
 	strcspn.c strlcat.c strlcpy.c strlen.c strncat.c strncmp.c strncpy.c \
 	strpbrk.c strrchr.c strsep.c strspn.c strstr.c strtok.c swab.c
 .if ${MACHINE_CPUARCH} == "arm"
diff --git lib/libstand/stand.h lib/libstand/stand.h
index f77a586fb06..066aff05d65 100644
--- lib/libstand/stand.h
+++ lib/libstand/stand.h
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD$
- * From	$NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $	
+ * From	$NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $
  */
 
 /*-
@@ -131,7 +131,7 @@ extern struct fs_ops pkgfs_fsops;
 #define	SEEK_CUR	1	/* set file offset to current plus offset */
 #define	SEEK_END	2	/* set file offset to EOF plus offset */
 
-/* 
+/*
  * Device switch
  */
 struct devsw {
@@ -166,8 +166,9 @@ struct devdesc
 #define DEVT_NONE	0
 #define DEVT_DISK	1
 #define DEVT_NET	2
-#define DEVT_CD		3
+#define DEVT_CD	3
 #define DEVT_ZFS	4
+#define DEVT_EFI	5
     int			d_unit;
     void		*d_opendata;
 };
@@ -279,7 +280,7 @@ extern struct	dirent *readdirfd(int);
 
 extern void	srandom(u_long seed);
 extern u_long	random(void);
-    
+
 /* imports from stdlib, locally modified */
 extern long	strtol(const char *, char **, int);
 extern unsigned long	strtoul(const char *, char **, int);
@@ -368,9 +369,9 @@ extern int	null_stat(struct open_file *f, struct stat *sb);
 extern int	null_readdir(struct open_file *f, struct dirent *d);
 
 
-/* 
- * Machine dependent functions and data, must be provided or stubbed by 
- * the consumer 
+/*
+ * Machine dependent functions and data, must be provided or stubbed by
+ * the consumer
  */
 extern int		getchar(void);
 extern int		ischar(void);
diff --git sys/boot/efi/Makefile sys/boot/efi/Makefile
index 66481f8513f..00490d0e943 100644
--- sys/boot/efi/Makefile
+++ sys/boot/efi/Makefile
@@ -15,7 +15,7 @@ SUBDIR+=	fdt
 .if ${MACHINE_CPUARCH} == "aarch64" || \
     ${MACHINE_CPUARCH} == "amd64" || \
     ${MACHINE_CPUARCH} == "arm"
-SUBDIR+=	libefi loader boot1
+SUBDIR+=	libefi drivers loader boot1
 .endif
 
 .endif # ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500
diff --git sys/boot/efi/boot1/Makefile sys/boot/efi/boot1/Makefile
index 110a8577491..7480c9c3eca 100644
--- sys/boot/efi/boot1/Makefile
+++ sys/boot/efi/boot1/Makefile
@@ -8,34 +8,50 @@ MK_SSP=		no
 
 PROG=		boot1.sym
 INTERNALPROG=
-WARNS?=		6
+WARNS?=		3
+
+# Include bcache code.
+HAVE_BCACHE=    yes
 
 .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
+CWARNFLAGS.boot1.c += -Wno-missing-variable-declarations
+CWARNFLAGS.zfs.c += -Wno-incompatible-pointer-types-discards-qualifiers
+CWARNFLAGS.zfs.c += -Wno-missing-variable-declarations
+CWARNFLAGS.zfs.c += -Wno-array-bounds
+CWARNFLAGS.zfs.c += -Wno-cast-align
+CWARNFLAGS.zfs.c += -Wno-cast-qual
+CWARNFLAGS.zfs.c += -Wno-missing-prototypes
+CWARNFLAGS.zfs.c += -Wno-sign-compare
+CWARNFLAGS.zfs.c += -Wno-unused-parameter
+CWARNFLAGS.zfs.c += -Wno-unused-function
 CWARNFLAGS.skein.c += -Wno-cast-align
 CWARNFLAGS.skein.c += -Wno-missing-variable-declarations
 .endif
 
+
 # architecture-specific loader code
-SRCS=	boot1.c self_reloc.c start.S ufs_module.c
+SRCS=	boot1.c self_reloc.c start.S
 .if ${MK_ZFS} != "no"
-SRCS+=		zfs_module.c
+.PATH:		${.CURDIR}/../../../crypto/skein
 SRCS+=		skein.c skein_block.c
 # Do not unroll skein loops, reduce code size
 CFLAGS+=	-DSKEIN_LOOP=111
-.PATH:		${.CURDIR}/../../../crypto/skein
+.PATH:		${.CURDIR}/../../zfs
+SRCS+=		zfs.c
 .endif
 
+# Always add MI sources
+.PATH:		${.CURDIR}/../../common
+.include	"${.CURDIR}/../../common/Makefile.inc"
+CFLAGS+=	-I${.CURDIR}/../../common
+
+.PATH: ${.CURDIR}/arch/${MACHINE}
+
 CFLAGS+=	-I.
 CFLAGS+=	-I${.CURDIR}/../include
 CFLAGS+=	-I${.CURDIR}/../include/${MACHINE}
+CFLAGS+=	-I${.CURDIR}/../drivers/
 CFLAGS+=	-I${.CURDIR}/../../../contrib/dev/acpica/include
 CFLAGS+=	-I${.CURDIR}/../../..
 CFLAGS+=	-DEFI_UFS_BOOT
@@ -56,6 +72,20 @@ CFLAGS+=	-DEFI_ZFS_BOOT
 .PATH:		${.CURDIR}/../../common
 CFLAGS+=	-I${.CURDIR}/../../common
 
+# make buildenv doesn't set DESTDIR, this means LIBSTAND
+# will be wrong when crossbuilding.
+.if exists(${.OBJDIR}/../../../../lib/libstand/libstand.a)
+LIBSTAND=	${.OBJDIR}/../../../../lib/libstand/libstand.a
+.endif
+
+# Add libefi
+.PATH:		${.CURDIR}/../libefi
+LIBEFI=		${.OBJDIR}/../libefi/libefi.a
+
+.PATH:		${.CURDIR}/../drivers
+LIBEFI_DRIVERS=	${.OBJDIR}/../drivers/libefi_drivers.a
+
+
 FILES=	boot1.efi boot1.efifat
 FILESMODE_boot1.efi=	${BINMODE}
 
@@ -75,8 +105,8 @@ LDFLAGS+=	-Wl,-znocombreloc
 # __aeabi_* (arm) or __divdi3 (i386).
 # as well as required string and memory functions for all platforms.
 #
-DPADD+=		${LIBSTAND}
-LDADD+=		-lstand
+DPADD+=		${LIBEFI_DRIVERS} ${LIBEFI} ${LIBSTAND}
+LDADD+=		${LIBEFI_DRIVERS} ${LIBEFI} ${LIBSTAND}
 
 DPADD+=		${LDSCRIPT}
 
@@ -102,7 +132,7 @@ boot1.efi: ${PROG}
 	SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
 	${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \
 		-j .dynamic -j .dynsym -j .rel.dyn \
-		-j .rela.dyn -j .reloc -j .eh_frame \
+		-j .rela.dyn -j .reloc -j .eh_frame -j set_Xcommand_set \
 		--output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET}
 
 boot1.o: ${.CURDIR}/../../common/ufsread.c
@@ -111,7 +141,7 @@ boot1.o: ${.CURDIR}/../../common/ufsread.c
 # created by generate-fat.sh
 
 .include "${.CURDIR}/Makefile.fat"
-BOOT1_MAXSIZE?=	131072
+BOOT1_MAXSIZE?=	524288
 
 boot1.efifat: boot1.efi
 	@set -- `ls -l boot1.efi`; \
diff --git sys/boot/efi/boot1/Makefile.fat sys/boot/efi/boot1/Makefile.fat
index c86a7c3f585..e2cda1ceb79 100644
--- sys/boot/efi/boot1/Makefile.fat
+++ sys/boot/efi/boot1/Makefile.fat
@@ -1,4 +1,4 @@
 # This file autogenerated by generate-fat.sh - DO NOT EDIT
 # $FreeBSD$
 BOOT1_OFFSET=0x2d
-BOOT1_MAXSIZE=131072
+BOOT1_MAXSIZE=524288
diff --git sys/boot/efi/boot1/boot1.c sys/boot/efi/boot1/boot1.c
index 80b1895d4b2..9e02bfa3849 100644
--- sys/boot/efi/boot1/boot1.c
+++ sys/boot/efi/boot1/boot1.c
@@ -26,62 +26,118 @@ __FBSDID("$FreeBSD$");
 #include <machine/elf.h>
 #include <machine/stdarg.h>
 #include <stand.h>
+#include <string16.h>
 
 #include <efi.h>
+#include <efilib.h>
+#include <efiprot.h>
 #include <eficonsctl.h>
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+
+#include <bootstrap.h>
 
-#include "boot_module.h"
+#include "efi_drivers.h"
 #include "paths.h"
 
-static const boot_module_t *boot_modules[] =
-{
-#ifdef EFI_ZFS_BOOT
-	&zfs_module,
-#endif
-#ifdef EFI_UFS_BOOT
-	&ufs_module
+#ifdef EFI_DEBUG
+#define DPRINTF(fmt, args...) printf(fmt, ##args)
+#define DSTALL(d) bs->Stall(d)
+#else
+#define DPRINTF(fmt, ...) {}
+#define DSTALL(d) {}
 #endif
+
+struct arch_switch archsw;	/* MI/MD interface boundary */
+
+static const efi_driver_t *efi_drivers[] = {
+        &fs_driver,
+        NULL
 };
 
-#define	NUM_BOOT_MODULES	nitems(boot_modules)
-/* The initial number of handles used to query EFI for partitions. */
-#define NUM_HANDLES_INIT	24
+extern struct console efi_console;
+#if defined(__amd64__) || defined(__i386__)
+extern struct console comconsole;
+extern struct console nullconsole;
+#endif
 
-void putchar(int c);
-EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
+struct fs_ops *file_system[] = {
+	&dosfs_fsops,
+	&ufs_fsops,
+	&cd9660_fsops,
+	&nfs_fsops,
+	&gzipfs_fsops,
+	&bzipfs_fsops,
+	NULL
+};
 
-EFI_SYSTEM_TABLE *systab;
-EFI_BOOT_SERVICES *bs;
-static EFI_HANDLE *image;
+struct devsw *devsw[] = {
+	&efipart_dev,
+#ifdef EFI_ZFS_BOOT
+	&zfs_dev,
+#endif
+	NULL
+};
 
-static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
-static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
-static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
-static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+struct console *consoles[] = {
+	&efi_console,
+	NULL
+};
 
-/*
- * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
- * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
- * EFI methods.
+/* Definitions we don't actually need for boot, but we need to define
+ * to make the linker happy.
  */
-void *
-Malloc(size_t len, const char *file __unused, int line __unused)
+struct file_format *file_formats[] = { NULL };
+
+struct netif_driver *netif_drivers[] = { NULL };
+
+static int
+efi_autoload(void)
 {
-	void *out;
+  printf("******** Boot block should not call autoload\n");
+  return (-1);
+}
 
-	if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
-		return (out);
+static int efi_getdev(void **vdev __unused, const char *devspec __unused,
+    const char **path __unused)
+{
+  printf("******** Boot block should not call getdev\n");
+  return (-1);
+}
 
-	return (NULL);
+static ssize_t
+efi_copyin(const void *src __unused, vm_offset_t dest __unused,
+    const size_t len __unused)
+{
+  printf("******** Boot block should not call copyin\n");
+  return (-1);
 }
 
-void
-Free(void *buf, const char *file __unused, int line __unused)
+static ssize_t
+efi_copyout(vm_offset_t src __unused, void *dest __unused,
+    const size_t len __unused)
 {
-	if (buf != NULL)
-		(void)bs->FreePool(buf);
+  printf("******** Boot block should not call copyout\n");
+  return (-1);
+}
+
+static ssize_t
+efi_readin(int fd __unused, vm_offset_t dest __unused,
+    const size_t len __unused)
+{
+  printf("******** Boot block should not call readin\n");
+  return (-1);
 }
 
+/* The initial number of handles used to query EFI for partitions. */
+#define NUM_HANDLES_INIT	24
+
+static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
+static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+static EFI_GUID SimpleFileSystemProtocolGUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
+static EFI_GUID FileInfoGUID = EFI_FILE_INFO_ID;;
+
 /*
  * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
  * FALSE otherwise.
@@ -142,6 +198,7 @@ devpath_last(EFI_DEVICE_PATH *devpath)
 	return (devpath);
 }
 
+#ifdef EFI_DEBUG
 /*
  * devpath_node_str is a basic output method for a devpath node which
  * only understands a subset of the available sub types.
@@ -273,7 +330,7 @@ devpath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath)
  * devpath_strlcat appends a text description of devpath to buf but not more
  * than size - 1 characters followed by NUL-terminator.
  */
-int
+static int
 devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath)
 {
 	size_t len, used;
@@ -304,48 +361,207 @@ devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath)
  * devpath_str is convenience method which returns the text description of
  * devpath using a static buffer, so it isn't thread safe!
  */
-char *
+static char *
 devpath_str(EFI_DEVICE_PATH *devpath)
 {
 	static char buf[256];
 
 	devpath_strlcat(buf, sizeof(buf), devpath);
 
-	return buf;
+	return (buf);
+}
+#endif
+
+static EFI_STATUS
+efi_load(EFI_HANDLE dev, const char *filepath, void **bufp, size_t *bufsize)
+{
+        UINTN infosize = sizeof(EFI_FILE_INFO) +
+          ((strlen(filepath) + 1) * sizeof(CHAR16));
+        EFI_FILE_INFO *finfo;
+	EFI_STATUS status;
+        EFI_FILE_IO_INTERFACE *iface;
+        EFI_FILE_HANDLE root;
+        EFI_FILE_HANDLE target;
+        CHAR16 path16[strlen(filepath) + 1];
+	void *buf;
+
+        finfo = malloc(infosize);
+
+        if (finfo == NULL) {
+                return (EFI_OUT_OF_RESOURCES);
+        }
+
+	status = BS->OpenProtocol(dev, &SimpleFileSystemProtocolGUID,
+            (void **)&iface, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+        if (status != EFI_SUCCESS) {
+                free(finfo);
+                printf("Open protocol failed! %ld\n", EFI_ERROR_CODE(status));
+                return (status);
+        }
+
+        status = iface->OpenVolume(iface, &root);
+
+        if (status != EFI_SUCCESS) {
+                BS->CloseProtocol(dev, &SimpleFileSystemProtocolGUID, IH, NULL);
+                free(finfo);
+                printf("Open volume failed! %ld\n", EFI_ERROR_CODE(status));
+                return (status);
+        }
+
+        strcpy_to_16(path16, filepath);
+        status = root->Open(root, &target, path16, EFI_FILE_MODE_READ, 0);
+
+        if (status != EFI_SUCCESS) {
+                BS->CloseProtocol(dev, &SimpleFileSystemProtocolGUID, IH, NULL);
+                free(finfo);
+
+                return (status);
+        }
+
+        *bufsize = infosize;
+        status = target->GetInfo(target, &FileInfoGUID, bufsize, finfo);
+
+        if (status != EFI_SUCCESS) {
+                BS->CloseProtocol(dev, &SimpleFileSystemProtocolGUID, IH, NULL);
+                free(finfo);
+                printf("Get info failed! %ld\n", EFI_ERROR_CODE(status));
+                return (status);
+        }
+
+        *bufsize = finfo->FileSize;
+
+        if ((buf = malloc(finfo->FileSize)) == NULL) {
+                BS->CloseProtocol(dev, &SimpleFileSystemProtocolGUID, IH, NULL);
+                free(finfo);
+                printf("Failed to allocate load buffer %zd for '%s' "
+                       "(%lu)\n", finfo->FileSize, filepath, EFI_ERROR_CODE(status));
+                return (EFI_OUT_OF_RESOURCES);
+        }
+
+        *bufp = buf;
+        status = target->Read(target, bufsize, buf);
+        BS->CloseProtocol(dev, &SimpleFileSystemProtocolGUID, IH, NULL);
+        free(finfo);
+
+        if (status != EFI_SUCCESS) {
+                printf("Read failed! %ld\n", EFI_ERROR_CODE(status));
+                return (status);
+        }
+
+        return (EFI_SUCCESS);
 }
 
 /*
  * load_loader attempts to load the loader image data.
  *
- * It tries each module and its respective devices, identified by mod->probe,
- * in order until a successful load occurs at which point it returns EFI_SUCCESS
- * and EFI_NOT_FOUND otherwise.
+ * This tries all handles which support the EFI_SIMPLE_FILE_SYSTEM interface.
+ * It is expected that the drivers will have installed this interface on every
+ * handle representing a device containing a supported file system.
+ *
+ * Note: In the future, this may be altered to use the EFI_LOAD_FILE interface,
+ * which should work transparently with network booting.
  *
  * Only devices which have preferred matching the preferred parameter are tried.
  */
 static EFI_STATUS
-load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
-    size_t *bufsize, BOOLEAN preferred)
+load_loader(EFI_HANDLE *handlep, void **bufp, size_t *bufsize)
 {
-	UINTN i;
-	dev_info_t *dev;
-	const boot_module_t *mod;
-
-	for (i = 0; i < NUM_BOOT_MODULES; i++) {
-		mod = boot_modules[i];
-		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
-			if (dev->preferred != preferred)
-				continue;
-
-			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
-			    EFI_SUCCESS) {
-				*devinfop = dev;
-				*modp = mod;
-				return (EFI_SUCCESS);
-			}
+	EFI_DEVICE_PATH *imgpath;
+	EFI_DEVICE_PATH *devpath;
+	EFI_LOADED_IMAGE *boot_image;
+        EFI_HANDLE *boot_handle;
+	EFI_HANDLE *preferred;
+	EFI_HANDLE *handles;
+        EFI_STATUS status;
+	UINTN i, hsize, nhandles, npreferred;
+
+	if ((status = BS->OpenProtocol(IH, &LoadedImageGUID,
+            (VOID**)&boot_image, IH, NULL,
+            EFI_OPEN_PROTOCOL_GET_PROTOCOL)) != EFI_SUCCESS) {
+		panic("Failed to query LoadedImage (%lu)\n",
+		    EFI_ERROR_CODE(status));
+	}
+
+        boot_handle = boot_image->DeviceHandle;
+
+	if ((status = BS->OpenProtocol(boot_handle, &DevicePathGUID,
+            (void **)&imgpath, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL))) {
+                panic("Failed to get image DevicePath (%lu)\n",
+                    EFI_ERROR_CODE(status));
+        }
+        DPRINTF("boot1 imagepath: %s\n", devpath_str(imgpath));
+
+        /* Allocate space for the handles */
+	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
+	if ((handles = malloc(hsize)) == NULL)
+		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
+		    EFI_ERROR_CODE(status));
+
+	status = BS->LocateHandle(ByProtocol, &SimpleFileSystemProtocolGUID, NULL,
+	    &hsize, handles);
+
+	switch (status) {
+	case EFI_SUCCESS:
+		break;
+	case EFI_BUFFER_TOO_SMALL:
+		(void)BS->FreePool(handles);
+		if ((handles = malloc(hsize)) == NULL) {
+			panic("Failed to allocate %zu handles (%lu)", hsize /
+			    sizeof(*handles), EFI_ERROR_CODE(status));
 		}
+		status = BS->LocateHandle(ByProtocol,
+                    &SimpleFileSystemProtocolGUID, NULL, &hsize, handles);
+		if (status != EFI_SUCCESS)
+			panic("Failed to get device handles (%lu)\n",
+			    EFI_ERROR_CODE(status));
+		break;
+	default:
+		panic("Failed to get device handles (%lu)",
+		    EFI_ERROR_CODE(status));
 	}
 
+	if ((preferred = malloc(hsize)) == NULL)
+		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
+		    EFI_ERROR_CODE(status));
+
+        npreferred = 0;
+	nhandles = hsize / sizeof(*handles);
+
+        /* Figure out which handles are preferred */
+        for (i = 0; i < nhandles; i++) {
+                if (BS->OpenProtocol(handles[i], &DevicePathGUID,
+                    (void **)&devpath, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL) ==
+                    EFI_SUCCESS) {
+                        if (device_paths_match(imgpath, devpath)) {
+                                preferred[npreferred] = handles[i];
+                                npreferred++;
+                        }
+                        BS->CloseProtocol(handles[i], &DevicePathGUID, IH, NULL);
+                }
+        }
+
+        BS->CloseProtocol(boot_handle, &DevicePathGUID, IH, NULL);
+
+        /* Try the preferred handles first, then all the handles */
+        for (i = 0; i < npreferred; i++) {
+                if (efi_load(preferred[i], PATH_LOADER_EFI, bufp, bufsize) ==
+                   EFI_SUCCESS) {
+                        *handlep = preferred[i];
+                        return (EFI_SUCCESS);
+                }
+        }
+
+        for (i = 0; i < nhandles; i++) {
+                if (efi_load(handles[i], PATH_LOADER_EFI, bufp, bufsize) ==
+                   EFI_SUCCESS) {
+                        *handlep = handles[i];
+                        return (EFI_SUCCESS);
+                }
+        }
+
+        printf("Failed to load %s from any device!\n", PATH_LOADER_EFI);
+
 	return (EFI_NOT_FOUND);
 }
 
@@ -359,20 +575,27 @@ try_boot(void)
 	size_t bufsize, loadersize, cmdsize;
 	void *buf, *loaderbuf;
 	char *cmd;
-	dev_info_t *dev;
-	const boot_module_t *mod;
+        EFI_HANDLE fshandle;
 	EFI_HANDLE loaderhandle;
 	EFI_LOADED_IMAGE *loaded_image;
 	EFI_STATUS status;
+        EFI_DEVICE_PATH *fspath;
+
+	status = load_loader(&fshandle, &loaderbuf, &loadersize);
 
-	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
-	if (status != EFI_SUCCESS) {
-		status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
-		    FALSE);
+        if (status != EFI_SUCCESS) {
+                return (status);
+        }
+
+	fspath = NULL;
+	if (status == EFI_SUCCESS) {
+		status = BS->OpenProtocol(fshandle, &DevicePathGUID,
+                    (void **)&fspath, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 		if (status != EFI_SUCCESS) {
-			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
-			return (status);
-		}
+			DPRINTF("Failed to get image DevicePath (%lu)\n",
+			    EFI_ERROR_CODE(status));
+                }
+		DPRINTF("filesystem device path: %s\n", devpath_str(fspath));
 	}
 
 	/*
@@ -387,9 +610,9 @@ try_boot(void)
 	 */
 	cmd = NULL;
 	cmdsize = 0;
-	status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
+	status = efi_load(fshandle, PATH_DOTCONFIG, &buf, &bufsize);
 	if (status == EFI_NOT_FOUND)
-		status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
+		status = efi_load(fshandle, PATH_CONFIG, &buf, &bufsize);
 	if (status == EFI_SUCCESS) {
 		cmdsize = bufsize + 1;
 		cmd = malloc(cmdsize);
@@ -401,24 +624,25 @@ try_boot(void)
 		buf = NULL;
 	}
 
-	if ((status = bs->LoadImage(TRUE, image, devpath_last(dev->devpath),
+	if ((status = BS->LoadImage(TRUE, IH, devpath_last(fspath),
 	    loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
-		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
-		     mod->name, loadersize, EFI_ERROR_CODE(status));
+		printf("Failed to load image, size: %zu, (%lu)\n",
+		     loadersize, EFI_ERROR_CODE(status));
 		goto errout;
 	}
 
-	if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID,
-	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
-		printf("Failed to query LoadedImage provided by %s (%lu)\n",
-		    mod->name, EFI_ERROR_CODE(status));
+	if ((status = BS->OpenProtocol(loaderhandle, &LoadedImageGUID,
+            (VOID**)&loaded_image, IH, NULL,
+            EFI_OPEN_PROTOCOL_GET_PROTOCOL)) != EFI_SUCCESS) {
+		printf("Failed to query LoadedImage (%lu)\n",
+		    EFI_ERROR_CODE(status));
 		goto errout;
 	}
 
 	if (cmd != NULL)
 		printf("    command args: %s\n", cmd);
 
-	loaded_image->DeviceHandle = dev->devhandle;
+	loaded_image->DeviceHandle = fshandle;
 	loaded_image->LoadOptionsSize = cmdsize;
 	loaded_image->LoadOptions = cmd;
 
@@ -434,10 +658,10 @@ try_boot(void)
 	DSTALL(1000000);
 	DPRINTF(".\n");
 
-	if ((status = bs->StartImage(loaderhandle, NULL, NULL)) !=
+	if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
 	    EFI_SUCCESS) {
-		printf("Failed to start image provided by %s (%lu)\n",
-		    mod->name, EFI_ERROR_CODE(status));
+		printf("Failed to start image (%lu)\n",
+		    EFI_ERROR_CODE(status));
 		loaded_image->LoadOptionsSize = 0;
 		loaded_image->LoadOptions = NULL;
 	}
@@ -453,134 +677,19 @@ errout:
 	return (status);
 }
 
-/*
- * probe_handle determines if the passed handle represents a logical partition
- * if it does it uses each module in order to probe it and if successful it
- * returns EFI_SUCCESS.
- */
-static EFI_STATUS
-probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
-{
-	dev_info_t *devinfo;
-	EFI_BLOCK_IO *blkio;
-	EFI_DEVICE_PATH *devpath;
-	EFI_STATUS status;
-	UINTN i;
-
-	/* Figure out if we're dealing with an actual partition. */
-	status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
-	if (status == EFI_UNSUPPORTED)
-		return (status);
-
-	if (status != EFI_SUCCESS) {
-		DPRINTF("\nFailed to query DevicePath (%lu)\n",
-		    EFI_ERROR_CODE(status));
-		return (status);
-	}
-
-	DPRINTF("probing: %s\n", devpath_str(devpath));
-
-	status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
-	if (status == EFI_UNSUPPORTED)
-		return (status);
-
-	if (status != EFI_SUCCESS) {
-		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
-		    EFI_ERROR_CODE(status));
-		return (status);
-	}
-
-	if (!blkio->Media->LogicalPartition)
-		return (EFI_UNSUPPORTED);
-
-	*preferred = device_paths_match(imgpath, devpath);
-
-	/* Run through each module, see if it can load this partition */
-	for (i = 0; i < NUM_BOOT_MODULES; i++) {
-		if ((status = bs->AllocatePool(EfiLoaderData,
-		    sizeof(*devinfo), (void **)&devinfo)) !=
-		    EFI_SUCCESS) {
-			DPRINTF("\nFailed to allocate devinfo (%lu)\n",
-			    EFI_ERROR_CODE(status));
-			continue;
-		}
-		devinfo->dev = blkio;
-		devinfo->devpath = devpath;
-		devinfo->devhandle = h;
-		devinfo->devdata = NULL;
-		devinfo->preferred = *preferred;
-		devinfo->next = NULL;
-
-		status = boot_modules[i]->probe(devinfo);
-		if (status == EFI_SUCCESS)
-			return (EFI_SUCCESS);
-		(void)bs->FreePool(devinfo);
-	}
-
-	return (EFI_UNSUPPORTED);
-}
-
-/*
- * probe_handle_status calls probe_handle and outputs the returned status
- * of the call.
- */
-static void
-probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
-{
-	EFI_STATUS status;
-	BOOLEAN preferred;
-
-	status = probe_handle(h, imgpath, &preferred);
-	
-	DPRINTF("probe: ");
-	switch (status) {
-	case EFI_UNSUPPORTED:
-		printf(".");
-		DPRINTF(" not supported\n");
-		break;
-	case EFI_SUCCESS:
-		if (preferred) {
-			printf("%c", '*');
-			DPRINTF(" supported (preferred)\n");
-		} else {
-			printf("%c", '+');
-			DPRINTF(" supported\n");
-		}
-		break;
-	default:
-		printf("x");
-		DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
-		break;
-	}
-	DSTALL(500000);
-}
-
 EFI_STATUS
-efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
+main(int argc __unused, CHAR16 *argv[] __unused)
 {
-	EFI_HANDLE *handles;
-	EFI_LOADED_IMAGE *img;
-	EFI_DEVICE_PATH *imgpath;
 	EFI_STATUS status;
-	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
 	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
-	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
-
-	/* Basic initialization*/
-	systab = Xsystab;
-	image = Ximage;
-	bs = Xsystab->BootServices;
-
-	/* Set up the console, so printf works. */
-	status = bs->LocateProtocol(&ConsoleControlGUID, NULL,
-	    (VOID **)&ConsoleControl);
-	if (status == EFI_SUCCESS)
-		(void)ConsoleControl->SetMode(ConsoleControl,
-		    EfiConsoleControlScreenText);
+	UINTN i, max_dim, best_mode, cols, rows;
+
+	cons_probe();
+
 	/*
 	 * Reset the console and find the best text mode.
 	 */
-	conout = systab->ConOut;
+	conout = ST->ConOut;
 	conout->Reset(conout, TRUE);
 	max_dim = best_mode = 0;
 	for (i = 0; ; i++) {
@@ -597,123 +706,31 @@ efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
 	conout->EnableCursor(conout, TRUE);
 	conout->ClearScreen(conout);
 
-	printf("\n>> FreeBSD EFI boot block\n");
-	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
-	printf("   Initializing modules:");
-	for (i = 0; i < NUM_BOOT_MODULES; i++) {
-		printf(" %s", boot_modules[i]->name);
-		if (boot_modules[i]->init != NULL)
-			boot_modules[i]->init();
-	}
-	putchar('\n');
-
-	/* Get all the device handles */
-	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
-	if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles))
-	    != EFI_SUCCESS)
-		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
-		    EFI_ERROR_CODE(status));
-
-	status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
-	    &hsize, handles);
-	switch (status) {
-	case EFI_SUCCESS:
-		break;
-	case EFI_BUFFER_TOO_SMALL:
-		(void)bs->FreePool(handles);
-		if ((status = bs->AllocatePool(EfiLoaderData, hsize,
-		    (void **)&handles)) != EFI_SUCCESS) {
-			panic("Failed to allocate %zu handles (%lu)", hsize /
-			    sizeof(*handles), EFI_ERROR_CODE(status));
-		}
-		status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
-		    NULL, &hsize, handles);
-		if (status != EFI_SUCCESS)
-			panic("Failed to get device handles (%lu)\n",
-			    EFI_ERROR_CODE(status));
-		break;
-	default:
-		panic("Failed to get device handles (%lu)",
-		    EFI_ERROR_CODE(status));
-	}
+	/*
+	 * Initialise the block cache. Set the upper limit.
+	 */
+	bcache_init(32768, 512);
 
-	/* Scan all partitions, probing with all modules. */
-	nhandles = hsize / sizeof(*handles);
-	printf("   Probing %zu block devices...", nhandles);
-	DPRINTF("\n");
+	printf("\n>> FreeBSD EFI boot block\n");
 
-	/* Determine the devpath of our image so we can prefer it. */
-	status = bs->HandleProtocol(image, &LoadedImageGUID, (VOID**)&img);
-	imgpath = NULL;
-	if (status == EFI_SUCCESS) {
-		status = bs->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
-		    (void **)&imgpath);
-		if (status != EFI_SUCCESS)
-			DPRINTF("Failed to get image DevicePath (%lu)\n",
-			    EFI_ERROR_CODE(status));
-		DPRINTF("boot1 imagepath: %s\n", devpath_str(imgpath));
-	}
+	archsw.arch_autoload = efi_autoload;
+	archsw.arch_getdev = efi_getdev;
+	archsw.arch_copyin = efi_copyin;
+	archsw.arch_copyout = efi_copyout;
+	archsw.arch_readin = efi_readin;
 
-	for (i = 0; i < nhandles; i++)
-		probe_handle_status(handles[i], imgpath);
-	printf(" done\n");
+	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
+	printf("   Initializing modules:");
 
-	/* Status summary. */
-	for (i = 0; i < NUM_BOOT_MODULES; i++) {
-		printf("    ");
-		boot_modules[i]->status();
+	for (i = 0; efi_drivers[i] != NULL; i++) {
+		printf(" %s", efi_drivers[i]->name);
+		if (efi_drivers[i]->init != NULL)
+			efi_drivers[i]->init();
 	}
+	putchar('\n');
 
 	try_boot();
 
 	/* If we get here, we're out of luck... */
 	panic("No bootable partitions found!");
 }
-
-/*
- * add_device adds a device to the passed devinfo list.
- */
-void
-add_device(dev_info_t **devinfop, dev_info_t *devinfo)
-{
-	dev_info_t *dev;
-
-	if (*devinfop == NULL) {
-		*devinfop = devinfo;
-		return;
-	}
-
-	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
-		;
-
-	dev->next = devinfo;
-}
-
-void
-panic(const char *fmt, ...)
-{
-	va_list ap;
-
-	printf("panic: ");
-	va_start(ap, fmt);
-	vprintf(fmt, ap);
-	va_end(ap);
-	printf("\n");
-
-	while (1) {}
-}
-
-void
-putchar(int c)
-{
-	CHAR16 buf[2];
-
-	if (c == '\n') {
-		buf[0] = '\r';
-		buf[1] = 0;
-		systab->ConOut->OutputString(systab->ConOut, buf);
-	}
-	buf[0] = c;
-	buf[1] = 0;
-	systab->ConOut->OutputString(systab->ConOut, buf);
-}
diff --git sys/boot/efi/boot1/boot_module.h sys/boot/efi/boot1/boot_module.h
deleted file mode 100644
index 296d5a67a10..00000000000
--- sys/boot/efi/boot1/boot_module.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*-
- * 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$
- */
-
-#ifndef _BOOT_MODULE_H_
-#define _BOOT_MODULE_H_
-
-#include <stdbool.h>
-
-#include <efi.h>
-#include <efilib.h>
-#include <eficonsctl.h>
-
-#ifdef EFI_DEBUG
-#define DPRINTF(fmt, args...) printf(fmt, ##args)
-#define DSTALL(d) bs->Stall(d)
-#else
-#define DPRINTF(fmt, ...) {}
-#define DSTALL(d) {}
-#endif
-
-/* EFI device info */
-typedef struct dev_info
-{
-	EFI_BLOCK_IO *dev;
-	EFI_DEVICE_PATH *devpath;
-	EFI_HANDLE *devhandle;
-	void *devdata;
-	BOOLEAN preferred;
-	struct dev_info *next;
-} dev_info_t;
-
-/*
- * A boot loader module.
- *
- * This is a standard interface for filesystem modules in the EFI system.
- */
-typedef struct boot_module_t
-{
-	const char *name;
-
-	/* init is the optional initialiser for the module. */
-	void (*init)();
-
-	/*
-	 * probe checks to see if the module can handle dev.
-	 *
-	 * Return codes:
-	 * EFI_SUCCESS = The module can handle the device.
-	 * EFI_NOT_FOUND = The module can not handle the device.
-	 * Other = The module encountered an error.
-	 */
-	EFI_STATUS (*probe)(dev_info_t* dev);
-
-	/*
-	 * load should select the best out of a set of devices that probe
-	 * indicated were loadable and load the specified file.
-	 *
-	 * Return codes:
-	 * EFI_SUCCESS = The module can handle the device.
-	 * EFI_NOT_FOUND = The module can not handle the device.
-	 * Other = The module encountered an error.
-	 */
-	EFI_STATUS (*load)(const char *filepath, dev_info_t *devinfo,
-	    void **buf, size_t *bufsize);
-
-	/* status outputs information about the probed devices. */
-	void (*status)();
-
-	/* valid devices as found by probe. */
-	dev_info_t *(*devices)();
-} boot_module_t;
-
-/* Standard boot modules. */
-#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);
-extern void panic(const char *fmt, ...) __dead2;
-extern int printf(const char *fmt, ...);
-extern int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
-
-extern EFI_SYSTEM_TABLE *systab;
-extern EFI_BOOT_SERVICES *bs;
-
-extern int devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath);
-extern char *devpath_str(EFI_DEVICE_PATH *devpath);
-#endif
diff --git sys/boot/efi/boot1/fat-amd64.tmpl.bz2.uu sys/boot/efi/boot1/fat-amd64.tmpl.bz2.uu
index a3ec32d1451..c7308a47985 100644
--- sys/boot/efi/boot1/fat-amd64.tmpl.bz2.uu
+++ sys/boot/efi/boot1/fat-amd64.tmpl.bz2.uu
@@ -2,25 +2,50 @@ FAT template boot filesystem created by generate-fat.sh
 DO NOT EDIT
 $FreeBSD$
 begin 644 fat-amd64.tmpl.bz2
-M0EIH.3%!62936?$)3$,`&U9_____Z^KJZ_^N_^O^Z_^[ON_NK^JJ^KZNKNNJ
-MZNKNZOJ^P`+\#$!0$#1D-,@#)DT#1B&AIDQ,AD:#1HTR`-#)D80Q!D:,)D``
-M#1 at F30```&@`$JJ9!_HU4:",3!,F0&C(83`F30T`T!B&!,F`(P3`"#0,@`/2
-M::`#0!D`,C1`T9#3(`R9-`T8AH:9,3(9&@T:-,@#0R9&$,09&C"9```T8)DT
-M```!H``JBD at _2B:FF9)IHQ/1#:@-`:`R#(T`&@:-`!H8FAH:::'J::-`9&1Z
-MFF!'HAB8U/1/*8---3EV+*<ZL\S9BXEC[6I",BE$0BDA$1$%G66TH6PF$0(0
-MXDA!"&<HJ,=-W=K>PZ5:L^\19>>T2K3OA>TUS8M>VJ_;9P7"8;&7&8R51N>F
-M1E%A#6+F9*H@$(9J<K2V7=DH[YSK)LDN618]0S$3"M6JK5JU:M9J*5)44444
-M45JU%:5::U:M6K<:T42HHHHHHYI:J)44444,+B;R5>%%%%%%%$>!!!!!!!!!
-M!!!!!!!#76C@<C1HT:-&C1!!!!!!!!!TX&#!@P8,&#J8Y*BBBBBBBB'@<#!@
-MP8,&#"$4*******.?ZO)U59NW9Y_5RB9=SGK%AO91D/,RW:JUEWJ7CICT$M,
-M]]>OM;-N%^W[!<=H[<-1<QY)W<`%BRT<D`+3375[\FOS\YBD0`RSL8,UV51>
-MRR"SVLL>LG*Z1G+K%N;);>IIYL-7L*D<B5U+2/2:5J6K7K+ON7S<-ZW[`8;$
-M9ESS6Y>[Q-SH;5D;RU_+ZK.+TEMX%K(QQ813V:GS1ZT;"R\!H$K/Z:=%386M
-MPJL.<K?C*J6 at DI)\#6KU=, at V3:-JW3>.`X;BN0SS2,I;U*I;;.]O5ZMI9CMV
-MY(;8*\CXX-SPW(^Z5WIWPW0V@?7DLGZ at QJQHC6!J-\U[>@T$2F)3"7UM2V+:
-M+YMFW;MO6^<- at L)R7+<WLVJEO,&HNI+>XU0JB(L,KI:F[OD%ZUC=-0WB\7C?
-M."P'%83CL1BM$RLHD):&7,DOSLY1=2Y,H_EOG`<%YS0,!Q&$XS]')8CXEXP_
-MFX2-7*,#%P;!Q]V)E#BO"83#?\V3C..Y#E,1?L5S&,NC%DQI>;+`DS>NQL5?
-?+6IF915V$0"$)@`M+"Q)@(_9+!?^+N2*<*$AXA*8A@``
+M0EIH.3%!629363$"%&\`'YA______________^O^Z_^[_N[^J^NK^KZOKONJ
+MZNK^ZN[^X`C_%-``HH`%41"1(*)`BJ!H:```?M5```#0````````-`:-#0``
+M`````````````555$_2@!O_U2J/_TJA_JJ>4!H````T``````&@`````:```
+M!H``>H`&@!HR`0,F3(!DR``TP0T`:``#0Q#30#(``-`-`R&@&@!D!H&FC$&C
+M0``&@`#0 at 9,F0#)D`!I at AH`T``&AB&F@&0``:`:!D-`-`#(#0--&(-&@``-`
+M`&A`R9,@&3(`#3!#0!H``-#$--`,@``T`T#(:`:`&0&@::,0:-```:``-`55
+M(_23]"13-3TIZJ?[*D:$_]5-3TVBFU-J;1&3)A'H0\F2/4Q&GJ!ZF`3U#U'E
+M/031B8U#$9'J!ZAM(\*#R#0,4S4_5,U/0VJ>$\B*9O7.9HQ&K at F!RV88885S
+M,&#####&POFPP!@<W- at R&;#`PPP'JJLYET/0LJ/Q4O7O:*/<NO9^T?#=J^2_
+MP?TNZ?0=X\)X;Q7EM7G/.YW1L-K_B,,,,,,,`%@W4$\J!`.@(!`*@0"!#`("
+M`@("`@("`@("`@("Z!^`\PXGK&QR'F\,S<S-S<EN;E6Y.YN;FYN;FYN;FYN;
+MFYN;G`Y'/<B'(Y%'(Y'(Y'(Y'(Y%UUUUUUUUUUV.LQU,=FL9CLJQF5LU%@RS
+M4\;0JS*JJJJJN@:E6IF79EUUUUUW\7J[?\YL+KKKKKKI:56E&&=999999*4I
+M2E*4K*K.F[3,Q49,F3)DR2E*4I2E+)5DUT0NNNNNNX/2=K========+4JU.\
+MHA======QJLEF2&=======S/09M>?ILGJGM'9/CNX=\\1]7S2JJJJJJJ7IE7
+M7(9UEEEEEDI2E*4I2LJLAG6666662E*4I2E*RJR&=999999?K++++++++)9*
+MLD,ZRRRRRS*JJJJJJJJ5U5W@=3BHR9,F3)DY555555554M*K2AG6666662E*
+M4I2E*RJR&=999999*RJR&=999999M]ST6EDR8Z75L[4S8=BSC1D=PZ]J#RWA
+M*>XSS+OI9+,OP:!G,`[#(RG-'!93*8`$4]KF#`!H][S_R?Y_E]3FG`P`><?@
+MNJ;UF]O;V]O;V]O;V]O;V]O;V]O;V]SSV3B>;;6UBVMK0VMK:VMK:VMK:VMK
+M:VMK:VMK:Z^ZZZZZZZZZZZZZZZZZZZZZZZ[+MSB;FU1#B<MPN4TLDI2E*4I2
+MNJNAG6666663A*4I2E*5E5GGZ(777777=955555555+)5DAG6666662E*4I2
+ME*RJR&=999999\BJJJJJJJJ5U5W1.57,Z%+0TM+2EI:4M*K2AG6666662[!V
+MCX[MW>/J/_7C^C55555552ZG]E9V3I_)YDL6IJ:FIJ:DI2E*4I2TJM+G^S^#
+MFT,6EI:6EI:4I2E*4I2TJM+^;^[%1DR9,F3)*4I2E*4LE62&=999999*4I2E
+M*4K*K(9UEEEEEDI2E*4I2LJL^[1"ZZZZZZ4I2E*4I7570SK+++++/R*KK+H9
+MUUUUUUUE55G3M,9UIK<&G/7:>^5EGWHT*,&#!$1$1$<#@<#@=(_=<MT;HN@=
+MMQN-QN-QN-QN-QN-QN-QN-QLPS6P_W[?"V6666666666666669#(9#(9;==2
+M\CT]5554?T&9111]'`7S#H#0T-#0A"$(0A#+][%BQ8NGP]UA\R?SW:OXWQ6M
+M11117Y?*YF7S+[&>',TM+2TO\J***,:***(0A"$(0O1C,9C,3$Q,3,,'G/3>
+MP]YDOJ9]^;^6Z(B(B(B(B(B(Y=WCP6;9%EEEEEEEEEEEEEEEFEQ$1$:0B(B(
+MB(LLLXG%111WF+U'JX['EU555=UW6+%BQ<_U&K0T-#0M1111^K^EBQ8L7*Z'
+MJNBLU,.M?M.S=NIP]AGY<I2EV>SL.W_;U+ZLF3)DA"$.D]GIK*4I0A"'*HHH
+MHA"$(0A#_-W+NW>N^>`^L^RYKQ7CORG#PN%PN%PL&#!@P8,&#!@P>,\UY3/*
+M4I2E*4I2E*4I2(B(B(B(B(B*4IL=_XF+%BQ0A"$(0A"$(0A"'F>UQ8L6+O>X
+M=L_D=VA"$(0A#]BBBBB$(0A"$(0A#F^%\(9\,,-#0T-"$(0^$^>Z5TKZSPV?
+M?D_1H&B?XB(B(B(B(B(CC6=?"R5*4I2E*4I2E*;/99 at P8,&#!@X7"X7"X7"X
+M7`X'$XG$XD(0A"$(0A"'6T444>51111"$(?]._?2?30A"'K****/-!1111"$
+M(;J***/O44440A"$(0A]=X3JWV'-?;>,[I]YSGQ'H'B]MK:VMK:VMK:VMK:V
+MMK:VMK>"\-]ET[8V-C8V-C8V-C8V-C8V*4B(B(B(B(B(BE*4Q,3$A\&BBBB$
+M(0A"$/^_(_3T-#0T(0A#[G?O$>Q<U"$(0A"$(0A2BBBB$(0A"$(0A"$(0^T^
+MV[UXS[C[KQW.?/>0\EY;@<#@1$1$1$1$1F6A=*T2E*4I2E*4I2E*4B(B(B(B
+M(B(BFQL;'@T4440A"'E4444>BHHHH\.BBBB$(0ZCKO*YSG.<YR$(0[ET>+%B
+MQ=;11113L;YQY+#P?@66666KA at 888#-AA@`>5H:&AH:****(?:4440>6S/_G
++WQ=R13A0D#$"%&\`
 `
 end
diff --git sys/boot/efi/boot1/fat-arm.tmpl.bz2.uu sys/boot/efi/boot1/fat-arm.tmpl.bz2.uu
index 23e063ee23d..ee2ee9954f5 100644
--- sys/boot/efi/boot1/fat-arm.tmpl.bz2.uu
+++ sys/boot/efi/boot1/fat-arm.tmpl.bz2.uu
@@ -2,25 +2,50 @@ FAT template boot filesystem created by generate-fat.sh
 DO NOT EDIT
 $FreeBSD$
 begin 644 fat-arm.tmpl.bz2
-M0EIH.3%!62936>#67)H`&U9_____Z^KJZ_ZN_^O^J_^[OJ[NK^JJ^KZNKNNJ
-MZNKNZOJ^P`+\```"``:`T::-`:8 at P@&1D-!H:808)B:#0`T``,$T#!, at PC09
-M#33"9,0`T(`!H#1IHT!IB#"`9&0T&AIA!@F)H-`#0``P30,$R#"-!D--,)DQ
-M`#0@`&@-&FC0&F(,(!D9#0:&F$&"8F at T`-``#!-`P3(,(T&0TTPF3$`-`522
-M$_(H2-DT0T:8C(T!H!ZC30R-&@-`,@&C1H:`:#1M3(T,@:::/1#331FII@)@
-M)Z:)RK5N6<7,O<%[)S4ZD(ODD1"*2"(B(+E99)"P2A$"$.#(((0Z^BHO)97*
-MKNTS2;//!6_,:)5IVI>LPGUM8_)MV&_QO'$<Q>Y;`J/TETR.=50:\2B`0AF9
-M<_.ME;:;0N<9%D%\Q['*&6B4*U:JM6K5JUQ12I)1111116K45I*TJU:M6K<:
-MZ424444448Y-1)111111[F<_6R+%BQ8L6+$X3333333333333333=7Z\E2M6
-MK5JU:M--------->R44444447N#[%BQ8L6+%BQ-M)*******,!0HHHHHHHY[
-M-RR5757;.MS?HE)GL[:M-Q)&/TG09Q6MZ%)XJ4><D]5[S5M>^]M6W8C at ..T=
-MV&HOX\@T$`%JWHY$`-/ZV!A?)K>YEEZ1`#*,&#,]G484F/7/RDR%Y<?]FG8X
-M&-?VUWJ*AR)+Z32//:9[#4M6R;8-FVK<,1OG$8K*LE&JR>4Q?U[J:^S$]EPK
-MD81)^D"<A>%H^>2OU:FLC31KK??,_$ES74S%+)U7JU5D:VQD)R-2U3"7[FWW
-M-DPVV;ANV^9-R&"T;G,A>6EJ3:=EVE6ENKC#PZG.]=9:75*G=>-4N\OA]'=-
-M`L[RN\MM/>Z'8:6Q.RAWU+=HOHDCZ6H?8_!^+:-JVS^6Z;UP'#<ERG-[$W'`
-MJ+^19>>T*HB+3GM+4VVS(NQ)&K?`_9IW\,PS#$;QOG"<-QV*QFB8^2)!)H).
-M7(_ at P9(R$G)DC<L1NV\;UGV_<)Q'&?FY+%?$Z1Q?DWR/?DC at 8W"M'(_<RTBJ
-M2'#=^XK8.,U[CN0Q'_,5MV,Y;F,`QI',DW7EU-_(S>MYF,_-.IT<D58,0"$)
-60`75I:D;]&V2<%_XNY(IPH2'!K+DT```
+M0EIH.3%!629363'ZIQP`'Z#______________^O^J_^[OJ[^J^JK^KZNKONZ
+MZNK^ZN[^X`C_```:*`!()"1(2)!!H&@&09!H&@:,3(T#0`8@`T#"&C0-````
+M-`9&C0````#1D9,F)H!DT8%552:!D9/_]453_]53_54_]53]4``-````````
+M``&@````!H```&@`T:``T!H:`0:!H!D&0:!H&C$R-`T`&(`-`PAHT#0```#0
+M&1HT`````T9&3)B:`9-&(-`T`R#(-`T#1B9&@:`#$`&@80T:!H```!H#(T:`
+M````:,C)DQ-`,FC$&@:`9!D&@:!HQ,C0-`!B`#0,(:-`T````T!D:-`````-
+M&1DR8F@&31 at 55(_2/0E-&HVE-XJ9ZJ3:G_JDC90]3(:&C0:-/4]0/4>H]0;4
+M`#0-&@VIZFFF:FC0:>IHVH>H!IHWHIH>2::#U/4]3#4]&3:FD[$4T=T[;+$:
+MN:8'3:!AAA70P8,,,,,;"^A@#`N-'Q-`PPPP'K:M)GVO:LZ/HDN[1WS1^6_`
+M:?U'@N_>Z>(\)[YX[_!_T\EYCYC5@>EXZ7BZ.V8;VEA at PPP'IM'5[.SK*.LZ
+MRKK.L[UP<'!P<'!P<'!P<'!P<'!U'9MCN7.VGI<-#>T-[>EO;U6].]O;V]O;
+MV]O;V]O;V]O;W,VNC:AM;5&UM;6UM;6UM;6UM;6UM;6UM;6UO;&]SM[M*-[0
+MHT;'3:W*U/-R5:%555557!J5:FA?#0NNNNNN]K]=XU\+KKKKKKI<BKD1AI66
+M66662E*4I2E*RJSN._T,5&;-FS9LTI2E*4I2S59NE1"ZZZZZ[F]7X%UUUUUU
+MUTM2K4]_1"ZZZZZ[LE6:S-#2NNNNNN[;U&CI:>XS>M??OTGN7]#X#RWP_2*J
+MJJJJJI>L5=ZAI6666662E*4I2E*RJS5R#PV';_R52Y7*Y7*Y7*E*4I2E*7*J
+MY4-*RRRRRSFJJJJJJJJE=5=WE$+KKKKKLZJJJJJJJI9JLWP:(777777=UTK+
+M++++++)<BKD0TK+++++)2E*4I2E959#2LLLLLLE959#2LLLLLLZOYW;<C-FQ
+MS#B659EI&4ZEG&51WCM&+S'BJ>X]59]#/KOP:)G,`]MV>'L#N\,,`"*?E:`P
+M`>T]KU?=^%[SNM$X&`#M'S;[=Q6<7%Q<7%Q<7%Q<7%Q<7%Q<7%Q<70[YL?--
+MS<Q;FYDW-S<W-S<W-S<W-S<W,C(R,C(NNNNNNNNNNNNNNNNNNNNZCJ.HZCH;
+MFQN;E$-CIM;E<C-*4I2E*4KJKH:5EEEEEDX2E*4I2E959ZBB%UUUUUW>5555
+M55552S59H:5EEEEEDI2E*4I2LJLAI666666>[JJJJJJJJE=5=Z9RUT/G>TJE
+MJ:FI5J:DM2K4AI6666662]@]H_??S/?OA/BO/^H55555552[K\A9W[U/R^WE
+MBU-34U-34E*4I2E*7(JY'5]QBHS9LV;-FE*4I2E*6:K-#2LLLLLLE*4I2E*5
+ME5D-*RRRRRR4I2E*4I6560TK+++++)2E*4I2E959\FB%UUUUUTI2E*4I2NJN
+MAI666666?3U7670TKKKKKKK,LRS+NK:<SK3VWLPSS,M`U!]Z/Y1 at P8(B(B(B
+M(B,VZAJ+86J93.V666666666666=#H=#AZ?L=SW>QL;&QL;&QL;&QL;&QL;&
+MQO;V]O<OK-W8[ZJJJJ/"-"BBCR,!?0.!DR9,D(0A[RBBBCD_1Q8L6+N,/S,/
+M#G[IX#P7[CN<*****^'R]OGKOIYT:>WY'(Y'(_LHHHHA"$(0A"$(0O1111"$
+M(=DUOZGO77?Y/@O^7E/C/.?4N9S.9$1$1$1$1'-N^>(\EJM[++++++++++++
+M+++,O$1$1IB(B(B(CH=#H=#HHHH\7%]5];'LNG5555U^OBQ8L75]5JR9,F2V
+M>+%BQ?@^OQ8L6+E[7[/YRS[AX3]5XC7^+S=.4I2\#G_&\3V6I?5FS9LT(0A]
+M]G.3)DR0A"&VBBBB$(0A"$/>O?/%>,_T?ZO]WE/_7F//>N:];6UM;!@P8,&#
+M!@P8,&#RG7/-9Y2E*4I2E*4I2E*1$1$1$1$1$12G.YWD>7BQ8L4(0A"$(0A"
+M$(0AV3#J^!DR9,F3X'];W3P7C(0A"$(0A"$/PJ***(0A"$(0Y^CVXTX889,F
+M3)"$(>W>.S;-OG?4^UH7Y-$T;_$1$1$1$1$1$<>]YGV at 4I2E*4I2E*4IDS3-
+M,&#!@UM;6UM;6UM;6UN9S-C8V-B$(0A"$(0A#[:BBBCT:***(0A#_-X[X3X:
+M$(0]=1111"$(>D"BBBC?\C%BQ8O.HHHHA"$(0A#R7_;[5_X^*^.\U[%YSY3W
+M#U+_[W/2=)TG2=)BQ8L6+%BQ8L6+['W-"VQ2E*4I2E*4I2E*1$1$1$1$1$12
+ME.="$(>VHHHHA"$.%%%%'E]CU^3)DR0A"'7\A\1WKXR$(0A"$(0A"E%%%$(0
+MA"$(0A"$(0AUWF/Z7FOD/DO/?*>.[#Y;_D1$1$1$1$1$7:-T[^E*4I2E*4I2
+ME*4I$1$1$1$1',YG,YW.YW.\FBBBB$(0]&BBBCK4444>51111"$(>J^[]'T'
+MH/0>@A"$/[GI\6+%B[RBBBBGL<6+%BICA at 888#1AA@`>CDR9,F5%%%$/CJ**
+1(/F-#S7_XNY(IPH2!C]4XX``
 `
 end
diff --git sys/boot/efi/boot1/fat-arm64.tmpl.bz2.uu sys/boot/efi/boot1/fat-arm64.tmpl.bz2.uu
index 1101deb0227..1fa41380542 100644
--- sys/boot/efi/boot1/fat-arm64.tmpl.bz2.uu
+++ sys/boot/efi/boot1/fat-arm64.tmpl.bz2.uu
@@ -2,25 +2,49 @@ FAT template boot filesystem created by generate-fat.sh
 DO NOT EDIT
 $FreeBSD$
 begin 644 fat-arm64.tmpl.bz2
-M0EIH.3%!629364C65#T`&U;_____Z^KJZ_^N_^O^J_^[OJ[NK^JJ^KZNKNNJ
-MZNKNZOJ^P`+\#0``0`#0#)D&@TR8AD`,0!D--&`@:!H&@`!B#)IHR:--#(9,
-MAA`8F at Q,0,#52`_U4?J@`&AH```:`:``````T-`T&@R&@T``````````$``T
-M`R9!H-,F(9`#$`9#31@(&@:!H``8 at R::,FC30R&3(80&)H,3$#`521(]I1I)
-MF*:8C1Z`F0:`TR,@:&C30:`R!HTT,$!DTT-J:&@R&C)IA,:F33(S4]-3`--$
-MZ--B6=6F1M!<28FJA"+E)$0B<@B(B"TK+:2%L)0B!"'(D$$(9F:@LRO+Q;TL
-MNJ9YY2QZK1*-0US5+UM6R;E?-T_QQG*=-<9#J:#>R[!&-40;(2B`0AE9=956
-MO+"IH&,8M=KEBF(3,A$H5JU%:M6K5K1-.A)------6K35I*TJU:M6K<RU323
-M333338A4FDFFFFFFMX;;,<,#`P,#`P,".`@0($"!`@0($"!`@0J5,OJY*%:M
-M6K5JU:I4J5*E2I4J*E2I4J5*EQ<9OW>[LK*RLK*RLK*INY)IIIIIIL6F3333
-M333=7VLL91VEN[UP[M$I,5X--+ at R1:XKTL?G%LL>,D\Y*-*DU+6MB^YMF\7S
-MB,!S6CMPUEU'H'B0`4V-'(@!JKN]^?Z<]*UB`&/9J#*9N at O9.H6FYDN5AT,L
-MS-UAW-A;:O3J61R=".=)EY-(TK3M8US8NM;1N&\<%Q%^Y3"9%=8Z-AUMYA;W
-MPYKK)5;?@6D;$DWL"J0LE*.Q]RA>Q[,;.QH%VDM/KGE)[.JBX4T=56_.0JD:
-MUL&Q73$OR;=NF^?PXKCL%SF=:1FKO&W%A1)O,YWU&FM;1N]W0QW=6U*UDS]Q
-M0M\CN\G:L^KT%O2U%G0;335JJYFAG8I,5$D?:UC\'Z/U;QO7`<)_3CL!R7/=
-M%B=L<+`H+J1;6??%$1%*H8[34M_N$+V0O6O;YJ7!95E7%<9?L%R7-83#:)BY
-M(D$F?DZ4B^,W)%W)SY(X;B/[<9?L\P'_-*YC<N>PGRLLY=[?HU\D?[A\BDOL
-MP)20P6A<ERG+;5S'-<YT&$OF&Z3IKLPY'3DPI,"1F-ET\-^SUZ&2DBC-1`(0
-6E`!:TJ9&`C?I,%_XNY(IPH2"1K*AZ```
+M0EIH.3%!629366M+$VP`'Z%______________^O^J_^[OJ[^J^NK^KZNK_NJ
+MZNK^ZN[^X`C_%``&B@`)"0D2$B0&JJ:8F`F``>]5````````````````````
+M``````````*JJDT`&G_^J2I_^I4_RJGJ``!H``-````````&@-`````````&
+M@#0`&AH!`:!H!H`:#30R`&FA at F@T-!IB!H#(#3$``9```:`#",`@`,@:````
+M0&@:`:`&@TT,@!IH8)H-#0:8@:`R`TQ``&0``&@`PC`(`#(&@```$!H&@&@!
+MH--#(`::&":#0T&F(&@, at -,0`!D``!H`,(P"``R!H````*JD?I'H1*;293/%
+M*FVJ;?JJ?JFGM2&F0VB`/4'J`>2&@R:#0#1H-'H3)IY0'J'I`T:&@TTT_1(T
+MVIFH'I-I/334]&3)E/-BFK[5U\L1LVS`Z+4,,,*ZF#!AAAAC87U88`P+CQM8
+MPPPP';U:S/K=9G1]*EW*/6M7YKUS7^J[U[-W[^I_$]^\)V'B/BO(>>V8':L.
+MF[3K4>#JPPP888`5#1,2SH&`V!@,!X#`8(,$"!`@0($"!`@0($"=FZ;LW.[A
+MS-QZ'#4X-3 at X)<'!5P3P<'!P<'!P<'!P<'!P<'!R-SLMR&YN4;FYN;FYN;FY
+MN;FYN;FYN;FYN;G!SN#F<':4<&I1JYW1;70;'DY*M2JJJJJKLVQ5L:E\-2ZZ
+MZZZ[]?[#X%\+KKKKKKI:*M$8:UEEEEEDI2E*4I2LJLZ_L]3%1FS9LV;-*4I2
+ME*4LU6:&M999999R\GU'M<V;-FS9LV:6Q5L=BB%UUUUUW259K,T-:ZZZZZ[K
+M_2:N77Z3-V[\)[)^Z]X^"^._XZJJJJJJJJ7U:KU:&M999999*4I2E*4K*K(:
+MUEEEEEDI2E*4I2LJLAK6666667JJJJJJJJE=5=W5$+KKKKKLZJJJJJJJI9JL
+MWQ-G<9,6C1HT:-$I2E*4I2T5:(:UEEEEEFW0>LPP];FS9LV;-FS2V*MB&M99
+M9999*RJR&M999999U/8=KHS9L='=,NS#1,MG'.,NCMG7,CR7@*>V]-F7T,\N
+M^]H'/8!V'4P_+.YPPP`(I^9J#`![;J>Y_A_D^TU98&`#T+M'<N*SBXN+BXN+
+MBXN+BXN+BXN+BXN+B[)ZQSNJWM[%O;V3>WM[>WM[>WM[>WM[>WM[*93*7777
+M777777777777777777=-TW9-[G;V]1#G=%M=!HS2E*4I2E*ZJZ&M999999.$
+MI2E*4I656>DHA======ZJJJJJJJJJ6:K-#6LLLLLLE*4I2E*5E5D-:RRRRRS
+MW-55555554KJKNLZ%=3T2631HT2T:):*M$-:RRRRRR7Y3VCOG\[L/]WR'E^I
+M55555552[C\A9W;TOS>O+%L;&QL;&Q*4I2E*4M%6CJ=YVV3%KT:-&C1H]3AA
+M99555555+8JV+^C^BR8M&C1HT:)2E*4I2EHJT0UK+++++)2E*4I2E959#6LL
+MLLLLE*4I2E*5E5GRZ(7777772E*4I2E*ZJZ&M999999LJNLNAK7777776556
+M>R7?OLW;-'OVQX;H/CN1Y;D;6UM<CD<B(B(B(C-,XU!L*VJY;N[+++++++++
+M++++++-9S7]^UPF+%BQ8L6+%BQ8L6+%BQ:FZCJ.HZ&K=YOQI2EDCOS4HHH^'
+M at +:AZ`R9,F2$(0A"$/K_T\6+%B]+A[##W4_=O:N]=R^@44445_FZ'7S]U?7S
+M(U^CT:-&C^ZBBBB$(0A"$(0A"]%%%$(0ATFU_8]Z[#X+XCQ'BO'>4^L<B(B(
+MB(B(B(CDG=/!9MF[ZI9999999999999999I<1$1&D(B(B.1R.1TG2=)TG/11
+M1X&+ZWMX]CT:JJJNQV,6+%B]!VVS)DR9+4444>NZN+%BQ=#K?;^BL[IW[NWN
+MW)Z_ER9,F3N^CZ_W?L=%],V;-FA"$/P,YR9,F2$(0Z=%%%$(0A"$(?WO?/`>
+M"_U>&_Z>*\9Y#RWV3;M;6#!@P8,&#!@P8,&#!XSJGD/54I2E*4I2E*4I2D1$
+M1$1$1$1$<SF<SF>%_[BQ8L4(0A"$(0A"$(0A\Y[3%BQ8O@>\=\[QX"$(0A"$
+M/>T444?XT4440A"$(0AS=EWHUX889,F3)"$(=Z^$Z[8GS,^^U^+\V@:%_B(B
+M(B(B(B(B.-9E\#/*4I2E*4I2E*4V;%BP8,&#:VMK:VMK:VMK:Y'(YW.YW.A"
+M$(0A"$(0^ZHHHH\ZBBBB$(0_T?"?#>&A"$/L:***(0A#JA1111PHHHH\JBBB
+MCQZ***(0A#_M\5]P^,\9\EY+L/*?,?N/3/([[E<KE<KE<K(R,C(R,C(R,C(S
+M[['W-J4I2E*4I2E*4I2D1$1$1$1$1$4I2D(0AWE%%%$(0A"$(>+YOX.3)DR0
+MA"'RO"=5]Z^0A"$(0A"$(0I1111"$(0A"$(0A"$(?)>0\%Y+Y3Y;RWS'A/-?
+M->>B(B(B(B(B(B[0ND?TI2E*4I2E*4I2E(B(B(B(B(CD<CF<SF<SQ****(0A
+M#SJ***/34444?^44440A"'TWJ_.\QYCS'F(0A#WSM<6+%B]511113\_%BQ8J
+G8X8&&&`U888`'G9,F3)E1111#QU%%$'GM3_Y_^+N2*<*$@UI8FV`
 `
 end
diff --git sys/boot/efi/boot1/fat-i386.tmpl.bz2.uu sys/boot/efi/boot1/fat-i386.tmpl.bz2.uu
index 74c734d2d37..c0440cae421 100644
--- sys/boot/efi/boot1/fat-i386.tmpl.bz2.uu
+++ sys/boot/efi/boot1/fat-i386.tmpl.bz2.uu
@@ -2,25 +2,49 @@ FAT template boot filesystem created by generate-fat.sh
 DO NOT EDIT
 $FreeBSD$
 begin 644 fat-i386.tmpl.bz2
-M0EIH.3%!629361/"1&$`&U;_____Z^KJZ_ZN_^O^J_^[OJ[NK^JJ^KZNKNNJ
-MZNKNZOJ^P`+\#0``0`#0&C31H#3$&$`R,AH-#3"#!,30:`&@`!@F at 8)D&$:#
-M(::83)B`&@:J0:?ZJ/U30`,@```-`,F@``````R-```R``````````$``T!H
-MTT:`TQ!A`,C(:#0TP at P3$T&@!H``8)H&"9!A&@R&FF$R8@!H"JB(GM*:(3VI
-MJ:;29-DF)ZC1H#)IA&0,C1H`R`VH#3$#(#R(R#(T8FFR$P(R8&AHQ3TTU.M/
-M4CH56XJBSBR%$R"%HBA""$HA"$(0*M)8Q06(C!"`@@OXA`@@[^28LHY?+JTZ
-MY4-*\Q4]=JDWP-DUR\;EM6^<1OW+<Q@,99V]K,<"*#>(;UG$,HJ0@""#-QRL
-MK%;U%@U#)LDM5=:+-(N(1 at I4IJ5*E2I54DID4DDDDDE*E)2BI1I4J5*EU;!)
-M%))))))CE"2*222222VAMLQPP,#`P,#`P(X"!`@0($"!`@0($"!"A0[?7Q3*
-M5*E2I4J5"A0H4*%"A04*%"A0H4+.ST&QK*RLK*RLK*RA>Q22222226J1))))
-M)))E.[CDIL[6:1_==<1)HM-I)YW&BA1::S+:)65-0B]%&'M(M<V2\;ANU\XC
-MDN at P6KK!L*\/4/(@`3U-7$@`^+([2[^_Q8V$(`,J\&!FM!,;2+'JN_BM,?5?
-M]<L]7Q:]TM\Q0QVOF&%%<Q:Q[:EL&S7C+-RWS at N,Y+G,!B+A<VSZ\M;XG"\:
-M2OF*-[X56%V1<"`HB+(LZB&<]^9>0NH;>IIW;(JNVEFI;>C')YXV5+]XBB)\
-M[ZEVKL at W3>-^_IQW*<UT6$T3U7N9.RG317VA\.:ZL*J]MKV=;9^M.HBT^EF6
-M68O<S8-.I\NM.KV7E;C6TJ*9&IE4G.PA%#\6O?HW;^%\X#AN0Y3FN at Z;#=9D
-M/V./M9BO$L;+8B:$(3K:ZF<+>(%VV;@O/<5F69<ES'/=%TV"Q&*U3*10B$7C
-MQ3(=>*8X9GXH9&+#BAR'^.4Y;FM,Y[HL!@OY8;$?8S;J7G.0^F*'/Q;^<WV%
-MPQ&*#I-2P'4=5NF"PF&_UB.*Q778RU,6)C18D5_$SNVQL5?-;,[6*$V>A`$$
-7$8`%A.GB7Z'$1=)_XNY(IPH2`GA(C"``
+M0EIH.3%!62936=SO#C@`'YC______________^O^J_^[OJ[^J^NK^KZOKONJ
+MZNK^ZN[^X`C_*``&B@`4"0D2(B0&I*````````#0```````#(-``R```````
+M```````"JJE'ZH`:?_ZDJG_ZJ>JG_JJ````````````````#0``T```````#
+M0:&AZ@$!H&@&@!H--#(`::&":#0T&F(&@, at -,0`!D``!H`,(P"``R!H```!`
+M:!H!H`:#30R`&FA at F@T-!IB!H#(#3$``9```:`#",`@`,@:````0&@:`:`&@
+MTT,@!IH8)H-#0:8@:`R`TQ``&0``&@`PC`(`#(&@````JJ1^E'Z:22>E-/U3
+MU*?_I2GM2)_ZJF3TVJ>H>IH:,C0,T@:;4'J&@:#U--`:`9`>H!ZC30:`>H`>
+MU3U&3TCU--&AZ3T33#4]J:3M1;5],[S;L'-SS`ZK4,LLKZF3)EEEELH8U99`
+MR/+U#+++(9CZ.Z#/T/H6=GI$O6OLUGWK[5KIA8"H%V,<7HTANC4'..@>0_ at A
+MKX(FK!2V=#7*(0A"$(`%PKSW6IUUG7==AUW7>"Z72Z72Z72Z72Z72Z72Z72Z
+M7<NEP>K=N^0//Y:G%J<7%+BXKN*>+BXN+BXN+BXN+BXN+BXM[O]'1HLT:+-&
+MC1HT:-&C1HT:-&C1HT:-'6<'6=NXN5G%J6:N#JN=U',^%M7:EUUUUUW=.9=S
+M-3#4PPPPPP_-]37^&K+######"6Y=N1EK4I2E*2E*4I2E*EU.Q^3J;%F;-FS
+M9LTI2E*4I2S79H:U*4I2G;;_3^TS9LV;-FS9I<R[F=FR&&&&&&'0NS4S0UL,
+M,,,,.QWVKMM?R^;Z1]H_%?JO%>Y>^?Z]TNNNNNNNEZA=X*&M2E*4I*4I2E*4
+MJ74AK4I2E*2E*4I2E*EU(:U*4I2F/JZ4I2E*4EFNS0UJ4I2E,[KKKKKKKI87
+M8>3S>KVMC<W-S<W-R4I2E*4I;EVY#6I2E*4E*4I2E*5+J0UJ4I2E)4NI#6I2
+ME*4T^^^3W,V;9N>M:W,U9>R>P:"CMW8,'DLXI[;TV+Z7QKOP9]SE at S>HY7-G
+M"965E`$6]AJ#(!^7UO;>U_<[_5MR,@'H'R+ZIR4Y.3DY.3DY.3DY.3DY.3DY
+M.3DY.X>N<'=-&C8T:-K1HT:-&C1HT:-&C1HT:-&CN7<KKKKKKKKKKKKKKKKK
+MKKKKKL6GLMHT60X.JYW4;F:4I2E*4I8780UJ4I2E)RE*4I2E*EU.]LAAAAAA
+MAX%UUUUUUUTLUV:&M2E*4I*4I2E*4J74AK4I2E*>O]M2E*4I2DLUV;T3??4[
+MNTMK<W-R[<W);EU(:U*4I2DONWLWB/WW9>[>_>9Z5=======+O_"4_&?,?&[
+M$MCF<SF<SF8YFJ[T?8PPPPPPPIU%V;K^'L69LV;-FS2E*4I2E+-=FAK4I2E*
+M2E*4I2E*EU(:U*4I2DI2E*4I2I=2&M2E*4I*4I2E*4J74^)9######"4I2E*
+M4I878>F960PPPPPPNNPIA#6PPPG###"E-(=.TEWK2FVLT]9FGRM+9U'\HLLL
+MB(B(C>WM[>WNP]DZKNWT'7>)T.AT.AT.AT.AT.AT.AT.AT.AZ#L=KQ_6<'!P
+M<'!P<'!P<'!P<'!P<'6=9UG6=3O=.UJE*4H]J:EEEG^60K4.N=,I2E"$(0A"
+M'T7X>QL;&QWV7L<O;SX#V;\]^F[U9999?]WJ=C/V^."(U=7<W-S<_FLLLLA"
+M$(0A"$(0Q9999"$(=#G>*_H>,]P_S?[O*?`?%>H;V]O;V]$1$1$1$<J[MG'B
+M.EU+%BQ8L6+%BQ8L6+%BQ8M'B(B(T1$1$1$1B[AW#N'"RRSL['T/J8_"ZMUU
+MUWC=?QMK:VMKK^EZFUM;6UBRRRSU_?[&QL;&_T7K.[I]8\)^._@;_"[;:VMK
+M:[OJ_=?J?@;F-V;-FS0A"'V.<[6UM;4(0AW-EEED(0A"$(?SOZG9?W/<O)>\
+M>4\M\%YCU3GYW.YW.YW.LLLLLLLLLL\5YCR'J*4I2E*4I2E*4I2(B(B(B(B(
+MB*4I3R/?;&QL;$(0A"$(0A"$(0AY[VFQL;&QX_BO$>&[*$(0A"$*LLLLA"$(
+M0A"$(0\OWOZ`UY99;6UM;4(0A[%[IWCO'O'_#RGY,\S[,?XB(B(B(B(B(CC6
+M;?"^12E*4I2E*4I2FS9;+66666666<[G<[G<[G;V]P<'!P0A"$(0A"$(>!99
+M99YUEEED(0A[AY#R7NT(0AZJRRRSN at LLLLA"$.-EEEGQ;+++(0A"$,C(^I]C
+M at WW/Q9Y^SUW\,QW+!^G>8,&#!@P8,&#!@P8,&#!];[6=;.I2E*4I2E*4I2E(
+MB(B(B(B(B(I2E,C(R,CP[+++(0A"$(0_Z[7KMK:VMJ$(0^'Y#_MX+WZ$(0A"
+M$(0A"UEEED(0A"$(0A"$(0A_X^"\=\)\-\1YC_Y[IVGQGQV]O;V]$1$1$1$1
+M=F.F?TI2E*4I2E*4I2E(B(B(B(B(B(I3MW;O>V6660A"'G6666=]9999_S99
+M99"$(>D^N\[S7FO->:A"$/['H]C8V-CP+++++?@[&QL;%MF61EED-6660!YV
+A<#S[+FVTI2&VRRRR'P%EED'QVI_Z__%W)%.%"0W.\..`
 `
 end
diff --git sys/boot/efi/boot1/generate-fat.sh sys/boot/efi/boot1/generate-fat.sh
index d9dfb27de25..7c46e926f60 100755
--- sys/boot/efi/boot1/generate-fat.sh
+++ sys/boot/efi/boot1/generate-fat.sh
@@ -13,7 +13,7 @@
 
 FAT_SIZE=1600 			#Size in 512-byte blocks of the produced image
 
-BOOT1_SIZE=128k
+BOOT1_SIZE=512k
 
 #
 # Known filenames
diff --git sys/boot/efi/boot1/ufs_module.c sys/boot/efi/boot1/ufs_module.c
deleted file mode 100644
index 0860a860fdf..00000000000
--- sys/boot/efi/boot1/ufs_module.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*-
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- * Copyright (c) 2001 Robert Drehmel
- * All rights reserved.
- * Copyright (c) 2014 Nathan Whitehorn
- * All rights reserved.
- * Copyright (c) 2015 Eric McCorkle
- * All rights reverved.
- *
- * 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 <stdarg.h>
-#include <stdbool.h>
-#include <sys/cdefs.h>
-#include <sys/param.h>
-#include <efi.h>
-
-#include "boot_module.h"
-
-static dev_info_t *devinfo;
-static dev_info_t *devices;
-
-static int
-dskread(void *buf, u_int64_t lba, int nblk)
-{
-	int size;
-	EFI_STATUS status;
-
-	lba = lba / (devinfo->dev->Media->BlockSize / DEV_BSIZE);
-	size = nblk * DEV_BSIZE;
-
-	status = devinfo->dev->ReadBlocks(devinfo->dev,
-	    devinfo->dev->Media->MediaId, lba, size, buf);
-
-	if (status != EFI_SUCCESS) {
-		DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
-		    "status: %lu\n", devinfo->dev,
-		    devinfo->dev->Media->MediaId, (uintmax_t)lba, size,
-		    EFI_ERROR_CODE(status));
-		return (-1);
-	}
-
-	return (0);
-}
-
-#include "ufsread.c"
-
-static struct dmadat __dmadat;
-
-static int
-init_dev(dev_info_t* dev)
-{
-
-	devinfo = dev;
-	dmadat = &__dmadat;
-
-	return fsread(0, NULL, 0);
-}
-
-static EFI_STATUS
-probe(dev_info_t* dev)
-{
-
-	if (init_dev(dev) < 0)
-		return (EFI_UNSUPPORTED);
-
-	add_device(&devices, dev);
-
-	return (EFI_SUCCESS);
-}
-
-static EFI_STATUS
-load(const char *filepath, dev_info_t *dev, void **bufp, size_t *bufsize)
-{
-	ufs_ino_t ino;
-	EFI_STATUS status;
-	size_t size;
-	ssize_t read;
-	void *buf;
-
-	DPRINTF("Loading '%s' from %s\n", filepath, devpath_str(dev->devpath));
-
-	if (init_dev(dev) < 0) {
-		DPRINTF("Failed to init device\n");
-		return (EFI_UNSUPPORTED);
-	}
-
-	if ((ino = lookup(filepath)) == 0) {
-		DPRINTF("Failed to lookup '%s' (file not found?)\n", filepath);
-		return (EFI_NOT_FOUND);
-	}
-
-	if (fsread_size(ino, NULL, 0, &size) < 0 || size <= 0) {
-		printf("Failed to read size of '%s' ino: %d\n", filepath, ino);
-		return (EFI_INVALID_PARAMETER);
-	}
-
-	if ((status = bs->AllocatePool(EfiLoaderData, size, &buf)) !=
-	    EFI_SUCCESS) {
-		printf("Failed to allocate read buffer %zu for '%s' (%lu)\n",
-		    size, filepath, EFI_ERROR_CODE(status));
-		return (status);
-	}
-
-	read = fsread(ino, buf, size);
-	if ((size_t)read != size) {
-		printf("Failed to read '%s' (%zd != %zu)\n", filepath, read,
-		    size);
-		(void)bs->FreePool(buf);
-		return (EFI_INVALID_PARAMETER);
-	}
-
-	DPRINTF("Load complete\n");
-
-	*bufp = buf;
-	*bufsize = size;
-
-	return (EFI_SUCCESS);
-}
-
-static void
-status(void)
-{
-	int i;
-	dev_info_t *dev;
-
-	for (dev = devices, i = 0; dev != NULL; dev = dev->next, i++)
-		;
-
-	printf("%s found ", ufs_module.name);
-	switch (i) {
-	case 0:
-		printf("no partitions\n");
-		break;
-	case 1:
-		printf("%d partition\n", i);
-		break;
-	default:
-		printf("%d partitions\n", i);
-	}
-}
-
-static dev_info_t *
-_devices(void)
-{
-
-	return (devices);
-}
-
-const boot_module_t ufs_module =
-{
-	.name = "UFS",
-	.probe = probe,
-	.load = load,
-	.status = status,
-	.devices = _devices
-};
diff --git sys/boot/efi/boot1/zfs_module.c sys/boot/efi/boot1/zfs_module.c
deleted file mode 100644
index 1926a33010d..00000000000
--- sys/boot/efi/boot1/zfs_module.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*-
- * 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: %jd, size: %zu,"
-		    " status: %lu\n", devinfo->dev,
-		    devinfo->dev->Media->MediaId, (intmax_t)lba, bytes,
-		    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
-load(const char *filepath, dev_info_t *devinfo, 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;
-
-	DPRINTF("load: '%s' spa: '%s', devpath: %s\n", filepath, spa->spa_name,
-	    devpath_str(devinfo->devpath));
-
-	if ((err = zfs_spa_init(spa)) != 0) {
-		DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err);
-		return (EFI_NOT_FOUND);
-	}
-
-	if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) {
-		DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err);
-		return (EFI_NOT_FOUND);
-	}
-
-	if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) {
-		if (err == ENOENT) {
-			DPRINTF("Failed to find '%s' on pool '%s' (%d)\n",
-			    filepath, spa->spa_name, err);
-			return (EFI_NOT_FOUND);
-		}
-		printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath,
-		    spa->spa_name, err);
-		return (EFI_INVALID_PARAMETER);
-	}
-
-	if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
-		printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath,
-		    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 %zd for pool '%s' for '%s' "
-		    "(%lu)\n", st.st_size, spa->spa_name, filepath, 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 void
-status(void)
-{
-	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(void)
-{
-
-	zfs_init();
-}
-
-static dev_info_t *
-_devices(void)
-{
-
-	return (devices);
-}
-
-const boot_module_t zfs_module =
-{
-	.name = "ZFS",
-	.init = init,
-	.probe = probe,
-	.load = load,
-	.status = status,
-	.devices = _devices
-};
diff --git sys/boot/efi/drivers/Makefile sys/boot/efi/drivers/Makefile
new file mode 100644
index 00000000000..5e1f18be252
--- /dev/null
+++ sys/boot/efi/drivers/Makefile
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+LIB=	efi_drivers
+INTERNALLIB=
+WARNS?=	2
+
+SRCS=	efipart.c fs_driver.c
+
+.if ${MACHINE_CPUARCH} == "aarch64"
+CFLAGS+=	-msoft-float -mgeneral-regs-only
+.endif
+.if ${MACHINE_ARCH} == "amd64"
+CFLAGS+= -fPIC -mno-red-zone
+.endif
+.if ${MK_ZFS} != "no"
+CFLAGS+=	-I${.CURDIR}/../../zfs
+CFLAGS+=	-I${.CURDIR}/../../../cddl/boot/zfs
+CFLAGS+=	-DEFI_ZFS_BOOT
+.endif
+
+CFLAGS+= -I${.CURDIR}/../include
+CFLAGS+= -I${.CURDIR}/../include/${MACHINE}
+CFLAGS+= -I${.CURDIR}/../../../../lib/libstand
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${.CURDIR}/../../common
+
+# Handle FreeBSD specific %b and %D printf format specifiers
+CFLAGS+= ${FORMAT_EXTENSIONS}
+CFLAGS+= -DTERM_EMU
+
+.include <bsd.lib.mk>
diff --git sys/boot/efi/drivers/efi_drivers.h sys/boot/efi/drivers/efi_drivers.h
new file mode 100644
index 00000000000..0c599d17af9
--- /dev/null
+++ sys/boot/efi/drivers/efi_drivers.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2016 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$
+ */
+
+#ifndef _EFI_DRIVERS_H_
+#define _EFI_DRIVERS_H_
+
+typedef struct efi_driver_t {
+        const char *name;
+        void (*init)(void);
+} efi_driver_t;
+
+extern struct devsw efipart_dev;
+
+/* EFI drivers. */
+extern const efi_driver_t fs_driver;
+
+#endif
diff --git sys/boot/efi/libefi/efipart.c sys/boot/efi/drivers/efipart.c
similarity index 75%
rename from sys/boot/efi/libefi/efipart.c
rename to sys/boot/efi/drivers/efipart.c
index 5a993812bce..3e3a9c04a17 100644
--- sys/boot/efi/libefi/efipart.c
+++ sys/boot/efi/drivers/efipart.c
@@ -74,90 +74,109 @@ static int npdinfo = 0;
 
 #define PD(dev)         (pdinfo[(dev)->d_unit])
 
+static EFI_STATUS
+efipart_supported(EFI_DRIVER_BINDING *This, EFI_HANDLE handle,
+                  EFI_DEVICE_PATH *RemainingDevicePath __unused)
+{
+        return BS->OpenProtocol(handle, &blkio_guid,
+                                NULL, IH, handle,
+                                EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+}
+
+static EFI_STATUS
+efipart_start(EFI_DRIVER_BINDING *This, EFI_HANDLE handle,
+              EFI_DEVICE_PATH *RemainingDevicePath __unused,
+              u_int* ndisk, uint* nrdisk)
+{
+        EFI_BLOCK_IO *blkio;
+        EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
+	EFI_STATUS status;
+        EFI_HANDLE alias;
+
+        devpath = efi_lookup_devpath(handle);
+
+        if (devpath == NULL) {
+                return (EFI_DEVICE_ERROR);
+        }
+
+        status = BS->HandleProtocol(handle, &blkio_guid,
+                                    (void**)&blkio);
+        if (EFI_ERROR(status))
+                return (status);
+
+        if (!blkio->Media->LogicalPartition) {
+                *nrdisk++;
+                return (EFI_UNSUPPORTED);
+        }
+
+        /*
+         * If we come across a logical partition of subtype CDROM
+         * it doesn't refer to the CD filesystem itself, but rather
+         * to any usable El Torito boot image on it. In this case
+         * we try to find the parent device and add that instead as
+         * that will be the CD filesystem.
+         */
+        node = efi_devpath_last_node(devpath);
+        if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+            DevicePathSubType(node) == MEDIA_CDROM_DP) {
+                devpathcpy = efi_devpath_trim(devpath);
+                tmpdevpath = devpathcpy;
+                status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
+                                              &alias);
+                free(devpathcpy);
+
+                if (EFI_ERROR(status))
+                        return (status);
+
+                efi_register_handle(&efipart_dev, handle, alias);
+        } else
+                efi_register_handle(&efipart_dev, handle, NULL);
+
+        pdinfo[npdinfo].pd_open = 0;
+        pdinfo[npdinfo].pd_bcache = NULL;
+        pdinfo[npdinfo].pd_unit = npdinfo;
+        npdinfo++;
+        *ndisk++;
+
+        return (EFI_SUCCESS);
+}
+
 static int
-efipart_init(void) 
+efipart_init(void)
 {
-	EFI_BLOCK_IO *blkio;
-	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
-	EFI_HANDLE *hin, *hout, *aliases, handle;
+	EFI_HANDLE *handles;
 	EFI_STATUS status;
 	UINTN sz;
-	u_int n, nin, nout, nrdisk;
+        u_int n, nin, ndisk, nrdisk;
 	int err;
 
 	sz = 0;
-	hin = NULL;
+	handles = NULL;
 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		hin = (EFI_HANDLE *)malloc(sz * 3);
+		handles = (EFI_HANDLE *)malloc(sz);
 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
-		    hin);
+		    handles);
 		if (EFI_ERROR(status))
-			free(hin);
+			free(handles);
 	}
 	if (EFI_ERROR(status))
 		return (efi_status_to_errno(status));
 
 	/* Filter handles to only include FreeBSD partitions. */
 	nin = sz / sizeof(EFI_HANDLE);
-	hout = hin + nin;
-	aliases = hout + nin;
-	nout = 0;
-	nrdisk = 0;
-
-	bzero(aliases, nin * sizeof(EFI_HANDLE));
 	pdinfo = malloc(nin * sizeof(*pdinfo));
-	if (pdinfo == NULL)
-		return (ENOMEM);
+        ndisk = 0;
+        nrdisk = 0;
 
 	for (n = 0; n < nin; n++) {
-		devpath = efi_lookup_devpath(hin[n]);
-		if (devpath == NULL) {
-			continue;
-		}
-
-		status = BS->HandleProtocol(hin[n], &blkio_guid,
-		    (void**)&blkio);
-		if (EFI_ERROR(status))
-			continue;
-		if (!blkio->Media->LogicalPartition) {
-			nrdisk++;
-			continue;
-		}
-
-		/*
-		 * If we come across a logical partition of subtype CDROM
-		 * it doesn't refer to the CD filesystem itself, but rather
-		 * to any usable El Torito boot image on it. In this case
-		 * we try to find the parent device and add that instead as
-		 * that will be the CD filesystem.
-		 */
-		node = efi_devpath_last_node(devpath);
-		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
-		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
-			devpathcpy = efi_devpath_trim(devpath);
-			tmpdevpath = devpathcpy;
-			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
-			    &handle);
-			free(devpathcpy);
-			if (EFI_ERROR(status))
-				continue;
-			hout[nout] = handle;
-			aliases[nout] = hin[n];
-		} else
-			hout[nout] = hin[n];
-		nout++;
-		pdinfo[npdinfo].pd_open = 0;
-		pdinfo[npdinfo].pd_bcache = NULL;
-		pdinfo[npdinfo].pd_unit = npdinfo;
-		npdinfo++;
+                efipart_start(NULL, handles[n], NULL, &ndisk, &nrdisk);
 	}
 
 	bcache_add_dev(npdinfo);
-	err = efi_register_handles(&efipart_dev, hout, aliases, nout);
-	free(hin);
+	free(handles);
 
-	if (nout == 0 && nrdisk > 0)
+	if (ndisk == 0 && nrdisk > 0)
 		printf("Found %d disk(s) but no logical partition\n", nrdisk);
 	return (err);
 }
diff --git sys/boot/efi/drivers/fs_driver.c sys/boot/efi/drivers/fs_driver.c
new file mode 100644
index 00000000000..b9c85362a71
--- /dev/null
+++ sys/boot/efi/drivers/fs_driver.c
@@ -0,0 +1,853 @@
+/*-
+ * Copyright (c) 2016 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 <efilib.h>
+#include <efiprot.h>
+#include <string16.h>
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+#include <bootstrap.h>
+
+#include "efi_drivers.h"
+
+static EFI_GUID SimpleFileSystemProtocolGUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
+
+static struct fs_ops *backend_file_system[] = {
+	&dosfs_fsops,
+	&ufs_fsops,
+	&cd9660_fsops,
+	&tftp_fsops,
+	&nfs_fsops,
+	&gzipfs_fsops,
+	&bzipfs_fsops,
+	NULL
+};
+
+static struct devsw *backend_devsw[] = {
+	&efipart_dev,
+#ifdef EFI_ZFS_BOOT
+	&zfs_dev,
+#endif
+	NULL
+};
+
+typedef struct volinfo_t {
+        struct fs_ops *fsops;
+        struct devdesc *dev;
+} volinfo_t;
+
+typedef struct fileinfo_t {
+        const volinfo_t* vinfo;
+        struct open_file fdata;
+        char path[];
+} fileinfo_t;
+
+static EFI_GUID FileInfoGUID = EFI_FILE_INFO_ID;;
+
+static EFIAPI EFI_STATUS
+file_open_impl(EFI_FILE_HANDLE File, EFI_FILE_HANDLE *NewHandle,
+               CHAR16 *FileName, UINT64 OpenMode, UINT64 Attributes);
+
+static EFIAPI EFI_STATUS
+file_close_impl(EFI_FILE_HANDLE File);
+
+static EFIAPI EFI_STATUS
+file_delete_impl(EFI_FILE_HANDLE File);
+
+static EFIAPI EFI_STATUS
+dir_read_impl(EFI_FILE_HANDLE File, UINTN *BufferSize, VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+dir_write_impl(EFI_FILE_HANDLE File, UINTN *BufferSize,
+               VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+file_read_impl(EFI_FILE_HANDLE File, UINTN *BufferSize, VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+file_write_impl(EFI_FILE_HANDLE File, UINTN *BufferSize,
+                VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+file_set_position_impl(EFI_FILE_HANDLE File, UINT64 Position);
+
+static EFIAPI EFI_STATUS
+file_get_position_impl(EFI_FILE_HANDLE File, UINT64 *Position);
+
+static EFIAPI EFI_STATUS
+file_get_info_impl(EFI_FILE_HANDLE File, EFI_GUID *InformationType,
+                   UINTN *BufferSize, VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+file_set_info_impl(EFI_FILE_HANDLE File, EFI_GUID *InformationType,
+                   UINTN BufferSize, VOID *Buffer);
+
+static EFIAPI EFI_STATUS
+file_flush_impl(EFI_FILE_HANDLE File);
+
+static struct devdesc*
+clone_devdesc(struct devdesc *dev)
+{
+        struct devdesc *out;
+
+        switch(dev->d_dev->dv_type) {
+        case DEVT_ZFS:
+                out = malloc(sizeof(struct zfs_devdesc));
+
+                if (out == NULL) {
+                        return NULL;
+                }
+
+                memcpy(out, dev, sizeof(struct zfs_devdesc));
+
+                return out;
+
+        default:
+                out = malloc(sizeof(struct devdesc));
+
+                if (out == NULL) {
+                        return NULL;
+                }
+
+                memcpy(out, dev, sizeof(struct devdesc));
+
+                return out;
+        }
+}
+
+static EFI_STATUS
+do_file_open(const volinfo_t *vinfo, const char filepath[],
+             int mode, EFI_FILE_HANDLE *out)
+{
+        EFI_FILE_HANDLE fhandle;
+        fileinfo_t *finfo;
+        struct stat st;
+        struct devdesc *tmpdev;
+	int err;
+
+        memset(&st, 0, sizeof(struct stat));
+        fhandle = malloc(sizeof(EFI_FILE) + sizeof(fileinfo_t) +
+                         strlen(filepath) + 1);
+
+        if (fhandle == NULL) {
+                return (EFI_OUT_OF_RESOURCES);
+        }
+
+        tmpdev = clone_devdesc(vinfo->dev);
+
+        if (tmpdev == NULL) {
+                free(fhandle);
+                return (EFI_OUT_OF_RESOURCES);
+        }
+
+        finfo = (fileinfo_t*)(fhandle + 1);
+        finfo->fdata.f_flags = mode + 1;
+        finfo->fdata.f_dev = NULL;
+        finfo->fdata.f_ops = NULL;
+        finfo->fdata.f_offset = 0;
+        finfo->fdata.f_devdata = NULL;
+
+        if ((err = vinfo->dev->d_dev->dv_open(&(finfo->fdata), tmpdev)) != 0) {
+                free(fhandle);
+                return (errno_to_efi_status(err));
+        }
+
+        finfo->fdata.f_dev = vinfo->dev->d_dev;
+
+        if ((err = vinfo->fsops->fo_open(filepath, &(finfo->fdata))) != 0) {
+                free(fhandle);
+                return (errno_to_efi_status(err));
+        }
+
+        finfo->fdata.f_ops = vinfo->fsops;
+
+        if ((err = vinfo->fsops->fo_stat(&(finfo->fdata), &st)) != 0) {
+                free(fhandle);
+                return (errno_to_efi_status(err));
+        }
+
+        strcpy(finfo->path, filepath);
+        finfo->vinfo = vinfo;
+        fhandle->Revision = EFI_FILE_HANDLE_REVISION;
+        fhandle->Open = file_open_impl;
+        fhandle->Close = file_close_impl;
+        fhandle->Delete = file_delete_impl;
+        fhandle->SetPosition = file_set_position_impl;
+        fhandle->GetPosition = file_get_position_impl;
+        fhandle->GetInfo = file_get_info_impl;
+        fhandle->SetInfo = file_set_info_impl;
+        fhandle->Flush = file_flush_impl;
+
+        if (S_ISDIR(st.st_mode)) {
+                fhandle->Read = dir_read_impl;
+                fhandle->Write = dir_write_impl;
+        } else {
+                fhandle->Read = file_read_impl;
+                fhandle->Write = file_write_impl;
+        }
+
+        *out = fhandle;
+
+        return (EFI_SUCCESS);
+}
+
+static int
+mode_from_efi(UINT64 efi_mode)
+{
+        int mode = 0;
+
+        if (efi_mode & EFI_FILE_MODE_WRITE) {
+                mode |= O_WRONLY;
+        } else if (efi_mode & EFI_FILE_MODE_READ) {
+                mode |= O_RDONLY;
+        } else if (efi_mode & EFI_FILE_MODE_READ &&
+                   efi_mode & EFI_FILE_MODE_WRITE) {
+                mode |= O_RDWR;
+        }
+
+        return mode;
+}
+
+static UINT64
+mode_to_efi(int mode)
+{
+        UINT64 efi_mode = 0;
+
+        if (mode & O_WRONLY) {
+                efi_mode |= EFI_FILE_MODE_WRITE;
+        } else if (mode & O_RDONLY) {
+                efi_mode |= EFI_FILE_MODE_READ;
+        } else if (mode & O_RDWR) {
+                efi_mode |= EFI_FILE_MODE_READ;
+                efi_mode |= EFI_FILE_MODE_WRITE;
+        }
+
+        return (efi_mode);
+}
+
+static EFIAPI EFI_STATUS
+file_open_impl(EFI_FILE_HANDLE File, EFI_FILE_HANDLE *NewHandle,
+               CHAR16 *FileName, UINT64 OpenMode, UINT64 Attributes)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        char buf[strlen(finfo->path) + strlen16(FileName) + 1];
+
+        strcpy_from_16(stpcpy(buf, finfo->path), FileName);
+
+        return do_file_open(finfo->vinfo, buf,
+                            mode_from_efi(OpenMode), NewHandle);
+}
+
+static EFIAPI EFI_STATUS
+file_close_impl(EFI_FILE_HANDLE File)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        int res;
+
+        if ((res = finfo->fdata.f_ops->fo_close(&(finfo->fdata))) != 0) {
+                return (errno_to_efi_status(res));
+        }
+
+        if ((res = finfo->vinfo->dev->d_dev->dv_close(&(finfo->fdata))) != 0) {
+                return (errno_to_efi_status(res));
+        }
+
+        free(File);
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+file_delete_impl(EFI_FILE_HANDLE File)
+{
+        file_close_impl(File);
+
+        return (EFI_WARN_DELETE_FAILURE);
+}
+
+static EFIAPI EFI_STATUS
+dir_read_impl(EFI_FILE_HANDLE File, UINTN *BufferSize, VOID *Buffer)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        fileinfo_t *entinfo;
+        struct dirent d;
+        struct stat st;
+        int err;
+        UINTN currsize = *BufferSize;
+        UINTN reqsize;
+        EFI_FILE_INFO *out = (EFI_FILE_INFO*)Buffer;
+        EFI_FILE_HANDLE enthandle;
+        EFI_STATUS status;
+        off_t currpos;
+
+        /* Record teh current position so we can rewind if we have to */
+        if ((currpos = finfo->fdata.f_ops->fo_seek(&(finfo->fdata),
+                                                  0, SEEK_CUR)) < 0) {
+                return (errno_to_efi_status(errno));
+        }
+
+	if ((err = finfo->fdata.f_ops->fo_readdir(&(finfo->fdata), &d)) != 0) {
+		return (errno_to_efi_status(errno));
+	}
+
+        reqsize = sizeof(EFI_FILE_INFO) +
+          ((strlen(d.d_name) + 1) * sizeof(CHAR16));
+
+        if (Buffer == NULL || currsize < reqsize) {
+                finfo->fdata.f_ops->fo_seek(&(finfo->fdata), currpos, SEEK_SET);
+                *BufferSize = reqsize;
+
+                return (EFI_BUFFER_TOO_SMALL);
+        }
+
+        // We have to actually open the file, since EFI directory
+        // reads are supposed to return stat information.
+        CHAR16 buf[d.d_namlen];
+        strcpy_to_16(buf, d.d_name);
+        status = file_open_impl(File, &enthandle, buf, EFI_FILE_READ_ONLY, 0);
+
+        if (EFI_ERROR(status)) {
+                finfo->fdata.f_ops->fo_seek(&(finfo->fdata), currpos, SEEK_SET);
+                return (status);
+        }
+
+        entinfo = (fileinfo_t*)(enthandle + 1);
+
+        if ((err = finfo->fdata.f_ops->fo_stat(&(entinfo->fdata), &st)) != 0) {
+                finfo->fdata.f_ops->fo_seek(&(finfo->fdata), currpos, SEEK_SET);
+		return (errno_to_efi_status(errno));
+        }
+
+        status = file_close_impl(enthandle);
+
+        if (EFI_ERROR(status)) {
+                finfo->fdata.f_ops->fo_seek(&(finfo->fdata), currpos, SEEK_SET);
+                return (status);
+        }
+
+        // We're good at this point, copy everything into place.
+        out->Size = reqsize;
+        out->FileSize = st.st_size;
+        out->PhysicalSize = st.st_blocks * st.st_blksize;
+        out->Attribute = mode_to_efi(st.st_mode);
+        to_efi_time(&(out->LastAccessTime), st.st_atime);
+        to_efi_time(&(out->ModificationTime), st.st_mtime);
+        to_efi_time(&(out->CreateTime), st.st_ctime);
+
+        if (S_ISDIR(st.st_mode)) {
+                out->Attribute |= EFI_FILE_DIRECTORY;
+        }
+
+        strcpy_to_16(out->FileName, d.d_name);
+        *BufferSize = reqsize;
+
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+dir_write_impl(EFI_FILE_HANDLE File __unused, UINTN *BufferSize __unused,
+               VOID *Buffer __unused)
+{
+        // EFI API doesn't allow writing to directories
+        return (EFI_UNSUPPORTED);
+}
+
+static EFIAPI EFI_STATUS
+file_read_impl(EFI_FILE_HANDLE File, UINTN *BufferSize, VOID *Buffer)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        size_t readsize = *BufferSize;
+        size_t resid;
+        int err;
+
+	if ((err = finfo->fdata.f_ops->fo_read(&(finfo->fdata), Buffer,
+                                               readsize, &resid)) != 0) {
+		return (errno_to_efi_status(errno));
+	}
+
+        *BufferSize = readsize - resid;
+
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+file_write_impl(EFI_FILE_HANDLE File, UINTN *BufferSize, VOID *Buffer)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        size_t writesize = *BufferSize;
+        size_t resid;
+        int err;
+
+	if ((err = finfo->fdata.f_ops->fo_write(&(finfo->fdata), Buffer,
+                                                writesize, &resid)) != 0) {
+		return (errno_to_efi_status(errno));
+	}
+
+        *BufferSize = writesize - resid;
+
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+file_set_position_impl(EFI_FILE_HANDLE File, UINT64 Position)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        int res;
+
+        if (Position != 0xffffffffffffffffLL) {
+                res = finfo->fdata.f_ops->fo_seek(&(finfo->fdata),
+                                                  Position, SEEK_SET);
+        } else {
+                res = finfo->fdata.f_ops->fo_seek(&(finfo->fdata),
+                                                  0, SEEK_END);
+        }
+
+        if (res > 0) {
+                return (EFI_SUCCESS);
+        } else {
+                return (errno_to_efi_status(res));
+        }
+}
+
+
+static EFIAPI EFI_STATUS
+file_get_position_impl(EFI_FILE_HANDLE File, UINT64 *Position)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+        off_t res;
+
+        res = finfo->fdata.f_ops->fo_seek(&(finfo->fdata), 0, SEEK_CUR);
+
+        if (res > 0) {
+                *Position = res;
+
+                return (EFI_SUCCESS);
+        } else {
+                return (errno_to_efi_status(errno));
+        }
+}
+
+static EFIAPI EFI_STATUS
+file_get_info_impl(EFI_FILE_HANDLE File, EFI_GUID *InformationType,
+                   UINTN *BufferSize, VOID *Buffer)
+{
+        fileinfo_t *finfo = (fileinfo_t*)(File + 1);
+
+        if (!memcmp(InformationType, &FileInfoGUID, sizeof(EFI_GUID))) {
+                EFI_FILE_INFO *out = (EFI_FILE_INFO*)Buffer;
+                UINTN str16len =(strlen(finfo->path) + 1) * sizeof(CHAR16);
+                UINTN currsize = *BufferSize;
+                UINTN reqsize = sizeof(EFI_FILE_INFO) + str16len;
+                struct stat st;
+                int err;
+
+                memset(&st, 0, sizeof(struct stat));
+                *BufferSize = reqsize;
+
+                if (Buffer == NULL ||
+                       currsize < sizeof(EFI_FILE_INFO) + str16len) {
+                        *BufferSize = sizeof(EFI_FILE_INFO) + str16len;
+                        return (EFI_BUFFER_TOO_SMALL);
+                }
+
+                if ((err = finfo->fdata.f_ops->fo_stat(&(finfo->fdata),
+                                                      &st)) != 0) {
+       		return (errno_to_efi_status(errno));
+                }
+
+                out->Size = sizeof(EFI_FILE_INFO) + str16len;
+                out->FileSize = st.st_size;
+                out->PhysicalSize = st.st_blocks * st.st_blksize;
+                to_efi_time(&(out->LastAccessTime), st.st_atime);
+                to_efi_time(&(out->ModificationTime), st.st_mtime);
+                to_efi_time(&(out->CreateTime), st.st_ctime);
+                out->Attribute = mode_to_efi(st.st_mode);
+
+                if (S_ISDIR(st.st_mode)) {
+                        out->Attribute |= EFI_FILE_DIRECTORY;
+                }
+
+                strcpy_to_16(out->FileName, finfo->path);
+        } else {
+                return (EFI_UNSUPPORTED);
+        }
+
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+file_set_info_impl(EFI_FILE_HANDLE File __unused,
+                   EFI_GUID *InformationType __unused,
+                   UINTN BufferSize __unused, VOID *Buffer __unused)
+{
+        return (EFI_WRITE_PROTECTED);
+}
+
+static EFIAPI EFI_STATUS
+file_flush_impl(EFI_FILE_HANDLE File __unused)
+{
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+open_volume_impl(EFI_FILE_IO_INTERFACE *This, EFI_FILE_HANDLE *Root)
+{
+        volinfo_t *vinfo = (volinfo_t*)(This + 1);
+
+        return do_file_open(vinfo, "", O_RDONLY, Root);
+}
+
+static struct fs_ops*
+fs_probe(struct devdesc *dev)
+{
+        struct open_file f;
+	int err, i;
+
+        f.f_flags = O_RDONLY + 1;
+        f.f_dev = (struct devsw *)0;
+        f.f_ops = (struct fs_ops *)0;
+        f.f_offset = 0;
+        f.f_devdata = dev;
+
+        if ((err = dev->d_dev->dv_open(&f, dev)) != 0) {
+                return NULL;
+        }
+
+        for (i = 0; backend_file_system[i] != NULL; i++) {
+                f.f_ops = backend_file_system[i];
+
+                if ((err = f.f_ops->fo_open("/", &f)) == 0) {
+                        return backend_file_system[i];
+                }
+        }
+
+        return NULL;
+}
+
+static EFI_STATUS
+make_fs_file_io_iface(struct devdesc *dev, EFI_FILE_IO_INTERFACE **out)
+{
+        static struct fs_ops* fsops;
+        EFI_FILE_IO_INTERFACE* fiface;
+        volinfo_t *vinfo;
+
+        switch (dev->d_type) {
+#ifdef EFI_ZFS_BOOT
+	case DEVT_ZFS:
+                fsops = &zfs_fsops;
+                break;
+#endif
+        default:
+                fsops = fs_probe(dev);
+                break;
+        }
+
+        if (fsops == NULL) {
+                return (EFI_UNSUPPORTED);
+        }
+
+        fiface = malloc(sizeof(EFI_FILE_IO_INTERFACE) + sizeof(volinfo_t));
+
+        if (fiface == NULL) {
+                return (EFI_OUT_OF_RESOURCES);
+        }
+
+        vinfo = (volinfo_t*)(fiface + 1);
+
+        fiface->Revision = EFI_FILE_IO_INTERFACE_REVISION;
+        fiface->OpenVolume = open_volume_impl;
+        vinfo->dev = dev;
+        vinfo->fsops = fsops;
+
+        *out = fiface;
+
+        return (EFI_SUCCESS);
+}
+
+static EFIAPI EFI_STATUS
+bind_iface(EFI_HANDLE handle, struct devdesc *dev)
+{
+        EFI_STATUS status;
+        EFI_FILE_IO_INTERFACE *iface;
+
+        // Check if there is already a filesystem interface
+        status = BS->OpenProtocol(handle, &SimpleFileSystemProtocolGUID,
+            NULL, IH, handle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+
+        if (!EFI_ERROR(status)) {
+                return (EFI_ACCESS_DENIED);
+        } else if (status != EFI_UNSUPPORTED) {
+                return (status);
+        }
+
+        status = make_fs_file_io_iface(dev, &iface);
+
+        if (EFI_ERROR(status)) {
+                return (status);
+        }
+
+        status = BS->InstallMultipleProtocolInterfaces(&handle,
+            &SimpleFileSystemProtocolGUID, iface, NULL);
+
+        if (EFI_ERROR(status)) {
+                free(iface);
+
+                return (status);
+        }
+
+	return (status);
+}
+
+static EFIAPI EFI_STATUS
+stop_impl(EFI_DRIVER_BINDING *This __unused, EFI_HANDLE ControllerHandle __unused,
+          UINTN NumberOfChildren __unused, EFI_HANDLE *ChildHandleBuffer __unused)
+{
+        // Get the protocol inteface, uninstall it, and free it
+        return (EFI_SUCCESS);
+}
+
+#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
+
+static int
+backend_parsedev(struct devdesc **dev, const char *devspec, const char **path)
+{
+	struct devdesc *idev;
+	struct devsw *dv;
+	char *cp;
+	const char *np;
+	int i;
+
+	/* minimum length check */
+	if (strlen(devspec) < 2)
+		return (EINVAL);
+
+	/* look for a device that matches */
+	for (i = 0; backend_devsw[i] != NULL; i++) {
+		dv = backend_devsw[i];
+		if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name)))
+			break;
+	}
+	if (backend_devsw[i] == NULL) {
+		return (ENOENT);
+        }
+
+	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)
+			return (ENOMEM);
+
+		idev->d_dev = dv;
+		idev->d_type = dv->dv_type;
+		idev->d_unit = -1;
+		if (*np != '\0' && *np != ':') {
+			idev->d_unit = strtol(np, &cp, 0);
+			if (cp == np) {
+				idev->d_unit = -1;
+				free(idev);
+				return (EUNIT);
+			}
+		}
+	}
+
+	if (*cp != '\0' && *cp != ':') {
+		free(idev);
+		return (EINVAL);
+	}
+
+	if (path != NULL)
+		*path = (*cp == 0) ? cp : cp + 1;
+	if (dev != NULL)
+		*dev = idev;
+	else
+		free(idev);
+	return (0);
+}
+
+static int
+backend_getdev(void **vdev, const char *devspec, const char **path)
+{
+	struct devdesc **dev = (struct devdesc **)vdev;
+	int rv;
+
+	/*
+	 * If it looks like this is just a path and no device, then
+	 * use the current device instead.
+	 */
+	if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) {
+		rv = backend_parsedev(dev, getenv("currdev"), NULL);
+		if (rv == 0 && path != NULL)
+			*path = devspec;
+		return (rv);
+	}
+
+	/* Parse the device name off the beginning of the devspec. */
+	return (backend_parsedev(dev, devspec, path));
+}
+
+static void
+init(void)
+{
+	EFI_HANDLE h;
+        EFI_STATUS status;
+	u_int unit;
+        int i;
+	struct devsw *dev;
+	uint64_t pool_guid;
+        int (*old_getdev)(void **, const char *, const char **) =
+          archsw.arch_getdev;
+
+	archsw.arch_getdev = backend_getdev;
+#ifdef EFI_ZFS_BOOT
+	/* Note this needs to be set before ZFS init. */
+	archsw.arch_zfs_probe = efi_zfs_probe;
+#endif
+
+        /* Initialize all the backend drivers */
+	for (i = 0; backend_devsw[i] != NULL; i++) {
+		if (backend_devsw[i]->dv_init != NULL)
+			(backend_devsw[i]->dv_init)();
+        }
+
+        /* Attach SIMPLE_FILE_SYSTEM interfaces to all efipart devices */
+	unit = 0;
+	h = efi_find_handle(&efipart_dev, 0);
+	for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) {
+		struct devdesc *currdev;
+
+                currdev = malloc(sizeof(struct devdesc));
+
+                if (currdev == NULL) {
+                        continue;
+                }
+
+                if (efi_handle_lookup(h, &dev, &unit, &pool_guid) != 0) {
+                        free(currdev);
+                        continue;
+                }
+
+		currdev->d_dev = dev;
+		currdev->d_unit = unit;
+		currdev->d_opendata = NULL;
+		currdev->d_type = currdev->d_dev->dv_type;
+
+                status = bind_iface(h, currdev);
+
+                if (EFI_ERROR(status) && status != EFI_UNSUPPORTED &&
+                   status != EFI_ACCESS_DENIED) {
+                         printf("Failed to attach filesystem interface to efipart%u (%ld)\n",
+                                unit, EFI_ERROR_CODE(status));
+                         free(currdev);
+                }
+        }
+
+        /* Attach SIMPLE_FILE_SYSTEM interface to all ZFS devices */
+#ifdef EFI_ZFS_BOOT
+	unit = 0;
+	h = efi_find_handle(&zfs_dev, 0);
+	for (i = 0; h != NULL; h = efi_find_handle(&zfs_dev, ++i)) {
+		struct zfs_devdesc *currdev;
+
+                currdev = malloc(sizeof(struct zfs_devdesc));
+
+                if (currdev == NULL) {
+                        continue;
+                }
+
+                if (efi_handle_lookup(h, &dev, &unit, &pool_guid) != 0) {
+                        free(currdev);
+                        continue;
+                }
+
+		currdev->d_dev = dev;
+		currdev->d_unit = unit;
+		currdev->d_opendata = NULL;
+		currdev->d_type = currdev->d_dev->dv_type;
+                currdev->pool_guid = pool_guid;
+                currdev->root_guid = 0;
+
+                status = bind_iface(h, (struct devdesc*)currdev);
+
+                if (EFI_ERROR(status)) {
+                  printf("Failed to attach filesystem interface to zfs%u (%ld)\n",
+                         unit, EFI_ERROR_CODE(status));
+                }
+        }
+#endif
+        archsw.arch_getdev = old_getdev;
+}
+
+const efi_driver_t fs_driver =
+{
+	.name = "FS Backend",
+	.init = init,
+};
diff --git sys/boot/efi/include/efilib.h sys/boot/efi/include/efilib.h
index 09ccc2f3647..15d4b487d40 100644
--- sys/boot/efi/include/efilib.h
+++ sys/boot/efi/include/efilib.h
@@ -31,22 +31,26 @@
 #define	_LOADER_EFILIB_H
 
 #include <stand.h>
+#include <time.h>
 
 extern EFI_HANDLE		IH;
-extern EFI_SYSTEM_TABLE		*ST;
+extern EFI_SYSTEM_TABLE	*ST;
 extern EFI_BOOT_SERVICES	*BS;
 extern EFI_RUNTIME_SERVICES	*RS;
 
-extern struct devsw efipart_dev;
+extern struct devsw efifs_dev;
 extern struct devsw efinet_dev;
+extern struct fs_ops efifs_fsops;
 extern struct netif_driver efinetif;
 
 void *efi_get_table(EFI_GUID *tbl);
 
 int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int);
+int efi_register_handle(struct devsw *, EFI_HANDLE, EFI_HANDLE);
 EFI_HANDLE efi_find_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_handle_remove_dev(EFI_HANDLE);
 
 EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE);
 EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE);
@@ -57,6 +61,7 @@ CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *);
 void efi_free_devpath_name(CHAR16 *);
 
 int efi_status_to_errno(EFI_STATUS);
+EFI_STATUS errno_to_efi_status(int errno);
 
 void efi_time_init(void);
 void efi_time_fini(void);
@@ -65,4 +70,7 @@ EFI_STATUS main(int argc, CHAR16 *argv[]);
 void exit(EFI_STATUS status);
 void delay(int usecs);
 
+time_t from_efi_time(EFI_TIME *efi_time);
+void to_efi_time(EFI_TIME *efi_time, time_t time);
+
 #endif	/* _LOADER_EFILIB_H */
diff --git sys/boot/efi/include/efiprot.h sys/boot/efi/include/efiprot.h
index 28cec5991e3..22215777cab 100644
--- sys/boot/efi/include/efiprot.h
+++ sys/boot/efi/include/efiprot.h
@@ -27,6 +27,8 @@ Revision History
 
 --*/
 
+#include <efidef.h>
+
 //
 // Device Path protocol
 //
@@ -307,9 +309,9 @@ typedef struct {
 //
 // The FileName field of the EFI_FILE_INFO data structure is variable length.
 // Whenever code needs to know the size of the EFI_FILE_INFO data structure, it needs to
-// be the size of the data structure without the FileName field.  The following macro 
+// be the size of the data structure without the FileName field.  The following macro
 // computes this size correctly no matter how big the FileName array is declared.
-// This is required to make the EFI_FILE_INFO data structure ANSI compilant. 
+// This is required to make the EFI_FILE_INFO data structure ANSI compilant.
 //
 
 #define SIZE_OF_EFI_FILE_INFO EFI_FIELD_OFFSET(EFI_FILE_INFO,FileName)
@@ -329,9 +331,9 @@ typedef struct {
 //
 // The VolumeLabel field of the EFI_FILE_SYSTEM_INFO data structure is variable length.
 // Whenever code needs to know the size of the EFI_FILE_SYSTEM_INFO data structure, it needs
-// to be the size of the data structure without the VolumeLable field.  The following macro 
+// to be the size of the data structure without the VolumeLable field.  The following macro
 // computes this size correctly no matter how big the VolumeLable array is declared.
-// This is required to make the EFI_FILE_SYSTEM_INFO data structure ANSI compilant. 
+// This is required to make the EFI_FILE_SYSTEM_INFO data structure ANSI compilant.
 //
 
 #define SIZE_OF_EFI_FILE_SYSTEM_INFO EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO,VolumeLabel)
@@ -411,7 +413,7 @@ typedef struct {
     EFI_DEVICE_IO                   Write;
 } EFI_IO_ACCESS;
 
-typedef 
+typedef
 EFI_STATUS
 (EFIAPI *EFI_PCI_DEVICE_PATH) (
     IN struct _EFI_DEVICE_IO_INTERFACE  *This,
@@ -555,4 +557,47 @@ typedef struct _EFI_UNICODE_COLLATION_INTERFACE {
     CHAR8                               *SupportedLanguages;
 } EFI_UNICODE_COLLATION_INTERFACE;
 
+//
+// Driver Binding protocol
+//
+
+#define DRIVER_BINDING_PROTOCOL \
+  { 0x18a031ab, 0xb443, 0x4d1a, {0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71} }
+
+INTERFACE_DECL(_EFI_DRIVER_BINDING);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_SUPPORTED) (
+    IN struct _EFI_DRIVER_BINDING *This,
+    IN EFI_HANDLE ControllerHandle,
+    IN EFI_DEVICE_PATH *RemainingPath
+    );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_START) (
+    IN struct _EFI_DRIVER_BINDING *This,
+    IN EFI_HANDLE ControllerHandle,
+    IN EFI_DEVICE_PATH *RemainingPath
+    );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_STOP) (
+    IN struct _EFI_DRIVER_BINDING *This,
+    IN EFI_HANDLE ControllerHandle,
+    IN UINTN NumberOfChildren,
+    IN EFI_HANDLE *ChildHandleBuffer
+    );
+
+typedef struct _EFI_DRIVER_BINDING {
+  EFI_DRIVER_BINDING_SUPPORTED Supported;
+  EFI_DRIVER_BINDING_START Start;
+  EFI_DRIVER_BINDING_STOP Stop;
+  UINT32 Version;
+  EFI_HANDLE ImageHandle;
+  EFI_HANDLE DriverBindingHandle;
+} EFI_DRIVER_BINDING;
+
 #endif
diff --git sys/boot/efi/include/string16.h sys/boot/efi/include/string16.h
new file mode 100644
index 00000000000..5e4f9c1ef1a
--- /dev/null
+++ sys/boot/efi/include/string16.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2016 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$
+ */
+
+#ifndef _STRING16_H_
+#define _STRING16_H_
+
+#include <efi.h>
+#include <stddef.h>
+
+/*
+ * These are 16-bit variants of string,h functions for use with EFI code.
+ */
+
+extern size_t strlen16(const CHAR16 *str);
+extern CHAR16* strcpy16(CHAR16 *dst, const CHAR16 *src);
+extern CHAR16* stpcpy16(CHAR16 *dst, const CHAR16 *src);
+extern CHAR16* strcpy_to_16(CHAR16 *dst, const char *src);
+extern char* strcpy_from_16(char *dst, const CHAR16 *src);
+
+#endif
diff --git sys/boot/efi/libefi/Makefile sys/boot/efi/libefi/Makefile
index d9619bef89d..8684403cd11 100644
--- sys/boot/efi/libefi/Makefile
+++ sys/boot/efi/libefi/Makefile
@@ -10,8 +10,8 @@ LIB=	efi
 INTERNALLIB=
 WARNS?=	2
 
-SRCS=	delay.c devpath.c efi_console.c efinet.c efipart.c errno.c \
-	handles.c libefi.c
+SRCS=	delay.c devpath.c efi_console.c efifs.c efinet.c env.c errno.c \
+	handles.c libefi.c string16.c
 
 .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
 SRCS+=	time.c
diff --git sys/boot/efi/libefi/efifs.c sys/boot/efi/libefi/efifs.c
new file mode 100644
index 00000000000..dd9fb0f0b41
--- /dev/null
+++ sys/boot/efi/libefi/efifs.c
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 2016 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 <efi.h>
+#include <efilib.h>
+#include <efiprot.h>
+#include <stand.h>
+#include <stdarg.h>
+#include <bootstrap.h>
+#include <string16.h>
+
+static EFI_GUID FileInfoGUID = EFI_FILE_INFO_ID;;
+static EFI_GUID SimpleFileSystemProtocolGUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
+static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
+static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
+
+static int efifs_open(const char *path, struct open_file *f);
+static int efifs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int efifs_close(struct open_file *f);
+static int efifs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t efifs_seek(struct open_file *f, off_t offset, int where);
+static int efifs_stat(struct open_file *f, struct stat *sb);
+static int efifs_readdir(struct open_file *f, struct dirent *d);
+
+static int efifs_dev_init(void);
+static int efifs_dev_strategy(void *, int, daddr_t, size_t, size_t, char *, size_t *);
+static int efifs_dev_open(struct open_file *, ...);
+static int efifs_dev_close(struct open_file *);
+static int efifs_dev_print(int);
+
+struct devsw efifs_dev = {
+	.dv_name = "EFI",
+	.dv_type = DEVT_EFI,
+	.dv_init = efifs_dev_init,
+	.dv_strategy = efifs_dev_strategy,
+	.dv_open = efifs_dev_open,
+	.dv_close = efifs_dev_close,
+	.dv_ioctl = noioctl,
+	.dv_print = efifs_dev_print,
+	.dv_cleanup = NULL
+};
+
+struct fs_ops efifs_fsops = {
+	"EFI",
+	efifs_open,
+	efifs_close,
+	efifs_read,
+	efifs_write,
+	efifs_seek,
+	efifs_stat,
+	efifs_readdir
+};
+
+static int
+efifs_dev_init(void)
+{
+	EFI_HANDLE *hin;
+	EFI_STATUS status;
+	UINTN sz;
+	u_int n, nin, unit;
+	int err;
+
+	sz = 0;
+	hin = NULL;
+	status = BS->LocateHandle(ByProtocol, &SimpleFileSystemProtocolGUID,
+                                  0, &sz, 0);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		hin = (EFI_HANDLE *)malloc(sz);
+		status = BS->LocateHandle(ByProtocol,
+                    &SimpleFileSystemProtocolGUID, 0, &sz, hin);
+		if (EFI_ERROR(status))
+			free(hin);
+	}
+	if (EFI_ERROR(status))
+		return (efi_status_to_errno(status));
+
+	/* Filter handles to only include FreeBSD partitions. */
+	nin = sz / sizeof(EFI_HANDLE);
+	unit = 0;
+
+	for (n = 0; n < nin; n++) {
+		status = BS->OpenProtocol(hin[n], &SimpleFileSystemProtocolGUID,
+                                          NULL, IH, NULL,
+                                          EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+		if (EFI_ERROR(status))
+			continue;
+
+                efi_handle_update_dev(hin[n], &efifs_dev, unit++, 0);
+	}
+
+	free(hin);
+	return (err);
+}
+
+
+static int
+efifs_dev_print(int verbose)
+{
+	char line[80];
+        EFI_DEVICE_PATH *devpath;
+        EFI_BLOCK_IO *blkio;
+	EFI_HANDLE h, *hin;
+	EFI_STATUS status;
+	u_int unit, n;
+
+	for (unit = 0, h = efi_find_handle(&efifs_dev, 0);
+	    h != NULL; h = efi_find_handle(&efifs_dev, ++unit)) {
+		sprintf(line, "    %s%d:", efifs_dev.dv_name, unit);
+		pager_output(line);
+                pager_output("    EFI(SIMPLE_FILE_SYSTEM");
+
+		status = BS->HandleProtocol(hin[n], &DevicePathGUID,
+		    (void **)&devpath);
+		if (!EFI_ERROR(status)) {
+                        pager_output(", DEVICE_PATH");
+		}
+
+		status = BS->HandleProtocol(hin[n], &BlockIoProtocolGUID,
+		    (void **)&blkio);
+		if (!EFI_ERROR(status)) {
+                        pager_output(", BLOCK_IO");
+		}
+		pager_output(")\n");
+	}
+        return (0);
+}
+
+static int
+efifs_dev_open(struct open_file *f, ...)
+{
+	va_list args;
+	struct devdesc *dev;
+	EFI_FILE_IO_INTERFACE *fsiface;
+	EFI_HANDLE h;
+	EFI_STATUS status;
+
+	va_start(args, f);
+	dev = va_arg(args, struct devdesc*);
+	va_end(args);
+
+	h = efi_find_handle(&efifs_dev, dev->d_unit);
+
+	if (h == NULL)
+		return (EINVAL);
+
+        status = BS->OpenProtocol(h, &SimpleFileSystemProtocolGUID,
+            (void**)&fsiface, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+        if (EFI_ERROR(status)) {
+		return (efi_status_to_errno(status));
+        }
+
+        dev->d_opendata = fsiface;
+	return (0);
+}
+
+static int
+efifs_dev_close(struct open_file *f)
+{
+	struct devdesc *dev;
+	EFI_HANDLE h;
+        EFI_STATUS status;
+
+	dev = (struct devdesc *)(f->f_devdata);
+	h = efi_find_handle(&efifs_dev, dev->d_unit);
+
+	if (h == NULL)
+		return (EINVAL);
+
+	if (dev->d_opendata == NULL)
+		return (EINVAL);
+
+        status = BS->CloseProtocol(h, &SimpleFileSystemProtocolGUID, IH, NULL);
+
+        if (EFI_ERROR(status))
+		return (efi_status_to_errno(status));
+
+	dev->d_opendata = NULL;
+	return (0);
+}
+
+
+/* Raw I/O isn't supported on EFI FS devices, as they talk through
+ * SIMPLE_FILE_SYSTEM_INTERFACE.
+ */
+static int
+efifs_dev_strategy(void *devdata __unused, int rw __unused, daddr_t blk __unused,
+                   size_t offset, size_t size __unused, char *buf __unused,
+                   size_t *rsize __unused)
+{
+        printf("Raw I/O not supported on EFI FS interface\n");
+	return ENOTSUP;
+}
+
+/*
+ * Open a file.
+ */
+static int
+efifs_open(const char *upath, struct open_file *f)
+{
+	struct devdesc *dev;
+	EFI_FILE_IO_INTERFACE *fsiface;
+	EFI_FILE_HANDLE root;
+        EFI_STATUS status;
+        CHAR16 path[strlen(upath) + 1];
+
+	dev = (struct devdesc *)(f->f_devdata);
+        fsiface = dev->d_opendata;
+
+        if (!strcmp(upath, "") || !strcmp(upath, "/")) {
+                return (fsiface->OpenVolume(fsiface,
+                                            (EFI_FILE_HANDLE*)&(f->f_fsdata)));
+        } else {
+                status = fsiface->OpenVolume(fsiface, &root);
+
+                if (EFI_ERROR(status)) {
+                        return (efi_status_to_errno(status));
+                }
+
+                strcpy_to_16(path, upath);
+                status = root->Open(root, (EFI_FILE_HANDLE*)&(f->f_fsdata), path,
+                                    EFI_FILE_MODE_READ, 0);
+
+                root->Close(root);
+
+                if (EFI_ERROR(status)) {
+                        return (efi_status_to_errno(status));
+                }
+
+                return 0;
+        }
+}
+
+static int
+efifs_close(struct open_file *f)
+{
+        EFI_FILE_HANDLE file = (EFI_FILE_HANDLE)f->f_fsdata;
+        EFI_STATUS status;
+
+        status = file->Close(file);
+
+        if (EFI_ERROR(status))
+		return (efi_status_to_errno(status));
+
+        return (0);
+}
+
+static int
+efifs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
+{
+        EFI_FILE_HANDLE file = (EFI_FILE_HANDLE)f->f_fsdata;
+        UINTN readsize = size;
+        EFI_STATUS status;
+
+        status = file->Read(file, &readsize, start);
+
+        if (EFI_ERROR(status))
+		return (efi_status_to_errno(status));
+
+	if (resid)
+		*resid = size - readsize;
+
+	return (0);
+}
+
+static int
+efifs_write(struct open_file *f, void *start, size_t size,
+            size_t *resid /* out */)
+{
+        EFI_FILE_HANDLE file = (EFI_FILE_HANDLE)f->f_fsdata;
+        UINTN writesize = size;
+        EFI_STATUS status;
+
+        status = file->Write(file, &writesize, start);
+
+        if (EFI_ERROR(status))
+		return (efi_status_to_errno(status));
+
+	if (resid)
+		*resid = size - writesize;
+
+	return (0);
+}
+
+static off_t
+efifs_seek(struct open_file *f, off_t offset, int where)
+{
+        EFI_FILE_HANDLE file = (EFI_FILE_HANDLE)f->f_fsdata;
+        UINT64 pos;
+        EFI_STATUS status;
+
+	switch (where) {
+	case SEEK_SET:
+                status = file->SetPosition(file, offset);
+
+                if (status != EFI_SUCCESS) {
+                        errno = (efi_status_to_errno(status));
+                        return -1;
+                }
+
+		break;
+	case SEEK_CUR:
+                status = file->GetPosition(file, &pos);
+
+                if (status != EFI_SUCCESS) {
+                        errno = (efi_status_to_errno(status));
+                        return -1;
+                }
+
+                status = file->SetPosition(file, pos + offset);
+
+                if (status != EFI_SUCCESS) {
+                        errno = (efi_status_to_errno(status));
+                        return -1;
+                }
+
+		break;
+	case SEEK_END:
+                status = file->SetPosition(file, 0xffffffffffffffff);
+
+                if (status != EFI_SUCCESS) {
+                        errno = (efi_status_to_errno(status));
+                        return -1;
+                }
+	default:
+		errno = EINVAL;
+		return (-1);
+	}
+
+        status = file->GetPosition(file, &pos);
+
+        if (status != EFI_SUCCESS) {
+                errno = (efi_status_to_errno(status));
+                return -1;
+        }
+
+	return (pos);
+}
+
+/* SIMPLE_FILE_SYSTEM_PROTOCOL is geared towards FAT, so we can't
+ * reproduce stat with absolute fidelity.
+ */
+static int
+efifs_stat(struct open_file *f, struct stat *sb)
+{
+        EFI_FILE_HANDLE file;
+        UINTN size = 0;
+        EFI_FILE_INFO *finfo;;
+        EFI_STATUS status;
+
+        file = (EFI_FILE_HANDLE)f->f_fsdata;
+        status = file->GetInfo(file, &FileInfoGUID, &size, NULL);
+
+        if (status != EFI_BUFFER_TOO_SMALL) {
+                errno = (efi_status_to_errno(status));
+                return -1;
+        }
+
+        finfo = malloc(size);
+        status = file->GetInfo(file, &FileInfoGUID, &size, finfo);
+
+        if (status != EFI_SUCCESS) {
+                errno = (efi_status_to_errno(status));
+                return -1;
+        }
+
+        /* We can't properly fill these in... */
+        sb->st_ino = 0;
+        sb->st_nlink = 0;
+        sb->st_uid = 0;
+        sb->st_gid = 0;
+        sb->st_blksize = 512;
+        /* Build the mode field */
+        if (finfo->Attribute & EFI_FILE_DIRECTORY) {
+                sb->st_mode = S_IFDIR;
+        } else {
+                sb->st_mode = S_IFREG;
+        }
+
+        if (finfo->Attribute & EFI_FILE_MODE_READ) {
+          sb->st_mode = S_IRUSR | S_IXUSR | S_IRGRP |
+            S_IXGRP | S_IROTH | S_IXOTH;
+        }
+
+        if (finfo->Attribute & EFI_FILE_MODE_READ) {
+          sb->st_mode = S_IWUSR | S_IWGRP | S_IWOTH;
+        }
+        /* This may or may not be supported, depending on the FS driver */
+        sb->st_blocks = finfo->PhysicalSize / 512;
+        /* These fields we can get right */
+        sb->st_size = finfo->FileSize;
+        sb->st_atime = from_efi_time(&(finfo->LastAccessTime));
+        sb->st_mtime = from_efi_time(&(finfo->ModificationTime));
+        sb->st_ctime = from_efi_time(&(finfo->CreateTime));
+
+        free(finfo);
+
+        return (0);
+}
+
+static int
+efifs_readdir(struct open_file *f, struct dirent *d)
+{
+        EFI_FILE_HANDLE file;
+        UINTN size = 0;
+        EFI_FILE_INFO *finfo;;
+        EFI_STATUS status;
+
+        file = (EFI_FILE_HANDLE)f->f_fsdata;
+        status = file->Read(file, &size, NULL);
+
+        if (status != EFI_BUFFER_TOO_SMALL) {
+                errno = (efi_status_to_errno(status));
+                return -1;
+        }
+
+        if (size == 0) {
+                return (ENOENT);
+        }
+
+        finfo = malloc(size);
+        status = file->Read(file, &size, finfo);
+
+        if (status != EFI_SUCCESS) {
+                errno = (efi_status_to_errno(status));
+                return -1;
+        }
+
+        strcpy_from_16(d->d_name, finfo->FileName);
+        d->d_namlen = strlen(d->d_name);
+        d->d_reclen = sizeof(struct dirent);
+        /* We can't faithfully reproduce this due to the limitations
+         * of the SIMPLE_FILE_SYSTEM interface */
+        d->d_fileno = 0;
+
+        free(finfo);
+
+        return (0);
+}
diff --git sys/boot/efi/libefi/errno.c sys/boot/efi/libefi/errno.c
index fac903faf27..0f354c3f3c8 100644
--- sys/boot/efi/libefi/errno.c
+++ sys/boot/efi/libefi/errno.c
@@ -30,6 +30,69 @@ __FBSDID("$FreeBSD$");
 #include <efi.h>
 #include <efilib.h>
 
+EFI_STATUS
+errno_to_efi_status(int errno)
+{
+        EFI_STATUS status;
+
+        switch (errno) {
+        case EPERM:
+                status = EFI_ACCESS_DENIED;
+                break;
+
+        case EOVERFLOW:
+                status = EFI_BUFFER_TOO_SMALL;
+                break;
+
+        case EIO:
+                status = EFI_DEVICE_ERROR;
+                break;
+
+        case EINVAL:
+                status = EFI_INVALID_PARAMETER;
+                break;
+
+        case ESTALE:
+                status = EFI_MEDIA_CHANGED;
+                break;
+
+        case ENXIO:
+                status = EFI_NO_MEDIA;
+                break;
+
+        case ENOENT:
+                status = EFI_NOT_FOUND;
+                break;
+
+        case ENOMEM:
+                status = EFI_OUT_OF_RESOURCES;
+                break;
+
+        case ENOTSUP:
+        case ENODEV:
+                status = EFI_UNSUPPORTED;
+                break;
+
+        case ENOSPC:
+                status = EFI_VOLUME_FULL;
+                break;
+
+        case EACCES:
+                status = EFI_WRITE_PROTECTED;
+                break;
+
+        case 0:
+                status = EFI_SUCCESS;
+                break;
+
+        default:
+                status = EFI_DEVICE_ERROR;
+                break;
+        }
+
+        return (status);
+}
+
 int
 efi_status_to_errno(EFI_STATUS status)
 {
diff --git sys/boot/efi/libefi/handles.c sys/boot/efi/libefi/handles.c
index 1e4ef6ffbd5..c118085c1c9 100644
--- sys/boot/efi/libefi/handles.c
+++ sys/boot/efi/libefi/handles.c
@@ -41,21 +41,36 @@ struct entry {
 struct entry *entry;
 int nentries;
 
+static int
+get_next_unit(struct devsw *sw)
+{
+        int i, idx;
+
+        for (i = 0, idx = 0; i < nentries; i++) {
+                if (entry[i].dev == sw) {
+                        idx++;
+                }
+        }
+
+        return (idx);
+}
+
 int
 efi_register_handles(struct devsw *sw, EFI_HANDLE *handles,
     EFI_HANDLE *aliases, int count)
 {
 	size_t sz;
-	int idx, unit;
+	int idx, unit, i;
 
 	idx = nentries;
 	nentries += count;
 	sz = nentries * sizeof(struct entry);
+        unit = get_next_unit(sw);
 	entry = (entry == NULL) ? malloc(sz) : realloc(entry, sz);
-	for (unit = 0; idx < nentries; idx++, unit++) {
-		entry[idx].handle = handles[unit];
+	for (i = 0; idx < nentries; idx++, unit++, i++) {
+		entry[idx].handle = handles[i];
 		if (aliases != NULL)
-			entry[idx].alias = aliases[unit];
+			entry[idx].alias = aliases[i];
 		else
 			entry[idx].alias = NULL;
 		entry[idx].dev = sw;
@@ -64,6 +79,16 @@ efi_register_handles(struct devsw *sw, EFI_HANDLE *handles,
 	return (0);
 }
 
+int
+efi_register_handle(struct devsw *sw, EFI_HANDLE handle, EFI_HANDLE alias)
+{
+        if (alias == NULL) {
+                return efi_register_handles(sw, &handle, NULL, 1);
+        } else {
+                return efi_register_handles(sw, &handle, &alias, 1);
+        }
+}
+
 EFI_HANDLE
 efi_find_handle(struct devsw *dev, int unit)
 {
@@ -116,3 +141,29 @@ efi_handle_update_dev(EFI_HANDLE h, struct devsw *dev, int unit,
 
 	return (ENOENT);
 }
+
+int
+efi_handle_remove_dev(EFI_HANDLE h)
+{
+	int idx;
+
+        /* Find the entry */
+	for (idx = 0; idx < nentries; idx++) {
+		if (entry[idx].handle != h)
+			continue;
+	}
+
+        if (idx >= nentries)
+                return (ENOENT);
+        else if (idx == nentries - 1) {
+                nentries--;
+                entry = realloc(entry, nentries * sizeof(struct entry));
+        } else {
+                memcpy(entry + idx, entry + idx + 1,
+                       sizeof(struct entry) * (nentries - (idx + 1)));
+                nentries--;
+                entry = realloc(entry, nentries * sizeof(struct entry));
+        }
+
+        return (0);
+}
diff --git sys/boot/efi/libefi/string16.c sys/boot/efi/libefi/string16.c
new file mode 100644
index 00000000000..299d72dd0f0
--- /dev/null
+++ sys/boot/efi/libefi/string16.c
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 2016 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 "string16.h"
+#include <stddef.h>
+
+size_t
+strlen16(const CHAR16 *str)
+{
+        size_t i;
+
+        for (i = 0; str[i] != 0; i++);
+
+        return i;
+}
+
+CHAR16 *
+strcpy16(CHAR16 *dst, const CHAR16 *src)
+{
+        stpcpy16(dst, src);
+
+        return (dst);
+}
+
+CHAR16 *
+stpcpy16(CHAR16 *dst, const CHAR16 *src)
+{
+        for (; *src != 0; src++, dst++) {
+                *dst = *src;
+        }
+
+        *dst = *src;
+
+        return dst;
+}
+
+char *
+strcpy_from_16(char *dst, const CHAR16 *src)
+{
+        int i;
+
+        for (i = 0; src[i] != 0; i++) {
+                dst[i] = src[i];
+        }
+
+        dst[i] = 0;
+
+        return (dst);
+}
+
+CHAR16 *
+strcpy_to_16(CHAR16 *dst, const char *src)
+{
+        int i;
+
+        for (i = 0; src[i] != 0; i++) {
+                dst[i] = src[i];
+        }
+
+        dst[i] = 0;
+
+        return (dst);
+}
diff --git sys/boot/efi/libefi/time.c sys/boot/efi/libefi/time.c
index 99831e1cf1a..2df466e57b7 100644
--- sys/boot/efi/libefi/time.c
+++ sys/boot/efi/libefi/time.c
@@ -2,28 +2,28 @@
  * Copyright (c) 1999, 2000
  * Intel Corporation.
  * 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.
- * 
+ *
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
- * 
+ *
  *    This product includes software developed by Intel Corporation and
  *    its contributors.
- * 
+ *
  * 4. Neither the name of Intel Corporation or its contributors may be
  *    used to endorse or promote products derived from this software
  *    without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION 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
@@ -35,7 +35,7 @@
  * 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.
- * 
+ *
  */
 
 #include <sys/cdefs.h>
@@ -58,6 +58,41 @@ __FBSDID("$FreeBSD$");
 #define SECSPERHOUR ( 60*60 )
 #define SECSPERDAY	(24 * SECSPERHOUR)
 
+/*
+//  These arrays give the cumulative number of days up to the first of the
+//  month number used as the index (1 -> 12) for regular and leap years.
+//  The value at index 13 is for the whole year.
+*/
+static const time_t CumulativeDays[2][14] = {
+  {0,
+   0,
+   31,
+   31 + 28,
+   31 + 28 + 31,
+   31 + 28 + 31 + 30,
+   31 + 28 + 31 + 30 + 31,
+   31 + 28 + 31 + 30 + 31 + 30,
+   31 + 28 + 31 + 30 + 31 + 30 + 31,
+   31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+   31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+   31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+   31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+   31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 },
+  {0,
+   0,
+   31,
+   31 + 29,
+   31 + 29 + 31,
+   31 + 29 + 31 + 30,
+   31 + 29 + 31 + 30 + 31,
+   31 + 29 + 31 + 30 + 31 + 30,
+   31 + 29 + 31 + 30 + 31 + 30 + 31,
+   31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
+   31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+   31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+   31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+   31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 }};
+
 void
 efi_time_init(void)
 {
@@ -68,45 +103,46 @@ efi_time_fini(void)
 {
 }
 
-static time_t
-efi_time(EFI_TIME *ETime)
+void
+to_efi_time(EFI_TIME *efi_time, time_t time)
 {
-    /*
-    //  These arrays give the cumulative number of days up to the first of the
-    //  month number used as the index (1 -> 12) for regular and leap years.
-    //  The value at index 13 is for the whole year.
-    */
-    static time_t CumulativeDays[2][14] = {
-    {0,
-     0,
-     31,
-     31 + 28,
-     31 + 28 + 31,
-     31 + 28 + 31 + 30,
-     31 + 28 + 31 + 30 + 31,
-     31 + 28 + 31 + 30 + 31 + 30,
-     31 + 28 + 31 + 30 + 31 + 30 + 31,
-     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
-     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
-     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
-     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
-     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 },
-    {0,
-     0,
-     31,
-     31 + 29,
-     31 + 29 + 31,
-     31 + 29 + 31 + 30,
-     31 + 29 + 31 + 30 + 31,
-     31 + 29 + 31 + 30 + 31 + 30,
-     31 + 29 + 31 + 30 + 31 + 30 + 31,
-     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
-     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
-     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
-     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
-     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 }};
-
-    time_t  UTime; 
+        if (time >= 0) {
+                for (efi_time->Year = 1970;
+                    time > CumulativeDays[isleap(efi_time->Year)][13] * SECSPERDAY;
+                    time -= CumulativeDays[isleap(efi_time->Year)][13] * SECSPERDAY,
+                    efi_time->Year++);
+
+                for (efi_time->Month = 0;
+                    time > CumulativeDays[isleap(efi_time->Year)][efi_time->Month] *
+                      SECSPERDAY;
+                    efi_time->Month++);
+
+                time -= CumulativeDays[isleap(efi_time->Year)][efi_time->Month - 1] *
+                    SECSPERDAY;
+
+                for (efi_time->Day = 0; time > SECSPERDAY;
+                    time -= SECSPERDAY, efi_time->Day++);
+
+                for (efi_time->Hour = 0; time > SECSPERHOUR;
+                    time -= SECSPERHOUR, efi_time->Hour++);
+
+                for (efi_time->Minute = 0; time > 60;
+                    time -= 60, efi_time->Minute++);
+
+                efi_time->Second = time;
+                efi_time->Nanosecond = 0;
+                efi_time->TimeZone = 0;
+                efi_time->Daylight = 0;
+        } else {
+                memset(efi_time, 0, sizeof(EFI_TIME));
+        }
+}
+
+time_t
+from_efi_time(EFI_TIME *ETime)
+{
+
+    time_t  UTime;
     int     Year;
 
     /*
@@ -134,7 +170,7 @@ efi_time(EFI_TIME *ETime)
     /*
     // UTime should now be set to 00:00:00 on Jan 1 of the file's year.
     //
-    // Months  
+    // Months
     */
     UTime += (CumulativeDays[isleap(ETime->Year)][ETime->Month] * SECSPERDAY);
 
@@ -170,7 +206,7 @@ efi_time(EFI_TIME *ETime)
     	*/
     	UTime += (ETime->TimeZone * 60);
     }
-    
+
     return UTime;
 }
 
@@ -196,7 +232,7 @@ EFI_GetTimeOfDay(
 	//  Convert to UNIX time (ie seconds since the epoch
 	*/
 
-	tp->tv_sec  = efi_time( &EfiTime );
+	tp->tv_sec  = from_efi_time( &EfiTime );
 	tp->tv_usec = 0; /* EfiTime.Nanosecond * 1000; */
 
 	/*
@@ -221,7 +257,7 @@ time(time_t *tloc)
 {
 	struct timeval tv;
 	EFI_GetTimeOfDay(&tv, 0);
-	
+
 	if (tloc)
 		*tloc = tv.tv_sec;
 	return tv.tv_sec;
diff --git sys/boot/efi/loader/Makefile sys/boot/efi/loader/Makefile
index d9d3532014f..300c65b10d9 100644
--- sys/boot/efi/loader/Makefile
+++ sys/boot/efi/loader/Makefile
@@ -50,6 +50,7 @@ CWARNFLAGS.main.c+=	-Wno-format
 CFLAGS+=	-I${.CURDIR}
 CFLAGS+=	-I${.CURDIR}/arch/${MACHINE}
 CFLAGS+=	-I${.CURDIR}/../include
+CFLAGS+=	-I${.CURDIR}/../drivers
 CFLAGS+=	-I${.CURDIR}/../include/${MACHINE}
 CFLAGS+=	-I${.CURDIR}/../../../contrib/dev/acpica/include
 CFLAGS+=	-I${.CURDIR}/../../..
@@ -68,6 +69,8 @@ CFLAGS+=	-DNO_PCI -DEFI
 LIBSTAND=	${.OBJDIR}/../../../../lib/libstand/libstand.a
 .endif
 
+LIBEFI_DRIVERS=	${.OBJDIR}/../drivers/libefi_drivers.a
+
 .if !defined(BOOT_HIDE_SERIAL_NUMBERS)
 # Export serial numbers, UUID, and asset tag from loader.
 CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
@@ -150,9 +153,10 @@ loader.efi: ${PROG}
 
 LIBEFI=		${.OBJDIR}/../libefi/libefi.a
 
-DPADD=		${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} \
-		${LDSCRIPT}
-LDADD=		${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND}
+DPADD=		${LIBFICL} ${LIBEFI} ${LIBEFI_DRIVERS} ${LIBFDT} ${LIBEFI_FDT} \
+		${LIBSTAND} ${LDSCRIPT}
+LDADD=		${LIBFICL} ${LIBEFI} ${LIBEFI_DRIVERS} ${LIBFDT} ${LIBEFI_FDT} \
+		${LIBSTAND}
 
 .include <bsd.prog.mk>
 
diff --git sys/boot/efi/loader/conf.c sys/boot/efi/loader/conf.c
index 3596a6360c3..56733ffa899 100644
--- sys/boot/efi/loader/conf.c
+++ sys/boot/efi/loader/conf.c
@@ -31,30 +31,22 @@ __FBSDID("$FreeBSD$");
 #include <bootstrap.h>
 #include <efi.h>
 #include <efilib.h>
-#ifdef EFI_ZFS_BOOT
-#include <libzfs.h>
-#endif
+
+#include "efi_drivers.h"
+
+const efi_driver_t *efi_drivers[] = {
+        &fs_driver,
+        NULL
+};
 
 struct devsw *devsw[] = {
-	&efipart_dev,
+        &efifs_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,
-	&tftp_fsops,
-	&nfs_fsops,
-	&gzipfs_fsops,
-	&bzipfs_fsops,
+        &efifs_fsops,
 	NULL
 };
 
diff --git sys/boot/efi/loader/loader_efi.h sys/boot/efi/loader/loader_efi.h
index ee7c4bb72eb..dbd36bfb4ca 100644
--- sys/boot/efi/loader/loader_efi.h
+++ sys/boot/efi/loader/loader_efi.h
@@ -33,6 +33,10 @@
 
 #include <stand.h>
 
+#include "efi_drivers.h"
+
+extern const efi_driver_t *efi_drivers[];
+
 int	efi_autoload(void);
 
 int	efi_getdev(void **vdev, const char *devspec, const char **path);
diff --git sys/boot/efi/loader/main.c sys/boot/efi/loader/main.c
index b97f2afb851..42e3ebe1830 100644
--- sys/boot/efi/loader/main.c
+++ sys/boot/efi/loader/main.c
@@ -87,10 +87,6 @@ EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
 EFI_GUID fdtdtb = FDT_TABLE_GUID;
 EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
 
-#ifdef EFI_ZFS_BOOT
-static void efi_zfs_probe(void);
-#endif
-
 /*
  * cpy8to16 copies a traditional C string into a CHAR16 string and
  * 0 terminates it. len is the size of *dst in bytes.
@@ -125,7 +121,7 @@ has_keyboard(void)
 	EFI_HANDLE *hin, *hin_end, *walker;
 	UINTN sz;
 	int retval = 0;
-	
+
 	/*
 	 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
 	 * do the typical dance to get the right sized buffer.
@@ -182,7 +178,7 @@ has_keyboard(void)
 			} else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
 			    DevicePathSubType(path) == MSG_USB_CLASS_DP) {
 				USB_CLASS_DEVICE_PATH *usb;
-			       
+
 				usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
 				if (usb->DeviceClass == 3 && /* HID */
 				    usb->DeviceSubClass == 1 && /* Boot devices */
@@ -263,10 +259,6 @@ 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
 
 	/* Init the time source */
 	efi_time_init();
@@ -391,6 +383,10 @@ main(int argc, CHAR16 *argv[])
 	/*
 	 * March through the device switch probing for things.
 	 */
+	for (i = 0; efi_drivers[i] != NULL; i++)
+		if (efi_drivers[i]->init != NULL)
+			(efi_drivers[i]->init)();
+
 	for (i = 0; devsw[i] != NULL; i++)
 		if (devsw[i]->dv_init != NULL)
 			(devsw[i]->dv_init)();
@@ -427,6 +423,7 @@ main(int argc, CHAR16 *argv[])
 	if (find_currdev(img, &dev, &unit, &pool_guid) != 0)
 		return (EFI_NOT_FOUND);
 
+        printf("Found efi device under %s\n", dev->dv_name);
 	switch (dev->dv_type) {
 #ifdef EFI_ZFS_BOOT
 	case DEVT_ZFS: {
@@ -1093,23 +1090,3 @@ 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 freebsd-amd64 mailing list