code wiki / _hdl_build / nx_rv64_mmu_oracle.nx

nx_rv64_mmu_oracle.nx source

↩ module page · 111 lines · 9067 B

1// nx_rv64_mmu_oracle.nx -- ROADMAP R2 (privilege, part 3): validate the golden sim's Sv39 MMU TRANSLATION -- the OS's 2// virtual-memory foundation -- against QEMU. The sim's nx_sv39_walk supports gigapage/megapage/4K leaves; this uses 3// GIGAPAGE (level-2 leaf) mappings for a simple 1-table setup. M-mode builds a root page table with 3 gigapage PTEs: 4// [0] identity VA 0x00000000-0x3FFFFFFF -> PA identity (covers UART 0x10000000 + finisher 0x100000) 5// [1] REMAP VA 0x40000000-0x7FFFFFFF -> PA 0x80000000-0xBFFFFFFF (the translation under test) 6// [2] identity VA 0x80000000-0xBFFFFFFF -> PA identity (code + RAM) 7// sets satp (Sv39, root@0x80005000), PMP allow-all, drops to S-mode with paging ON. S-mode stores 0xC3 through the 8// REMAPPED VA 0x40003000 (-> PA 0x80003000), reads it back through the IDENTITY VA 0x80003000 (-> PA 0x80003000) -- 9// if the walk translates both correctly they alias the same physical byte -> 0xC3. expect UART = c3 2a. 10// expect_exit: 0 NEVER-BRICK: simulation, writes 0 hardware state. 11import "nx_syscalls.nx" 12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 13import "nishi_hdl_primitives.nx" 14import "rv64im_min_decoder.nx" 15import "rv64im_min_alu.nx" 16import "rv64im_min_regfile.nx" 17import "rv64im_min_csr.nx" 18import "rv64im_min_clint.nx" 19import "rv64im_min_uart.nx" 20import "rv64im_min_virtio.nx" 21import "rv64im_min_mmu.nx" 22import "rv64im_min_sim.nx" 23import "nx_rv64_asm.nx" 24const DMEM_MAGIC_4096: i64 = 4096 25const DMEM_MAGIC_10000000: i64 = 10000000 26 27func 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 } 28// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 29// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 30// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 31// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 32func g_pn(v: i64) -> i64 { nxi_out(v); return 0 } 33func 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 } 34func 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 } 35 36const DMEM_BASE: i64 = 0x80000000 37const DMEM_SIZE: i64 = 65536 38const CSR_PMPCFG0: i64 = 0x3A0 39const CSR_PMPADDR0: i64 = 0x3B0 40const CSR_SATP: i64 = 0x180 41const CSR_MSTATUS: i64 = 0x300 42const CSR_MEPC: i64 = 0x341 43 44func main() -> i64 { 45 g_puts("nx_rv64_mmu_oracle (R2: Sv39 gigapage translation -- identity + REMAP -- on the golden sim, for QEMU diff)\n" as *u8) 46 var pass: i64=0; var total: i64=0 47 let code: *u8=sys_mmap(DMEM_MAGIC_4096); let po: *i64=sys_mmap(8) as *i64; po[0]=0 48 po[0]=ra_put(code, po[0], ra_u(0x37, 1, 0x10000)); po[0]=ra_put(code, po[0], ra_u(0x37, 2, 0x100)) 49 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)) 50 // PMP allow-all 51 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 5, 0, 0-1)); po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 5, CSR_PMPADDR0)) 52 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 6, 0, 0x1F)); po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 6, CSR_PMPCFG0)) 53 // PT base x20 = 0x80005000 (lui sign-extends -> mask) 54 po[0]=ra_put(code, po[0], ra_u(0x37, 20, 0x80005)); po[0]=ra_put(code, po[0], ra_i(0x13, 1, 20, 20, 32)); po[0]=ra_put(code, po[0], ra_i(0x13, 5, 20, 20, 32)) 55 // PTE[0] = 0xCF (identity gig0, V|R|W|X|A|D) 56 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 5, 0, 0xCF)); po[0]=ra_put(code, po[0], ra_s(0x23, 3, 20, 5, 0)) // sd x5, 0(x20) 57 // PTE[1] = PTE[2] = 0x200000CF (PPN 0x80000 -> PA 0x80000000, gigapage RWX+A+D) 58 po[0]=ra_put(code, po[0], ra_u(0x37, 6, 0x20000)); po[0]=ra_put(code, po[0], ra_i(0x13, 0, 6, 6, 0xCF)) 59 po[0]=ra_put(code, po[0], ra_s(0x23, 3, 20, 6, 8)); po[0]=ra_put(code, po[0], ra_s(0x23, 3, 20, 6, 16)) // sd x6, 8/16(x20) 60 // satp = (8<<60) | 0x80005 (Sv39, root PPN) 61 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 21, 0, 8)); po[0]=ra_put(code, po[0], ra_i(0x13, 1, 21, 21, 60)) // x21 = 8<<60 62 po[0]=ra_put(code, po[0], ra_u(0x37, 7, 0x80)); po[0]=ra_put(code, po[0], ra_i(0x13, 0, 7, 7, 5)) // x7 = 0x80005 63 po[0]=ra_put(code, po[0], ra_r(0x33, 6, 0, 21, 21, 7)) // or x21, x21, x7 64 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 21, CSR_SATP)) // csrrw x0, satp, x21 -> Sv39 ON 65 // MPP=S deterministic (build 0x1000 via shift; never addi 0x800/0x1000) 66 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 9, 0, 1)); po[0]=ra_put(code, po[0], ra_i(0x13, 1, 9, 9, 12)) 67 po[0]=ra_put(code, po[0], ra_i(0x73, 3, 0, 9, CSR_MSTATUS)); po[0]=ra_put(code, po[0], ra_i(0x13, 5, 9, 9, 1)) 68 po[0]=ra_put(code, po[0], ra_i(0x73, 3, 0, 9, CSR_MSTATUS)); po[0]=ra_put(code, po[0], ra_i(0x73, 2, 0, 9, CSR_MSTATUS)) 69 // mepc = s_entry; mret -> S-mode (paging ON) 70 let B: i64 = po[0]; po[0]=ra_put(code, po[0], ra_u(0x17, 10, 0)) 71 let addi10: i64 = po[0]; po[0]=ra_put(code, po[0], ra_i(0x13, 0, 10, 10, 0)) 72 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 10, CSR_MEPC)); po[0]=ra_put(code, po[0], ra_i(0x73, 0, 0, 0, 0x302)) 73 let s_entry: i64 = po[0] // S-mode, paging ON 74 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 23, 0, 0xC3)) // addi x23,x0,0xC3 75 po[0]=ra_put(code, po[0], ra_u(0x37, 22, 0x40003)) // lui x22, 0x40003 -> 0x40003000 (VA remap gig1, no sign ext) 76 po[0]=ra_put(code, po[0], ra_s(0x23, 0, 22, 23, 0)) // sb x23, 0(x22) store via REMAPPED VA -> PA 0x80003000 77 po[0]=ra_put(code, po[0], ra_u(0x37, 24, 0x80003)); po[0]=ra_put(code, po[0], ra_i(0x13, 1, 24, 24, 32)); po[0]=ra_put(code, po[0], ra_i(0x13, 5, 24, 24, 32)) // x24 = 0x80003000 (VA identity gig2) 78 po[0]=ra_put(code, po[0], ra_i(0x03, 4, 25, 24, 0)) // lbu x25, 0(x24) load via IDENTITY VA -> PA 0x80003000 -> 0xC3 79 po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 25, 0)) // sb x25, 0(x1) emit (0xC3 if MMU translates) 80 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 26, 0, 0x2A)); po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 26, 0)) // sentinel 81 po[0]=ra_put(code, po[0], ra_s(0x23, 2, 2, 3, 0)) // sw x3, 0(x2) finisher halt (VA 0x100000 identity) 82 po[0]=ra_put(code, po[0], ra_j(0, 0)) // j . spin (async shutdown safety) 83 let nb: i64 = po[0] 84 ra_put(code, addi10, ra_i(0x13, 0, 10, 10, s_entry - B)) // patch mepc-addr addi 85 86 g_puts(" emitted "); g_pn(nb/4); g_puts(" instr; s_entry@"); g_pn(s_entry); g_puts("\n" as *u8) 87 88 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 89 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 90 let mem: *u8=sys_mmap(DMEM_SIZE); let tx_buf: *u8=sys_mmap(256) 91 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile 92 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(128) as *NxRv64imSim 93 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) 94 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0) 95 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 } 96 nx_rv64im_sim_run(sim, DMEM_MAGIC_10000000) 97 let n: i64=nx_uart_tx_count(uart) 98 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) 99 g_puts(" expected : c3 2a (0xC3 stored via REMAP VA 0x40003000, read via IDENTITY VA 0x80003000 -> same PA)\n" as *u8) 100 101 var t1: i64=0; if n==2 { if tx_buf[0]==(0xc3 as u8) { if tx_buf[1]==(0x2a as u8) { t1=1 } } } 102 pass=pass+ck("T1: golden sim Sv39 translates identity + REMAP gigapages -> store/load alias the same PA -> UART = c3 2a" as *u8, t1); total=total+1 103 let fd: i64=sys_openat_wr("knowledge/hw/mmu.bin" as *u8, 420) 104 var t2: i64=0; if fd>=0 { let wn: i64=sys_write(fd, code, nb); sys_close(fd); if wn==nb { t2=1 } } 105 pass=pass+ck("T2: flat binary written to knowledge/hw/mmu.bin for the QEMU cross-check" as *u8, t2); total=total+1 106 107 var okall: i64=0; if pass==total { okall=1 } 108 g_puts("---- nx_rv64_mmu_oracle: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 109 if okall==1 { g_puts("verdict=GREEN (golden-sim Sv39 gigapage translation bytes emitted + binary written; run in qemu-system-riscv64 and diff)\n" as *u8); sys_exit(0); return 0 } 110 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 111}