From 28de700645f263cd1f3d638b179629285670e7a5 Mon Sep 17 00:00:00 2001 From: Robert Allan James Date: Sat, 29 Aug 2026 10:18:07 -0400 Subject: [PATCH] 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. --- FABRIC-3.md | 13 +++++++ capsules/BLOCK_MAP.md | 2 +- disk/artemis.img | Bin 31457280 -> 31457280 bytes src/starkernel/rng/rng.c | 82 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 90 insertions(+), 7 deletions(-) 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 ef9308134f83b4c3d25d5e09a9c451e0b39698b9..742e383a26818e42d1e2efecc317c2f23ea18a08 100644 GIT binary patch delta 5187 zcmeH}k5d%q8OJ#}IEjZKzi!VU0*XKYkwir)B7;BpDMa)X^@ylAh=UbtL)0P#vdW;~ z0U3@&PaB~cU;wGo1ESE>Dr$okq7g|DF=1k^3Q~)tG(h@&-`#hU@lWW$%rmpk=ly>6 zeRgl}*zD|Vn`~0NjGw}r^K#yTx8xQ4RNjhzji1IVd24<;Z^PU2Gk81RzQKVDmS<%9 zg*WaS_l%S&Te`NdFvH(Km4x;`+)2^m>p-0osvv89i~}bxeJmxuS-Y1els`!*VE-YC zZq1(0BrT-&6=;Q>9G{lZM1C?sek$F3eE||*9H*^!lx$jh9Sy|R_%Nq++eM1ZvwhiF z4RB&UL5ox9r(8-L(8wD5CR8#54$G(LgB@&@{&u&ib)iz{nTHF&MQ}k&jR%~rTFNUg zyQEwScn~BrW;=08d24+=X_v~%G=)x_yy@rR=krq33knraNKH{q8Ay!l7hOuxj+9`%najZTPUU+H- z3lCs*?2L+q2hmoP1dtYI9wOoR%>^txj#b68J{F#EQk!2a7P1X0ME^5a&!W~UPQKyS zAF=3kw54Y8yM*)Fl5k5-8EMJo%~Gk&pDbos7i8Z%vP?Ly(UoKsZN0l+`9wzjjY(uF z$5E|)b;;hytM*t~3@=zcU4|`|$jYius5pzzq;<4kDl-;AwFs>-v31R9tamG4vcCEZ z`eW{^Y>1Ei$0Tn`E}}ior>VvI0zj(dXjg4+RfHAEm;3kULWhLThXih z|IPBBk^CpM|6==VZe!gi`4sD`@1Spq`XlWzPY21jnVwKTRMwdIP<3Uxd7FXV(E;2; z{YVOX+6K{T4KKvoKLlA`{+k^3bd6)x zYBtIh<%V)c&B60L*WXmU;5Fm-9#<_bB1(T?mpJ5skq<@&*@_>DX553b_}$56tgoK% z;N%I$4GD$B@DvSv2P8!&QCI#o*(%{>ybEvTnRJqjd9HIR~YV@!H; zOc6tN%6cFn>sBR2FJA%)(iKQ{*Vbsn3y6DnJ55ZykdRMmDn;+w#%e^V9a5b|5tH_8 zx=Is|d?e&m*GAFu2q0mn9!Md7>txb}O4l`HymXU<8dl7s$jRypCOv}`QDL`1951}| zFikjLm(Z@*T#BrQfrOpLAZ7e_Et8bL|C%PEo#QE5E0cc^h(jT)^y1{UGd^J9GvkY5 z$yj4=Nhs@i5JfBFfCR}IQpVUlCIya^(8QDeFB$51P)1R>>^_rxA?1AOuu;6053`@s zgv~HPeqI=BqmAQ6afe`pJ4@EFaEd-Mj*OLYQ9?^x<0-m-2S^wz22$btQYQVq>jF(w zu9MK==8F_5(t!j?3#obAJtocRv3i^CluJ+|8SA_Ohe9~bi!)CRw{J`z_rwc`)2w%B z_J^G!QLbOLlah_O_6eer3FSpryr_g9YNmng(;`vcDQcnQW>Y*<3ZR@#C>9ju*<?r6o3(-0(_3 zUVx_E-KU5rJFIW~1-#t#289ie2z+AR*calk-b#N3a$Z#2&$6li4>WiD5P@Mb=@o1A zE#t7)9<{tXdBz&?GmkwS7*00o{TN7?-`Sg!?=aJ_#M=vZYDr=+dbO`86(wJ#=txMO zuPFIK>auTRiGVJ*L?V3^NRXd5?oe3*4uu_p5!Qb<)lWR#kM6lNd{b$~(6OUOC|dpo zkRZiC3cD4>615f!-yu63ACgdSaS%o49HW_}h4gKjj!9MZWi&BynxI57mVXltg&oom z)@|uz;g53VTgeW8&5_Xde2$`P=YRw$6H-E+{e1CSA}{TyiShFiIy-QXqKY;kK`MY0 zeMG~g^0@mn;r17T3dvZ#k8mi24G6bwDr4cVwgx7Vu^wkhD9ts5qP?eq1gQqnhrJJ& zq^m5Y37atqz05LD)b8BPq$`kIxS9UqwVaP0p^4fsf?CK}BdItPc4$ZVu|mtj;hh_{ znc=VBM?#^`wG^$401~7gNY;*cYJPtA&%?Vv-v7B#oiW`9ZzP|8v*nj>n`9Zqq133E zsMk@>s97i%)NGV1$_?d?nuF8Lbv5B=(ww|QlTX6rcV+T7Ve(h=|L(s7{>wS57u5|t HlG*+bm})Si delta 5203 zcmeH~i&ItA9>+ZoAmD%~T+Tkm;{eLz3X%d5NZ6!a1~`J4K4jC z%*SEuk^yNb2`CU~0@L6t2{CO(G>{l|G(sMFy<`P+ziX|%wodmyxC1l4nf?3h?`N;y zUT2>NIXO8Kb4ZB_Q7NiKwP+z)idN!NqP1uv+KL*{PP7*%iVor=akA)G=Ojd^Lmy6z zZpa$-iB@QuI#TCOz@L|w14rv_q*2r}7wGbsHr&o^a1zvQtL4Pagg0rT9T(HCy4yz?c&#&{0)&G zMFpB9`6AMH^7Ev&q8+XKwLPT8O5c<4iR=4$_y*3ly;aM@-DsaXu!Jm(a1RMDZOrE3 zew;lvx{Zei(5BxiAuYl~BwVuP4;~)H*@pFtGT2lVW6nC&VI}Fp6?Ixf?<87D=MwF- zg1Y~v4UgKRP5Z7!8p45uJ3k!ed*iCrsan6_Gq@q!KD|nsK~J)K+v79-@{0TxSpNz+ zwIao|@~@{H>qTvbwJOFjuOX{Ii`_r66Du4j+soR9u|n+>jr#dN1C;De58dd%X-^qSl zzx()E@(kj(D+bV(tXnKSRYPPspQO8_C6DAN&c2^q!S{U(?OBTtl;oL42`C+E3hHT; z3(6JchH^*gQ64BylouZ3se!#8KVw_}^E5ok!)NA5w>UHnYlh9=#`j}%nxH7H^Jo5gZ*R#(&;w`BET>gte`dEoP^$UFq?%XaT-P!?)f)4?)_p-)E zQ14w4ut1VdULU53{Y5fz_f*d#A5V4P0x`)2l8sd+mtNbFO%o9TGKw^a6t%_zG07iN z`QjQb-CEE~6MOzCBlU!SiXQ9%Vp0U8E0zD|((S|Z=95{llhc27s7kfYkYQ zXqI9g@7jiN{HKupSpH2J}Vcs8D`Y5HgkS*b_NZ^#_=iBV`SZ5pHGtD5|m zB`4oUP${>!vw-cn;g@2G;!+_I&ClnTQqppvoh!vyCcI$M5@{E-|Ei>kUn^zgkbjJ# zPcH(o*^WYT-tiKbW{p@G$U?d*8QC>!C`!2i#H2<@Ww!gc6#q^tO}rZ*qv>nbP}Cm- z#H2Pz8lMg>Wh`rzdjk&&Gn|J-A1=m zlI}t33^sBpYTrhh7+xl$^ti1QCB6&9RvLj67*fn7&E`&;XijD*L7}#MABQZg@x^yd zZzm5Q7!8RdW6hq@#nGn0aEi8t05QqMS5QU2s;~9HryTjtv<)5JO*3gFPr&c56;XJm zk-)pEhMq{L#nxtj$YsqbO!oJiWRo*UW6gsSBNq&eeMn)5UFwr=1l$W>l{{YfHFI=4 znK$>U1P!>Lei3Eu_X(5NHNf3$eeP;GU z50cTG!7UV-7XmTqC?s=tI+uRPvRXt|_{m}oN2faePSKnhKul_cbVpak6TP=Tqlujf z1mU;ata3aG>iUPi89H^}!55^D6HVO4&?s{8j`Kk5N-lna`d)&_6AOdWj3g18;vOd5 z#;#Q@6wO;^3X>#%NGlIE@Wjjxk0c`PY9%PZ54*DJ9uC2y{(myR4OqKO-3f|AHs z_Q!F^!X|_t?7q*#`?so-$qGd^GD`hWps1z;h)LOy-f(+PtS6x7ow7nCc?4dsr~qdZWaC@)-Ys=gOT> 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; } }