Page MenuHomeFreeBSD

D57677.diff
No OneTemporary

D57677.diff

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

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)

Event Timeline