code wiki / _hdl_build / _k_r2_001d_nvme_gate.nx

_k_r2_001d_nvme_gate.nx source

↩ module page · 332 lines · 18180 B

1// _k_r2_001d_nvme_gate.nx -- the X-DRV-W1 STAGE B gate (the 2nd-device-class round-trip + the 2// new-kind/TAMPER proof, resting on PROOF-1 the no-floating foundation). 3// 4// PROOF-1 (FOUNDATION -- judged FIRST, per no-floating): the device-AGNOSTIC shape library 5// (nx_driver_shapes) re-emits the virtio-blk driver byte-identically and the existing SECTGATE 6// sector-data round-trip still runs GREEN on the SOVEREIGN rv64 emu THROUGH the generic shape 7// path. Three lanes (round-trip / byte-identical / virtio-tamper) -- the stage-A regression guard. 8// PROOF-2 (2nd device class -- only claimed after PROOF-1 GREEN): the SAME shape library, driven 9// by knowledge/specs/nvme_admin_enable_virt.spec alone, has the emitter ORGAN (nx_drvgen) AUTHOR 10// an NVMe-class controller bring-up image; the SOVEREIGN rv64 emu (+ the new NVMe device model) 11// runs it; the driver completes CC.EN->poll-CSTS.RDY, binds ASQ/ACQ, lays the 64-byte SQE, rings 12// the SQ-Tail doorbell, the device posts the phase-tagged CQE, the driver polls the CQE phase bit 13// + reads NvmeCqPeek back == cqpeek_expected. Transcript must CONTAIN "VIO-NVME EN RDY ASQ SQE 14// CQ\n" AND a clean SiFive-finisher halt (BOOTSOV verdict=GREEN). 15// TAMPER (no-false-green): bump cqpeek_expected by ONE in the SPEC, re-emit via nx_drvgen, re-run. 16// The CqPeek read-back verify branches past the last stage -> the transcript loses ONLY its " CQ" 17// canary (the "...ASQ SQE" tail survives) -> the KAT goes RED. The PROOF-2 GREEN is gated on 18// tamper_bites==1, so the green is a REAL run, not an assertion. 19// DISTINCTNESS (refutes "virtio-disguised"): (1) STRUCTURAL -- the NVMe image CONTAINS a backward 20// branch (a poll loop: negative B-type immediate => instruction-word bit31 set on a 0x63 opcode), 21// and the virtio image contains NONE (its handshake is straight-line forward-only; constant- 22// lifting cannot add a loop -- the eoe/X-AUT-006d argument). (2) BYTE-DISTINCT -- the NVMe image 23// bytes != the virtio image bytes AND the goldens differ. 24// 25// Every verdict comes from a PRINTED marker off a REAL run, never an asserted GREEN; raw bytes are 26// read SOVEREIGNLY (sys_read in the organ; a PowerShell pipe would corrupt a raw byte read). NO 27// qemu / gcc / openssl / python anywhere -- the sovereign emu IS the runtime; NVMe/PCI is a last- 28// mile interop probe + measuring stick to EXCEED, never a substrate. Marker -> knowledge/status/ 29// virtio_blk.log (NVMEGATE row). Overall verdict=GREEN iff PROOF-1 GREEN AND PROOF-2 GREEN AND 30// TAMPER bites AND distinctness holds. Sovereign orchestration (fork/dup3/execve/wait4). 31// license_tier: ORIGINAL 32import "nx_syscalls.nx" 33 34func ng_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 35func ng_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 36func ng_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 37 38// run nx_virtio_hs_emit <spec> (the refactored, shape-library-driven virtio emitter); serial muted. 39func ng_run_virtio_emit(spec: *u8) -> i64 { 40 let pid: i64 = sys_fork() 41 if pid == 0 { 42 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 43 if dn >= 0 { sys_dup3(dn, 1, 0) } 44 let argv: *i64 = sys_mmap(32) as *i64 45 argv[0] = "_offc/nx_virtio_hs_emit.elf" as *u8 as i64 46 argv[1] = spec as i64 47 argv[2] = 0 48 let envp: *i64 = sys_mmap(16) as *i64 49 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 50 envp[1] = 0 51 sys_execve("_offc/nx_virtio_hs_emit.elf" as *u8, argv, envp) 52 sys_exit(127) 53 } 54 let st: *i64 = sys_mmap(16) as *i64 55 sys_wait4(pid, st, 0) 56 return st[0] 57} 58 59// run nx_drvgen <spec> (the GENERIC 2nd-class emitter ORGAN); serial muted. 60func ng_run_drvgen(spec: *u8) -> i64 { 61 let pid: i64 = sys_fork() 62 if pid == 0 { 63 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 64 if dn >= 0 { sys_dup3(dn, 1, 0) } 65 let argv: *i64 = sys_mmap(32) as *i64 66 argv[0] = "_offc/nx_drvgen.elf" as *u8 as i64 67 argv[1] = spec as i64 68 argv[2] = 0 69 let envp: *i64 = sys_mmap(16) as *i64 70 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 71 envp[1] = 0 72 sys_execve("_offc/nx_drvgen.elf" as *u8, argv, envp) 73 sys_exit(127) 74 } 75 let st: *i64 = sys_mmap(16) as *i64 76 sys_wait4(pid, st, 0) 77 return st[0] 78} 79 80// run the SOVEREIGN rv64 emulator on binpath; serial -> outpath; return wait status 81func ng_run_sov(binpath: *u8, outpath: *u8) -> i64 { 82 let pid: i64 = sys_fork() 83 if pid == 0 { 84 let ofd: i64 = sys_openat_wr(outpath, 0x1a4) 85 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 86 let argv: *i64 = sys_mmap(32) as *i64 87 argv[0] = "_offc/nx_boot_run_sov.elf" as *u8 as i64 88 argv[1] = binpath as i64 89 argv[2] = 0 90 let envp: *i64 = sys_mmap(16) as *i64 91 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 92 envp[1] = 0 93 sys_execve("_offc/nx_boot_run_sov.elf" as *u8, argv, envp) 94 sys_exit(127) 95 } 96 let st: *i64 = sys_mmap(16) as *i64 97 sys_wait4(pid, st, 0) 98 return st[0] 99} 100 101// read whole file into buf (cap-1 max) SOVEREIGNLY; return byte count (0 if absent) 102func ng_read(path: *u8, buf: *u8, cap: i64) -> i64 { 103 let fd: i64 = sys_openat_rd(path) 104 if fd < 0 { return 0 } 105 var n: i64 = 0 106 var go: i64 = 1 107 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap - 1 { go = 0 } } 108 sys_close(fd) 109 return n 110} 111 112// does buf[0,n) contain pat (length pl)? 1/0 113func ng_buf_has(buf: *u8, n: i64, pat: *u8, pl: i64) -> i64 { 114 if pl <= 0 { return 0 } 115 var i: i64 = 0 116 while i + pl <= n { 117 var k: i64 = 0 118 var hit: i64 = 1 119 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 120 if hit == 1 { return 1 } 121 i = i + 1 122 } 123 return 0 124} 125 126func ng_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 127 128// SOVEREIGN byte-equal: 1 iff same length AND every byte equal. 129func ng_bytes_equal(a: *u8, na: i64, b: *u8, nb: i64) -> i64 { 130 if na != nb { return 0 } 131 var i: i64 = 0 132 while i < na { if a[i] != b[i] { return 0 } i = i + 1 } 133 return 1 134} 135 136// read a little-endian 32-bit word from buf at byte offset o. 137func ng_w32(buf: *u8, o: i64) -> i64 { 138 let b0: i64 = buf[o] as i64 139 let b1: i64 = buf[o+1] as i64 140 let b2: i64 = buf[o+2] as i64 141 let b3: i64 = buf[o+3] as i64 142 return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) 143} 144 145// count BACKWARD branches in an rv64 flat image: a B-type branch (opcode 0x63) whose imm[12] 146// sign bit (instruction-word bit31) is set -> a negative (backward) branch displacement = a loop. 147// virtio's image has NONE (straight-line forward-only handshake); NVMe's has >=1 (the poll loops). 148func ng_count_backward_branches(buf: *u8, n: i64) -> i64 { 149 var c: i64 = 0 150 var o: i64 = 0 151 while o + 4 <= n { 152 let w: i64 = ng_w32(buf, o) 153 if (w & 0x7f) == 0x63 { if (w & 0x80000000) != 0 { c = c + 1 } } 154 o = o + 4 155 } 156 return c 157} 158 159func main() -> i64 { 160 // ---- PROOF-1 inputs (virtio, the foundation) ---- 161 let vspec: *u8 = "knowledge/specs/virtio_blk_sector_virt.spec" as *u8 162 let vbin: *u8 = "runtime/_hdl_build/_virtio_sector_virt.bin" as *u8 163 let vgold: *u8 = "runtime/_hdl_build/_virtio_sector_virt.bin.gold" as *u8 164 let vref: *u8 = "runtime/_hdl_build/_virtio_sector_virt.ref.bin" as *u8 165 let vtamper: *u8 = "/tmp/_ngate_vtamper.bin" as *u8 166 let vsov: *u8 = "/tmp/_ngate_vsov.txt" as *u8 167 let vsov_t: *u8 = "/tmp/_ngate_vsov_t.txt" as *u8 168 let v_statgold: *u8 = "VIO ACK DRV FEAT OK VQ DESC USED STAT" as *u8 169 // the unique `lwu t3, 0x6C(t2)` -- the virtio QueueSectPeek read-back load; its following li's 170 // high immediate byte (+11) is the byte the virtio tamper corrupts. 171 let NG_VSECT_LOAD: i64 = 0x06c3ee03 172 // ---- PROOF-2 inputs (NVMe, the 2nd class) ---- 173 let nspec: *u8 = "knowledge/specs/nvme_admin_enable_virt.spec" as *u8 174 let nbin: *u8 = "runtime/_hdl_build/_nvme_admin_virt.bin" as *u8 175 let ngold: *u8 = "runtime/_hdl_build/_nvme_admin_virt.bin.gold" as *u8 176 let ntspec: *u8 = "/tmp/_ngate_nvme_tamper.spec" as *u8 177 let nsov: *u8 = "/tmp/_ngate_nsov.txt" as *u8 178 let nsov_t: *u8 = "/tmp/_ngate_nsov_t.txt" as *u8 179 // the "...ASQ SQE" tail the NVMe tamper must KEEP (only " CQ" drops). 180 let n_sqegold: *u8 = "VIO-NVME EN RDY ASQ SQE" as *u8 181 182 ng_p("=== X-DRV-W1 stage B gate (PROOF-1 virtio-reemit foundation -> PROOF-2 NVMe-class round-trip -> TAMPER -> DISTINCTNESS; SOVEREIGN-only, no qemu) ===\n" as *u8) 183 184 let lfd: i64 = sys_openat_append("knowledge/status/virtio_blk.log" as *u8, 0x1a4) 185 186 // ================= PROOF-1: virtio re-emit byte-identical + SECTGATE round-trip ================ 187 let vest: i64 = ng_run_virtio_emit(vspec) 188 let vgbuf: *u8 = sys_mmap(512) 189 let vgn: i64 = ng_read(vgold, vgbuf, 512) 190 vgbuf[vgn] = 0 as u8 191 let vsst: i64 = ng_run_sov(vbin, vsov) 192 let vsbuf: *u8 = sys_mmap(65536) 193 let vsbn: i64 = ng_read(vsov, vsbuf, 65536) 194 var p1_roundtrip: i64 = 0 195 if vest == 0 { if vgn > 0 { if vsst == 0 { 196 if ng_buf_has(vsbuf, vsbn, vgbuf, vgn) == 1 { if ng_buf_has(vsbuf, vsbn, "BOOTSOV verdict=GREEN" as *u8, 21) == 1 { p1_roundtrip = 1 } } 197 } } } 198 // byte-identical vs the frozen reference 199 let vfresh: *u8 = sys_mmap(8192) 200 let vfn: i64 = ng_read(vbin, vfresh, 8192) 201 let vrbuf: *u8 = sys_mmap(8192) 202 let vrn: i64 = ng_read(vref, vrbuf, 8192) 203 var p1_byte_identical: i64 = 0 204 if vrn > 0 { if ng_bytes_equal(vfresh, vfn, vrbuf, vrn) == 1 { p1_byte_identical = 1 } } 205 // virtio tamper: corrupt the SectPeek-expected const; SECT must drop, STAT survive, byte-check flip 206 let vibuf: *u8 = sys_mmap(8192) 207 let vibn: i64 = ng_read(vbin, vibuf, 8192) 208 var vlpos: i64 = 0 - 1 209 var vo: i64 = 0 210 while vo + 4 <= vibn { if ng_w32(vibuf, vo) == NG_VSECT_LOAD { vlpos = vo; vo = vibn } else { vo = vo + 4 } } 211 var p1_tamper: i64 = 0 212 if vlpos >= 0 { 213 let veb: i64 = vlpos + 11 214 vibuf[veb] = (vibuf[veb] + 1) as u8 215 let vtfd: i64 = sys_openat_wr(vtamper, 0x1a4) 216 if vtfd >= 0 { sys_write(vtfd, vibuf, vibn); sys_close(vtfd) } 217 let vtst: i64 = ng_run_sov(vtamper, vsov_t) 218 let vtbuf: *u8 = sys_mmap(65536) 219 let vtbn: i64 = ng_read(vsov_t, vtbuf, 65536) 220 let drops: i64 = ng_buf_has(vtbuf, vtbn, vgbuf, vgn) // full golden should MISS 221 let keeps: i64 = ng_buf_has(vtbuf, vtbn, v_statgold, ng_strlen(v_statgold)) // STAT tail survives 222 let flip: i64 = ng_bytes_equal(vibuf, vibn, vrbuf, vrn) // byte-equal now FALSE 223 if drops == 0 { if keeps == 1 { if flip == 0 { p1_tamper = 1 } } } 224 } 225 var proof1: i64 = 0 226 if p1_roundtrip == 1 { if p1_byte_identical == 1 { if p1_tamper == 1 { proof1 = 1 } } } 227 228 ng_p(" PROOF-1(virtio foundation): roundtrip=" as *u8); if p1_roundtrip==1 {ng_p("GREEN" as *u8)} else {ng_p("RED" as *u8)} 229 ng_p(" byte-identical=" as *u8); if p1_byte_identical==1 {ng_p("yes(" as *u8); ng_fn(1,vfn); ng_p("B)" as *u8)} else {ng_p("no" as *u8)} 230 ng_p(" tamper=" as *u8); if p1_tamper==1 {ng_p("rejected(SECT-drops,STAT-survives,byte-flips)" as *u8)} else {ng_p("not-rejected" as *u8)} 231 ng_p("\n" as *u8) 232 233 // NO-FLOATING: PROOF-2 is only claimed once PROOF-1 is GREEN. 234 var proof2: i64 = 0 235 var nvme_tamper: i64 = 0 236 var distinct_struct: i64 = 0 237 var distinct_bytes: i64 = 0 238 var nback: i64 = 0 239 var vback: i64 = 0 240 var nfn: i64 = 0 241 if proof1 == 1 { 242 // ============== PROOF-2: nx_drvgen authors the NVMe image; sovereign emu round-trip ========== 243 let nest: i64 = ng_run_drvgen(nspec) 244 let ngbuf: *u8 = sys_mmap(512) 245 let ngn: i64 = ng_read(ngold, ngbuf, 512) 246 ngbuf[ngn] = 0 as u8 247 let nsst: i64 = ng_run_sov(nbin, nsov) 248 let nsbuf: *u8 = sys_mmap(65536) 249 let nsbn: i64 = ng_read(nsov, nsbuf, 65536) 250 if nest == 0 { if ngn > 0 { if nsst == 0 { 251 if ng_buf_has(nsbuf, nsbn, ngbuf, ngn) == 1 { if ng_buf_has(nsbuf, nsbn, "BOOTSOV verdict=GREEN" as *u8, 21) == 1 { proof2 = 1 } } 252 } } } 253 254 // ============== TAMPER: bump cqpeek_expected by 1 in the SPEC, re-emit, re-run ============== 255 // author a tampered copy of the spec: replace "cqpeek_expected 0x12340001" -> "...0x12340002". 256 let sbuf: *u8 = sys_mmap(8192) 257 let sn: i64 = ng_read(nspec, sbuf, 8192) 258 // find the literal "cqpeek_expected 0x12340001" and bump the final '1' digit to '2'. 259 let pat: *u8 = "cqpeek_expected 0x12340001" as *u8 260 let pl: i64 = ng_strlen(pat) 261 var ppos: i64 = 0 - 1 262 var si: i64 = 0 263 while si + pl <= sn { 264 var kk: i64 = 0 265 var hh: i64 = 1 266 while kk < pl { if sbuf[si+kk] != pat[kk] { hh = 0; kk = pl } else { kk = kk + 1 } } 267 if hh == 1 { ppos = si; si = sn } else { si = si + 1 } 268 } 269 if ppos >= 0 { 270 // the last char of the pattern is the low nibble '1'; bump it to '2' (0x12340001 -> ...02). 271 sbuf[ppos + pl - 1] = 50 as u8 // ASCII '2' 272 let tfd: i64 = sys_openat_wr(ntspec, 0x1a4) 273 if tfd >= 0 { sys_write(tfd, sbuf, sn); sys_close(tfd) } 274 // the tampered spec emits to its own `out` -- but the spec's out row points at the real 275 // nbin. Re-emitting the tampered spec OVERWRITES nbin with the bad-KAT image; run it, 276 // assert " CQ" drops + "...ASQ SQE" survives, then RE-EMIT the clean spec to restore nbin. 277 let tet: i64 = ng_run_drvgen(ntspec) 278 let tsst: i64 = ng_run_sov(nbin, nsov_t) 279 let tbuf: *u8 = sys_mmap(65536) 280 let tbn: i64 = ng_read(nsov_t, tbuf, 65536) 281 let cq_drops: i64 = ng_buf_has(tbuf, tbn, ngbuf, ngn) // full golden MISS 282 let sqe_keeps: i64 = ng_buf_has(tbuf, tbn, n_sqegold, ng_strlen(n_sqegold)) // ASQ SQE survives 283 if tet == 0 { if cq_drops == 0 { if sqe_keeps == 1 { nvme_tamper = 1 } } } 284 // restore the clean image from the real spec (additive: leave nbin == the GREEN image). 285 ng_run_drvgen(nspec) 286 } 287 288 // ============== DISTINCTNESS ============== 289 // (1) STRUCTURAL: NVMe image HAS backward branch(es) (poll loops); virtio image has NONE. 290 let nfresh: *u8 = sys_mmap(8192) 291 nfn = ng_read(nbin, nfresh, 8192) 292 nback = ng_count_backward_branches(nfresh, nfn) 293 vback = ng_count_backward_branches(vfresh, vfn) 294 if nback >= 1 { if vback == 0 { distinct_struct = 1 } } 295 // (2) BYTE-DISTINCT: NVMe image bytes != virtio image bytes AND goldens differ. 296 let bytes_differ: i64 = ng_bytes_equal(nfresh, nfn, vfresh, vfn) // 0 = differ 297 let gold_differ: i64 = ng_bytes_equal(ngbuf, ngn, vgbuf, vgn) // 0 = differ 298 if bytes_differ == 0 { if gold_differ == 0 { distinct_bytes = 1 } } 299 300 ng_p(" PROOF-2(NVMe 2nd class): roundtrip=" as *u8); if proof2==1 {ng_p("GREEN(transcript==golden+clean-halt, " as *u8); ng_fn(1,nfn); ng_p("B)" as *u8)} else {ng_p("RED" as *u8)} 301 ng_p(" tamper=" as *u8); if nvme_tamper==1 {ng_p("rejected(CQ-drops,SQE-survives)" as *u8)} else {ng_p("not-rejected" as *u8)} 302 ng_p("\n DISTINCT: nvme-backward-branches=" as *u8); ng_fn(1, nback); ng_p(" virtio-backward-branches=" as *u8); ng_fn(1, vback) 303 ng_p(" image-bytes-differ=" as *u8); if distinct_bytes==1 {ng_p("yes" as *u8)} else {ng_p("no" as *u8)} 304 ng_p("\n" as *u8) 305 } else { 306 ng_p(" PROOF-2 SKIPPED: PROOF-1 not GREEN (no-floating: the 2nd class may not rest on a non-DONE foundation)\n" as *u8) 307 } 308 309 var pass: i64 = 0 310 if proof1 == 1 { if proof2 == 1 { if nvme_tamper == 1 { if distinct_struct == 1 { if distinct_bytes == 1 { pass = 1 } } } } } 311 312 if pass == 1 { 313 ng_p("NVMEGATE verdict=GREEN stage=B (PROOF-1 virtio-reemit foundation GREEN -> PROOF-2 NVMe-class CC.EN->CSTS.RDY enable + ASQ/ACQ bind + 64B SQE + SQ-Tail doorbell + phase-tagged CQE + NvmeCqPeek read-back round-tripped GREEN on the sovereign emu; the CqPeek-expected spec TAMPER dropped ONLY the CQ canary; the NVMe driver control-flow is DISTINCT from virtio -- it carries poll-loop backward branches virtio lacks and its bytes+golden differ)\n" as *u8) 314 if lfd >= 0 { 315 ng_fp(lfd, "NVMEGATE verdict=GREEN stage=B runtime=sovereign-emu device=nvme enable=CC.EN->CSTS.RDY queue=ASQ/ACQ+doorbell sqe=64B cqe=phase-tagged cqpeek-readback=verified bytes=" as *u8); ng_fn(lfd, nfn) 316 ng_fp(lfd, " proof1=virtio-reemit-identical transcript==golden tamper=rejected(CQ-drops,SQE-survives) distinct=poll-loop+phase-bit(nvme-backward-branches=" as *u8); ng_fn(lfd, nback) 317 ng_fp(lfd, ",virtio-backward-branches=" as *u8); ng_fn(lfd, vback) 318 ng_fp(lfd, ",bytes+golden-differ) golden=VIO-NVME EN RDY ASQ SQE CQ epoch=" as *u8); ng_fn(lfd, sys_now_realtime_sec()); ng_fp(lfd, "\n" as *u8); sys_close(lfd) 319 } 320 sys_exit(0); return 0 321 } 322 ng_p("NVMEGATE verdict=RED stage=B (one of PROOF-1/PROOF-2/TAMPER/DISTINCT not satisfied)\n" as *u8) 323 if lfd >= 0 { 324 ng_fp(lfd, "NVMEGATE verdict=RED stage=B proof1=" as *u8); ng_fn(lfd, proof1) 325 ng_fp(lfd, " proof2=" as *u8); ng_fn(lfd, proof2) 326 ng_fp(lfd, " nvme_tamper=" as *u8); ng_fn(lfd, nvme_tamper) 327 ng_fp(lfd, " distinct_struct=" as *u8); ng_fn(lfd, distinct_struct) 328 ng_fp(lfd, " distinct_bytes=" as *u8); ng_fn(lfd, distinct_bytes); ng_fp(lfd, "\n" as *u8); sys_close(lfd) 329 } 330 sys_exit(1) 331 return 1 332}