code wiki / _hdl_build / nx_nishios_kernel_gate.nx
nx_nishios_kernel_gate.nx source
↩ module page · 228 lines · 11176 B
1// nx_nishios_kernel_gate.nx -- the TRANSCRIPT gate for the composed NishiOS kernel boot.
2//
3// WHY THIS EXISTS (a measured instrument defect, not a hypothetical): the BOOTSOV runner decides
4// its verdict from `halted && halt_code==0` ALONE -- it never reads the serial transcript. Proven
5// 2026-07-31: a kernel image deliberately tampered so the Sv39 walk faults printed
6// "...TICK TX" instead of "...PAGE USER OK" and BOOTSOV still returned verdict=GREEN exit=0.
7// A runner that returns GREEN on a broken boot is not a check. This gate reads what the kernel
8// actually SAID and compares it to the table-computed golden transcript.
9//
10// TEETH (each independently able to fail; the two NEG teeth are the non-vacuity proof):
11// T1 image present and not the 72-byte banner it replaced
12// T2 boots to a clean finisher halt
13// T3 transcript is EXACTLY the golden transcript
14// T4 every phase marker present (banner/trap/sched/timer/paging/user)
15// T5 NEG-comparator: the comparator must REJECT a mutated golden (proves it can say no)
16// T6 NEG-subject: a corrupted kernel image must NOT reproduce the golden transcript
17// T7 step count is far above the 17-step banner baseline (the boot does real work)
18// Sovereign: the emulator IS the runtime. license_tier: ORIGINAL
19// Boot-and-capture comes from the SHARED primitive, not a private copy: this gate, the census
20// and nx_kernel_adoption must agree byte-for-byte about what "booting the image" means, and three
21// hand-maintained copies is three chances for them to drift apart silently.
22import "nx_bootcap.nx"
23import "nx_gate_verdict.nx"
24
25const NKG_BIN: *u8 = "runtime/_hdl_build/_boot_nishi_virt.bin"
26const NKG_GOLD: *u8 = "runtime/_hdl_build/_boot_nishi_virt.bin.gold"
27const NKG_BIN_ALT: *u8 = "_boot_nishi_virt.bin"
28const NKG_GOLD_ALT: *u8 = "runtime/_hdl_build/_boot_nishi_virt.bin.gold"
29const NKG_LOG: *u8 = "knowledge/status/boot_stub.log"
30
31const NKG_MEM_BASE: i64 = 0x80000000
32const NKG_MEM_SIZE: i64 = 65536
33const NKG_TX_CAP: i64 = 4096
34const NKG_MAX_STEPS: i64 = 100000
35const NKG_BANNER_BYTES: i64 = 72
36const NKG_BANNER_STEPS: i64 = 17
37
38func nkg_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
39func nkg_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 }
40func nkg_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(fd,"-" as *u8,1)}; 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 }
41
42// Boot one image on a FRESH machine. Thin wrapper over the shared bootcap primitive so this
43// gate cannot drift from the census/adoption rulers about what a boot is.
44// res[0]=halted res[1]=halt_code res[2]=steps res[3]=tx_count
45func nkg_run(img: *u8, ilen: i64, tx_buf: *u8, res: *i64) -> i64 {
46 bootcap_run(img, ilen, tx_buf, NKG_TX_CAP, res)
47 return 0
48}
49
50func nkg_eq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 {
51 if an != bn { return 0 }
52 var i: i64 = 0
53 while i < an { if a[i] != b[i] { return 0 } i = i + 1 }
54 return 1
55}
56// substring search: 1 if `needle` (NUL-terminated) occurs in hay[0..hn)
57func nkg_has(hay: *u8, hn: i64, needle: *u8) -> i64 {
58 var nn: i64 = 0
59 while needle[nn] != (0 as u8) { nn = nn + 1 }
60 if nn == 0 { return 1 }
61 if nn > hn { return 0 }
62 var i: i64 = 0
63 while i <= hn - nn {
64 var k: i64 = 0
65 var ok: i64 = 1
66 while k < nn { if hay[i+k] != needle[k] { ok = 0; k = nn } else { k = k + 1 } }
67 if ok == 1 { return 1 }
68 i = i + 1
69 }
70 return 0
71}
72
73// ---- D001 MIGRATION 2026-08-06 -- IDIOM H: TOOTH HELPER + fails==0 GUARD ---------------------
74// Same family as nx_suitebench_gate. nx_gate_dry_apply rightly refuses to map the GUARD here (its own
75// text: the fails==0 family -- a direct operand map would INVERT the gate green->red). The correct
76// migration converts the TOOTH HELPER: every nkg_tooth call already carries (name, condition), which
77// is gv_check's signature, so the pass/total counter is BUILT from the teeth that already exist.
78// pass[]/fail[] are still maintained so every other reader of them keeps working unchanged.
79static nkg_ctr: *i64
80
81func nkg_tooth(name: *u8, ok: i64, pass: *i64, fail: *i64) -> i64 {
82 if (nkg_ctr as i64) == 0 { nkg_ctr = gv_ctr() }
83 gv_check(name, ok, nkg_ctr)
84 if ok == 1 { pass[0] = pass[0] + 1 }
85 if ok != 1 { fail[0] = fail[0] + 1 }
86 return 0
87}
88
89// argv[1] = image path, argv[2] = golden path. Defaults match the buildroot layout; the LIVE NAS
90// keeps the image at the SERVING ROOT and the golden under runtime/_hdl_build/ (the "two roots, one
91// name" split), so the gate must be told where to look rather than assume one tree.
92func main(argc: i64, argv: *i64) -> i64 {
93 var binp: *u8 = NKG_BIN
94 var goldp: *u8 = NKG_GOLD
95 if argc >= 2 { binp = argv[1] as *u8 }
96 if argc >= 3 { goldp = argv[2] as *u8 }
97 var pass: *i64 = sys_mmap(16) as *i64
98 var fail: *i64 = sys_mmap(16) as *i64
99 pass[0] = 0
100 fail[0] = 0
101 nkg_p("=== nx_nishios_kernel_gate -- the composed NishiOS boot, checked by TRANSCRIPT ===\n" as *u8)
102 nkg_p(" image=" as *u8); nkg_p(binp); nkg_p(" golden=" as *u8); nkg_p(goldp); nkg_p("\n" as *u8)
103
104 // RESOLVE, THEN SAY WHICH. The buildroot keeps the image under runtime/_hdl_build/ while the
105 // live serving root keeps it at ./ -- one name, two roots. A gate that silently picked the
106 // wrong one would measure an artifact nobody boots, so try both and PRINT the resolved path.
107 let lenp: *i64 = sys_mmap(16) as *i64
108 var img: *u8 = sys_read_file(binp, lenp)
109 var ilen: i64 = lenp[0]
110 if ilen <= 0 {
111 binp = NKG_BIN_ALT
112 img = sys_read_file(binp, lenp)
113 ilen = lenp[0]
114 }
115 let glenp: *i64 = sys_mmap(16) as *i64
116 var gold: *u8 = sys_read_file(goldp, glenp)
117 var glen: i64 = glenp[0]
118 if glen <= 0 {
119 goldp = NKG_GOLD_ALT
120 gold = sys_read_file(goldp, glenp)
121 glen = glenp[0]
122 }
123 nkg_p(" resolved image=" as *u8); nkg_p(binp); nkg_p(" golden=" as *u8); nkg_p(goldp); nkg_p("\n" as *u8)
124
125 if ilen <= 0 { nkg_p("NISHIOSKERNELGATE verdict=RED reason=image-missing\n" as *u8); return 1 }
126 if glen <= 0 { nkg_p("NISHIOSKERNELGATE verdict=RED reason=golden-missing\n" as *u8); return 1 }
127
128 // T1 -- the chokepoint image is no longer the banner it replaced
129 var t1: i64 = 0
130 if ilen > NKG_BANNER_BYTES { t1 = 1 }
131 nkg_tooth("T1 image is a composed kernel, not the 72-byte banner" as *u8, t1, pass, fail)
132
133 let tx: *u8 = sys_mmap(NKG_TX_CAP)
134 let res: *i64 = sys_mmap(64) as *i64
135 nkg_run(img, ilen, tx, res)
136 let halted: i64 = res[0]
137 let hcode: i64 = res[1]
138 let steps: i64 = res[2]
139 let txn: i64 = res[3]
140
141 // T2 -- clean finisher halt
142 var t2: i64 = 0
143 if halted == 1 { if hcode == 0 { t2 = 1 } }
144 nkg_tooth("T2 booted to a clean finisher halt" as *u8, t2, pass, fail)
145
146 // T3 -- the transcript is EXACTLY the golden (what BOOTSOV never checks)
147 let t3: i64 = nkg_eq(tx, txn, gold, glen)
148 nkg_tooth("T3 serial transcript == golden transcript" as *u8, t3, pass, fail)
149
150 // T4 -- every phase left its marker
151 var t4: i64 = 1
152 if nkg_has(tx, txn, "NISHI" as *u8) != 1 { t4 = 0 }
153 if nkg_has(tx, txn, "TRAP" as *u8) != 1 { t4 = 0 }
154 if nkg_has(tx, txn, "ABABABABABAB" as *u8) != 1 { t4 = 0 }
155 if nkg_has(tx, txn, "SCHED" as *u8) != 1 { t4 = 0 }
156 if nkg_has(tx, txn, "TICK" as *u8) != 1 { t4 = 0 }
157 if nkg_has(tx, txn, "PQPQPQ" as *u8) != 1 { t4 = 0 }
158 if nkg_has(tx, txn, "PREEMPT" as *u8) != 1 { t4 = 0 }
159 if nkg_has(tx, txn, "BLK" as *u8) != 1 { t4 = 0 }
160 if nkg_has(tx, txn, "NET" as *u8) != 1 { t4 = 0 }
161 if nkg_has(tx, txn, "HEAP" as *u8) != 1 { t4 = 0 }
162 if nkg_has(tx, txn, "PAGE" as *u8) != 1 { t4 = 0 }
163 if nkg_has(tx, txn, "USER" as *u8) != 1 { t4 = 0 }
164 nkg_tooth("T4 all ten phase markers (banner/trap/sched/timer/preempt/blk/net/heap/paging/user)" as *u8, t4, pass, fail)
165
166 // T4b -- no phase-failure marker may appear
167 var t4b: i64 = 1
168 if nkg_has(tx, txn, "PGX" as *u8) == 1 { t4b = 0 }
169 if nkg_has(tx, txn, "TX" as *u8) == 1 { t4b = 0 }
170 if nkg_has(tx, txn, "UX" as *u8) == 1 { t4b = 0 }
171 if nkg_has(tx, txn, "BX" as *u8) == 1 { t4b = 0 }
172 if nkg_has(tx, txn, "NF" as *u8) == 1 { t4b = 0 }
173 if nkg_has(tx, txn, "HX" as *u8) == 1 { t4b = 0 }
174 nkg_tooth("T4b no phase-failure marker (PGX/TX/UX/BX/NF/HX) in the transcript" as *u8, t4b, pass, fail)
175
176 // T5 -- NEG-comparator: a mutated golden MUST be rejected, else T3 proves nothing
177 let mg: *u8 = sys_mmap(NKG_TX_CAP)
178 var c: i64 = 0
179 while c < glen { mg[c] = gold[c]; c = c + 1 }
180 mg[0] = (mg[0] + 1) as u8
181 var t5: i64 = 0
182 if nkg_eq(tx, txn, mg, glen) == 0 { t5 = 1 }
183 nkg_tooth("T5 NEG comparator rejects a mutated golden (the check can say no)" as *u8, t5, pass, fail)
184
185 // T6 -- NEG-subject: a corrupted kernel must NOT reproduce the golden transcript
186 let bad: *u8 = sys_mmap(NKG_MEM_SIZE)
187 var d: i64 = 0
188 while d < ilen { bad[d] = img[d]; d = d + 1 }
189 bad[0] = 0 as u8
190 bad[1] = 0 as u8
191 bad[2] = 0 as u8
192 bad[3] = 0 as u8
193 let tx2: *u8 = sys_mmap(NKG_TX_CAP)
194 let res2: *i64 = sys_mmap(64) as *i64
195 nkg_run(bad, ilen, tx2, res2)
196 var t6: i64 = 0
197 if nkg_eq(tx2, res2[3], gold, glen) == 0 { t6 = 1 }
198 nkg_tooth("T6 NEG subject: a corrupted kernel does not reproduce the golden" as *u8, t6, pass, fail)
199
200 // T7 -- the boot does real work, far above the banner baseline
201 var t7: i64 = 0
202 if steps > NKG_BANNER_STEPS * 5 { t7 = 1 }
203 nkg_tooth("T7 step count far exceeds the 17-step banner baseline" as *u8, t7, pass, fail)
204
205 nkg_p(" transcript=[" as *u8); sys_write(1, tx, txn); nkg_p("]\n" as *u8)
206 nkg_p(" bytes=" as *u8); nkg_fn(1, ilen)
207 nkg_p(" steps=" as *u8); nkg_fn(1, steps)
208 nkg_p(" serial_bytes=" as *u8); nkg_fn(1, txn); nkg_p("\n" as *u8)
209
210 let total: i64 = pass[0] + fail[0]
211 // SINGLE verdict anchor on stdout -- gv_verdict replaces the hand-rolled line and is GREEN iff
212 // pass==total, the same condition as the old fail[0]==0. The log row below keeps its own verdict
213 // token because the rollup reads that FILE, not stdout.
214 if (nkg_ctr as i64) == 0 { nkg_ctr = gv_ctr() }
215 let rc__g: i64 = gv_verdict("NISHIOSKERNELGATE" as *u8, nkg_ctr, "composed NishiOS kernel boot judged by its TRANSCRIPT, not merely by halted+halt_code -- the defect this gate exists to catch" as *u8)
216
217 let lf: i64 = sys_openat_append(NKG_LOG, 420)
218 if lf >= 0 {
219 nkg_fp(lf, "NISHIOSKERNELGATE pass=" as *u8); nkg_fn(lf, pass[0])
220 nkg_fp(lf, " of=" as *u8); nkg_fn(lf, total)
221 nkg_fp(lf, " bytes=" as *u8); nkg_fn(lf, ilen)
222 nkg_fp(lf, " steps=" as *u8); nkg_fn(lf, steps)
223 nkg_fp(lf, " transcript=" as *u8); sys_write(lf, tx, txn)
224 if fail[0] == 0 { nkg_fp(lf, " verdict=GREEN\n" as *u8) } else { nkg_fp(lf, " verdict=RED\n" as *u8) }
225 sys_close(lf)
226 }
227 return rc__g
228}