git: 25b58eb37a79 - main - loader.efi: download md images through file API
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 11 Sep 2026 05:04:29 UTC
The branch main has been updated by imp:
URL: https://cgit.FreeBSD.org/src/commit/?id=25b58eb37a7971f94f61f3a001b0d2c47228f08e
commit 25b58eb37a7971f94f61f3a001b0d2c47228f08e
Author: Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-09-11 04:14:18 +0000
Commit: Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-09-11 04:15:29 +0000
loader.efi: download md images through file API
Feed the existing decompressor from loader file descriptors as well as
iPXE callbacks, allowing arbitrary HTTP filesystem URLs to become
kernel-exportable memory disks.
Sponsored by: Netflix
Differential Revision: https://reviews.freebsd.org/D59431
---
stand/efi/loader/loader_efi.h | 1 +
stand/efi/loader/memdisk.c | 102 +++++++++++++++++++++++++++++++++++-------
2 files changed, 88 insertions(+), 15 deletions(-)
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
index c53edb639ad5..6fe547b83453 100644
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -60,6 +60,7 @@ void efi_copy_finish(void);
void efi_copy_finish_nop(void);
void maybe_download_ramdisk(int argc, CHAR16 **argv);
+int download_md_image(const char *url);
#if defined(__amd64__) || defined(__i386__)
/* Need this to setup page tables */
diff --git a/stand/efi/loader/memdisk.c b/stand/efi/loader/memdisk.c
index a425d628a85f..d02b99a0677c 100644
--- a/stand/efi/loader/memdisk.c
+++ b/stand/efi/loader/memdisk.c
@@ -17,6 +17,7 @@
#include <sys/_param.h>
#define ULL(x) ((unsigned long long)(x))
+#define DOWNLOAD_BUFSIZE (64 * 1024)
static EFI_GUID ipxeGuid = IPXE_DOWNLOAD_PROTOCOL_GUID;
static EFI_GUID ramdiskGuid = EFI_RAM_DISK_PROTOCOL_GUID;
@@ -35,6 +36,7 @@ static struct dl_state
size_t size;
EFI_STATUS status;
decomp_state *dctx;
+ bool complete;
} dl;
static void
@@ -45,13 +47,13 @@ download_cleanup(dl_state *ctx)
ctx->in_progress = false;
}
-static EFI_STATUS EFIAPI
-download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN FileOffset)
+static EFI_STATUS
+download_chunk(dl_state *ctx, void *buffer, size_t length, size_t offset)
{
- dl_state *ctx = Context;
decomp_state *dctx = ctx->dctx;
+ enum step_return sr;
- if (FileOffset == 0 && BufferLength == 0) {
+ if (offset == 0 && length == 0) {
printf("Staritng the download\n");
return (EFI_SUCCESS);
}
@@ -59,9 +61,9 @@ download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN
/*
* Make a note of the size when we're hinted about it.
*/
- if (BufferLength == 0) {
- printf("We know we will download %llu bytes\n", ULL(FileOffset));
- ctx->size = FileOffset;
+ if (length == 0) {
+ printf("We know we will download %llu bytes\n", ULL(offset));
+ ctx->size = offset;
ctx->status = EFI_SUCCESS;
return (EFI_SUCCESS);
}
@@ -69,8 +71,8 @@ download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN
/*
* 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 (offset == 0) {
+ dctx = decomp_init(buffer, length, ctx->size);
if (dctx == NULL) {
ctx->in_progress = false;
ctx->status = EFI_VOLUME_CORRUPTED;
@@ -79,16 +81,16 @@ download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN
ctx->dctx = dctx;
}
- enum step_return sr = decomp_step(dctx, Buffer, BufferLength, FileOffset);
+ sr = decomp_step(dctx, buffer, length, offset);
if (sr == err) {
printf("Error on download\n");
- decomp_fini(dctx, true);
return (EFI_VOLUME_CORRUPTED);
}
+ ctx->complete = (sr == done);
- unsigned long long sofar = FileOffset + BufferLength;
+ unsigned long long sofar = offset + length;
#define MB 1000000
- if (sofar / MB != FileOffset / MB) {
+ if (sofar / MB != offset / MB) {
if (ctx->size)
printf("%dMB / %dMB (%d%%)\r",
(int)(sofar / MB),
@@ -100,6 +102,14 @@ download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN
return (EFI_SUCCESS);
}
+static EFI_STATUS EFIAPI
+download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength,
+ IN UINTN FileOffset)
+{
+
+ return (download_chunk(Context, Buffer, BufferLength, FileOffset));
+}
+
static void EFIAPI
download_finish(IN VOID *Context, IN EFI_STATUS Status)
{
@@ -111,7 +121,7 @@ download_finish(IN VOID *Context, IN EFI_STATUS Status)
decomp_fini(ctx->dctx, EFI_ERROR(Status));
}
-static void
+static int
fallback_to_md(EFI_PHYSICAL_ADDRESS pa, size_t len)
{
int unit;
@@ -120,9 +130,71 @@ fallback_to_md(EFI_PHYSICAL_ADDRESS pa, size_t len)
if (unit < 0) {
printf("Could not register downloaded image as an md: %s\n",
strerror(errno));
- return;
+ return (errno);
}
setenv("uefi_ignore_boot_mgr", "true", 1);
+ return (0);
+}
+
+int
+download_md_image(const char *url)
+{
+ struct stat sb;
+ dl_state ctx;
+ uint8_t *buf;
+ size_t offset;
+ ssize_t nread;
+ int error, fd;
+
+ fd = open(url, O_RDONLY);
+ if (fd < 0)
+ return (errno);
+ if (fstat(fd, &sb) != 0) {
+ error = errno;
+ goto out_close;
+ }
+ if (sb.st_size <= 0 || (uintmax_t)sb.st_size > SIZE_MAX) {
+ error = EINVAL;
+ goto out_close;
+ }
+
+ buf = malloc(DOWNLOAD_BUFSIZE);
+ if (buf == NULL) {
+ error = ENOMEM;
+ goto out_close;
+ }
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.size = sb.st_size;
+ offset = 0;
+ while ((nread = read(fd, buf, DOWNLOAD_BUFSIZE)) > 0) {
+ if (EFI_ERROR(download_chunk(&ctx, buf, nread, offset))) {
+ error = EIO;
+ goto out_decomp;
+ }
+ offset += nread;
+ }
+ if (nread < 0) {
+ error = errno;
+ goto out_decomp;
+ }
+ if (offset != ctx.size || !ctx.complete) {
+ error = EIO;
+ goto out_decomp;
+ }
+
+ decomp_fini(ctx.dctx, false);
+ error = fallback_to_md(decomp_buffer(ctx.dctx),
+ decomp_buffer_length(ctx.dctx));
+ goto out_free;
+
+out_decomp:
+ if (ctx.dctx != NULL)
+ decomp_fini(ctx.dctx, true);
+out_free:
+ free(buf);
+out_close:
+ close(fd);
+ return (error);
}
static void