code wiki / _hdl_build / nx_drv_bind.nx

nx_drv_bind.nx source

↩ module page · 279 lines · 14136 B

1// nx_drv_bind.nx -- AUTONOMOUS DRIVER BRING-UP (X-DRV-W2), the driver registry binder. 2// 3// Composes X-DRV-W0 (HWMAP probe) + X-DRV-W1 (driver-protocol-from-spec emit) into a hands-off 4// loop that brings up ANY iron with NO per-device code: 5// PROBE -- read the device manifest (emu_devmap.tsv = the emu's MMIO bus probe output, the 6// analog of hwmap.tsv for PCI): each row = (base, DeviceID). 7// LOOK UP -- for each device, find its DeviceID in driver_registry.tsv -> a driver SPEC (an 8// X-DRV-W1 op-list). Unknown DeviceID -> UNBOUND (honest: no driver for that iron, 9// never a false bind). 10// EMIT -- run nx_drv_proto_emit <spec> <probed-base> <out> : the SAME spec is BOUND to the 11// address the probe found (the base override) -> bring up the device wherever it is. 12// RUN -- run the emitted driver on the SOVEREIGN rv64 emu; if the serial contains the 13// driver's golden + a clean halt, the device is BOUND + WORKING. 14// 15// A NEW device class = a NEW registry row + a NEW op-list spec; THIS organ and the emitter never 16// change. Evidence -> knowledge/status/driver_bind.log. argv[1]=devmap path (default 17// emu_devmap.tsv), argv[2]=registry path (default driver_registry.tsv) -- scratch lanes for the 18// gate's adversarial manifests. Sovereign (fork/dup3/execve/wait4), no gcc/.sh. 19// license_tier: ORIGINAL 20import "nx_syscalls.nx" 21const NB_MAGIC_65536: i64 = 65536 22const NB_MAGIC_2048: i64 = 2048 23 24const NB_EMIT_ELF: *u8 = "_offc/nx_drv_proto_emit.elf" 25const NB_SOV_ELF: *u8 = "_offc/nx_boot_run_sov.elf" 26const NB_MAXDEV: i64 = 64 27const NB_MAXREG: i64 = 64 28 29func nb_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 30func nb_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 } 31func nb_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 } 32func nb_n(v: i64) -> i64 { nb_fn(1, v); return 0 } 33 34// write decimal string of v into out (NUL-term); return length. 35func nb_itoa(v: i64, out: *u8) -> i64 { 36 var m: i64 = v 37 if m < 0 { m = 0 - m } 38 let t: *u8 = sys_mmap(28); var k: i64 = 0 39 if m == 0 { t[0] = 48; k = 1 } 40 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 41 var i: i64 = 0 42 while i < k { out[i] = t[k-1-i]; i = i + 1 } 43 out[k] = 0 as u8 44 return k 45} 46 47func nb_read(path: *u8, buf: *u8, cap: i64) -> i64 { 48 let fd: i64 = sys_openat_rd(path) 49 if fd < 0 { return 0 } 50 var n: i64 = 0 51 var go: i64 = 1 52 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 } } 53 sys_close(fd) 54 return n 55} 56 57// parse a hex (0x..) or decimal token at p in [.,le); returns value; *endp = next. 58func nb_parse_num(buf: *u8, p: i64, le: i64, endp: *i64) -> i64 { 59 var q: i64 = p 60 var val: i64 = 0 61 if q + 1 < le { if buf[q] == (48 as u8) { if buf[q+1] == (120 as u8) { 62 q = q + 2 63 var go: i64 = 1 64 while go == 1 { 65 if q >= le { go = 0 } else { 66 let c: i64 = buf[q] as i64 67 var d: i64 = 0 - 1 68 if c >= 48 { if c <= 57 { d = c - 48 } } 69 if c >= 97 { if c <= 102 { d = c - 87 } } 70 if c >= 65 { if c <= 70 { d = c - 55 } } 71 if d < 0 { go = 0 } else { val = (val * 16) + d; q = q + 1 } 72 } 73 } 74 endp[0] = q 75 return val 76 }}} 77 var go2: i64 = 1 78 while go2 == 1 { 79 if q >= le { go2 = 0 } else { 80 let c: i64 = buf[q] as i64 81 if c >= 48 { if c <= 57 { val = (val * 10) + (c - 48); q = q + 1 } else { go2 = 0 } } else { go2 = 0 } 82 } 83 } 84 endp[0] = q 85 return val 86} 87 88// return the start offset of the idx-th TAB-separated field in line [ls,le), or -1 if absent. 89func nb_field_start(buf: *u8, ls: i64, le: i64, idx: i64) -> i64 { 90 if idx == 0 { return ls } 91 var f: i64 = 0 92 var p: i64 = ls 93 while p < le { 94 if buf[p] == (9 as u8) { 95 f = f + 1 96 if f == idx { return p + 1 } 97 } 98 p = p + 1 99 } 100 return 0 - 1 101} 102 103// run prog with up to 3 args (any may be null after the first null); stdout/stderr -> outpath; 104// return WEXITSTATUS (or 128+sig). 105func nb_run(prog: *u8, a1: *u8, a2: *u8, a3: *u8, outpath: *u8) -> i64 { 106 let pid: i64 = sys_fork() 107 if pid == 0 { 108 if outpath != (0 as *u8) { let ofd: i64 = sys_openat_wr(outpath, 0x1a4); if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } } 109 else { let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4); if dn >= 0 { sys_dup3(dn, 1, 0) } } 110 let argv: *i64 = sys_mmap(64) as *i64 111 argv[0] = prog as i64 112 var k: i64 = 1 113 if a1 != (0 as *u8) { argv[k] = a1 as i64; k = k + 1 } 114 if a2 != (0 as *u8) { argv[k] = a2 as i64; k = k + 1 } 115 if a3 != (0 as *u8) { argv[k] = a3 as i64; k = k + 1 } 116 argv[k] = 0 117 let envp: *i64 = sys_mmap(16) as *i64 118 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 119 sys_execve(prog, argv, envp) 120 sys_exit(127) 121 } 122 let st: *i64 = sys_mmap(16) as *i64 123 sys_wait4(pid, st, 0) 124 let sig: i64 = st[0] & 0x7f 125 if sig != 0 { return 128 + sig } 126 return (st[0] >> 8) & 0xff 127} 128 129// does buf[0,n) contain pat (length pl)? 1/0 130func nb_has(buf: *u8, n: i64, pat: *u8, pl: i64) -> i64 { 131 if pl <= 0 { return 0 } 132 var i: i64 = 0 133 while i + pl <= n { 134 var k: i64 = 0; var hit: i64 = 1 135 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 136 if hit == 1 { return 1 } 137 i = i + 1 138 } 139 return 0 140} 141 142func main(argc: i64, argv: *i64) -> i64 { 143 var devmap_path: *u8 = "knowledge/registry/emu_devmap.tsv" as *u8 144 var reg_path: *u8 = "knowledge/registry/driver_registry.tsv" as *u8 145 if argc >= 2 { devmap_path = argv[1] as *u8 } 146 if argc >= 3 { reg_path = argv[2] as *u8 } 147 148 nb_p("=== nx_drv_bind: autonomous driver bring-up (probe -> registry lookup -> emit-bound -> run) ===\n" as *u8) 149 let lfd: i64 = sys_openat_append("knowledge/status/driver_bind.log" as *u8, 0x1a4) 150 151 // ---- load the driver registry: deviceid -> spec ---- 152 let regbuf: *u8 = sys_mmap(NB_MAGIC_65536) 153 let regn: i64 = nb_read(reg_path, regbuf, NB_MAGIC_65536) 154 if regn <= 0 { nb_p("DRVBIND verdict=RED reason=registry-missing\n" as *u8); if lfd>=0 { nb_fp(lfd, "DRVBIND verdict=RED reason=registry-missing\n" as *u8); sys_close(lfd) } sys_exit(1); return 1 } 155 let reg_id: *i64 = sys_mmap(8 * NB_MAXREG) as *i64 156 let reg_spec: *u8 = sys_mmap(NB_MAXREG * 256) // 256 bytes per spec path 157 var nreg: i64 = 0 158 let ep: *i64 = sys_mmap(16) as *i64 159 var rls: i64 = 0 160 while rls < regn { 161 var rle: i64 = rls 162 var sc: i64 = 1 163 while sc == 1 { if rle >= regn { sc = 0 } else { if regbuf[rle] == (10 as u8) { sc = 0 } else { rle = rle + 1 } } } 164 if regbuf[rls] != (35 as u8) { 165 let f0: i64 = nb_field_start(regbuf, rls, rle, 0) 166 let f1: i64 = nb_field_start(regbuf, rls, rle, 1) 167 if f0 >= 0 { if f1 >= 0 { 168 if nreg < NB_MAXREG { 169 reg_id[nreg] = nb_parse_num(regbuf, f0, rle, ep) 170 // copy spec field (until tab/eol) into reg_spec[nreg*256] 171 var c: i64 = 0 172 var q: i64 = f1 173 while q < rle { if regbuf[q] == (9 as u8) { q = rle + 0 } else { reg_spec[nreg*256 + c] = regbuf[q]; c = c + 1; q = q + 1 } } 174 reg_spec[nreg*256 + c] = 0 as u8 175 nreg = nreg + 1 176 } 177 } } 178 } 179 rls = rle + 1 180 } 181 182 // ---- load the device manifest (probe): base, deviceid ---- 183 let devbuf: *u8 = sys_mmap(NB_MAGIC_65536) 184 let devn: i64 = nb_read(devmap_path, devbuf, NB_MAGIC_65536) 185 if devn <= 0 { nb_p("DRVBIND verdict=RED reason=devmap-missing\n" as *u8); if lfd>=0 { nb_fp(lfd, "DRVBIND verdict=RED reason=devmap-missing\n" as *u8); sys_close(lfd) } sys_exit(1); return 1 } 186 187 var probed: i64 = 0 188 var bound_working: i64 = 0 189 var unbound: i64 = 0 190 var failed: i64 = 0 191 192 let basebuf: *u8 = sys_mmap(32) 193 let outpath: *u8 = sys_mmap(256) 194 let goldpath: *u8 = sys_mmap(256) 195 let golden: *u8 = sys_mmap(NB_MAGIC_2048) 196 let serial: *u8 = sys_mmap(NB_MAGIC_65536) 197 let idbuf: *u8 = sys_mmap(32) 198 199 var dls: i64 = 0 200 while dls < devn { 201 var dle: i64 = dls 202 var sc2: i64 = 1 203 while sc2 == 1 { if dle >= devn { sc2 = 0 } else { if devbuf[dle] == (10 as u8) { sc2 = 0 } else { dle = dle + 1 } } } 204 if devbuf[dls] != (35 as u8) { 205 let g0: i64 = nb_field_start(devbuf, dls, dle, 0) 206 let g1: i64 = nb_field_start(devbuf, dls, dle, 1) 207 if g0 >= 0 { if g1 >= 0 { 208 let base: i64 = nb_parse_num(devbuf, g0, dle, ep) 209 let did: i64 = nb_parse_num(devbuf, g1, dle, ep) 210 probed = probed + 1 211 // registry lookup: deviceid -> spec 212 var spec_idx: i64 = 0 - 1 213 var ri: i64 = 0 214 while ri < nreg { if reg_id[ri] == did { spec_idx = ri } ri = ri + 1 } 215 nb_p(" probe base=" as *u8); nb_n(base); nb_p(" deviceid=" as *u8); nb_n(did) 216 if spec_idx < 0 { 217 unbound = unbound + 1 218 nb_p(" -> UNBOUND (no registry entry)\n" as *u8) 219 if lfd >= 0 { nb_fp(lfd, "DRVBIND probe deviceid=" as *u8); nb_fn(lfd, did); nb_fp(lfd, " base=" as *u8); nb_fn(lfd, base); nb_fp(lfd, " status=UNBOUND\n" as *u8) } 220 } else { 221 let spec: *u8 = (reg_spec as i64 + spec_idx*256) as *u8 222 // out path = runtime/_hdl_build/_bound_dev<did>.bin 223 var oo: i64 = 0 224 let pfx: *u8 = "runtime/_hdl_build/_bound_dev" as *u8 225 while pfx[oo] != (0 as u8) { outpath[oo] = pfx[oo]; oo = oo + 1 } 226 let idl: i64 = nb_itoa(did, idbuf) 227 var ic: i64 = 0 228 while ic < idl { outpath[oo] = idbuf[ic]; oo = oo + 1; ic = ic + 1 } 229 let sfx: *u8 = ".bin" as *u8 230 var sj: i64 = 0 231 while sfx[sj] != (0 as u8) { outpath[oo] = sfx[sj]; oo = oo + 1; sj = sj + 1 } 232 outpath[oo] = 0 as u8 233 // base as decimal string for the emitter override arg 234 nb_itoa(base, basebuf) 235 // EMIT the driver bound to the probed base 236 let est: i64 = nb_run(NB_EMIT_ELF, spec, basebuf, outpath, "/tmp/_drvbind_emit.out" as *u8) 237 if est != 0 { 238 failed = failed + 1 239 nb_p(" -> spec=" as *u8); nb_p(spec); nb_p(" EMIT-FAILED\n" as *u8) 240 if lfd >= 0 { nb_fp(lfd, "DRVBIND probe deviceid=" as *u8); nb_fn(lfd, did); nb_fp(lfd, " status=EMIT-FAILED\n" as *u8) } 241 } else { 242 // golden path = <out>.gold 243 var gj: i64 = 0 244 while outpath[gj] != (0 as u8) { goldpath[gj] = outpath[gj]; gj = gj + 1 } 245 let gext: *u8 = ".gold" as *u8 246 var ge: i64 = 0 247 while gext[ge] != (0 as u8) { goldpath[gj] = gext[ge]; gj = gj + 1; ge = ge + 1 } 248 goldpath[gj] = 0 as u8 249 let gn: i64 = nb_read(goldpath, golden, NB_MAGIC_2048) 250 golden[gn] = 0 as u8 251 // RUN on the sovereign emu 252 let rst: i64 = nb_run(NB_SOV_ELF, outpath, 0 as *u8, 0 as *u8, "/tmp/_drvbind_serial.txt" as *u8) 253 let sn: i64 = nb_read("/tmp/_drvbind_serial.txt" as *u8, serial, NB_MAGIC_65536) 254 let work: i64 = nb_has(serial, sn, golden, gn) 255 let halt: i64 = nb_has(serial, sn, "BOOTSOV verdict=GREEN" as *u8, 21) 256 var ok: i64 = 0 257 if rst == 0 { if gn > 0 { if work == 1 { if halt == 1 { ok = 1 } } } } 258 if ok == 1 { 259 bound_working = bound_working + 1 260 nb_p(" -> spec=" as *u8); nb_p(spec); nb_p(" BOUND+WORKING golden=" as *u8); nb_p(golden); nb_p("\n" as *u8) 261 if lfd >= 0 { nb_fp(lfd, "DRVBIND probe deviceid=" as *u8); nb_fn(lfd, did); nb_fp(lfd, " base=" as *u8); nb_fn(lfd, base); nb_fp(lfd, " spec=" as *u8); nb_fp(lfd, spec); nb_fp(lfd, " status=BOUND-WORKING golden=" as *u8); nb_fp(lfd, golden); nb_fp(lfd, "\n" as *u8) } 262 } else { 263 failed = failed + 1 264 nb_p(" -> spec=" as *u8); nb_p(spec); nb_p(" BOUND-NOT-WORKING\n" as *u8) 265 if lfd >= 0 { nb_fp(lfd, "DRVBIND probe deviceid=" as *u8); nb_fn(lfd, did); nb_fp(lfd, " status=BOUND-NOT-WORKING\n" as *u8) } 266 } 267 } 268 } 269 } } 270 } 271 dls = dle + 1 272 } 273 274 nb_p("DRVBIND summary probed=" as *u8); nb_n(probed); nb_p(" bound_working=" as *u8); nb_n(bound_working); nb_p(" unbound=" as *u8); nb_n(unbound); nb_p(" failed=" as *u8); nb_n(failed); nb_p("\n" as *u8) 275 if lfd >= 0 { nb_fp(lfd, "DRVBIND summary probed=" as *u8); nb_fn(lfd, probed); nb_fp(lfd, " bound_working=" as *u8); nb_fn(lfd, bound_working); nb_fp(lfd, " unbound=" as *u8); nb_fn(lfd, unbound); nb_fp(lfd, " failed=" as *u8); nb_fn(lfd, failed); nb_fp(lfd, " epoch=" as *u8); nb_fn(lfd, sys_now_realtime_sec()); nb_fp(lfd, "\n" as *u8); sys_close(lfd) } 276 if failed > 0 { sys_exit(1); return 1 } 277 sys_exit(0) 278 return 0 279}