From 09857b7228d52df70f2bf8cfb6ac0a804b9d8e87 Mon Sep 17 00:00:00 2001 From: Robert Allan James Date: Sat, 29 Aug 2026 09:57:17 -0400 Subject: [PATCH] G.2 (v2.0.0): unified rng_get_bytes() entropy entry point; virtio-rng sole backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QEMU-verifiable slice of the real-hardware RNG driver (per FABRIC-3.md §G.2). New include/starkernel/rng.h + src/starkernel/rng/rng.c provide the single entropy entry point: rng_init() probes the backend set (v2.0.0: virtio-rng only) and, on no backend, prints a loud boot-time warning while rng_get_bytes() returns RNG_ERR_NO_BACKEND - never silently degrading to a deterministic seed. The backend-selection switch in rng.c is the exact seam v2.5.0's per-arch drivers (amd64 RDRAND, riscv64 Zkr, aarch64 peripheral) plug into without touching the call path. Consumers route through the unified layer instead of virtio-rng directly: capsule_mint.c (identity seed + drive_uuid) and kernel_main.c phase 8 (rng_init()). virtio_rng.c stays as the sole backend. Built clean on amd64/aarch64/riscv64. QEMU amd64 boot: POST 1012/0/0 + ok>, "rng: backend = virtio-rng" + "entropy: ready", Zuse identity confirmed from thumbdrive - mint/cert behavior unchanged. FABRIC-3.md §G.2 v2.0.0 slice marked BUILT+VERIFIED. --- FABRIC-3.md | 13 ++++++ Makefile.starkernel | 2 + capsules/BLOCK_MAP.md | 2 +- disk/artemis.img | Bin 31457280 -> 31457280 bytes include/starkernel/rng.h | 65 ++++++++++++++++++++++++++ src/starkernel/capsule/capsule_mint.c | 8 ++-- src/starkernel/kernel_main.c | 24 +++++----- src/starkernel/rng/rng.c | 60 ++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 include/starkernel/rng.h create mode 100644 src/starkernel/rng/rng.c diff --git a/FABRIC-3.md b/FABRIC-3.md index 4933d67..78ec99a 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -3362,6 +3362,19 @@ redesign of the call path. loudly (boot-time message, safe fallback to this-boot-only cert) if no backend is present, and the pre-existing Zuse mint/cert behavior is unchanged on all three arches. The per-arch backends themselves are parked, explicitly, for v2.5.0. +- **v2.0.0 slice — BUILT and VERIFIED 2026-08-29.** `include/starkernel/rng.h` + + `src/starkernel/rng/rng.c` provide the single `rng_get_bytes()` entry point; `rng_init()` + probes the backend set (v2.0.0: virtio-rng only) and, on no backend, prints a loud + boot-time warning and `rng_get_bytes()` returns `RNG_ERR_NO_BACKEND` — it never silently + degrades to a deterministic seed. The backend-selection switch in `rng.c` is the exact seam + the v2.5.0 real per-arch drivers (amd64 RDRAND, riscv64 Zkr, aarch64 peripheral RNG) plug + into without touching the call path. Kernel consumers no longer touch `virtio_rng_*` + directly: `capsule_mint.c` (identity seed + `drive_uuid`) and `kernel_main.c` phase 8 + (`rng_init()`) route through the unified layer; `virtio_rng.c` stays as the sole backend. + Built clean on all three arches (`make -f Makefile.starkernel ARCH={amd64,aarch64,riscv64}`). + QEMU amd64 boot: POST `1012/0/0` + `ok>`, `rng: backend = virtio-rng` + `entropy: ready` + printed by the unified layer, and Zuse attach/identity confirmed from the thumbdrive — + Zuse mint/cert behavior unchanged. - **Exit criterion (v2.5.0 completion, for reference):** on each real board `rng_get_bytes()` returns genuinely non-deterministic bytes (two boots differ) and the Zuse mint path seeded from it produces a valid distinct cert per boot when bleached. diff --git a/Makefile.starkernel b/Makefile.starkernel index d1e0be2..e16f0f1 100644 --- a/Makefile.starkernel +++ b/Makefile.starkernel @@ -431,6 +431,7 @@ LOADER_SRCS_BASE := \ $(wildcard $(KERNEL_SRC)/capsule/*.c) \ $(wildcard $(KERNEL_SRC)/pci/*.c) \ $(wildcard $(KERNEL_SRC)/virtio/*.c) \ + $(wildcard $(KERNEL_SRC)/rng/*.c) \ $(wildcard $(KERNEL_SRC)/usb/*.c) \ $(KERNEL_SRC)/repl.c \ $(KERNEL_SRC)/doe_log.c \ @@ -483,6 +484,7 @@ KERNEL_SRCS_BASE := \ $(wildcard $(KERNEL_SRC)/capsule/*.c) \ $(wildcard $(KERNEL_SRC)/pci/*.c) \ $(wildcard $(KERNEL_SRC)/virtio/*.c) \ + $(wildcard $(KERNEL_SRC)/rng/*.c) \ $(wildcard $(KERNEL_SRC)/usb/*.c) \ $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.c) \ $(KERNEL_SRC)/repl.c \ diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index c7b2736..fb9945c 100644 --- a/capsules/BLOCK_MAP.md +++ b/capsules/BLOCK_MAP.md @@ -1,5 +1,5 @@ # Capsule Block Manifest — Auto-generated - + diff --git a/disk/artemis.img b/disk/artemis.img index 8af565f59287e4e94021029fbd7899c30b762048..6c0c8eb96ee88990d57ba618a480857a2251cdc4 100644 GIT binary patch delta 5198 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 ACIA2c diff --git a/include/starkernel/rng.h b/include/starkernel/rng.h new file mode 100644 index 0000000..1293644 --- /dev/null +++ b/include/starkernel/rng.h @@ -0,0 +1,65 @@ +/* + * rng.h — Unified entropy entry point for StarKernel + * + * The single place any kernel consumer (keygen, identity mint, drive_uuid, + * certificate serials, ...) asks for entropy. All entropy flows through + * rng_get_bytes() and never touches a backend directly. + * + * The set of active backends is determined at rng_init() time by probing, + * in order, until one (or more) come up: + * - v2.0.0 (QEMU): virtio-rng is the sole backend — there is no virtio-rng + * on real hardware, but QEMU exposes it uniformly on all three arches + * (amd64/aarch64/riscv64), and the paravirtualized device sidesteps the + * per-ISA gap where no single CPU RNG covers all three models (amd64 has + * RDRAND, riscv64 has Zkr, but QEMU's aarch64 CPU models expose neither — + * see virtio_rng.h / vm_uuid.h for the identical finding). + * - v2.5.0 (real hardware): real per-arch backends are inserted here without + * touching the call path — amd64 RDRAND, riscv64 Zkr (RNDR), aarch64 + * peripheral RNG — each handled by a case in rng_init() and rng_get_bytes() + * (grid §G.4). On QEMU all three arches stay on virtio-rng; nothing changes. + * + * Probe-and-refuse-loudly contract (§G.2): if no backend comes up at + * rng_init(), the kernel prints a loud boot-time message. A later + * rng_get_bytes() call with no backend returns -1 (RNG_ERR_NO_BACKEND) rather + * than ever silently degrading to a deterministic throwaway — the exact failure + * Phases A/G call out as unacceptable. Callers (e.g. capsule_mint_identity) + * must surface that refusal as an explicit no-entropy error, never proceed with + * a deterministic seed. + * + * Important ordering: rng_init() must run before any rng_get_bytes()/mint call + * (it already does in kernel_main phase 8, ahead of Zuse boot attach, which is + * the only mint path in v2.0.0). rng_get_bytes() with rng_init() never + * successful returns RNG_ERR_NO_BACKEND, never blocks. + */ + +#ifndef STARKERNEL_RNG_H +#define STARKERNEL_RNG_H + +#include +#include + +/* Return codes (negative = failure). */ +#define RNG_ERR_NO_BACKEND (-1) /* rng_init() found no working entropy source */ + +/* + * rng_init — probe and bring up the entropy backends. Returns 0 if at least + * one backend is active (rng_get_bytes() will succeed), nonzero otherwise. + * Prints a loud boot-time message when no backend comes up. Call once, early. + */ +int rng_init(void); + +/* + * rng_ready — 1 if at least one backend is active, 0 otherwise. + */ +int rng_ready(void); + +/* + * rng_get_bytes — fill buf with n bytes of real entropy, blocking until all + * n bytes are obtained. + * + * Returns 0 on success (buf fully filled). + * Returns RNG_ERR_NO_BACKEND (-1) if no backend is active. + */ +int rng_get_bytes(uint8_t *buf, size_t n); + +#endif /* STARKERNEL_RNG_H */ diff --git a/src/starkernel/capsule/capsule_mint.c b/src/starkernel/capsule/capsule_mint.c index 83bf841..3f35c3c 100644 --- a/src/starkernel/capsule/capsule_mint.c +++ b/src/starkernel/capsule/capsule_mint.c @@ -16,7 +16,7 @@ #include "starkernel/user_identity_seed.h" #include "starkernel/x509_ed25519.h" #include "starkernel/ed25519.h" -#include "starkernel/virtio_rng.h" +#include "starkernel/rng.h" #include "block_subsystem.h" /* compute_crc64() */ #include "blkio.h" #include @@ -101,11 +101,11 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm, /* issuer_vm==NULL is genesis mode (§F.21) -- no existing Zuse to * require a cert from. */ if (issuer_vm && !issuer_vm->zuse_cert_installed) return MINT_ERR_NO_ZUSE_CERT; - if (!virtio_rng_ready()) return MINT_ERR_NO_ENTROPY; + if (!rng_ready()) return MINT_ERR_NO_ENTROPY; /* Fresh identity keypair. */ uint8_t seed[32], pubkey[32]; - if (virtio_rng_get_bytes(seed, sizeof(seed)) != 0) return MINT_ERR_NO_ENTROPY; + if (rng_get_bytes(seed, sizeof(seed)) != 0) return MINT_ERR_NO_ENTROPY; ed25519_keygen(seed, pubkey); if (out_pubkey) memcpy(out_pubkey, pubkey, 32); if (out_seed) memcpy(out_seed, seed, 32); @@ -114,7 +114,7 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm, * the identity seed (§F.8 decision 3: "which physical drive," not * "whose identity"). */ uint8_t drive_uuid[16]; - if (virtio_rng_get_bytes(drive_uuid, sizeof(drive_uuid)) != 0) + if (rng_get_bytes(drive_uuid, sizeof(drive_uuid)) != 0) return MINT_ERR_NO_ENTROPY; uint32_t cert_devblock = 0; diff --git a/src/starkernel/kernel_main.c b/src/starkernel/kernel_main.c index 05517d2..e4a384c 100644 --- a/src/starkernel/kernel_main.c +++ b/src/starkernel/kernel_main.c @@ -65,7 +65,7 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL; #include "starkernel/repl.h" #include "starkernel/pci.h" #include "starkernel/virtio_blk.h" -#include "starkernel/virtio_rng.h" +#include "starkernel/rng.h" #include "starkernel/virtio_input.h" #include "starkernel/xhci_driver.h" #include "block_subsystem.h" @@ -590,20 +590,20 @@ static void kernel_main_deep(BootInfo *boot_info) { } } - /* Phase 8: virtio-rng entropy source. Real per-arch RNG doesn't cover - * all three architectures (amd64 RDRAND, riscv64 Zkr, but aarch64 has - * neither in QEMU's CPU models -- see vm_uuid.h's identical finding), - * so signing/keygen entropy comes from this paravirtualized device - * instead. Unconditional call site, same graceful-noop precedent as - * virtio_blk_find_artemis() above -- boot proceeds either way, the - * device is only required once something actually calls - * virtio_rng_get_bytes(). */ + /* Phase 8: entropy. Real per-arch RNG doesn't cover all three + * architectures (amd64 RDRAND, riscv64 Zkr, but aarch64 has neither in + * QEMU's CPU models -- see vm_uuid.h's identical finding), so signing/ + * keygen entropy comes from the unified rng_get_bytes() layer, whose + * v2.0.0 backend is the paravirtualized virtio-rng device. Unconditional + * call site, same graceful-noop precedent as virtio_blk_find_artemis() + * above -- boot proceeds either way, the device is only required once + * something actually calls rng_get_bytes(). */ { - int rrc = virtio_rng_init(); + int rrc = rng_init(); if (rrc == 0) { - console_println("virtio-rng: ready"); + console_println("entropy: ready"); } else { - console_println("virtio-rng: not available (continuing without)"); + console_println("entropy: not available (continuing without)"); } } diff --git a/src/starkernel/rng/rng.c b/src/starkernel/rng/rng.c new file mode 100644 index 0000000..685429c --- /dev/null +++ b/src/starkernel/rng/rng.c @@ -0,0 +1,60 @@ +/* + StarKernel — Unified entropy layer (rng_get_bytes) + + Single entropy entry point for the kernel; see include/starkernel/rng.h for + the contract and the probe-and-refuse-loudly discipline this implements. + + v2.0.0: virtio-rng is the sole active backend (QEMU-only, uniform across all + three arches). The backend-selection switch in rng_get_bytes() is the exact + seam where the v2.5.0 real per-arch drivers (amd64 RDRAND, riscv64 Zkr, + aarch64 peripheral RNG) plug in without touching the call path. +*/ + +#ifndef __STARKERNEL__ +#error "rng.c is kernel-only" +#endif + +#include "starkernel/rng.h" +#include "starkernel/virtio_rng.h" +#include "starkernel/console.h" + +/* Number of entropy backends known to this build. Each maps to one case in + * rng_init() and rng_get_bytes(). v2.0.0 has exactly one: virtio-rng. */ +enum { + RNG_BACKEND_NONE = 0, + RNG_BACKEND_VIRTIO, /* virtio-rng (QEMU, all three arches) */ +}; + +static int g_rng_backend = RNG_BACKEND_NONE; + +int rng_init(void) { + /* Probe backends in priority order; first success wins. virtio-rng is + * the sole backend at v2.0.0; v2.5.0 adds real per-arch drivers here. */ + if (virtio_rng_init() == 0) { + g_rng_backend = RNG_BACKEND_VIRTIO; + console_println("rng: backend = virtio-rng"); + return 0; + } + + /* Refuse loudly: never fall through to a deterministic seed. */ + g_rng_backend = RNG_BACKEND_NONE; + console_println( + "rng: WARNING — no entropy backend available; rng_get_bytes() " + "will refuse (no deterministic seed fallback)"); + return -1; +} + +int rng_ready(void) { + return g_rng_backend != RNG_BACKEND_NONE; +} + +int rng_get_bytes(uint8_t *buf, size_t n) { + switch (g_rng_backend) { + case RNG_BACKEND_VIRTIO: + return virtio_rng_get_bytes(buf, n); + default: + /* v2.5.0 real per-arch cases land here. No backend: refuse loudly, + * never return a deterministic throwaway. */ + return RNG_ERR_NO_BACKEND; + } +}