v2.0.1: G.4 amd64 RDRAND backend behind rng_get_bytes() (SER5 entropy)
First real per-arch RNG backend, added to the v2.0.0 unified entry point in src/starkernel/rng/rng.c, #if-guarded to amd64: CPUID.01H:ECX[30] RDRAND detection + inline-asm rdrand draws feeding rdrand_fill() (whole-byte emission from the low end; a partial final draw is discarded -- throwing away entropy is always safe). Probe order honors the release policy: virtio-rng is tried first, so the QEMU path stays on virtio-rng unchanged; RDRAND is the fallback only real hardware (which has no virtio-rng device) 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: fills 32-byte/16-byte buffers and yields differing draws run-to-run (non-deterministic). aarch64/riscv64 builds unaffected (guarded off). riscv64 Zkr and aarch64 peripheral-RNG backends remain parked for their real boards. FABRIC-3.md G.4 amd64 slice marked BUILT + QEMU-verified.
This commit is contained in:
+13
@@ -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)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-29T14:11:01Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-29T14:16:48Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user