Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160467561
D57677.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D57677.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/loader/Makefile b/stand/efi/loader/Makefile
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -25,6 +25,7 @@
efi_main.c \
framebuffer.c \
main.c \
+ memdisk.c \
self_reloc.c \
vers.c \
gfx_fb.c \
@@ -49,6 +50,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
@@ -151,4 +153,8 @@
LDADD= ${LDR_INTERP} ${LIBEFI} ${LIBSAFDT} ${LIBEFI_FDT} ${LIBSA}
.endif
+RamDisk.hex: RamDisk.asl
+ iasl -vs -p${.OBJDIR}/RamDisk -tc ${.ALLSRC}
+memdisk.o: RamDisk.hex
+
.include <bsd.prog.mk>
diff --git a/stand/efi/loader/RamDisk.asl b/stand/efi/loader/RamDisk.asl
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/RamDisk.asl
@@ -0,0 +1,38 @@
+/** @file
+ The definition block in ACPI table for NVDIMM root device.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+DefinitionBlock (
+ "RamDisk.aml",
+ "SSDT",
+ 2,
+ "INTEL ",
+ "RamDisk ",
+ 0x1000
+ )
+{
+ Scope (\_SB)
+ {
+ Device (NVDR)
+ {
+ //
+ // Define _HID, "ACPI0012" NVDIMM Root Device
+ //
+ Name (_HID, "ACPI0012")
+
+ //
+ // Readable name of this device
+ //
+ Name (_STR, Unicode ("NVDIMM Root Device"))
+
+ Method (_STA, 0)
+ {
+ Return (0x0f)
+ }
+ }
+ }
+}
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
@@ -1299,6 +1299,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.
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,301 @@
+/*
+ * 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 <IndustryStandard/Acpi61.h>
+#include <Protocol/AcpiSystemDescriptionTable.h>
+#include <Protocol/AcpiTable.h>
+#include <Protocol/RamDisk.h>
+#include <ipxe_download.h>
+#include <sys/_param.h>
+#include "RamDisk.hex"
+
+#define ULL(x) ((unsigned long long)(x))
+
+static EFI_GUID acpitableGuid = EFI_ACPI_TABLE_PROTOCOL_GUID;
+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 EFI_MEMORY_TYPE mem_type = EfiReservedMemoryType;
+
+static EFI_ACPI_TABLE_PROTOCOL *acpi_table;
+static IPXE_DOWNLOAD_PROTOCOL *ipxe_download;
+static EFI_RAM_DISK_PROTOCOL *ram_disk;
+
+static struct download_state
+{
+ bool in_progress;
+ UINTN size;
+ EFI_PHYSICAL_ADDRESS buf;
+ EFI_STATUS status;
+} dl;
+
+static void
+download_cleanup(void)
+{
+ if (dl.buf && dl.size)
+ BS->FreePages(dl.buf, dl.size >> 12);
+}
+
+static EFI_STATUS EFIAPI
+download_data(IN VOID *Context, IN VOID *Buffer, IN UINTN BufferLength, IN UINTN FileOffset)
+{
+ UINTN pages;
+
+ if (dl.size == 0 && BufferLength == 0) {
+ dl.size = FileOffset;
+ dl.size = roundup2(dl.size, 4096);
+ pages = dl.size >> 12;
+ EFI_STATUS status = BS->AllocatePages(AllocateAnyPages, mem_type, pages, &dl.buf);
+ if (EFI_ERROR(status)) {
+ printf("Failed to allocate memory for %llu bytes\n", ULL(dl.size));
+ dl.size = 0;
+ dl.in_progress = false;
+ dl.status = status;
+ return (status);
+ }
+ BS->SetMem((void *)dl.buf, pages << 12, 0);
+ dl.status = EFI_SUCCESS;
+ return (EFI_SUCCESS);
+ }
+ if (FileOffset + BufferLength > dl.size) {
+ printf("Download exceeded buffer, throwing it all away\n");
+ dl.in_progress = false;
+ dl.status = EFI_BAD_BUFFER_SIZE;
+ download_cleanup();
+ dl.buf = 0;
+ return (dl.status);
+ }
+ CHAR8 *src = Buffer;
+ CHAR8 *dst = (void*)(dl.buf + FileOffset);
+ BS->CopyMem(dst, src, BufferLength);
+ unsigned long long sofar = FileOffset + BufferLength;
+#define MB 1000000
+ if (sofar / MB != FileOffset / MB) {
+ printf("\r%dMB / %dMB (%d%%)",
+ (int)(sofar / MB),
+ (int)(dl.size / MB),
+ (int)(100 * sofar / dl.size));
+ }
+ return (EFI_SUCCESS);
+}
+
+static void EFIAPI
+download_finish(IN VOID *Context, IN EFI_STATUS Status)
+{
+ dl.in_progress = false;
+ dl.status = Status;
+}
+
+static UINT8 EFIAPI
+checksum8(IN CONST UINT8 *buf, IN UINTN len)
+{
+ UINT8 sum;
+
+ for (sum = 0; len > 0; len--)
+ sum = (UINT8)(sum + *buf++);
+ return (UINT8)(0x100 - sum);
+}
+
+static void
+setup_nvdimm_table(EFI_ACPI_TABLE_PROTOCOL *acpi_table, EFI_GUID disk_type)
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *ram_disk_path = NULL;
+ CHAR16 *text = NULL;
+ EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE *Nfit = NULL;
+ EFI_ACPI_DESCRIPTION_HEADER *NfitHeader;
+ EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *SpaRange;
+ UINT32 NfitLen;
+ UINTN SSDTKey = 0, NfitKey;
+
+ /*
+ * Register the RamDisk with UEFI. This registers it so the rest of the
+ * boot loader can see it as a block device. We use these mechanisms so
+ * that we could, in theory, chain boot a disk with Linux or other
+ * operating systems. The rest of the loader will also notice if someone
+ * chain-booted us with these structures, though of course we won't take
+ * this path if so.
+ */
+ Status = ram_disk->Register(dl.buf, dl.size, &disk_type, NULL, &ram_disk_path);
+ if (EFI_ERROR(Status)) {
+ printf("failed to register ram disk %u\n", (unsigned)Status);
+ goto unwind_error;
+ }
+
+ text = efi_devpath_name(ram_disk_path);
+ if (text != NULL) {
+ printf("Installed RAM disk as %S\n", text);
+ } else {
+ printf("Installed RAM disk to unknown device type\n");
+ }
+
+ /* Add SSDT for the NVDIMM Root device for our ram disk */
+ Status = acpi_table->InstallAcpiTable(acpi_table,
+ ramdisk_aml_code, sizeof(ramdisk_aml_code), &SSDTKey);
+ if (EFI_ERROR(Status)) {
+ printf("Couldn't add SSDT table\n");
+ goto unwind_error;
+ }
+
+ /*
+ * The NVDIMM Firmware Interface Table (NFIT) structure is a standard
+ * ACPI table header with some padding, followed by the NFIT list. We
+ * pass in just the SPA range describing the PA range, writeback caching
+ * and one of the two RAM disk types:
+ * - RAM Disk supporting a Virtual Disk Region
+ * - RAM Disk supporting a Virtual CD Region
+ * The FreeBSD kernel uses this structure to know about the ram disk
+ * being passed into it as an alternative vector for ram disks.
+ */
+ NfitLen = sizeof(*Nfit) + sizeof(*SpaRange);
+ Status = BS->AllocatePool(EfiACPIReclaimMemory, NfitLen, (VOID *)&Nfit);
+ if (EFI_ERROR(Status)) {
+ printf("Cannot allocate NFIT space\n");
+ goto unwind_error;
+ }
+ BS->SetMem(Nfit, NfitLen, 0);
+
+ NfitHeader = &Nfit->Header;
+ *NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER) {
+ .Signature = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE,
+ .Length = NfitLen,
+ .Revision = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_REVISION,
+ .OemTableId = 0x204b5349444d454dull, // "MEMDISK "
+ .OemId = { 'M', 'E', 'M', 'D', 'S', 'K' },
+ };
+ SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)(Nfit + 1);
+ *SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE) {
+ .Type = EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE,
+ .Length = sizeof(*SpaRange),
+ .SystemPhysicalAddressRangeBase = dl.buf,
+ .SystemPhysicalAddressRangeLength = dl.size,
+ .AddressRangeTypeGUID = disk_type,
+ .AddressRangeMemoryMappingAttribute = EFI_MEMORY_WB,
+ };
+ NfitHeader->Checksum = checksum8((UINT8 *)Nfit, NfitHeader->Length);
+
+ /*
+ * Publish the NFIT to the ACPI table.
+ */
+ Status = acpi_table->InstallAcpiTable(acpi_table, Nfit, NfitHeader->Length, &NfitKey);
+ if (EFI_ERROR(Status)) {
+ printf("Couldn't add NFIT table\n");
+ goto unwind_error;
+ }
+
+ /*
+ * OK. We succeeded. Set the boot path to this. It's too early to set
+ * diskXXX since those haven't been probed yet, but we can pass in the
+ * UEFI path since we computed it above.
+ */
+ if (text != NULL) {
+ CHAR8 uefi_path[1024];
+
+ 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);
+ }
+ return;
+unwind_error:
+ if (text)
+ efi_free_devpath_name(text);
+ if (Nfit)
+ BS->FreePool(Nfit);
+ if (SSDTKey != 0)
+ acpi_table->UninstallAcpiTable(acpi_table, SSDTKey);
+ if (ram_disk_path) {
+ ram_disk->Unregister(ram_disk_path);
+ BS->FreePool(ram_disk_path);
+ }
+ download_cleanup();
+ return;
+}
+
+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;
+
+ IPXE_DOWNLOAD_FILE token;
+
+ 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? */
+ Status = BS->LocateProtocol(&acpitableGuid, NULL, (void**)&acpi_table);
+ if (EFI_ERROR(Status))
+ return; /* XXX whine about it? */
+
+ printf("Downloading %s as a %s\n", url, is_disk ? "disk" : "cd");
+ dl.in_progress = true;
+ Status = ipxe_download->Start(ipxe_download, url, download_data, download_finish,
+ NULL, &token);
+ if (EFI_ERROR(Status)) {
+ printf("Couldn't start download %u\n", (unsigned)Status);
+ download_cleanup();
+ return;
+ }
+ while (dl.in_progress) {
+ ipxe_download->Poll(ipxe_download);
+ }
+ if (EFI_ERROR(dl.status)) {
+ printf("Download had error %u\n", (unsigned)dl.status);
+ download_cleanup();
+ return;
+ }
+ if (dl.size == 0) {
+ printf("Nothing downloaded\n");
+ download_cleanup();
+ return;
+ }
+
+ printf("\nDownloaded %llu bytes -- registering ramdisk\n", ULL(dl.size));
+
+ /*
+ * finally, add the ramdisk as an nvdimm
+ */
+ setup_nvdimm_table(acpi_table, disk_type);
+}
+
+/*
+ * 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
Thu, Jun 25, 7:41 PM (11 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34295038
Default Alt Text
D57677.diff (13 KB)
Attached To
Mode
D57677: loader.efi: Recognize new memdisk=<url> and memcd=<url> options
Attached
Detach File
Event Timeline
Log In to Comment