code wiki / _hdl_build / nx_emu_testbed.nx
nx_emu_testbed.nx source
↩ module page · 244 lines · 11309 B
1// nx_emu_testbed.nx -- THE EMU TEST-MATRIX organ: which common laptop configs can the Nishi emu
2// boot VIRTUALLY on THIS machine, right now, within its resources.
3//
4// module: nishi-core.genealogy.emu_testbed
5// capability: CORE_COMPUTE (the safe S-class test bench: boot all-the-way-up + flash-a-BIOS in a
6// disposable VM, never the host -- the never-brick law made concrete; see CLAUDE.md #26)
7//
8// THE MODEL (operator 2026-06-16): the emu exists so we can test the brickable boot chain
9// (CMOS->POST->BIOS/UEFI flash->bootloader->NishiOS->spore germination) on models of the most
10// common laptops -- VIRTUALLY, sized to whatever host the spore lands on. This organ:
11// * PROBES the real host LIVE: MemAvailable (/proc/meminfo) + core count (/proc/cpuinfo),
12// via sys_read_file (a read-until-EOF loop, so /proc's size-0 files read fine);
13// * reads the laptop profiles (DATA) and computes, per profile, whether it FITS virtually here
14// (guest RAM <= EMU_RAM_BUDGET_PCT% of host avail, vCPU <= host cores, arch emulatable);
15// * enforces NEVER-BRICK: every profile's brickable test must run in the emu sandbox
16// (test_mode=emu); a host-mode profile is a violation -> RED.
17// The FIT list = the spore's build/test matrix on this machine. DYNAMIC/LIVING: run it on an 8GB
18// box and fewer fit; on a 128GB workstation, all do. Sovereign: imports only nx_syscalls.
19// license_tier: ORIGINAL
20
21import "nx_syscalls.nx"
22const LP_MAGIC_1024: i64 = 1024
23
24const LP_FILE: *u8 = "knowledge/registry/laptop_profiles.tsv"
25const TB_MEMINFO: *u8 = "/proc/meminfo"
26const TB_CPUINFO: *u8 = "/proc/cpuinfo"
27const EMU_RAM_BUDGET_PCT: i64 = 80 // a guest may use at most 80% of the host's available RAM
28
29func tb_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } sys_write(1, s, n); return 0 }
30func tb_putn(v: i64) -> i64 {
31 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
32 var m: i64 = v
33 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
34 let d: *u8 = sys_mmap(24); var k: i64 = 0
35 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
36 var j: i64 = k - 1
37 while j >= 0 { sys_write(1, ((d as i64)+j) as *u8, 1); j = j - 1 }
38 return 0
39}
40func tb_strlen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { i = i + 1 } return i }
41func tb_streq(a: *u8, b: *u8) -> i64 {
42 var i: i64 = 0
43 while a[i] != 0 as u8 { if a[i] != b[i] { return 0 } i = i + 1 }
44 if b[i] != 0 as u8 { return 0 }
45 return 1
46}
47func tb_atoi(s: *u8) -> i64 {
48 var i: i64 = 0; var v: i64 = 0
49 while s[i] != 0 as u8 {
50 if s[i] >= 48 as u8 { if s[i] <= 57 as u8 { v = v * 10 + (s[i] as i64 - 48) } }
51 i = i + 1
52 }
53 return v
54}
55
56// TAB field f (0-based) of line[ls,le) copied into a FRESH NUL-term mmap; returns ptr.
57func tb_field_dup(b: *u8, ls: i64, le: i64, f: i64) -> *u8 {
58 var cur: i64 = 0
59 var i: i64 = ls
60 while cur < f {
61 if i >= le { let e: *u8 = sys_mmap(2); e[0] = 0 as u8; return e }
62 if b[i] == 9 as u8 { cur = cur + 1 }
63 i = i + 1
64 }
65 let out: *u8 = sys_mmap(256)
66 var o: i64 = 0
67 while i < le {
68 if b[i] == 9 as u8 { i = le } else { if o < 255 { out[o] = b[i]; o = o + 1 } i = i + 1 }
69 }
70 out[o] = 0 as u8
71 return out
72}
73
74// does `key` (NUL-term) match buf at position pos (within [0,n))? 1/0.
75func tb_match_at(buf: *u8, pos: i64, n: i64, key: *u8) -> i64 {
76 var j: i64 = 0
77 while key[j] != 0 as u8 {
78 if (pos + j) >= n { return 0 }
79 if buf[pos + j] != key[j] { return 0 }
80 j = j + 1
81 }
82 return 1
83}
84
85// find `key` in buf[0,n), then parse the first run of digits AFTER it. -1 if not found.
86func tb_find_int_after(buf: *u8, n: i64, key: *u8) -> i64 {
87 let kl: i64 = tb_strlen(key)
88 var i: i64 = 0
89 while i < n {
90 if tb_match_at(buf, i, n, key) == 1 {
91 var p: i64 = i + kl
92 var v: i64 = 0; var seen: i64 = 0; var go: i64 = 1
93 while go == 1 {
94 if p >= n { go = 0 }
95 else {
96 let c: u8 = buf[p]
97 if c >= 48 as u8 {
98 if c <= 57 as u8 { v = v * 10 + (c as i64 - 48); seen = 1; p = p + 1 }
99 else { if seen == 1 { go = 0 } else { p = p + 1 } }
100 } else { if seen == 1 { go = 0 } else { p = p + 1 } }
101 }
102 }
103 if seen == 1 { return v }
104 return 0 - 1
105 }
106 i = i + 1
107 }
108 return 0 - 1
109}
110
111// count lines whose start matches `key` (e.g. "processor" in /proc/cpuinfo).
112func tb_count_line_prefix(buf: *u8, n: i64, key: *u8) -> i64 {
113 var cnt: i64 = 0; var atline: i64 = 1; var i: i64 = 0
114 while i < n {
115 if atline == 1 { if tb_match_at(buf, i, n, key) == 1 { cnt = cnt + 1 } }
116 if buf[i] == 10 as u8 { atline = 1 } else { atline = 0 }
117 i = i + 1
118 }
119 return cnt
120}
121
122func tb_arch_covered(arch: *u8) -> i64 {
123 if tb_streq(arch, "x86_64" as *u8) == 1 { return 1 }
124 if tb_streq(arch, "aarch64" as *u8) == 1 { return 1 }
125 if tb_streq(arch, "riscv64" as *u8) == 1 { return 1 }
126 return 0
127}
128
129// PROBE the host + analyze the profiles file into outs. Returns 0 ok, <0 on probe/read failure.
130// outs[0]=nprofiles outs[1]=host_avail_mb outs[2]=host_cores outs[3]=budget_mb
131// outs[4]=fit outs[5]=too_big(resource) outs[6]=arch_uncovered outs[7]=host_mode(never-brick viol)
132func tb_analyze(pfile: *u8, outs: *i64) -> i64 {
133 let mlen: *i64 = sys_mmap(16) as *i64; mlen[0] = 0
134 let mbuf: *u8 = sys_read_file(TB_MEMINFO, mlen)
135 if (mbuf as i64) == 0 { return 0 - 1 }
136 let avail_kb: i64 = tb_find_int_after(mbuf, mlen[0], "MemAvailable:" as *u8)
137 if avail_kb < 0 { return 0 - 2 }
138 let avail_mb: i64 = avail_kb / LP_MAGIC_1024
139
140 let clen: *i64 = sys_mmap(16) as *i64; clen[0] = 0
141 let cbuf: *u8 = sys_read_file(TB_CPUINFO, clen)
142 if (cbuf as i64) == 0 { return 0 - 3 }
143 let cores: i64 = tb_count_line_prefix(cbuf, clen[0], "processor" as *u8)
144
145 let budget_mb: i64 = avail_mb * EMU_RAM_BUDGET_PCT / 100
146
147 let plen: *i64 = sys_mmap(16) as *i64; plen[0] = 0
148 let pbuf: *u8 = sys_read_file(pfile, plen)
149 if (pbuf as i64) == 0 { return 0 - 4 }
150 let pn: i64 = plen[0]
151
152 var nprof: i64 = 0; var nfit: i64 = 0; var ntoobig: i64 = 0; var nuncov: i64 = 0; var nhost: i64 = 0
153 var ls: i64 = 0; var i: i64 = 0
154 while i <= pn {
155 var atend: i64 = 0
156 if i == pn { atend = 1 }
157 if i < pn { if pbuf[i] == 10 as u8 { atend = 1 } }
158 if atend == 1 {
159 if i > ls { if pbuf[ls] != 35 as u8 {
160 nprof = nprof + 1
161 let arch: *u8 = tb_field_dup(pbuf, ls, i, 1)
162 let ram: i64 = tb_atoi(tb_field_dup(pbuf, ls, i, 2))
163 let vcpu: i64 = tb_atoi(tb_field_dup(pbuf, ls, i, 3))
164 let tmode: *u8 = tb_field_dup(pbuf, ls, i, 7)
165 let covered: i64 = tb_arch_covered(arch)
166 if covered == 0 { nuncov = nuncov + 1 }
167 if tb_streq(tmode, "emu" as *u8) == 0 { nhost = nhost + 1 }
168 var ram_ok: i64 = 0; if ram <= budget_mb { ram_ok = 1 }
169 var cpu_ok: i64 = 0; if vcpu <= cores { cpu_ok = 1 }
170 if ram_ok == 1 { if cpu_ok == 1 { if covered == 1 { nfit = nfit + 1 } } }
171 if ram_ok == 0 { ntoobig = ntoobig + 1 } else { if cpu_ok == 0 { ntoobig = ntoobig + 1 } }
172 } }
173 ls = i + 1
174 }
175 i = i + 1
176 }
177 outs[0] = nprof; outs[1] = avail_mb; outs[2] = cores; outs[3] = budget_mb
178 outs[4] = nfit; outs[5] = ntoobig; outs[6] = nuncov; outs[7] = nhost
179 return 0
180}
181
182// print each profile's verdict using the already-probed budget_mb + cores (no re-probe).
183func tb_print_profiles(pfile: *u8, budget_mb: i64, cores: i64) -> i64 {
184 let plen: *i64 = sys_mmap(16) as *i64; plen[0] = 0
185 let pbuf: *u8 = sys_read_file(pfile, plen)
186 if (pbuf as i64) == 0 { return 0 - 1 }
187 let pn: i64 = plen[0]
188 var ls: i64 = 0; var i: i64 = 0
189 while i <= pn {
190 var atend: i64 = 0
191 if i == pn { atend = 1 }
192 if i < pn { if pbuf[i] == 10 as u8 { atend = 1 } }
193 if atend == 1 {
194 if i > ls { if pbuf[ls] != 35 as u8 {
195 let id: *u8 = tb_field_dup(pbuf, ls, i, 0)
196 let arch: *u8 = tb_field_dup(pbuf, ls, i, 1)
197 let ram: i64 = tb_atoi(tb_field_dup(pbuf, ls, i, 2))
198 let vcpu: i64 = tb_atoi(tb_field_dup(pbuf, ls, i, 3))
199 let fw: *u8 = tb_field_dup(pbuf, ls, i, 5)
200 let tmode: *u8 = tb_field_dup(pbuf, ls, i, 7)
201 let covered: i64 = tb_arch_covered(arch)
202 var ram_ok: i64 = 0; if ram <= budget_mb { ram_ok = 1 }
203 var cpu_ok: i64 = 0; if vcpu <= cores { cpu_ok = 1 }
204 tb_puts(" ")
205 if tb_streq(tmode, "emu" as *u8) == 0 { tb_puts("[NEVER-BRICK!host] ") }
206 else { if covered == 0 { tb_puts("[UNCOVERED-arch ] ") }
207 else { if ram_ok == 0 { tb_puts("[TOO-BIG: RAM ] ") }
208 else { if cpu_ok == 0 { tb_puts("[TOO-BIG: vCPU ] ") }
209 else { tb_puts("[FITS-VIRTUALLY ] ") } } } }
210 tb_puts(id); tb_puts(" "); tb_puts(arch); tb_puts(" ")
211 tb_putn(ram); tb_puts("MB/"); tb_putn(vcpu); tb_puts("vcpu ")
212 tb_puts(fw); tb_puts(" sandbox="); tb_puts(tmode); tb_puts("\n")
213 } }
214 ls = i + 1
215 }
216 i = i + 1
217 }
218 return 0
219}
220
221func main() -> i64 {
222 tb_puts("=== NISHI EMU TESTBED -- which common laptops boot virtually on THIS machine ===\n")
223 let outs: *i64 = sys_mmap(256) as *i64
224 let rc: i64 = tb_analyze(LP_FILE, outs)
225 if rc != 0 {
226 tb_puts("FATAL: probe/read failed rc="); tb_putn(rc); tb_puts(" (need /proc/meminfo + /proc/cpuinfo + "); tb_puts(LP_FILE); tb_puts(")\n")
227 sys_exit(1); return 1
228 }
229 tb_puts(" HOST (live probe): avail_RAM="); tb_putn(outs[1]); tb_puts("MB cores="); tb_putn(outs[2])
230 tb_puts(" guest_RAM_budget("); tb_putn(EMU_RAM_BUDGET_PCT); tb_puts("%)="); tb_putn(outs[3]); tb_puts("MB\n")
231 tb_print_profiles(LP_FILE, outs[3], outs[2])
232 tb_puts(" CENSUS: profiles="); tb_putn(outs[0])
233 tb_puts(" fit_virtually_here="); tb_putn(outs[4])
234 tb_puts(" too_big(resource)="); tb_putn(outs[5])
235 tb_puts(" arch_uncovered="); tb_putn(outs[6])
236 tb_puts(" never_brick_violations(host-mode)="); tb_putn(outs[7]); tb_puts("\n")
237 if outs[7] > 0 { tb_puts(" VERDICT: NEVER-BRICK VIOLATION -- a profile tests brickable ops on HOST not the emu sandbox\n"); sys_exit(1); return 1 }
238 if outs[0] == 0 { tb_puts(" VERDICT: no laptop profiles in the matrix\n"); sys_exit(1); return 1 }
239 if outs[4] == 0 { tb_puts(" VERDICT: NO profile fits virtually here -- this machine lacks the RAM/cores; spore must target a bigger host\n"); sys_exit(1); return 1 }
240 tb_puts(" VERDICT: "); tb_putn(outs[4]); tb_puts(" of "); tb_putn(outs[0])
241 tb_puts(" common laptop configs are testable virtually on this machine (all brickable tests emu-sandboxed); ")
242 if outs[5] > 0 { tb_putn(outs[5]); tb_puts(" need a bigger host = the matrix worklist\n") } else { tb_puts("full matrix fits\n") }
243 sys_exit(0); return 0
244}