code wiki / _hdl_build / _k_r2_001b2a_gate.nx
_k_r2_001b2a_gate.nx source
↩ module page · 338 lines · 17021 B
1// _k_r2_001b2a_gate.nx -- the K-R2-001b2a gate (virtio-MMIO DESCRIPTOR-DMA layer; the
2// second slice of the K-R2-001b virtqueue epic, on top of b1's queue-config). Drives the
3// full author->handshake->queue-config->descriptor-DMA chain with NO mocks: runs the REAL
4// nx_virtio_hs_emit (the team AUTHORS the rv64 image + the TABLE-COMPUTED golden transcript
5// from the descriptor-DMA spec), then RUNS the image on the SOVEREIGN rv64 emulator
6// (rv64im_min_sim + the rv64im_min_virtio device now DMA-reading the descriptor on the
7// QueueNotify kick and exposing the read field at the QueueDescPeek RO register -- the
8// PRIMARY, gating lane: Nishi owns the runtime) and asserts the captured serial transcript
9// CONTAINS the emitter's golden ("VIO ACK DRV FEAT OK VQ DESC\n" -- after the queue config
10// the driver laid ONE legacy virtio descriptor into the QueuePFN-bound guest ring page,
11// kicked QueueNotify so the device DMA-read it, then READ QueueDescPeek BACK and verified it
12// == the descriptor addr-low word it wrote: the descriptor-DMA binding proof) AND the
13// sovereign emu reports a clean SiFive-finisher halt.
14//
15// Then the ALIGNMENT lane: qemu-system-riscv64 -machine virt -global
16// virtio-mmio.force-legacy=true with a REAL virtio-blk-device backing. The gate authors a
17// SECOND image from a base-rewritten spec (the blk device lands at slot 0x10008000) and runs
18// the SAME driver against the REAL legacy virtio-blk transport: it completes the handshake +
19// queue config, lays the SAME descriptor into qemu guest RAM at the ring page, and kicks the
20// real QueueNotify -- the real legacy transport genuinely walks the ring. The QueueDescPeek
21// result register is a sovereign-model instrument the real device does not expose (it reads
22// 0), so on qemu the descriptor-DMA read-back verify gracefully drops ONLY " DESC"; the gate
23// asserts the qemu transcript contains the b1 prefix golden ("VIO ACK DRV FEAT OK VQ") --
24// proving the SAME driver lays the descriptor + kicks the real device -> lanes AGREE on every
25// register the real transport can observe.
26//
27// Finally a TAMPER test: corrupt the QueueDescPeek-EXPECTED constant in the sovereign image
28// (the driver loads its own QueueDescPeek read-back into t3 via lwu and compares against this
29// li-loaded expected in t4; bumping the expected's immediate makes t3 != t4) -> the read-back
30// verify branches PAST stage 6 straight to the finisher -> the transcript loses ONLY its
31// " DESC" canary (the "VIO ACK DRV FEAT OK VQ" tail survives) -> the gate MUST go RED.
32// Evidence -> knowledge/status/virtio_blk.log (DESCGATE row; the queue row's ||MARK= reads
33// it). Sovereign orchestration (fork/dup3/execve/wait4). license_tier: ORIGINAL
34import "nx_syscalls.nx"
35
36// the unique rv64 encoding of `lwu t3, 0x60(t2)` -- the QueueDescPeek read-back load (f3=6,
37// zero-extend; an address is unsigned). The DescPeek-expected constant the driver compares
38// against is the li that follows it (lui at +4, addi at +8); corrupting the addi's high
39// immediate byte (+11) is the descriptor-DMA tamper.
40const G_QDESCPEEK_LOAD_WORD: i64 = 0x0603ee03 // lwu t3, 0x060(t2) (QueueDescPeek read-back)
41
42func g_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
43func g_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 }
44func g_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 }
45
46// run nx_virtio_hs_emit <spec>; return child wait status (0 = ok)
47func g_run_emit(spec: *u8) -> i64 {
48 let pid: i64 = sys_fork()
49 if pid == 0 {
50 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
51 if dn >= 0 { sys_dup3(dn, 1, 0) }
52 let argv: *i64 = sys_mmap(32) as *i64
53 argv[0] = "_offc/nx_virtio_hs_emit.elf" as *u8 as i64
54 argv[1] = spec as i64
55 argv[2] = 0
56 let envp: *i64 = sys_mmap(16) as *i64
57 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
58 envp[1] = 0
59 sys_execve("_offc/nx_virtio_hs_emit.elf" as *u8, argv, envp)
60 sys_exit(127)
61 }
62 let st: *i64 = sys_mmap(16) as *i64
63 sys_wait4(pid, st, 0)
64 return st[0]
65}
66
67// run the SOVEREIGN rv64 emulator on binpath; serial -> outpath; return wait status
68func g_run_sov(binpath: *u8, outpath: *u8) -> i64 {
69 let pid: i64 = sys_fork()
70 if pid == 0 {
71 let ofd: i64 = sys_openat_wr(outpath, 0x1a4)
72 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
73 let argv: *i64 = sys_mmap(32) as *i64
74 argv[0] = "_offc/nx_boot_run_sov.elf" as *u8 as i64
75 argv[1] = binpath as i64
76 argv[2] = 0
77 let envp: *i64 = sys_mmap(16) as *i64
78 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
79 envp[1] = 0
80 sys_execve("_offc/nx_boot_run_sov.elf" as *u8, argv, envp)
81 sys_exit(127)
82 }
83 let st: *i64 = sys_mmap(16) as *i64
84 sys_wait4(pid, st, 0)
85 return st[0]
86}
87
88// run qemu-system-riscv64 virt + legacy virtio-blk-device on binpath; serial -> outpath.
89func g_run_qemu(binpath: *u8, outpath: *u8, backing: *u8) -> i64 {
90 let pid: i64 = sys_fork()
91 if pid == 0 {
92 let ofd: i64 = sys_openat_wr(outpath, 0x1a4)
93 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
94 let argv: *i64 = sys_mmap(128) as *i64
95 argv[0] = "/usr/bin/qemu-system-riscv64" as *u8 as i64
96 argv[1] = "-machine" as *u8 as i64
97 argv[2] = "virt" as *u8 as i64
98 argv[3] = "-global" as *u8 as i64
99 argv[4] = "virtio-mmio.force-legacy=true" as *u8 as i64
100 argv[5] = "-nographic" as *u8 as i64
101 argv[6] = "-bios" as *u8 as i64
102 argv[7] = binpath as i64
103 argv[8] = "-drive" as *u8 as i64
104 argv[9] = "file=/tmp/_descgate_backing.img,if=none,format=raw,id=hd0" as *u8 as i64
105 argv[10] = "-device" as *u8 as i64
106 argv[11] = "virtio-blk-device,drive=hd0" as *u8 as i64
107 argv[12] = 0
108 let envp: *i64 = sys_mmap(16) as *i64
109 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
110 envp[1] = 0
111 sys_execve("/usr/bin/qemu-system-riscv64" as *u8, argv, envp)
112 sys_exit(127)
113 }
114 let st: *i64 = sys_mmap(16) as *i64
115 sys_wait4(pid, st, 0)
116 return st[0]
117}
118
119// create a small raw backing file for the qemu virtio-blk device (4 sectors of zeros).
120func g_make_backing(path: *u8) -> i64 {
121 let fd: i64 = sys_openat_wr(path, 0x1a4)
122 if fd < 0 { return 0 - 1 }
123 let z: *u8 = sys_mmap(2048)
124 var i: i64 = 0
125 while i < 2048 { z[i] = 0 as u8; i = i + 1 }
126 sys_write(fd, z, 2048)
127 sys_close(fd)
128 return 0
129}
130
131// read whole file into buf (cap-1 max); return byte count (0 if absent)
132func g_read(path: *u8, buf: *u8, cap: i64) -> i64 {
133 let fd: i64 = sys_openat_rd(path)
134 if fd < 0 { return 0 }
135 var n: i64 = 0
136 var go: i64 = 1
137 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 } }
138 sys_close(fd)
139 return n
140}
141
142// does buf[0,n) contain pat (length pl)? 1/0
143func g_buf_has(buf: *u8, n: i64, pat: *u8, pl: i64) -> i64 {
144 if pl <= 0 { return 0 }
145 var i: i64 = 0
146 while i + pl <= n {
147 var k: i64 = 0
148 var hit: i64 = 1
149 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } }
150 if hit == 1 { return 1 }
151 i = i + 1
152 }
153 return 0
154}
155
156// read a little-endian 32-bit word from buf at byte offset o.
157func g_w32(buf: *u8, o: i64) -> i64 {
158 let b0: i64 = buf[o] as i64
159 let b1: i64 = buf[o+1] as i64
160 let b2: i64 = buf[o+2] as i64
161 let b3: i64 = buf[o+3] as i64
162 return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
163}
164
165// find the byte offset of the (unique) QueueDescPeek read-back load word in the image; -1 if
166// absent. The DescPeek-expected li the driver compares against is at off+4 (lui) / off+8
167// (addi); the addi's high immediate byte is at off+11.
168func g_find_descpeek_load(buf: *u8, n: i64) -> i64 {
169 var o: i64 = 0
170 while o + 4 <= n {
171 if g_w32(buf, o) == G_QDESCPEEK_LOAD_WORD { return o }
172 o = o + 4
173 }
174 return 0 - 1
175}
176
177func g_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
178
179// does line [ls,le) of buf begin with key? 1/0
180func g_line_is(buf: *u8, ls: i64, le: i64, key: *u8) -> i64 {
181 var k: i64 = 0
182 while key[k] != (0 as u8) {
183 if ls + k >= le { return 0 }
184 if buf[ls + k] != key[k] { return 0 }
185 k = k + 1
186 }
187 return 1
188}
189
190// author the qemu-alignment spec: copy the main spec, but replace the `base ` line value
191// with the qemu blk slot and the `out ` line with the qemu image path. (The descriptor ring
192// page + data buffer addresses are guest-RAM addresses valid on both lanes -- left as is.)
193func g_write_qemu_spec(mainspec: *u8, derived: *u8, qemu_out: *u8) -> i64 {
194 let buf: *u8 = sys_mmap(8192)
195 let n: i64 = g_read(mainspec, buf, 8192)
196 if n <= 0 { return 0 - 1 }
197 let ofd: i64 = sys_openat_wr(derived, 0x1a4)
198 if ofd < 0 { return 0 - 1 }
199 var ls: i64 = 0
200 while ls < n {
201 var le: i64 = ls
202 var scan: i64 = 1
203 while scan == 1 { if le >= n { scan = 0 } else { if buf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } } }
204 if g_line_is(buf, ls, le, "base " as *u8) == 1 {
205 g_fp(ofd, "base 0x10008000\n" as *u8)
206 } else {
207 if g_line_is(buf, ls, le, "out " as *u8) == 1 {
208 g_fp(ofd, "out " as *u8); g_fp(ofd, qemu_out); g_fp(ofd, "\n" as *u8)
209 } else {
210 sys_write(ofd, (buf as i64 + ls) as *u8, le - ls)
211 g_fp(ofd, "\n" as *u8)
212 }
213 }
214 ls = le + 1
215 }
216 sys_close(ofd)
217 return 0
218}
219
220func main() -> i64 {
221 let spec: *u8 = "knowledge/specs/virtio_blk_descdma_virt.spec" as *u8
222 let binpath: *u8 = "runtime/_hdl_build/_virtio_descdma_virt.bin" as *u8
223 let goldpath: *u8 = "runtime/_hdl_build/_virtio_descdma_virt.bin.gold" as *u8
224 let qemu_spec: *u8 = "/tmp/_descgate_qemu.spec" as *u8
225 let qemu_bin: *u8 = "/tmp/_descgate_qemu.bin" as *u8
226 let backing: *u8 = "/tmp/_descgate_backing.img" as *u8
227 let tamper_bin: *u8 = "/tmp/_descgate_tamper.bin" as *u8
228 let sov_serial: *u8 = "/tmp/_descgate_sov.txt" as *u8
229 let qemu_serial: *u8 = "/tmp/_descgate_qemu.txt" as *u8
230 let sov_tamper: *u8 = "/tmp/_descgate_sov_tamper.txt" as *u8
231 // the b1 prefix golden the qemu alignment lane asserts (the real device has no
232 // QueueDescPeek register, so on qemu the descriptor-DMA verify drops ONLY " DESC").
233 let prefix_gold: *u8 = "VIO ACK DRV FEAT OK VQ" as *u8
234 g_p("=== virtio-MMIO descriptor-DMA gate (K-R2-001b2a: SOVEREIGN rv64 descriptor lay + device DMA-read + QueueDescPeek read-back + qemu virtio-blk alignment) ===\n" as *u8)
235
236 let lfd: i64 = sys_openat_append("knowledge/status/virtio_blk.log" as *u8, 0x1a4)
237
238 // STEP 1: author the SOVEREIGN image (base 0x10001000) + the golden transcript.
239 let est: i64 = g_run_emit(spec)
240 if est != 0 {
241 g_p("DESCGATE verdict=RED reason=emit-failed\n" as *u8)
242 if lfd >= 0 { g_fp(lfd, "DESCGATE verdict=RED reason=emit-failed\n" as *u8); sys_close(lfd) }
243 sys_exit(1); return 1
244 }
245 let gold: *u8 = sys_mmap(512)
246 let gn: i64 = g_read(goldpath, gold, 512)
247 gold[gn] = 0 as u8
248 if gn <= 0 {
249 g_p("DESCGATE verdict=RED reason=golden-missing\n" as *u8)
250 if lfd >= 0 { g_fp(lfd, "DESCGATE verdict=RED reason=golden-missing\n" as *u8); sys_close(lfd) }
251 sys_exit(1); return 1
252 }
253
254 // STEP 2 (PRIMARY): the Nishi sovereign rv64 emulator RUNS the image -- handshake +
255 // queue config + descriptor lay + QueueNotify kick + device DMA-read + QueueDescPeek
256 // read-back verify -> transcript must CONTAIN the full golden ("...VQ DESC\n").
257 let sst: i64 = g_run_sov(binpath, sov_serial)
258 let sbuf: *u8 = sys_mmap(65536)
259 let sbn: i64 = g_read(sov_serial, sbuf, 65536)
260 let trans_ok: i64 = g_buf_has(sbuf, sbn, gold, gn)
261 let halt_ok: i64 = g_buf_has(sbuf, sbn, "BOOTSOV verdict=GREEN" as *u8, 21)
262 var sov_ok: i64 = 0
263 if sst == 0 { if trans_ok == 1 { if halt_ok == 1 { sov_ok = 1 } } }
264
265 // STEP 3 (ALIGNMENT): author the qemu-base image (slot 0x10008000) + run qemu with a
266 // REAL legacy virtio-blk-device; the SAME driver lays the SAME descriptor into qemu guest
267 // RAM and kicks the real QueueNotify (the real transport walks the ring). The sovereign-
268 // only QueueDescPeek register reads 0 on the real device, so the descriptor-DMA verify
269 // drops " DESC" there; assert the b1 prefix golden ("VIO ACK DRV FEAT OK VQ") appears,
270 // proving the driver runs identically against the real transport up to the kick.
271 g_make_backing(backing)
272 g_write_qemu_spec(spec, qemu_spec, qemu_bin)
273 let qest: i64 = g_run_emit(qemu_spec)
274 var align: i64 = 0
275 if qest == 0 {
276 let qst: i64 = g_run_qemu(qemu_bin, qemu_serial, backing)
277 let qbuf: *u8 = sys_mmap(65536)
278 let qbn: i64 = g_read(qemu_serial, qbuf, 65536)
279 let qtrans: i64 = g_buf_has(qbuf, qbn, prefix_gold, g_strlen(prefix_gold))
280 if qst == 0 { if qtrans == 1 { align = 1 } }
281 }
282
283 // STEP 4 (TAMPER): corrupt the QueueDescPeek-EXPECTED constant in the sovereign image.
284 // Scan for the unique QueueDescPeek read-back load word; the expected li the driver
285 // compares against is the addi at load+8. Bump that addi's high immediate byte (load+11)
286 // so the expected != the value the device DMA-read+exposed -> the read-back verify
287 // branches to the finisher -> ONLY the " DESC" canary drops -> gate MUST go RED.
288 let ibuf: *u8 = sys_mmap(8192)
289 let ibn: i64 = g_read(binpath, ibuf, 8192)
290 let lpos: i64 = g_find_descpeek_load(ibuf, ibn)
291 var tamper_built: i64 = 0
292 if lpos >= 0 {
293 let ebyte: i64 = lpos + 11 // high immediate byte of the expected addi
294 ibuf[ebyte] = (ibuf[ebyte] + 1) as u8
295 let tfd: i64 = sys_openat_wr(tamper_bin, 0x1a4)
296 if tfd >= 0 { sys_write(tfd, ibuf, ibn); sys_close(tfd); tamper_built = 1 }
297 }
298 var tamper_bites: i64 = 0
299 var tamper_keeps_vq: i64 = 0
300 if tamper_built == 1 {
301 let tst: i64 = g_run_sov(tamper_bin, sov_tamper)
302 let tbuf: *u8 = sys_mmap(65536)
303 let tbn: i64 = g_read(sov_tamper, tbuf, 65536)
304 let tamper_trans: i64 = g_buf_has(tbuf, tbn, gold, gn) // full golden should now MISS
305 let tamper_prefix: i64 = g_buf_has(tbuf, tbn, prefix_gold, g_strlen(prefix_gold)) // VQ tail survives
306 if tamper_trans == 0 { tamper_bites = 1 }
307 if tamper_prefix == 1 { tamper_keeps_vq = 1 }
308 }
309
310 g_p(" sovereign_emu=" as *u8)
311 if sov_ok == 1 { g_p("GREEN(transcript==golden+clean-halt)" as *u8) } else { g_p("RED" as *u8) }
312 g_p(" qemu_align=" as *u8)
313 if align == 1 { g_p("yes(real-virtio-blk @0x10008000, same descriptor laid + real kick, VQ prefix)" as *u8) } else { g_p("no" as *u8) }
314 g_p(" tamper_bites=" as *u8)
315 if tamper_bites == 1 { g_p("yes(DescPeek-expected corrupt -> DESC drops, VQ survives)\n" as *u8) } else { g_p("no\n" as *u8) }
316
317 var pass: i64 = 0
318 if sov_ok == 1 { if align == 1 { if tamper_bites == 1 { if tamper_keeps_vq == 1 { pass = 1 } } } }
319
320 if pass == 1 {
321 g_p("DESCGATE verdict=GREEN (sovereign rv64 emu drove the descriptor-DMA layer; descriptor laid at the QueuePFN-bound ring page; QueueNotify kicked; device DMA-read the descriptor addr-low field; QueueDescPeek read-back verified == driver-written value; transcript==golden; clean halt; qemu virtio-blk-device cross-check lays the SAME descriptor + kicks the real transport AGREEING on the VQ prefix; DescPeek tamper REJECTED -- DESC drops, VQ survives)\n" as *u8)
322 if lfd >= 0 {
323 g_fp(lfd, "DESCGATE verdict=GREEN runtime=sovereign-emu transport=legacy-virtio-mmio layer=descriptor-dma desc=addr|len|flags|next ring=QueuePFN<<12 kick=QueueNotify dma=device-read descpeek-readback=verified transcript==golden align_qemu=yes(virtio-blk-device,VQ-prefix) tamper=rejected(DESC-drops,VQ-survives) golden=" as *u8)
324 g_fp(lfd, gold)
325 g_fp(lfd, " epoch=" as *u8); g_fn(lfd, sys_now_realtime_sec()); g_fp(lfd, "\n" as *u8); sys_close(lfd)
326 }
327 sys_exit(0); return 0
328 }
329 g_p("DESCGATE verdict=RED (sov_ok/align/tamper not all green)\n" as *u8)
330 if lfd >= 0 {
331 g_fp(lfd, "DESCGATE verdict=RED sov_ok=" as *u8); g_fn(lfd, sov_ok)
332 g_fp(lfd, " align=" as *u8); g_fn(lfd, align)
333 g_fp(lfd, " tamper_bites=" as *u8); g_fn(lfd, tamper_bites)
334 g_fp(lfd, " tamper_keeps_vq=" as *u8); g_fn(lfd, tamper_keeps_vq); g_fp(lfd, "\n" as *u8); sys_close(lfd)
335 }
336 sys_exit(1)
337 return 1
338}