Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160616568
D57677.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D57677.id.diff
View Options
diff --git a/stand/efi/include/ipxe_download.h b/stand/efi/include/ipxe_download.h
new file mode 100644
--- /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
--- a/stand/efi/libefi/efipart.c
+++ b/stand/efi/libefi/efipart.c
@@ -1255,11 +1255,11 @@
* 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 @@
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
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -22,9 +22,11 @@
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}/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
diff --git a/stand/efi/loader/decompress.h b/stand/efi/loader/decompress.h
new file mode 100644
--- /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/decompress.c b/stand/efi/loader/decompress.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/decompress.c
@@ -0,0 +1,410 @@
+/*
+ * 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>
+#include <zstd.h>
+#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;
+ ZSTD_DStream *zstdstrm;
+ };
+ 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
+ */
+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);
+}
+
+/*
+ * 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;
+ case zstd:
+ dctx->init = zstd_init;
+ dctx->step = zstd_step;
+ dctx->fini = zstd_fini;
+ break;
+ case none:
+ dctx->init = null_init;
+ dctx->step = null_step;
+ dctx->fini = null_fini;
+ }
+
+ 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/loader_efi.h b/stand/efi/loader/loader_efi.h
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -59,6 +59,8 @@
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
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1319,6 +1319,14 @@
*/
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 @@
"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
--- /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;
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jun 27, 6:10 AM (8 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34383996
Default Alt Text
D57677.id.diff (21 KB)
Attached To
Mode
D57677: loader.efi: Recognize new memdisk=<url> and memcd=<url> options
Attached
Detach File
Event Timeline
Log In to Comment