code wiki / _hdl_build / nx_driver_gen.nx

nx_driver_gen.nx source

↩ module page · 202 lines · 14099 B

1// nx_driver_gen.nx -- GATE: AUTO-GENERATE a virtio-mmio DEVICE DRIVER from a discovered device + VALIDATE it drives 2// the device. Consumes a device discovered by nx_hw_discover (a virtio-mmio device + its DeviceID) and GENERATES the 3// register-protocol driver the device needs -- the piece nx_driver_emit (probe/bind only) left open. The driver is 4// the real virtio-mmio spec sequence: reset -> ACKNOWLEDGE -> DRIVER -> feature-negotiate -> FEATURES_OK -> virtqueue 5// setup -> DRIVER_OK -> submit a request -> device fills the used ring -> completion. The SUBMIT command is derived 6// from the DeviceID (blk=read-sector, console=write, rng=fill-entropy) => a NEW device class = a NEW generated driver, 7// no new hand-code. Validated against a faithful virtio-mmio device model. 8// ★ NEVER-BRICK (cardinal 26): the generated driver's opcode set has ZERO firmware/flash-write ops; the executor 9// REFUSES any firmware-write op. A driver writes device registers -- the firmware-write path does not exist here. 10// T1 generate+run a virtio-blk driver -> the handshake drives the device to DRIVER_OK (status 0xf). 11// T2 the driver submits a request -> device processes the virtqueue -> used ring updated -> completion read. 12// T3 different DeviceIDs generate DIFFERENT drivers (blk/console/rng), each reaching DRIVER_OK (generated, not hard-coded). 13// T4 teeth: a firmware-write op in the driver stream is REFUSED (never-brick) + 0 fw-write ops in a normal driver. 14// T5 FAILED path: a device that rejects FEATURES_OK -> driver aborts cleanly (FAILED, no hang) + determinism. 15// expect_exit: 0 Sovereign: nx_syscalls. Grounded: the virtio-mmio spec (hw_mmio/hw_pcie research). 16import "nx_syscalls.nx" 17import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 18import "nx_g_puts_lib.nx" 19 20// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 21// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 22// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 23// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 24func g_pn(v: i64) -> i64 { nxi_out(v); return 0 } 25func g_hex(v: i64) -> i64 { g_puts("0x" as *u8); let b: *u8=sys_mmap(20); var st: i64=0; var k: i64=0; var i: i64=60; while i>=0 { let nib: i64=(v>>i)&15; if nib!=0 { st=1 } if st==1 { if nib<10 { b[k]=(48+nib) as u8 } else { b[k]=(87+nib) as u8 } k=k+1 } i=i-4 } if k==0 { b[0]=48 as u8; k=1 } sys_write(1,b,k); return 0 } 26func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 27 28// virtio-mmio register offsets (byte)/4 = word index into the device register array 29const R_MAGIC: i64 = 0 30const R_VERSION: i64 = 1 // 0x04 31const R_DEVID: i64 = 2 // 0x08 32const R_VENDOR: i64 = 3 // 0x0c 33const R_DEVFEAT: i64 = 4 // 0x10 34const R_DRVFEAT: i64 = 8 // 0x20 35const R_QSEL: i64 = 12 // 0x30 36const R_QNUMMAX: i64 = 13 // 0x34 37const R_QNUM: i64 = 14 // 0x38 38const R_QREADY: i64 = 17 // 0x44 39const R_QNOTIFY: i64 = 20 // 0x50 40const R_STATUS: i64 = 28 // 0x70 41const R_USEDCNT: i64 = 40 // 0xA0 (model: used-ring completion counter) 42const R_LASTCMD: i64 = 41 // model: last command the device processed 43const NREG: i64 = 64 44const VIRTIO_MAGIC: i64 = 0x74726976 45// status bits 46const ST_ACK: i64 = 1 47const ST_DRIVER: i64 = 2 48const ST_DRIVER_OK: i64 = 4 49const ST_FEATURES_OK: i64 = 8 50const ST_FAILED: i64 = 128 51// device ids 52const DEV_NET: i64 = 1 53const DEV_BLK: i64 = 2 54const DEV_CONSOLE: i64 = 3 55const DEV_RNG: i64 = 4 56// generated driver op set (device-register ops ONLY -- no firmware write) 57const OP_RESET: i64 = 1 58const OP_SETSTATUS: i64 = 2 // arg = bit to OR into STATUS 59const OP_NEGFEAT: i64 = 3 // read DEVFEAT -> write DRVFEAT 60const OP_CHECKFEAT: i64 = 4 // verify FEATURES_OK still set (device may clear it to reject) 61const OP_QSETUP: i64 = 5 // QSEL=0, QNUM=arg, QREADY=1 62const OP_SUBMIT: i64 = 6 // arg = device-specific command code; write + QNOTIFY 63const OP_WAITUSED: i64 = 7 // poll the used-ring counter 64const OP_FW_WRITE: i64 = 100 // firmware write -- NOT in the driver set; only to prove refusal 65func op_safe(op: i64) -> i64 { if op>=1 { if op<=7 { return 1 } } return 0 } 66// device-specific command derived from DeviceID 67func dev_cmd(devid: i64) -> i64 { if devid==DEV_BLK { return 0x101 } if devid==DEV_CONSOLE { return 0x202 } if devid==DEV_RNG { return 0x404 } return 0x001 } 68func dev_qnum(devid: i64) -> i64 { if devid==DEV_BLK { return 8 } if devid==DEV_CONSOLE { return 4 } if devid==DEV_RNG { return 2 } return 1 } 69 70// ---- the DEVICE MODEL (faithful virtio-mmio) ---- 71func dev_new(devid: i64) -> *i64 { 72 let d: *i64 = sys_mmap(NREG*8) as *i64 73 var i: i64=0; while i<NREG { d[i]=0; i=i+1 } 74 d[R_MAGIC]=VIRTIO_MAGIC; d[R_VERSION]=2; d[R_DEVID]=devid; d[R_VENDOR]=0x554D4551 75 d[R_DEVFEAT]=0x110 // some feature bits the device offers 76 d[R_QNUMMAX]=8 77 return d 78} 79// device reaction to an MMIO write (models the hardware state machine). 80func dev_mmio_w(d: *i64, reg: i64, val: i64, reject_featok: i64) -> i64 { 81 if reg==R_STATUS { 82 if val==0 { d[R_STATUS]=0; return 0 } // reset 83 var ns: i64 = d[R_STATUS] | val 84 if (val & ST_FEATURES_OK)!=0 { if reject_featok==1 { ns = ns & (0-1-ST_FEATURES_OK); ns = ns | ST_FAILED } } // device rejects -> clears FEATURES_OK, sets FAILED 85 d[R_STATUS]=ns; return 0 86 } 87 if reg==R_QNOTIFY { 88 if (d[R_STATUS] & ST_DRIVER_OK)!=0 { d[R_USEDCNT]=d[R_USEDCNT]+1; d[R_LASTCMD]=val } // process the queue -> bump used ring 89 return 0 90 } 91 d[reg]=val; return 0 92} 93func dev_mmio_r(d: *i64, reg: i64) -> i64 { return d[reg] } 94 95// ---- the GENERATED DRIVER: an op stream derived from the discovered DeviceID ---- 96func gen_driver(devid: i64, op: *i64, arg: *i64) -> i64 { 97 var n: i64=0 98 op[n]=OP_RESET; arg[n]=0; n=n+1 99 op[n]=OP_SETSTATUS; arg[n]=ST_ACK; n=n+1 100 op[n]=OP_SETSTATUS; arg[n]=ST_DRIVER; n=n+1 101 op[n]=OP_NEGFEAT; arg[n]=0; n=n+1 102 op[n]=OP_SETSTATUS; arg[n]=ST_FEATURES_OK; n=n+1 103 op[n]=OP_CHECKFEAT; arg[n]=0; n=n+1 104 op[n]=OP_QSETUP; arg[n]=dev_qnum(devid); n=n+1 // queue size derived from device 105 op[n]=OP_SETSTATUS; arg[n]=ST_DRIVER_OK; n=n+1 106 op[n]=OP_SUBMIT; arg[n]=dev_cmd(devid); n=n+1 // command derived from device 107 op[n]=OP_WAITUSED; arg[n]=1; n=n+1 108 return n 109} 110// ---- the DRIVER EXECUTOR: runs the generated ops against the device; never-brick guard refuses firmware writes. 111// returns: 2 = OK (DRIVER_OK + command completed), 1 = FAILED (clean abort), -1 = REFUSED (firmware-write op). 112func run_driver(d: *i64, op: *i64, arg: *i64, n: i64) -> i64 { 113 var i: i64=0 114 while i<n { 115 let o: i64=op[i] 116 if op_safe(o)==0 { return 0-1 } // NEVER-BRICK: refuse a firmware-write op 117 if o==OP_RESET { dev_mmio_w(d, R_STATUS, 0, 0) } 118 if o==OP_SETSTATUS { dev_mmio_w(d, R_STATUS, arg[i], 0) } 119 if o==OP_NEGFEAT { let f: i64=dev_mmio_r(d, R_DEVFEAT); dev_mmio_w(d, R_DRVFEAT, f & 0x110, 0) } 120 if o==OP_CHECKFEAT { if (dev_mmio_r(d, R_STATUS) & ST_FEATURES_OK)==0 { return 1 } } // device rejected features -> FAILED 121 if o==OP_QSETUP { dev_mmio_w(d, R_QSEL, 0, 0); dev_mmio_w(d, R_QNUM, arg[i], 0); dev_mmio_w(d, R_QREADY, 1, 0) } 122 if o==OP_SUBMIT { dev_mmio_w(d, R_QNOTIFY, arg[i], 0) } 123 if o==OP_WAITUSED { if dev_mmio_r(d, R_USEDCNT) < arg[i] { return 1 } } 124 i=i+1 125 } 126 if (dev_mmio_r(d, R_STATUS) & ST_DRIVER_OK)!=0 { return 2 } 127 return 1 128} 129// the same executor but the device REJECTS features (for the FAILED test) -- runs with reject flag on STATUS writes. 130func run_driver_reject(d: *i64, op: *i64, arg: *i64, n: i64) -> i64 { 131 var i: i64=0 132 while i<n { 133 let o: i64=op[i] 134 if op_safe(o)==0 { return 0-1 } 135 if o==OP_RESET { dev_mmio_w(d, R_STATUS, 0, 1) } 136 if o==OP_SETSTATUS { dev_mmio_w(d, R_STATUS, arg[i], 1) } 137 if o==OP_NEGFEAT { let f: i64=dev_mmio_r(d, R_DEVFEAT); dev_mmio_w(d, R_DRVFEAT, f, 1) } 138 if o==OP_CHECKFEAT { if (dev_mmio_r(d, R_STATUS) & ST_FEATURES_OK)==0 { return 1 } } 139 if o==OP_QSETUP { dev_mmio_w(d, R_QSEL, 0, 1); dev_mmio_w(d, R_QNUM, arg[i], 1); dev_mmio_w(d, R_QREADY, 1, 1) } 140 if o==OP_SUBMIT { dev_mmio_w(d, R_QNOTIFY, arg[i], 1) } 141 if o==OP_WAITUSED { if dev_mmio_r(d, R_USEDCNT) < arg[i] { return 1 } } 142 i=i+1 143 } 144 if (dev_mmio_r(d, R_STATUS) & ST_DRIVER_OK)!=0 { return 2 } 145 return 1 146} 147func count_unsafe(op: *i64, n: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { if op_safe(op[i])==0 { c=c+1 } i=i+1 } return c } 148 149func main() -> i64 { 150 g_puts("nx_driver_gen (AUTO-GENERATE a virtio-mmio driver from a discovered device + VALIDATE it drives the device)\n" as *u8) 151 var pass: i64=0; var total: i64=0 152 let op: *i64 = sys_mmap(32*8) as *i64; let arg: *i64 = sys_mmap(32*8) as *i64 153 154 // T1: generate a virtio-blk driver, run it -> device reaches DRIVER_OK 155 let blk: *i64 = dev_new(DEV_BLK) 156 let n1: i64 = gen_driver(DEV_BLK, op, arg) 157 let r1: i64 = run_driver(blk, op, arg, n1) 158 let st1: i64 = dev_mmio_r(blk, R_STATUS) 159 var t1: i64=0; if r1==2 { if st1==(ST_ACK|ST_DRIVER|ST_FEATURES_OK|ST_DRIVER_OK) { t1=1 } } 160 g_puts(" T1 virtio-blk: generated "); g_pn(n1); g_puts("-op driver; run="); g_pn(r1); g_puts(" (2=OK); device STATUS="); g_hex(st1); g_puts(" (0xf=ACK|DRIVER|FEAT_OK|DRIVER_OK)\n" as *u8) 161 pass=pass+ck("T1: generate a virtio-blk driver -> the virtio handshake drives the device to DRIVER_OK" as *u8, t1); total=total+1 162 163 // T2: the request completed -- device processed the virtqueue (used ring bumped, last cmd = the blk read) 164 let used: i64 = dev_mmio_r(blk, R_USEDCNT); let lastcmd: i64 = dev_mmio_r(blk, R_LASTCMD) 165 var t2: i64=0; if used>=1 { if lastcmd==dev_cmd(DEV_BLK) { t2=1 } } 166 g_puts(" T2 request: used-ring count="); g_pn(used); g_puts(" last-cmd="); g_hex(lastcmd); g_puts(" (0x101=blk read-sector) -> completed\n" as *u8) 167 pass=pass+ck("T2: the driver submits a request -> device processes the virtqueue -> used ring updated (end-to-end I/O)" as *u8, t2); total=total+1 168 169 // T3: different DeviceIDs generate DIFFERENT drivers, each reaching DRIVER_OK 170 let con: *i64 = dev_new(DEV_CONSOLE); let n_con: i64 = gen_driver(DEV_CONSOLE, op, arg); let r_con: i64 = run_driver(con, op, arg, n_con); let cmd_con: i64 = dev_mmio_r(con, R_LASTCMD) 171 let rng: *i64 = dev_new(DEV_RNG); let n_rng: i64 = gen_driver(DEV_RNG, op, arg); let r_rng: i64 = run_driver(rng, op, arg, n_rng); let cmd_rng: i64 = dev_mmio_r(rng, R_LASTCMD) 172 var t3: i64=0; if r_con==2 { if r_rng==2 { if cmd_con==dev_cmd(DEV_CONSOLE) { if cmd_rng==dev_cmd(DEV_RNG) { if cmd_con!=cmd_rng { t3=1 } } } } } 173 g_puts(" T3 per-device generation: console cmd="); g_hex(cmd_con); g_puts(" (run="); g_pn(r_con); g_puts(") rng cmd="); g_hex(cmd_rng); g_puts(" (run="); g_pn(r_rng); g_puts(") -- distinct, both DRIVER_OK\n" as *u8) 174 pass=pass+ck("T3: a NEW DeviceID generates a NEW driver (blk/console/rng distinct commands + queue sizes), all reach DRIVER_OK" as *u8, t3); total=total+1 175 176 // T4 teeth: a normal driver has 0 firmware-write ops; injecting one is REFUSED by the executor 177 let unsafe_norm: i64 = count_unsafe(op, n_rng) 178 op[n_rng]=OP_FW_WRITE; arg[n_rng]=0xDEAD // splice a firmware-write op into the stream 179 let dev4: *i64 = dev_new(DEV_BLK) 180 let r4: i64 = run_driver(dev4, op, arg, n_rng+1) 181 var t4: i64=0; if unsafe_norm==0 { if op_safe(OP_FW_WRITE)==0 { if r4==(0-1) { t4=1 } } } 182 g_puts(" T4 never-brick: fw-write ops in a normal driver="); g_pn(unsafe_norm); g_puts(" (0); spliced-fw-write run="); g_pn(r4); g_puts(" (-1=REFUSED)\n" as *u8) 183 pass=pass+ck("T4 (teeth, NEVER-BRICK): the generated driver has 0 firmware-write ops; an injected one is REFUSED" as *u8, t4); total=total+1 184 185 // T5: FAILED path -- a device that rejects FEATURES_OK -> driver aborts cleanly; + determinism 186 let devx: *i64 = dev_new(DEV_BLK); let nx: i64 = gen_driver(DEV_BLK, op, arg); let rx: i64 = run_driver_reject(devx, op, arg, nx) 187 let stx: i64 = dev_mmio_r(devx, R_STATUS) 188 // determinism: regenerate + rerun blk -> same result 189 let devd: *i64 = dev_new(DEV_BLK); let nd: i64 = gen_driver(DEV_BLK, op, arg); let rd: i64 = run_driver(devd, op, arg, nd) 190 var t5: i64=0; if rx==1 { if (stx & ST_FAILED)!=0 { if (stx & ST_DRIVER_OK)==0 { if rd==2 { if nd==n1 { t5=1 } } } } } 191 g_puts(" T5 FAILED path: feature-reject run="); g_pn(rx); g_puts(" (1=clean abort) STATUS="); g_hex(stx); g_puts(" (FAILED set, DRIVER_OK clear); determinism rerun="); g_pn(rd); g_puts("\n" as *u8) 192 pass=pass+ck("T5: a device rejecting FEATURES_OK -> driver aborts cleanly (FAILED, no hang) + deterministic generation" as *u8, t5); total=total+1 193 194 var okall: i64=0; if pass==total { okall=1 } 195 g_puts("---- nx_driver_gen: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 196 if okall==1 { 197 let logf: i64=sys_openat_append("knowledge/status/driver_gen.log" as *u8, 420) 198 if logf>=0 { let z: i64=sys_write(logf,"NXDRIVERGEN GREEN: auto-generate a virtio-mmio driver from a discovered DeviceID -> real handshake to DRIVER_OK + virtqueue request completes; per-device generation; NEVER-BRICK fw-write refused; FAILED path clean\n" as *u8,205); sys_close(logf) } 199 g_puts("verdict=GREEN (auto-generate device drivers: a discovered DeviceID -> a real virtio-mmio driver that drives the device to DRIVER_OK + completes a request; never-brick by construction)\n" as *u8); sys_exit(0); return 0 200 } 201 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 202}