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}