code wiki / _hdl_build / _trap_syscall_gate.nx

_trap_syscall_gate.nx source

↩ module page · 216 lines · 10145 B

1// _trap_syscall_gate.nx -- the K-R1 gate (kernel-up ladder rung 1). Drives the full 2// author->trap->syscall chain with NO mocks: runs the REAL nx_trap_syscall_emit (the 3// team AUTHORS the rv64 trap+syscall image + the TABLE-COMPUTED golden transcript from 4// the spec), then RUNS the image on the SOVEREIGN rv64 emulator (rv64im_min_sim -- the 5// PRIMARY, gating lane: Nishi owns the runtime) and asserts the captured serial 6// transcript CONTAINS the emitter's golden (banner + each syscall's emit byte, in order) 7// AND the sovereign emu reports a clean SiFive-finisher halt (BOOTSOV verdict=GREEN). 8// Then it runs the SAME image on qemu-system-riscv64 -machine virt (the declared 9// hardware-alignment lane, NOT the runtime -- the diff-lane signal) and asserts the 10// lanes AGREE on the transcript. Finally a TAMPER test: corrupt one syscall's a7 in the 11// image -> that handler never fires -> the transcript loses a byte -> the gate must go 12// RED (proves the gate bites, not a rubber stamp). Evidence -> knowledge/status/ 13// trap_syscall.log (TRAPGATE row; the queue row's ||MARK= reads it). Sovereign 14// orchestration (fork/dup3/execve/wait4). license_tier: ORIGINAL 15import "nx_syscalls.nx" 16 17func g_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 18func g_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 } 19func g_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 } 20 21// run nx_trap_syscall_emit <spec>; return child wait status (0 = ok) 22func g_run_emit(spec: *u8) -> i64 { 23 let pid: i64 = sys_fork() 24 if pid == 0 { 25 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 26 if dn >= 0 { sys_dup3(dn, 1, 0) } 27 let argv: *i64 = sys_mmap(32) as *i64 28 argv[0] = "_offc/nx_trap_syscall_emit.elf" as *u8 as i64 29 argv[1] = spec as i64 30 argv[2] = 0 31 let envp: *i64 = sys_mmap(16) as *i64 32 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 33 envp[1] = 0 34 sys_execve("_offc/nx_trap_syscall_emit.elf" as *u8, argv, envp) 35 sys_exit(127) 36 } 37 let st: *i64 = sys_mmap(16) as *i64 38 sys_wait4(pid, st, 0) 39 return st[0] 40} 41 42// run qemu-system-riscv64 on binpath; serial -> outpath; return child wait status 43func g_run_qemu(binpath: *u8, outpath: *u8) -> i64 { 44 let pid: i64 = sys_fork() 45 if pid == 0 { 46 let ofd: i64 = sys_openat_wr(outpath, 0x1a4) 47 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 48 let argv: *i64 = sys_mmap(64) as *i64 49 argv[0] = "/usr/bin/qemu-system-riscv64" as *u8 as i64 50 argv[1] = "-machine" as *u8 as i64 51 argv[2] = "virt" as *u8 as i64 52 argv[3] = "-nographic" as *u8 as i64 53 argv[4] = "-bios" as *u8 as i64 54 argv[5] = binpath as i64 55 argv[6] = 0 56 let envp: *i64 = sys_mmap(16) as *i64 57 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 58 envp[1] = 0 59 sys_execve("/usr/bin/qemu-system-riscv64" as *u8, argv, envp) 60 sys_exit(127) 61 } 62 let st: *i64 = sys_mmap(16) as *i64 63 sys_wait4(pid, st, 0) 64 return st[0] 65} 66 67// run the SOVEREIGN rv64 emulator on binpath; serial -> outpath; return wait status 68func g_run_sov(binpath: *u8, outpath: *u8) -> i64 { 69 let pid: i64 = sys_fork() 70 if pid == 0 { 71 let ofd: i64 = sys_openat_wr(outpath, 0x1a4) 72 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 73 let argv: *i64 = sys_mmap(32) as *i64 74 argv[0] = "_offc/nx_boot_run_sov.elf" as *u8 as i64 75 argv[1] = binpath as i64 76 argv[2] = 0 77 let envp: *i64 = sys_mmap(16) as *i64 78 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 79 envp[1] = 0 80 sys_execve("_offc/nx_boot_run_sov.elf" as *u8, argv, envp) 81 sys_exit(127) 82 } 83 let st: *i64 = sys_mmap(16) as *i64 84 sys_wait4(pid, st, 0) 85 return st[0] 86} 87 88// read whole file into buf (cap-1 max); return byte count (0 if absent) 89func g_read(path: *u8, buf: *u8, cap: i64) -> i64 { 90 let fd: i64 = sys_openat_rd(path) 91 if fd < 0 { return 0 } 92 var n: i64 = 0 93 var go: i64 = 1 94 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 } } 95 sys_close(fd) 96 return n 97} 98 99// does the file at path contain pat (length pl)? 1/0 100func g_buf_has(buf: *u8, n: i64, pat: *u8, pl: i64) -> i64 { 101 if pl <= 0 { return 0 } 102 var i: i64 = 0 103 while i + pl <= n { 104 var k: i64 = 0 105 var hit: i64 = 1 106 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 107 if hit == 1 { return 1 } 108 i = i + 1 109 } 110 return 0 111} 112 113func g_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 114 115// file path -> contains pat? reads from disk. 1/0 116func g_file_has(path: *u8, pat: *u8) -> i64 { 117 let buf: *u8 = sys_mmap(65536) 118 let n: i64 = g_read(path, buf, 65536) 119 return g_buf_has(buf, n, pat, g_strlen(pat)) 120} 121 122func main() -> i64 { 123 let spec: *u8 = "knowledge/specs/trap_syscall_virt.spec" as *u8 124 let binpath: *u8 = "runtime/_hdl_build/_trap_syscall_virt.bin" as *u8 125 let goldpath: *u8 = "runtime/_hdl_build/_trap_syscall_virt.bin.gold" as *u8 126 let tamper_bin: *u8 = "/tmp/_trapgate_tamper.bin" as *u8 127 let sov_serial: *u8 = "/tmp/_trapgate_sov.txt" as *u8 128 let qemu_serial: *u8 = "/tmp/_trapgate_qemu.txt" as *u8 129 let sov_tamper: *u8 = "/tmp/_trapgate_sov_tamper.txt" as *u8 130 g_p("=== trap+syscall gate (K-R1: SOVEREIGN rv64 runtime + qemu alignment diff-lane) ===\n" as *u8) 131 132 let est: i64 = g_run_emit(spec) 133 let lfd: i64 = sys_openat_append("knowledge/status/trap_syscall.log" as *u8, 0x1a4) 134 if est != 0 { 135 g_p("TRAPGATE verdict=RED reason=emit-failed\n" as *u8) 136 if lfd >= 0 { g_fp(lfd, "TRAPGATE verdict=RED reason=emit-failed\n" as *u8); sys_close(lfd) } 137 sys_exit(1); return 1 138 } 139 140 // load the TABLE-COMPUTED golden transcript the emitter authored 141 let gold: *u8 = sys_mmap(512) 142 let gn: i64 = g_read(goldpath, gold, 512) 143 gold[gn] = 0 as u8 144 if gn <= 0 { 145 g_p("TRAPGATE verdict=RED reason=golden-missing\n" as *u8) 146 if lfd >= 0 { g_fp(lfd, "TRAPGATE verdict=RED reason=golden-missing\n" as *u8); sys_close(lfd) } 147 sys_exit(1); return 1 148 } 149 150 // PRIMARY: the Nishi sovereign rv64 emulator RUNS the image (Nishi owns the runtime) 151 let sst: i64 = g_run_sov(binpath, sov_serial) 152 let sbuf: *u8 = sys_mmap(65536) 153 let sbn: i64 = g_read(sov_serial, sbuf, 65536) 154 let trans_ok: i64 = g_buf_has(sbuf, sbn, gold, gn) // serial CONTAINS golden 155 let halt_ok: i64 = g_buf_has(sbuf, sbn, "BOOTSOV verdict=GREEN" as *u8, 21) 156 var sov_ok: i64 = 0 157 if sst == 0 { if trans_ok == 1 { if halt_ok == 1 { sov_ok = 1 } } } 158 159 // ALIGNMENT: qemu cross-check only (NOT the runtime -- the diff-lane signal). 160 // qemu's raw serial == the golden transcript exactly (no runner prefix). 161 let qst: i64 = g_run_qemu(binpath, qemu_serial) 162 let qbuf: *u8 = sys_mmap(65536) 163 let qbn: i64 = g_read(qemu_serial, qbuf, 65536) 164 let qtrans: i64 = g_buf_has(qbuf, qbn, gold, gn) 165 var align: i64 = 0 166 if qst == 0 { if qtrans == 1 { align = 1 } } 167 168 // TAMPER: corrupt one syscall's a7 -> its handler never fires -> transcript loses a 169 // byte -> the gate MUST go RED on the sovereign lane. We flip the a7 immediate of 170 // the first driver `addi a7,x0,64` (write). Image layout (table-known): boot is 171 // auipc+addi+csrrw+lui t0 (4 words=16B) then banner "K1\n" = 3*2 words (24B); the 172 // first driver addi is at byte 40. Corrupt its imm field (top byte of the word). 173 let ibuf: *u8 = sys_mmap(8192) 174 let ibn: i64 = g_read(binpath, ibuf, 8192) 175 var ti: i64 = 0 176 while ti < ibn { ibuf[ti] = ibuf[ti]; ti = ti + 1 } 177 // word at byte 40 = addi a7,x0,64 ; its imm[11:0] sits in bits [31:20] = bytes 42-43. 178 // Bump byte 43 to derange the syscall number to one with no handler row. 179 ibuf[40 + 3] = (ibuf[40 + 3] + 1) as u8 180 let tfd: i64 = sys_openat_wr(tamper_bin, 0x1a4) 181 if tfd >= 0 { sys_write(tfd, ibuf, ibn); sys_close(tfd) } 182 let tst: i64 = g_run_sov(tamper_bin, sov_tamper) 183 let tbuf: *u8 = sys_mmap(65536) 184 let tbn: i64 = g_read(sov_tamper, tbuf, 65536) 185 let tamper_trans: i64 = g_buf_has(tbuf, tbn, gold, gn) // should now MISS 186 var tamper_bites: i64 = 0 187 if tamper_trans == 0 { tamper_bites = 1 } // golden no longer present = gate bites 188 189 g_p(" sovereign_emu=" as *u8) 190 if sov_ok == 1 { g_p("GREEN(transcript==golden+clean-halt)" as *u8) } else { g_p("RED" as *u8) } 191 g_p(" qemu_align=" as *u8) 192 if align == 1 { g_p("yes" as *u8) } else { g_p("no" as *u8) } 193 g_p(" tamper_bites=" as *u8) 194 if tamper_bites == 1 { g_p("yes\n" as *u8) } else { g_p("no\n" as *u8) } 195 196 var pass: i64 = 0 197 if sov_ok == 1 { if align == 1 { if tamper_bites == 1 { pass = 1 } } } 198 199 if pass == 1 { 200 g_p("TRAPGATE verdict=GREEN (sovereign rv64 emu ran trap+8-syscall surface; transcript==golden; qemu cross-check AGREES; tamper REJECTED)\n" as *u8) 201 if lfd >= 0 { 202 g_fp(lfd, "TRAPGATE verdict=GREEN runtime=sovereign-emu transcript==golden align_qemu=yes tamper=rejected nsys=8 golden=" as *u8) 203 g_fp(lfd, gold) 204 g_fp(lfd, " epoch=" as *u8); g_fn(lfd, sys_now_realtime_sec()); g_fp(lfd, "\n" as *u8); sys_close(lfd) 205 } 206 sys_exit(0); return 0 207 } 208 g_p("TRAPGATE verdict=RED (sov_ok/align/tamper not all green)\n" as *u8) 209 if lfd >= 0 { 210 g_fp(lfd, "TRAPGATE verdict=RED sov_ok=" as *u8); g_fn(lfd, sov_ok) 211 g_fp(lfd, " align=" as *u8); g_fn(lfd, align) 212 g_fp(lfd, " tamper_bites=" as *u8); g_fn(lfd, tamper_bites); g_fp(lfd, "\n" as *u8); sys_close(lfd) 213 } 214 sys_exit(1) 215 return 1 216}