code wiki / _hdl_build / nx_rv64_sdeleg_oracle.nx
nx_rv64_sdeleg_oracle.nx source
↩ module page · 117 lines · 9344 B
1// nx_rv64_sdeleg_oracle.nx -- ROADMAP R2 (privilege, part 2): validate the golden sim's S-MODE TRAP DELEGATION -- the
2// REAL kernel trap path -- against QEMU. M-mode sets up PMP allow-all (harmless no-op in the sim; lets S-mode touch
3// memory in QEMU), delegates ecall-from-S (medeleg bit 9) to S-mode, sets MPP=S DETERMINISTICALLY (clear-then-set:
4// the sim resets MPP=M but QEMU resets MPP=U), mret's DOWN to S-mode. In S-mode an ecall (cause 9) is DELEGATED to
5// stvec (the S-handler), which reads scause(=9), bumps sepc, sret's back. Then S-mode emits scause + sentinel + halts.
6// If sim==QEMU, the delegation path (medeleg/stvec/scause/sepc/sstatus.SPP/sret) is validated vs the world reference.
7// expect UART = 09 2a. expect_exit: 0 NEVER-BRICK: simulation, writes 0 hardware state.
8import "nx_syscalls.nx"
9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
10import "nishi_hdl_primitives.nx"
11import "rv64im_min_decoder.nx"
12import "rv64im_min_alu.nx"
13import "rv64im_min_regfile.nx"
14import "rv64im_min_csr.nx"
15import "rv64im_min_clint.nx"
16import "rv64im_min_uart.nx"
17import "rv64im_min_virtio.nx"
18import "rv64im_min_sim.nx"
19import "nx_rv64_asm.nx"
20const DMEM_MAGIC_4096: i64 = 4096
21const DMEM_MAGIC_10000000: i64 = 10000000
22
23func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
24// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
25// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
26// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
27// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
28func g_pn(v: i64) -> i64 { nxi_out(v); return 0 }
29func g_hx(v: i64) -> i64 { let b: *u8=sys_mmap(4); let n0: i64=(v>>4)&15; let n1: i64=v&15; if n0<10 { b[0]=(48+n0) as u8 } else { b[0]=(87+n0) as u8 } if n1<10 { b[1]=(48+n1) as u8 } else { b[1]=(87+n1) as u8 } b[2]=32 as u8; sys_write(1,b,3); return 0 }
30func 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 }
31
32const DMEM_BASE: i64 = 0x80000000
33const DMEM_SIZE: i64 = 65536
34const CSR_PMPCFG0: i64 = 0x3A0
35const CSR_PMPADDR0: i64 = 0x3B0
36const CSR_MEDELEG: i64 = 0x302 // machine EXCEPTION delegation (0x303 is mideleg = INTERRUPT delegation -- ecall is an exception!)
37const CSR_MSTATUS: i64 = 0x300
38const CSR_MEPC: i64 = 0x341
39const CSR_STVEC: i64 = 0x105
40const CSR_SEPC: i64 = 0x141
41const CSR_SCAUSE: i64 = 0x142
42
43func main() -> i64 {
44 g_puts("nx_rv64_sdeleg_oracle (R2: S-mode trap DELEGATION -- the real kernel trap path -- on the golden sim, for QEMU diff)\n" as *u8)
45 var pass: i64=0; var total: i64=0
46 let code: *u8=sys_mmap(DMEM_MAGIC_4096); let po: *i64=sys_mmap(8) as *i64; po[0]=0
47 po[0]=ra_put(code, po[0], ra_u(0x37, 1, 0x10000)); po[0]=ra_put(code, po[0], ra_u(0x37, 2, 0x100))
48 po[0]=ra_put(code, po[0], ra_u(0x37, 3, 0x5)); po[0]=ra_put(code, po[0], ra_i(0x13, 0, 3, 3, 0x555))
49 // PMP allow-all (harmless no-op in sim; enables S-mode mem access in QEMU)
50 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 5, 0, 0-1)) // addi x5,x0,-1 (all ones)
51 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 5, CSR_PMPADDR0)) // csrrw x0, pmpaddr0, x5
52 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 6, 0, 0x1F)) // addi x6,x0,0x1F (NAPOT|RWX)
53 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 6, CSR_PMPCFG0)) // csrrw x0, pmpcfg0, x6
54 // delegate ecall-from-S (cause 9) to S-mode
55 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 7, 0, 0x200)) // addi x7,x0,0x200 (bit 9)
56 po[0]=ra_put(code, po[0], ra_i(0x73, 2, 0, 7, CSR_MEDELEG)) // csrrs x0, medeleg, x7
57 // MPP = S deterministically: clear bit12, clear bit11, set bit11 -> MPP=01. ⚠ 0x800/0x1000 EXCEED the 12-bit
58 // SIGNED addi immediate [-2048,2047] (0x800=2048 wraps to -2048!) -- BUILD them via SHIFT (1<<12, >>1), never addi.
59 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 9, 0, 1)) // addi x9,x0,1
60 po[0]=ra_put(code, po[0], ra_i(0x13, 1, 9, 9, 12)) // slli x9,x9,12 -> 0x1000 (bit 12)
61 po[0]=ra_put(code, po[0], ra_i(0x73, 3, 0, 9, CSR_MSTATUS)) // csrrc mstatus, x9 (clear bit 12)
62 po[0]=ra_put(code, po[0], ra_i(0x13, 5, 9, 9, 1)) // srli x9,x9,1 -> 0x800 (bit 11)
63 po[0]=ra_put(code, po[0], ra_i(0x73, 3, 0, 9, CSR_MSTATUS)) // csrrc mstatus, x9 (clear bit 11) -> MPP=00
64 po[0]=ra_put(code, po[0], ra_i(0x73, 2, 0, 9, CSR_MSTATUS)) // csrrs mstatus, x9 (set bit 11) -> MPP=01=S
65 // stvec = s_handler (auipc + patched addi)
66 let A: i64 = po[0]; po[0]=ra_put(code, po[0], ra_u(0x17, 8, 0)) // auipc x8, 0
67 let addi8: i64 = po[0]; po[0]=ra_put(code, po[0], ra_i(0x13, 0, 8, 8, 0)) // addi x8,x8,<H-A>
68 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 8, CSR_STVEC)) // csrrw x0, stvec, x8
69 // mepc = s_entry (auipc + patched addi); mret -> S-mode
70 let B: i64 = po[0]; po[0]=ra_put(code, po[0], ra_u(0x17, 10, 0)) // auipc x10, 0
71 let addi10: i64 = po[0]; po[0]=ra_put(code, po[0], ra_i(0x13, 0, 10, 10, 0)) // addi x10,x10,<E-B>
72 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 10, CSR_MEPC)) // csrrw x0, mepc, x10
73 po[0]=ra_put(code, po[0], ra_i(0x73, 0, 0, 0, 0x302)) // mret -> S-mode at s_entry
74 let s_entry: i64 = po[0] // s_entry (S-mode) = ecall
75 po[0]=ra_put(code, po[0], ra_i(0x73, 0, 0, 0, 0)) // ecall (from S -> cause 9, delegated)
76 // --- sret returns HERE (ecall+4), still S-mode; x11 = scause ---
77 po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 11, 0)) // sb x11, 0(x1) -> 0x09
78 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 13, 0, 0x2A)) // addi x13,x0,0x2A
79 po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 13, 0)) // sb x13, 0(x1) -> sentinel
80 po[0]=ra_put(code, po[0], ra_s(0x23, 2, 2, 3, 0)) // sw x3, 0(x2) finisher halt (S-mode, PMP allows)
81 po[0]=ra_put(code, po[0], ra_j(0, 0)) // j . (spin) -- QEMU shutdown is ASYNC; spin so it doesn't fall through
82 let s_handler: i64 = po[0] // S-mode delegated handler
83 po[0]=ra_put(code, po[0], ra_i(0x73, 2, 11, 0, CSR_SCAUSE)) // csrrs x11, scause, x0 -> x11=9
84 po[0]=ra_put(code, po[0], ra_i(0x73, 2, 14, 0, CSR_SEPC)) // csrrs x14, sepc, x0
85 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 14, 14, 4)) // addi x14,x14,4 (skip ecall)
86 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 14, CSR_SEPC)) // csrrw x0, sepc, x14
87 po[0]=ra_put(code, po[0], ra_i(0x73, 0, 0, 0, 0x102)) // sret -> ecall+4 in S-mode
88 let nb: i64 = po[0]
89 ra_put(code, addi8, ra_i(0x13, 0, 8, 8, s_handler - A)) // patch stvec-addr addi
90 ra_put(code, addi10, ra_i(0x13, 0, 10, 10, s_entry - B)) // patch mepc-addr addi
91
92 g_puts(" emitted "); g_pn(nb/4); g_puts(" instr; s_entry@"); g_pn(s_entry); g_puts(" s_handler@"); g_pn(s_handler); g_puts("\n" as *u8)
93
94 let rf_storage: *i64=sys_mmap(8*NX_RV64IM_RF_N_REGS) as *i64; let csr_storage: *i64=sys_mmap(8*NX_CSR_SLOT_N) as *i64
95 let clint_storage: *i64=sys_mmap(8*NX_CLINT_SLOT_N) as *i64; let uart_storage: *i64=sys_mmap(8*NX_UART_SLOT_N) as *i64
96 let mem: *u8=sys_mmap(DMEM_SIZE); let tx_buf: *u8=sys_mmap(256)
97 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile
98 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(128) as *NxRv64imSim
99 nx_rv64im_rf_init(rf, rf_storage); nx_rv64im_csr_init(csr, csr_storage, 0); nx_clint_init(clint, clint_storage); nx_uart_init(uart, uart_storage, tx_buf, 256)
100 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0)
101 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 }
102 nx_rv64im_sim_run(sim, DMEM_MAGIC_10000000)
103 let n: i64=nx_uart_tx_count(uart)
104 g_puts(" golden sim UART ("); g_pn(n); g_puts(" bytes): "); i=0; while i<n { g_hx(tx_buf[i] as i64); i=i+1 } g_puts("\n" as *u8)
105 g_puts(" expected : 09 2a (scause=0x09 ecall-from-S DELEGATED to S-mode handler, sentinel)\n" as *u8)
106
107 var t1: i64=0; if n==2 { if tx_buf[0]==(0x09 as u8) { if tx_buf[1]==(0x2a as u8) { t1=1 } } }
108 pass=pass+ck("T1: golden sim delegates ecall-from-S to the S-mode handler (scause=9) + sret round-trip -> UART = 09 2a" as *u8, t1); total=total+1
109 let fd: i64=sys_openat_wr("knowledge/hw/sdeleg.bin" as *u8, 420)
110 var t2: i64=0; if fd>=0 { let wn: i64=sys_write(fd, code, nb); sys_close(fd); if wn==nb { t2=1 } }
111 pass=pass+ck("T2: flat binary written to knowledge/hw/sdeleg.bin for the QEMU cross-check" as *u8, t2); total=total+1
112
113 var okall: i64=0; if pass==total { okall=1 }
114 g_puts("---- nx_rv64_sdeleg_oracle: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8)
115 if okall==1 { g_puts("verdict=GREEN (golden-sim S-mode delegation bytes emitted + binary written; run in qemu-system-riscv64 and diff)\n" as *u8); sys_exit(0); return 0 }
116 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
117}