diff --git a/FABRIC-3.md b/FABRIC-3.md index d0db8fa..196c36a 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -3413,6 +3413,19 @@ there. primitive) returns genuinely non-deterministic bytes (two boots differ), the Zuse mint path seeded from it produces a valid distinct cert per boot when bleached, and QEMU behavior is unchanged. +- **v2.0.1 — amd64 RDRAND backend BUILT + QEMU-verified 2026-08-29 (SER5 slice).** The first + real per-arch backend lands in `src/starkernel/rng/rng.c`, behind the v2.0.0 unified + entry point, `#if`-guarded to amd64: CPUID.01H:ECX[30] detection + inline-asm `rdrand` + draws feeding `rdrand_fill()` (whole-byte emission, partial draw discarded — throwing + away entropy is always safe). Probe order honors the policy: virtio-rng is tried first, + so the QEMU path stays on virtio-rng unchanged; RDRAND is the fallback that only real + hardware (which has no virtio-rng) reaches. QEMU-verified both ways on amd64: with + virtio-rng present → `rng: backend = virtio-rng` (unchanged); with virtio-rng absent and + RDRAND exposed (`-cpu max`) → `rng: backend = rdrand` + `entropy: ready` + Zuse attach + confirmed. `rdrand_fill()`'s exact logic host-proven to fill 32-byte/16-byte buffers and + produce differing draws run-to-run (non-deterministic). aarch64/riscv64 builds unaffected + (guarded off). Still parked for their real boards: riscv64 Zkr (RNDR), aarch64 peripheral + RNG. Full "two distinct certs per bleached boot" proof remains v2.5.0/real-board. #### G.5 [v2.5.0] Real-machine boot validation (SER5 / RasPi 6 / Milk-V) diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index 9e28d25..e631868 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 ef93081..742e383 100644 Binary files a/disk/artemis.img and b/disk/artemis.img differ diff --git a/src/starkernel/rng/rng.c b/src/starkernel/rng/rng.c index 685429c..6686131 100644 --- a/src/starkernel/rng/rng.c +++ b/src/starkernel/rng/rng.c @@ -6,8 +6,16 @@ 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. + seam where the v2.5.0 real per-arch drivers plug in without touching the call + path. + + v2.0.1 (G.4, metadata): the real per-arch backends begin to land here, each + guarded by its ISA. amd64 RDRAND is first (SER5). Backend probe order matters + and honors the release policy: virtio-rng is tried first so the QEMU path + stays on virtio-rng unchanged; the real CPU primitive (RDRAND on amd64) is + the fallback that only real hardware reaches, because real boards have no + virtio-rng device. Each backend prints its identity at rng_init() so a boot + log states exactly which source serves entropy. */ #ifndef __STARKERNEL__ @@ -19,23 +27,80 @@ #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. */ + * rng_init() and rng_get_bytes(). v2.0.0: virtio-rng. v2.0.1 adds the amd64 + * RDRAND backend (real-hardware/SER5 path). */ enum { RNG_BACKEND_NONE = 0, RNG_BACKEND_VIRTIO, /* virtio-rng (QEMU, all three arches) */ +#if defined(__x86_64__) || defined(__amd64__) + RNG_BACKEND_RDRAND, /* amd64 RDRAND (real hardware, e.g. Beelink SER5) */ +#endif }; static int g_rng_backend = RNG_BACKEND_NONE; +/* --- amd64 RDRAND backend (G.4) ---------------------------------------- */ + +#if defined(__x86_64__) || defined(__amd64__) + +/* CPUID.01H:ECX bit 30 = RDRAND supported. */ +static inline int rdrand_available(void) { + uint32_t eax, ebx, ecx, edx; + __asm__ volatile ("cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(1u), "c"(0u)); + (void)eax; (void)ebx; (void)edx; + return (int)((ecx >> 30) & 1u); +} + +/* Execute RDRAND; returns 1 on a valid draw (CF=1), 0 otherwise. */ +static inline int rdrand64(uint64_t *out) { + uint8_t ok; + __asm__ volatile ("rdrand %0; setc %1" + : "=r"(*out), "=qm"(ok) + : + : "cc"); + return (int)ok; +} + +static int rdrand_fill(uint8_t *buf, size_t n) { + size_t filled = 0; + uint32_t attempts = 0; + const uint32_t MAX_ATTEMPTS = 128u; /* generous: hardware RDRAND is fast */ + while (filled < n) { + uint64_t w; + if (!rdrand64(&w)) { + if (++attempts > MAX_ATTEMPTS) return RNG_ERR_NO_BACKEND; + continue; + } + /* Emit whole bytes from the low end. The last (partial) draw is + * simply discarded — throwing away entropy is always safe. */ + for (size_t i = 0; i < sizeof(w) && filled < n; i++) + buf[filled++] = (uint8_t)(w >> (8 * i)); + } + return 0; +} + +#endif /* amd64 */ + 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. */ + * tried first so the QEMU path stays on virtio-rng unchanged; the real + * per-arch primitive is the fallback that only real hardware reaches. */ if (virtio_rng_init() == 0) { g_rng_backend = RNG_BACKEND_VIRTIO; console_println("rng: backend = virtio-rng"); return 0; } +#if defined(__x86_64__) || defined(__amd64__) + if (rdrand_available()) { + g_rng_backend = RNG_BACKEND_RDRAND; + console_println("rng: backend = rdrand"); + return 0; + } +#endif + /* Refuse loudly: never fall through to a deterministic seed. */ g_rng_backend = RNG_BACKEND_NONE; console_println( @@ -49,12 +114,17 @@ int rng_ready(void) { } int rng_get_bytes(uint8_t *buf, size_t n) { + if (!buf || n == 0) return RNG_ERR_NO_BACKEND; switch (g_rng_backend) { case RNG_BACKEND_VIRTIO: return virtio_rng_get_bytes(buf, n); +#if defined(__x86_64__) || defined(__amd64__) + case RNG_BACKEND_RDRAND: + return rdrand_fill(buf, n); +#endif default: - /* v2.5.0 real per-arch cases land here. No backend: refuse loudly, - * never return a deterministic throwaway. */ + /* No backend: refuse loudly, never return a deterministic + * throwaway. */ return RNG_ERR_NO_BACKEND; } }