git: afee781523e4 - main - loader.efi: Recognize new memdisk=<url> and memcd=<url> options

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 26 Jun 2026 14:59:23 UTC
The branch main has been updated by imp:

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

commit afee781523e45198c7be0a19281bcae2c4ab66db
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-06-26 14:47:36 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-06-26 14:54:37 +0000

    loader.efi: Recognize new memdisk=<url> and memcd=<url> options
    
    Support ipxe downloading of a memory disk (either presented to the OS as
    a harddisk or a cd). This requires an ipxe server since it uses the ipxe
    download protocol to grab the disk. If there is a disk, we add it to the
    environment as a disk, and then the rest of the bootloader just sees it
    and boots from it.
    
    I've cribbed code from https://github.com/russor/memdisk_uefi and
    adapted it to work in the context of the FreeBSD bootloader.
    
    The ipxe_download.h file was created from the documentation of the
    interface.
    
    So a .ipxe file with the line
    chain http://10.2.0.1/loader.efi memdisk=${cwduri}FreeBSD-15.1-RELEASE-amd64-bootonly.iso
    would use the FreeBSD boot loader to boot the FreeBSD 15.1 release.
    
    md(9) has a bug at the moment that prevents it from probing the
    partitions on it.
    
    Also, we'll automatically decompress gzip, bzip2 and zstd files
    automatically.
    
    Sponsored by:           Netflix
    Differential Revision:  https://reviews.freebsd.org/D57677
---
 stand/efi/include/ipxe_download.h |  33 +++
 stand/efi/libefi/efipart.c        |   8 +-
 stand/efi/loader/Makefile         |   4 +
 stand/efi/loader/decompress.c     | 421 ++++++++++++++++++++++++++++++++++++++
 stand/efi/loader/decompress.h     |  19 ++
 stand/efi/loader/loader_efi.h     |   2 +
 stand/efi/loader/main.c           |  13 ++
 stand/efi/loader/memdisk.c        | 203 ++++++++++++++++++
 8 files changed, 700 insertions(+), 3 deletions(-)

diff --git a/stand/efi/include/ipxe_download.h b/stand/efi/include/ipxe_download.h
new file mode 100644
index 000000000000..dcf616da7745
--- /dev/null
+++ b/stand/efi/include/ipxe_download.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2026 Netfilix, Inc. Written by Warner Losh
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * IPXE protocol to download files
+ *
+ * Written from https://dox.ipxe.org/efi__download_8h.html using the names
+ * contained there for compatibility. See that for the full docs. Provides the
+ * same interface as <ipxe/efi/efi_download.h> from the ipxe distribution.
+ */
+#pragma once
+
+typedef struct _IPXE_DOWNLOAD_PROTOCOL IPXE_DOWNLOAD_PROTOCOL;
+typedef void *IPXE_DOWNLOAD_FILE;
+typedef EFI_STATUS(EFIAPI *IPXE_DOWNLOAD_DATA_CALLBACK)(IN VOID *, IN VOID *, IN UINTN, IN UINTN);
+typedef void(EFIAPI *IPXE_DOWNLOAD_FINISH_CALLBACK)(IN VOID *, IN EFI_STATUS);
+typedef EFI_STATUS(EFIAPI *IPXE_DOWNLOAD_START)(IN IPXE_DOWNLOAD_PROTOCOL *,
+    IN CHAR8 *, IN IPXE_DOWNLOAD_DATA_CALLBACK, IN IPXE_DOWNLOAD_FINISH_CALLBACK,
+    IN VOID *, OUT IPXE_DOWNLOAD_FILE *);
+typedef EFI_STATUS(EFIAPI *IPXE_DOWNLOAD_ABORT)(IN IPXE_DOWNLOAD_PROTOCOL *,
+    IN IPXE_DOWNLOAD_FILE, IN EFI_STATUS);
+typedef EFI_STATUS(EFIAPI *IPXE_DOWNLOAD_POLL) (IN IPXE_DOWNLOAD_PROTOCOL *);
+
+struct _IPXE_DOWNLOAD_PROTOCOL {
+	IPXE_DOWNLOAD_START 	Start;
+	IPXE_DOWNLOAD_ABORT 	Abort;
+	IPXE_DOWNLOAD_POLL 	Poll;
+};
+#define IPXE_DOWNLOAD_PROTOCOL_GUID \
+	{ 0x3eaeaebd, 0xdecf, 0x493b, { 0x9b, 0xd1, 0xcd, 0xb2, 0xde, 0xca, 0xe7, 0x19 } }
diff --git a/stand/efi/libefi/efipart.c b/stand/efi/libefi/efipart.c
index e02993064d26..b4842e1b52aa 100644
--- a/stand/efi/libefi/efipart.c
+++ b/stand/efi/libefi/efipart.c
@@ -1255,11 +1255,11 @@ error:
  * Predicate: is this a VirtualDisk as far as UEFI is concerned?
  */
 static bool
-testmd(pdinfo_t *hd, pdinfo_t *data __unused)
+testmd(pdinfo_t *md)
 {
 	EFI_DEVICE_PATH *node;
 
-	node = efi_devpath_last_node(hd->pd_devpath);
+	node = efi_devpath_last_node(md->pd_devpath);
 	if (node == NULL)
 		return (false);
 	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
@@ -1281,7 +1281,9 @@ efiblk_memdisk_preload(void)
 	char key[32], value[32];
 	int i;
 
-	while ((md = efipart_get_pd(&pdinfo, testmd, NULL)) != NULL) {
+	STAILQ_FOREACH(md, &pdinfo, pd_link) {
+		if (!testmd(md))
+			continue;
 		ram = (MEDIA_RAM_DISK_DEVICE_PATH *)efi_devpath_last_node(md->pd_devpath);
 		i = ram->Instance;
 		start = ((uint64_t)ram->StartingAddr[1] << 32) | ram->StartingAddr[0];
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
index 44d7347ee7dd..164b16ff2a59 100644
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -22,9 +22,11 @@ SRCS=	autoload.c \
 	bootinfo.c \
 	conf.c \
 	copy.c \
+	decompress.c \
 	efi_main.c \
 	framebuffer.c \
 	main.c \
+	memdisk.c \
 	self_reloc.c \
 	vers.c \
 	gfx_fb.c \
@@ -50,6 +52,7 @@ CFLAGS.framebuffer.c += -I$(SRCTOP)/sys/teken
 CFLAGS.framebuffer.c += -I${SRCTOP}/contrib/pnglite
 CFLAGS.main.c += -I$(SRCTOP)/sys/teken
 CFLAGS.main.c += -I${SRCTOP}/contrib/pnglite
+CFLAGS.memdisk.c += -I${.OBJDIR}
 CFLAGS.gfx_fb.c += -I$(SRCTOP)/sys/teken
 CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
 CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite
@@ -61,6 +64,7 @@ CFLAGS.gfx_fb.c += ${ZLIB_CFLAGS}
 # of a short. There's no good cast to use here so just ignore the
 # warnings for now.
 CWARNFLAGS.main.c+=	-Wno-format
+CWARNFLAGS.memdisk.c+=	-Wno-format
 
 .PATH: ${.CURDIR}/../loader
 .PATH: ${.CURDIR}/../loader/arch/${__arch}
diff --git a/stand/efi/loader/decompress.c b/stand/efi/loader/decompress.c
new file mode 100644
index 000000000000..1f44d1000f11
--- /dev/null
+++ b/stand/efi/loader/decompress.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2026 Netflix, Inc. Written by Warner Losh
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "stand.h"
+#include "loader_efi.h"
+#include <efilib.h>
+#include "decompress.h"
+
+#include <zlib.h>
+#include <bzlib.h>
+#ifdef LOADER_ZFS_SUPPORT	/* ZSTD only available with ZFS */
+#include <zstd.h>
+#endif
+#include <sys/_param.h>
+
+#define ULL(x) ((unsigned long long)(x))
+
+static EFI_MEMORY_TYPE mem_type = EfiReservedMemoryType;
+
+struct decomp_state
+{
+	enum compression type;
+	size_t size;		/* Best guess at final size */
+	size_t alloc_size;	/* Current size of `buf` */
+	size_t pages;		/* Alloc_size in pages */
+	uint8_t *buf_cur;	/* Current output buffer */
+	uint8_t *buf_end;	/* Current end of allocated buffer */
+	EFI_PHYSICAL_ADDRESS buf; /* Decompression buffer */
+	union {
+		z_stream zstrm;
+		bz_stream bzstrm;
+#ifdef LOADER_ZFS_SUPPORT
+		ZSTD_DStream *zstdstrm;
+#endif
+	};
+	EFI_STATUS (*init)(decomp_state *dctx, uint8_t *first_buf, size_t buflen,
+	    size_t size_hint);
+	enum step_return (*step)(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset);
+	void (*fini)(decomp_state *dctx, bool flush);
+};
+
+static enum compression
+what_compressed(uint8_t *buf, size_t len)
+{
+	/*
+	 * Failsafe
+	 */
+	if (len < 32)
+		return (none);
+	if (memcmp(buf, "\x1f\x8b", 2) == 0) {
+		printf("GZIP\n");
+		return (zlib);
+	}
+	if (memcmp(buf, "BZh", 3) == 0) {
+		printf("BZIP2\n");
+		return (bzip2);
+	}
+	if (memcmp(buf, "\x28\xb5\x2f\xfd", 4) == 0) {
+		printf("zstd\n");
+		return (zstd);
+	}
+	if (memcmp(buf, "\xfd""7zXZ\x00", 6) == 0) {
+		printf("xz -- unsupproted\n");
+	} else {
+		printf("Not compressed\n");
+	}
+	return (none);
+}
+
+static EFI_STATUS
+alloc_buffer(decomp_state *dctx, size_t size)
+{
+	dctx->alloc_size = roundup2(size, EFI_PAGE_SIZE);
+	dctx->pages = dctx->alloc_size / EFI_PAGE_SIZE;
+	EFI_STATUS status = BS->AllocatePages(AllocateAnyPages, mem_type, dctx->pages, &dctx->buf);
+	if (EFI_ERROR(status)) {
+		printf("Failed to allocate memory for %llu bytes\n", ULL(dctx->alloc_size));
+		return (status);
+	}
+	BS->SetMem((void *)dctx->buf, dctx->alloc_size, 0);
+	dctx->buf_cur = (uint8_t *)dctx->buf;
+	dctx->buf_end = (uint8_t *)dctx->buf + dctx->alloc_size;
+	return (EFI_SUCCESS);
+}
+
+static EFI_STATUS
+grow_buffer(decomp_state *dctx)
+{
+	/*
+	 * a 1.5 exp growth trades a few more copies for a little less waste.
+	 */
+	size_t newsz = roundup2(dctx->alloc_size * 3 / 2, EFI_PAGE_SIZE);
+	size_t newpages = newsz / EFI_PAGE_SIZE;
+	EFI_PHYSICAL_ADDRESS newbuf;
+	EFI_STATUS status = BS->AllocatePages(AllocateAnyPages, mem_type, newpages, &newbuf);
+	if (EFI_ERROR(status)) {
+		printf("Failed to allocate memory for %llu bytes\n", ULL(newsz));
+		return (status);
+	}
+	memcpy((void *)newbuf, (void *)dctx->buf, dctx->alloc_size);
+	BS->FreePages(dctx->buf, dctx->pages);
+	dctx->buf = newbuf;
+	dctx->pages = newpages;
+	dctx->buf_cur = (uint8_t *)dctx->buf + dctx->alloc_size;
+	dctx->buf_end = (uint8_t *)dctx->buf + newsz;
+	BS->SetMem(dctx->buf_cur, newsz - dctx->alloc_size, 0);
+	dctx->alloc_size = newsz;
+	return (EFI_SUCCESS);
+}
+
+static void
+free_buffer(decomp_state *dctx)
+{
+	if (dctx->buf)
+		BS->FreePages(dctx->buf, dctx->pages);
+	dctx->buf = 0;
+}
+
+/*
+ * zlib supprot
+ */
+static EFI_STATUS
+zlib_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
+{
+	z_stream *strm = &dctx->zstrm;
+
+	/*
+	 * Assume 4x compression, but start at 64MB
+	 */
+	dctx->size = max(size_hint * 4, M(64));
+	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
+	if (EFI_ERROR(status))
+		return (status);
+        memset(strm, 0, sizeof(*strm));
+        strm->next_in = first_buf;
+        strm->avail_in = buflen;
+        return (inflateInit2(strm, 15 + 16) == Z_OK ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);
+}
+
+static enum step_return
+zlib_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	z_stream *strm = &dctx->zstrm;
+	size_t outlen = dctx->buf_end - dctx->buf_cur;
+
+        strm->next_in = buf;
+        strm->avail_in = len;
+        strm->next_out = dctx->buf_cur;
+        strm->avail_out = outlen;
+	
+        int ret = inflate(strm, Z_NO_FLUSH);
+	dctx->buf_cur += outlen - strm->avail_out;
+
+        if (ret == Z_STREAM_END)
+                return (done);
+        if (ret != Z_OK)
+                return (err);
+        if (dctx->buf_cur < dctx->buf_end) /* Have output space */
+		return (ok);
+
+	/*
+	 * We're out of space, grow the buffer and try again if there's buffer
+	 * space. We try again recursively since we know that will usually go
+	 * only 1 deep.
+	 */
+	if (EFI_ERROR(grow_buffer(dctx)))
+		return (err);
+	if (strm->avail_in == 0)
+		return (ok);
+	size_t consumed = len - strm->avail_in;
+	return (zlib_step(dctx, buf + consumed, strm->avail_in, offset + consumed));
+}
+
+static void
+zlib_fini(decomp_state *dctx, bool flush)
+{
+	inflateEnd(&dctx->zstrm);
+	if (!flush)
+		return;
+	free_buffer(dctx);
+}
+
+/*
+ * Bzip2 supprot
+ */
+static EFI_STATUS
+bzip2_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
+{
+	bz_stream *strm = &dctx->bzstrm;
+
+	/*
+	 * Assume 4x compression, but start at 64MB
+	 */
+	dctx->size = max(size_hint * 4, M(64));
+	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
+	if (EFI_ERROR(status))
+		return (status);
+        memset(strm, 0, sizeof(*strm));
+        strm->next_in = first_buf;
+        strm->avail_in = buflen;
+        return (BZ2_bzDecompressInit(strm, 0, 0) == BZ_OK ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);
+}
+
+static enum step_return
+bzip2_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	bz_stream *strm = &dctx->bzstrm;
+	size_t outlen = dctx->buf_end - dctx->buf_cur;
+
+        strm->next_in = buf;
+        strm->avail_in = len;
+        strm->next_out = dctx->buf_cur;
+        strm->avail_out = outlen;
+	
+        int ret = BZ2_bzDecompress(strm);
+	dctx->buf_cur += outlen - strm->avail_out;
+
+        if (ret == BZ_STREAM_END)
+                return (done);
+        if (ret != Z_OK)
+                return (err);
+        if (dctx->buf_cur < dctx->buf_end) /* Have output space */
+		return (ok);
+
+	/*
+	 * We're out of space, grow the buffer and try again if there's buffer
+	 * space. We try again recursively since we know that will usually go
+	 * only 1 deep.
+	 */
+	if (EFI_ERROR(grow_buffer(dctx)))
+		return (err);
+	if (strm->avail_in == 0)
+		return (ok);
+	size_t consumed = len - strm->avail_in;
+	return (bzip2_step(dctx, buf + consumed, strm->avail_in, offset + consumed));
+}
+
+static void
+bzip2_fini(decomp_state *dctx, bool flush)
+{
+	BZ2_bzDecompressEnd(&dctx->bzstrm);
+	if (!flush)
+		return;
+	free_buffer(dctx);
+}
+
+/*
+ * ZSTD supprot
+ */
+#ifdef LOADER_ZFS_SUPPORT
+static EFI_STATUS
+zstd_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
+{
+	unsigned long long size = ZSTD_getFrameContentSize(first_buf, buflen);
+	if (size == ZSTD_CONTENTSIZE_ERROR)
+		return (EFI_VOLUME_CORRUPTED);
+	if (size == ZSTD_CONTENTSIZE_UNKNOWN)
+		dctx->size = max(size_hint * 4, M(64));		/* Guess 4x compression or 64M */
+	else
+		dctx->size = size;				/* We know the size */
+	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
+	if (EFI_ERROR(status))
+		return (status);
+
+        dctx->zstdstrm = ZSTD_createDStream();
+        if (dctx->zstdstrm == NULL)
+                return (EFI_OUT_OF_RESOURCES);
+        if (ZSTD_isError(ZSTD_initDStream(dctx->zstdstrm))) {
+		ZSTD_freeDStream(dctx->zstdstrm);
+		dctx->zstdstrm = NULL;
+                return (EFI_OUT_OF_RESOURCES);
+	}
+	return (EFI_SUCCESS);
+}
+	
+static enum step_return
+zstd_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	size_t outlen = dctx->buf_end - dctx->buf_cur;
+	ZSTD_inBuffer inbuf = { buf, len, 0 };
+	ZSTD_outBuffer outbuf = { dctx->buf_cur, outlen, 0 };
+	size_t ret;
+
+	ret = ZSTD_decompressStream(dctx->zstdstrm, &outbuf, &inbuf);
+	dctx->buf_cur += outbuf.pos;
+
+        if (ZSTD_isError(ret))
+                return (err);
+        if (ret == 0)
+                return (done);
+        if (dctx->buf_cur < dctx->buf_end) /* Have output space */
+		return (ok);
+
+	/*
+	 * We're out of space, grow the buffer and try again if there's buffer
+	 * space. We try again recursively since we know that will usually go
+	 * only 1 deep.
+	 */
+	if (EFI_ERROR(grow_buffer(dctx)))
+		return (err);
+	if (inbuf.size == inbuf.pos)
+		return (ok);
+	return (zstd_step(dctx, buf + inbuf.pos, inbuf.size - inbuf.pos, offset + inbuf.pos));
+}
+
+static void
+zstd_fini(decomp_state *dctx, bool flush)
+{
+	ZSTD_freeDStream(dctx->zstdstrm);
+	if (!flush)
+		return;
+	free_buffer(dctx);
+}
+#endif
+
+/*
+ * No / Unknown decompression fallback
+ */
+static EFI_STATUS
+null_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
+{
+	dctx->size = size_hint;
+	return (alloc_buffer(dctx, size_hint));
+}
+
+static enum step_return
+null_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	size_t end = offset + len;
+	
+	if (end > dctx->size) {
+		printf("Too much data recieved!");
+		return (err);
+	}
+
+	CHAR8 *src = buf;
+	CHAR8 *dst = (void*)(dctx->buf + offset);
+	BS->CopyMem(dst, src, len);
+	return (end == dctx->size ? done : ok);
+}
+
+static void
+null_fini(decomp_state *dctx, bool flush)
+{
+	if (!flush)
+		return;
+	free_buffer(dctx);
+}
+
+decomp_state *
+decomp_init(uint8_t *buf, size_t buflen, size_t size_hint)
+{
+	decomp_state *dctx;
+	
+	dctx = malloc(sizeof(*dctx));
+	memset(dctx, 0, sizeof(*dctx));
+	dctx->type = what_compressed(buf, buflen);
+	switch (dctx->type) {
+	case zlib:
+		dctx->init = zlib_init;
+		dctx->step = zlib_step;
+		dctx->fini = zlib_fini;
+		break;
+	case bzip2:
+		dctx->init = bzip2_init;
+		dctx->step = bzip2_step;
+		dctx->fini = bzip2_fini;
+		break;
+#ifdef LOADER_ZFS_SUPPORT
+	case zstd:
+		dctx->init = zstd_init;
+		dctx->step = zstd_step;
+		dctx->fini = zstd_fini;
+		break;
+#endif
+	case none:
+		dctx->init = null_init;
+		dctx->step = null_step;
+		dctx->fini = null_fini;
+		break;
+	default:
+		return (NULL);
+	}
+
+	if (EFI_ERROR(dctx->init(dctx, buf, buflen, size_hint))) {
+		free(dctx);
+		dctx = NULL;
+	}
+	return (dctx);
+}
+
+enum step_return
+decomp_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	return (dctx->step(dctx, buf, len, offset));
+}
+
+void
+decomp_fini(decomp_state *dctx, bool flush)
+{
+	return (dctx->fini(dctx, flush));
+}
+
+EFI_PHYSICAL_ADDRESS
+decomp_buffer(decomp_state *dctx)
+{
+	if (dctx == NULL)
+		return (0);
+	return (dctx->buf);
+}
+
+size_t
+decomp_buffer_length(decomp_state *dctx)
+{
+	if (dctx == NULL)
+		return (0);
+	return ((void *)dctx->buf_cur - (void *)dctx->buf);
+}
diff --git a/stand/efi/loader/decompress.h b/stand/efi/loader/decompress.h
new file mode 100644
index 000000000000..43c48078f822
--- /dev/null
+++ b/stand/efi/loader/decompress.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2026 Netflix, Inc. Written by Warner Losh
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+enum compression { none, zlib, bzip2, zstd };
+enum step_return { ok, done, err };
+
+typedef struct decomp_state decomp_state;
+
+decomp_state *decomp_init(uint8_t *buf, size_t buflen, size_t size_hint);
+enum step_return decomp_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset);
+void decomp_fini(decomp_state *dctx, bool flush);
+EFI_PHYSICAL_ADDRESS decomp_buffer(decomp_state *dctx);
+size_t decomp_buffer_length(decomp_state *dctx);
+
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
index be991eeb9573..c53edb639ad5 100644
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -59,6 +59,8 @@ void * efi_translate(vm_offset_t ptr);
 void	efi_copy_finish(void);
 void	efi_copy_finish_nop(void);
 
+void maybe_download_ramdisk(int argc, CHAR16 **argv);
+
 #if defined(__amd64__) || defined(__i386__)
 /* Need this to setup page tables */
 extern EFI_PHYSICAL_ADDRESS staging;
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index 050e802181b1..cc7a9c4ac814 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1319,6 +1319,14 @@ main(int argc, CHAR16 *argv[])
 	 */
 	bcache_init(32768, 512);
 
+	/*
+	 * Scan the command line args for memdisk=<url> and download that image
+	 * to install as a ramdisk. This needs to be done before we scan the
+	 * handles because it installs a handle and creates the right ACPI
+	 * tables for the kernel to find it.
+	 */
+	maybe_download_ramdisk(argc, argv);
+
 	/*
 	 * Scan the BLOCK IO MEDIA handles then
 	 * march through the device switch probing for things.
@@ -1329,6 +1337,11 @@ main(int argc, CHAR16 *argv[])
 		    "failures\n", i);
 	}
 
+	/*
+	 * Scan all the VirtualDisks, passing them along to the FreeBSD kernel.
+	 */
+	efiblk_memdisk_preload();
+
 	devinit();
 
 	/*
diff --git a/stand/efi/loader/memdisk.c b/stand/efi/loader/memdisk.c
new file mode 100644
index 000000000000..8d34d3ceae42
--- /dev/null
+++ b/stand/efi/loader/memdisk.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2026 Netflix, Inc. Written by Warner Losh
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Derived from memdisk_uefi.c
+ * Copyright 2025 Richard Russo
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ */
+
+#include "loader_efi.h"
+#include <efilib.h>
+#include <Protocol/RamDisk.h>
+#include "decompress.h"
+#include <ipxe_download.h>
+#include <sys/_param.h>
+
+#define ULL(x) ((unsigned long long)(x))
+
+static EFI_GUID ipxeGuid = IPXE_DOWNLOAD_PROTOCOL_GUID;
+static EFI_GUID ramdiskGuid = EFI_RAM_DISK_PROTOCOL_GUID;
+static EFI_GUID virtual_disk_guid = EFI_VIRTUAL_DISK_GUID;
+static EFI_GUID virtual_cd_guid = EFI_VIRTUAL_CD_GUID;
+
+static IPXE_DOWNLOAD_PROTOCOL *ipxe_download;
+static EFI_RAM_DISK_PROTOCOL *ram_disk;
+
+struct dl_state;
+typedef struct dl_state dl_state;
+
+static struct dl_state
+{
+	bool in_progress;
+	size_t size;
+	EFI_STATUS status;
+	decomp_state *dctx;
+} dl;
+
+static void
+download_cleanup(dl_state *ctx)
+{
+	if (ctx->dctx)
+		decomp_fini(ctx->dctx, true);
+	ctx->in_progress = false;
+}
+
+static EFI_STATUS EFIAPI
+download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN FileOffset)
+{
+	dl_state *ctx = Context;
+	decomp_state *dctx = ctx->dctx;
+
+	/*
+	 * Make a note of the size when we're hinted about it. But once
+	 * we start the download, ignore the hints.
+	 */
+	if (ctx->size == 0 && BufferLength == 0) {
+		printf("We know we will download %llu bytes\n", ULL(FileOffset));
+		ctx->size = FileOffset;
+		ctx->status = EFI_SUCCESS;
+		return (EFI_SUCCESS);
+	}
+
+	/*
+	 * Peek into the first chunk to see the format of the data.
+	 */
+	if (FileOffset == 0) {
+		dctx = decomp_init((uint8_t *)Buffer, (size_t)BufferLength, ctx->size);
+		if (dctx == NULL) {
+			ctx->in_progress = false;
+			ctx->status = EFI_VOLUME_CORRUPTED;
+			return (ctx->status);
+		}
+		ctx->dctx = dctx;
+	}
+
+	enum step_return sr = decomp_step(dctx, Buffer, BufferLength, FileOffset);
+	if (sr == err) {
+		printf("Error on download\n");
+		decomp_fini(dctx, true);
+		return (EFI_VOLUME_CORRUPTED);
+	}
+
+	unsigned long long sofar = FileOffset + BufferLength;
+#define MB  1000000
+	if (sofar / MB != FileOffset / MB) {
+		if (ctx->size)
+			printf("%dMB / %dMB (%d%%)\r",
+			    (int)(sofar / MB),
+			    (int)(ctx->size / MB),
+			    (int)(100 * sofar / ctx->size));
+		else
+			printf("%dMB\r", (int)(sofar / MB));
+	}
+	return (EFI_SUCCESS);
+}
+
+static void EFIAPI
+download_finish(IN VOID *Context, IN EFI_STATUS Status)
+{
+	dl_state *ctx = Context;
+
+	ctx->in_progress = false;
+	ctx->status = Status;
+	if (ctx->dctx)
+		decomp_fini(ctx->dctx, EFI_ERROR(Status));
+}
+
+static void
+do_download_ramdisk(CHAR8 *url, bool is_disk)
+{
+	EFI_STATUS Status;
+	EFI_GUID disk_type = is_disk ? virtual_disk_guid : virtual_cd_guid;
+	EFI_DEVICE_PATH_PROTOCOL *ram_disk_path;
+	IPXE_DOWNLOAD_FILE token;
+	dl_state *ctx = &dl;
+
+	Status = BS->LocateProtocol(&ipxeGuid, NULL, (void**)&ipxe_download);
+	if (EFI_ERROR(Status))
+		return; /* most uses won't have this, don't whine */
+	Status = BS->LocateProtocol(&ramdiskGuid, NULL, (void**)&ram_disk);
+	if (EFI_ERROR(Status))
+		return; /* XXX whine about it? */
+
+	printf("Downloading %s as a %s\n", url, is_disk ? "disk" : "cd");
+	ctx->in_progress = true;
+	Status = ipxe_download->Start(ipxe_download, url, download_data, download_finish,
+	    &dl, &token);
+	if (EFI_ERROR(Status)) {
+		printf("Couldn't start download %u\n", (unsigned)Status);
+		download_cleanup(ctx);
+		return;
+	}
+	while (ctx->in_progress) {
+		ipxe_download->Poll(ipxe_download);
+	}
+	if (EFI_ERROR(ctx->status)) {
+		printf("Download had error %u\n", (unsigned)ctx->status);
+		download_cleanup(ctx);
+		return;
+	}
+	if (ctx->size == 0) {
+		printf("Nothing downloaded\n");
+		download_cleanup(ctx);
+		return;
+	}
+
+	printf("\nDownloaded %llu bytes, actual size %llu -- registering ramdisk\n",
+	    ULL(ctx->size), ULL(decomp_buffer_length(ctx->dctx)));
+
+	/*
+	 * Register the RamDisk with UEFI. This registers it so the rest of the
+	 * boot loader can see it as a block device.
+	 */
+	Status = ram_disk->Register(decomp_buffer(ctx->dctx), decomp_buffer_length(ctx->dctx),
+	    &disk_type, NULL, &ram_disk_path);
+	if (EFI_ERROR(Status)) {
+		printf("failed to register ram disk %u\n", (unsigned)Status);
+		download_cleanup(ctx);
+		return;
+	}
+
+	CHAR16 *text = efi_devpath_name(ram_disk_path);
+	if (text != NULL) {
+		CHAR8 uefi_path[1024];
+		printf("Installed RAM disk as %S\n", text);
+
+		cpy16to8(text, uefi_path, sizeof(uefi_path));
+		setenv("uefi_ignore_boot_mgr", "true", 1);
+		setenv("uefi_rootdev", uefi_path, 1);
+		efi_free_devpath_name(text);
+	} else {
+		printf("Installed RAM disk to unknown device type\n");
+	}
+}
+
+/*
+ * Scan the command line for memdisk=url or memcd=url. Do nothing if that's not
+ * present, otherwise try to download that image.
+ *
+ * Open Question: Do we want some way to chain boot into the /boot/loader.efi or
+ * \efi\boot\bootXXXXX.efi inside the ram disk we load? If so, how do we keep
+ * from infinite chainbooting? Also, I don't understand the load it but don't save
+ * it option...
+ */
+void
+maybe_download_ramdisk(int argc, CHAR16 **argv)
+{
+	char var[256];
+
+	for (int i = 0; i < argc; i++) {
+		cpy16to8(argv[i], var, sizeof(var));
+		if (strncmp(var, "memdisk=", 8) == 0) {
+			do_download_ramdisk(var + 8, true);
+			return;
+		}
+		if (strncmp(var, "memcd=", 6) == 0) {
+			do_download_ramdisk(var + 6, false);
+			return;
+		}
+	}
+	return;
+}