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}