code wiki / _hdl_build / nx_drv_proto_emit.nx
nx_drv_proto_emit.nx
buildroot/runtime/_hdl_build/nx_drv_proto_emit.nx
about
nx_drv_proto_emit.nx -- DRIVER-PROTOCOL-FROM-SPEC (X-DRV-W1), the GENERIC driver emitter.
The thesis (driver-as-shape-composition): a driver SPEC carries the device register map
(base + offsets) AND -- crucially -- the PROTOCOL STEP-SEQUENCE itself as DATA: an op-list
(the STATE_MACHINE shape). This organ WALKS that op-list and SYNTHESIZES a bare-metal rv64
driver image whose control flow IS the spec's sequence -- so a DIFFERENT protocol sequence is
a DIFFERENT spec -> a DIFFERENT driver, with ZERO emitter changes. That is the emitter-of-
emitters keystone (X-AUT-006c/e) COMPOSED into the device-driver layer (X-DRV-W0 HWMAP gives
the bind side; this gives the protocol side).
CONTRAST with nx_virtio_hs_emit: that emitter parameterises the virtio register offsets/values
from a spec but HARD-CODES the protocol sequence (identity->ACK->FEAT->VQ->DESC->USED->...).
Here the sequence is DATA, so virtio-blk is merely ONE spec instance (the last-mile/benchmark
transport per the nishi-ecosystem-only law); a virtio-net handshake, or a future Nishi-native
device protocol, is simply a different op-list. The gate proves this by emitting TWO distinct
op-lists from the ONE binary and running BOTH against the real devices on the sovereign emu.
SPEC FORMAT (line-based, '#'=comment):
out <path> where to write the flat image (and <path>.gold)
base <hex> device MMIO base (0x1000-aligned -> lui t2)
op verify <regoff> <const> LW reg ; li const ; bne -> fail (signed read-back)
op verifyu <regoff> <const> LWU reg ; li const ; bne -> fail (unsigned/address read-back)
op write <regoff> <val> li val ; SW reg (also: notify = a write)
op notify <regoff> <val> alias for write (device kick)
op read <regoff> LW reg (exercise a RO register, value discarded)
op checkbit <regoff> <mask> LW reg ; li mask ; and ; beq x0 -> fail (bit-stuck proof)
op setbase <addr> li32u t5 = addr (membase for the following memstore ops)
op memstore <memoff> <f3> <val> li val ; S[f3] val, memoff(t5) (f3: 1=SH 2=SW; ring/desc lay)
op emit <literal-to-eol> write the literal bytes over the UART (the transcript token)
The image always ends with the SiFive finisher (clean halt). Any verify/checkbit that fails
BRANCHES PAST the rest of the op-list to the finisher, so the transcript loses its tail --
that is the tamper handle the gate exploits.
nx_drv_proto_emit <specpath> -> the spec's `out` (flat rv64 image) + <out>.gold (golden
transcript = the concatenation of the emit literals, in op order).
VERDICT log -> knowledge/status/driver_spec.log. Sovereign: syscalls only, no gcc/.sh.
license_tier: ORIGINAL
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 39 | const DP_MAGIC_16384: i64 = 16384 |
| 42 | const DP_UART: i64 = 0x10000000 // NS16550A THR (write a byte = transmit) |
| 43 | const DP_FIN: i64 = 0x100000 // SiFive test finisher (write to exit) |
| 44 | const DP_PASS: i64 = 0x5555 // FINISHER_PASS low half -> clean halt |
| 46 | const RV_X0: i64 = 0 |
| 47 | const RV_T0: i64 = 5 // UART base |
| 48 | const RV_T1: i64 = 6 // scratch / transcript byte / write value |
| 49 | const RV_T2: i64 = 7 // device MMIO base |
| 50 | const RV_T3: i64 = 28 // loaded register value (actual) |
| 51 | const RV_T4: i64 = 29 // expected constant |
| 52 | const RV_T5: i64 = 30 // finisher base / membase (set by setbase) |
| 55 | const OP_VERIFY: i64 = 1 // a0=regoff a1=const (signed LW) |
| 56 | const OP_VERIFYU: i64 = 2 // a0=regoff a1=const (unsigned LWU) |
| 57 | const OP_WRITE: i64 = 3 // a0=regoff a1=val |
| 58 | const OP_READ: i64 = 4 // a0=regoff |
| 59 | const OP_CHECKBIT: i64 = 5 // a0=regoff a1=mask |
| 60 | const OP_SETBASE: i64 = 6 // a0=addr (li32u t5) |
| 61 | const OP_MEMSTORE: i64 = 7 // a0=memoff a1=f3 a2=val |
| 62 | const OP_EMIT: i64 = 8 // a0=tail-offset a1=len |
| 64 | const DP_MAXOP: i64 = 256 |
| 65 | const DP_TAILCAP: i64 = 2048 |
functions
| 68 | func dp_lui(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x37 } |
| 69 | func dp_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 } |
| 71 | func dp_load(rd: i64, rs1: i64, f3: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x03 } |
| 73 | func dp_store(rs2: i64, rs1: i64, f3: i64, imm: i64) -> i64 |
| 79 | func dp_branch(rs1: i64, rs2: i64, f3: i64, imm: i64) -> i64 |
| 87 | func dp_jal(rd: i64, imm: i64) -> i64 called by 1: dp_emit_finisher |
| 95 | func dp_and(rd: i64, rs1: i64, rs2: i64) -> i64 { return (rs2 << 20) | (rs1 << 15) | (7 << 12) | (rd << 7) | 0x33 } called by 1: dp_op_checkbit |
| 97 | func dp_shift(rd: i64, rs1: i64, f3: i64, shamt: i64) -> i64 { return ((shamt & 0x3f) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x13 } called by 1: dp_li32u |
| 99 | func dp_w32(buf: *u8, off: i64, w: i64) -> i64 |
| 110 | func dp_li32(buf: *u8, off: i64, rd: i64, val: i64) -> i64 |
| 121 | func dp_li32u(buf: *u8, off: i64, rd: i64, val: i64) -> i64 |
| 129 | func dp_emit_bytes(buf: *u8, off: i64, s: *u8, n: i64) -> i64 |
| 142 | func dp_op_verify(buf: *u8, off: i64, regoff: i64, expected: i64, f3sel: i64, fail_off: i64) -> i64 |
| 151 | func dp_op_write(buf: *u8, off: i64, regoff: i64, val: i64) -> i64 |
| 158 | func dp_op_read(buf: *u8, off: i64, regoff: i64) -> i64 |
| 163 | func dp_op_checkbit(buf: *u8, off: i64, regoff: i64, mask: i64, fail_off: i64) -> i64 |
| 173 | func dp_op_memstore(buf: *u8, off: i64, memoff: i64, f3: i64, val: i64) -> i64 |
| 180 | func dp_emit_finisher(buf: *u8, off: i64) -> i64 |
| 191 | func dp_emit_image(buf: *u8, base: i64, opc: *i64, oa0: *i64, oa1: *i64, oa2: *i64, nop: i64, tail: *u8, fail_off: i64) -> i64 |
| 213 | func dp_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 214 | func dp_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 } |
| 215 | func dp_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 } |
| 219 | func dp_parse_num(buf: *u8, p: i64, le: i64, endp: *i64) -> i64 |
| 251 | func dp_num_field(buf: *u8, ls: i64, le: i64, key: *u8, out: *i64) -> i64 |
| 264 | func dp_str_field(buf: *u8, ls: i64, le: i64, key: *u8, out: *u8) -> i64 called by 1: main |
| 279 | func dp_kw(buf: *u8, p: i64, le: i64, kw: *u8) -> i64 called by 1: main |
| 291 | func dp_log(name: *u8, bytes: i64, golden: *u8, verdict: *u8) -> i64 |
| 302 | func main(argc: i64, argv: *i64) -> i64 |