:: commit 0d79a93b2b77c6c062de8aa3f8f64835a2bab63d

Mintsuki <mintsuki@protonmail.com> — 2026-04-29 20:48

parents: 232c500efe

lib/rng_seed: Publish EFI RNG entropy as LINUX_EFI_RANDOM_SEED configuration table

diff --git a/common/lib/misc.c b/common/lib/misc.c
index fbc18570..1173e70d 100644
--- a/common/lib/misc.c
+++ b/common/lib/misc.c
@@ -9,6 +9,7 @@
 #include <lib/config.h>
 #include <lib/uri.h>
 #include <lib/bli.h>
+#include <lib/rng_seed.h>
 #include <fs/file.h>
 #include <mm/pmm.h>
 #include <libfdt.h>
@@ -261,6 +262,10 @@ no_unwind bool efi_boot_services_exited = false;
 bool efi_exit_boot_services(void) {
     EFI_STATUS status;
 
+    // Pull entropy from EFI_RNG_PROTOCOL while it's still callable and
+    // publish it for the kernel to mix into its early RNG state.
+    rng_seed_install();
+
     EFI_MEMORY_DESCRIPTOR tmp_mmap[1];
     efi_mmap_size = sizeof(tmp_mmap);
 
diff --git a/common/lib/rng_seed.c b/common/lib/rng_seed.c
new file mode 100644
index 00000000..11028050
--- /dev/null
+++ b/common/lib/rng_seed.c
@@ -0,0 +1,142 @@
+#if defined (UEFI)
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <efi.h>
+#include <lib/rng_seed.h>
+#include <lib/misc.h>
+#include <lib/print.h>
+#include <lib/libc.h>
+
+#define LINUX_EFI_RANDOM_SEED_TABLE_GUID \
+    { 0x1ce1e5bc, 0x7ceb, 0x42f2, { 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b } }
+
+#define EFI_RANDOM_SEED_SIZE 32
+
+struct linux_efi_random_seed {
+    uint32_t size;
+    uint8_t  bits[];
+} __attribute__((packed));
+
+// Pull entropy from EFI_RNG_PROTOCOL while boot services are alive, mix in
+// the NVRAM-resident "RandomSeed" variable if the OS left one for us, and
+// publish the result as the LINUX_EFI_RANDOM_SEED_TABLE configuration table
+// for the kernel's RNG to consume during early boot. Limine bypasses the
+// EFI stub that would normally do this.
+void rng_seed_install(void) {
+    EFI_GUID rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID;
+
+    // The RNG protocol is optional; the NVRAM seed alone is also a valid
+    // source.
+    EFI_GUID rng_guid = EFI_RNG_PROTOCOL_GUID;
+    EFI_RNG_PROTOCOL *rng = NULL;
+    if (gBS->LocateProtocol(&rng_guid, NULL, (void **)&rng) != EFI_SUCCESS) {
+        rng = NULL;
+    }
+
+    // Probe the NVRAM "RandomSeed" variable; if the OS left one for us,
+    // we'll consume and delete it so it isn't reused on the next boot.
+    UINTN nv_seed_size = 0;
+    EFI_STATUS probe = gRT->GetVariable(L"RandomSeed", &rng_table_guid,
+                                        NULL, &nv_seed_size, NULL);
+    if (probe != EFI_BUFFER_TOO_SMALL || nv_seed_size > 512) {
+        nv_seed_size = 0;
+    }
+
+    // A prior boot stage (shim, another stub) may have installed a seed
+    // already. Preserve it by concatenating rather than overwriting.
+    struct linux_efi_random_seed *prev_seed = NULL;
+    uint32_t prev_seed_size = 0;
+    for (UINTN i = 0; i < gST->NumberOfTableEntries; i++) {
+        if (memcmp(&gST->ConfigurationTable[i].VendorGuid,
+                   &rng_table_guid, sizeof(EFI_GUID)) == 0) {
+            prev_seed = gST->ConfigurationTable[i].VendorTable;
+            if (prev_seed->size <= 512) {
+                prev_seed_size = prev_seed->size;
+            }
+            break;
+        }
+    }
+
+    UINTN rng_bytes = (rng != NULL) ? EFI_RANDOM_SEED_SIZE : 0;
+
+    if (rng_bytes == 0 && nv_seed_size == 0 && prev_seed_size == 0) {
+        return;
+    }
+
+    UINTN total_size = sizeof(struct linux_efi_random_seed)
+                     + rng_bytes + nv_seed_size + prev_seed_size;
+
+    struct linux_efi_random_seed *seed = NULL;
+    EFI_STATUS status = gBS->AllocatePool(EfiACPIReclaimMemory, total_size,
+                                          (void **)&seed);
+    if (status != EFI_SUCCESS) {
+        printv("rng: failed to allocate random seed table: %X\n", (uint64_t)status);
+        return;
+    }
+
+    memset(seed, 0, total_size);
+
+    UINTN offset = 0;
+
+    // EFI_RNG_PROTOCOL output. Prefer the raw algorithm.
+    if (rng != NULL) {
+        EFI_GUID rng_algo_raw = EFI_RNG_ALGORITHM_RAW;
+        status = rng->GetRNG(rng, &rng_algo_raw, EFI_RANDOM_SEED_SIZE, seed->bits);
+        if (status == EFI_UNSUPPORTED) {
+            status = rng->GetRNG(rng, NULL, EFI_RANDOM_SEED_SIZE, seed->bits);
+        }
+        if (status == EFI_SUCCESS) {
+            offset += EFI_RANDOM_SEED_SIZE;
+        } else {
+            printv("rng: GetRNG failed: %X\n", (uint64_t)status);
+        }
+    }
+
+    // NVRAM "RandomSeed" variable, then delete it so the same bytes
+    // aren't reused on the next boot.
+    if (nv_seed_size > 0) {
+        UINTN got_size = nv_seed_size;
+        status = gRT->GetVariable(L"RandomSeed", &rng_table_guid, NULL,
+                                  &got_size, seed->bits + offset);
+        if (status == EFI_SUCCESS) {
+            gRT->SetVariable(L"RandomSeed", &rng_table_guid, 0, 0, NULL);
+            offset += got_size;
+        } else {
+            // Read failed despite probe succeeding. Wipe the slot to avoid
+            // publishing stale heap contents.
+            memset(seed->bits + offset, 0, nv_seed_size);
+        }
+    }
+
+    // Previous-stage seed.
+    if (prev_seed_size > 0) {
+        memcpy(seed->bits + offset, prev_seed->bits, prev_seed_size);
+        offset += prev_seed_size;
+    }
+
+    if (offset == 0) {
+        gBS->FreePool(seed);
+        return;
+    }
+
+    seed->size = (uint32_t)offset;
+
+    status = gBS->InstallConfigurationTable(&rng_table_guid, seed);
+    if (status != EFI_SUCCESS) {
+        printv("rng: failed to install random seed table: %X\n", (uint64_t)status);
+        gBS->FreePool(seed);
+        return;
+    }
+
+    if (prev_seed_size > 0) {
+        memset(prev_seed->bits, 0, prev_seed_size);
+        gBS->FreePool(prev_seed);
+    }
+
+    printv("rng: installed %u-byte random seed as configuration table\n",
+           seed->size);
+}
+
+#endif
diff --git a/common/lib/rng_seed.h b/common/lib/rng_seed.h
new file mode 100644
index 00000000..14605bfd
--- /dev/null
+++ b/common/lib/rng_seed.h
@@ -0,0 +1,10 @@
+#ifndef LIB__RNG_SEED_H__
+#define LIB__RNG_SEED_H__
+
+#if defined (UEFI)
+
+void rng_seed_install(void);
+
+#endif
+
+#endif
tab: 248 wrap: offon