code wiki / _hdl_build / nx_4kpage_emit.nx

nx_4kpage_emit.nx source

↩ module page · 184 lines · 10353 B

1// nx_4kpage_emit.nx -- FULL 3-LEVEL Sv39 walk with a 4KB-page leaf (X-PAGE-4K-001). Closes the noted 2// gap "single gigapage leaf exercised (walk is general 3-level)": all prior tests used a level-2 gigapage 3// leaf (one table read). Here VA 0xC0009000 resolves through ALL THREE levels -- root[VPN2=3] -> L1 table 4// -> L1[VPN1=0] -> L0 table -> L0[VPN0=9] = a 4KB leaf -> PA -- the real page granularity + the prerequisite 5// for distinct per-process page tables (which need 4KB pages to differ within the 64KB emu RAM). 6// 7// Physical layout: code @0x80000000; root table @0x80001000 (ppn 0x80001); L1 @0x80002000; L0 @0x80003000; 8// data 4KB page @0x80004000 (sentinel). root[VPN2=2]=identity gigapage (so the S-mode code stays fetchable). 9// l1_pte (argv[1]) -- the L1 PTE (default 0x20000C01 = V, ->L0 ppn 0x80003). The gate tampers it to 10// 0x20000C00 (V=0) to prove the walk genuinely VALIDATES the intermediate level. 11// out-path (argv[2]). S-mode loads VA 0xC0009000 -> "K4OK" iff the 3-level walk reads the sentinel. 12// Sovereign, no gcc/.sh. license_tier: ORIGINAL 13import "nx_syscalls.nx" 14const K4_MAGIC_8192: i64 = 8192 15 16const K4_OUT: *u8 = "runtime/_hdl_build/_4kpage_virt.bin" 17const K4_GOLD: *u8 = "runtime/_hdl_build/_4kpage_virt.bin.gold" 18const K4_LOG: *u8 = "knowledge/status/paging.log" 19 20const K4_UART: i64 = 0x10000000 21const K4_FIN: i64 = 0x100000 22const K4_PASS: i64 = 0x5555 23const K4_MEM_BASE: i64 = 0x80000000 24const K4_CSR_SATP: i64 = 0x180 25const K4_CSR_MSTATUS: i64 = 0x300 26const K4_CSR_MEPC: i64 = 0x341 27const K4_MRET: i64 = 0x30200073 28const K4_MPP_S: i64 = 0x800 29 30const RV_X0: i64 = 0 31const RV_T0: i64 = 5 32const RV_T1: i64 = 6 33const RV_T2: i64 = 7 34const RV_T3: i64 = 28 35const RV_T4: i64 = 29 36const RV_T5: i64 = 30 37 38const K4_ROOT: i64 = 0x80001000 // root table (ppn 0x80001) 39const K4_L1: i64 = 0x80002000 // level-1 table 40const K4_L0: i64 = 0x80003000 // level-0 table 41const K4_DATA: i64 = 0x80004000 // 4KB data page (the leaf target) 42const K4_SENT_VAL: i64 = 0x5ECA1234 43const K4_VA: i64 = 0xC0009000 // VPN2=3, VPN1=0, VPN0=9 -> full 3-level walk 44const K4_SATP_PPN: i64 = 0x80001 // root >> 12 45const K4_SV39: i64 = 8 46const K4_CODE_PTE: i64 = 0x2000000F // root[VPN2=2] identity gigapage (V|R|W|X) for the S-mode code 47const K4_ROOT3_PTE: i64 = 0x20000801 // root[VPN2=3] non-leaf -> L1 (V, ppn 0x80002) 48const K4_L0_LEAF: i64 = 0x20001007 // L0[VPN0=9] leaf -> data (V|R|W, ppn 0x80004) 49 50func k4_lui(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x37 } 51func k4_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 } 52func k4_load(rd: i64, rs1: i64, f3: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x03 } 53func k4_store(rs2: i64, rs1: i64, f3: i64, imm: i64) -> i64 { 54 let hi: i64 = ((imm >> 5) & 0x7f) << 25 55 let lo: i64 = (imm & 0x1f) << 7 56 return hi | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | lo | 0x23 57} 58func k4_branch(rs1: i64, rs2: i64, f3: i64, imm: i64) -> i64 { 59 let b12: i64 = ((imm >> 12) & 0x1) << 31 60 let b11: i64 = ((imm >> 11) & 0x1) << 7 61 let b10_5: i64 = ((imm >> 5) & 0x3f) << 25 62 let b4_1: i64 = ((imm >> 1) & 0xf) << 8 63 return b12 | b10_5 | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | b4_1 | b11 | 0x63 64} 65func k4_jal(rd: i64, imm: i64) -> i64 { 66 let b20: i64 = ((imm >> 20) & 0x1) << 31 67 let b19_12: i64 = ((imm >> 12) & 0xff) << 12 68 let b11: i64 = ((imm >> 11) & 0x1) << 20 69 let b10_1: i64 = ((imm >> 1) & 0x3ff) << 21 70 return b20 | b10_1 | b11 | b19_12 | (rd << 7) | 0x6f 71} 72func k4_slli(rd: i64, rs1: i64, shamt: i64) -> i64 { return ((shamt & 0x3f) << 20) | (rs1 << 15) | (1 << 12) | (rd << 7) | 0x13 } 73func k4_srli(rd: i64, rs1: i64, shamt: i64) -> i64 { return ((shamt & 0x3f) << 20) | (rs1 << 15) | (5 << 12) | (rd << 7) | 0x13 } 74func k4_or(rd: i64, rs1: i64, rs2: i64) -> i64 { return (rs2 << 20) | (rs1 << 15) | (6 << 12) | (rd << 7) | 0x33 } 75func k4_csrrw(rd: i64, csr: i64, rs1: i64) -> i64 { return ((csr & 0xfff) << 20) | (rs1 << 15) | (1 << 12) | (rd << 7) | 0x73 } 76func k4_w32(buf: *u8, off: i64, w: i64) -> i64 { buf[off]=(w&0xff) as u8; buf[off+1]=((w>>8)&0xff) as u8; buf[off+2]=((w>>16)&0xff) as u8; buf[off+3]=((w>>24)&0xff) as u8; return off+4 } 77func k4_li32(buf: *u8, off: i64, rd: i64, val: i64) -> i64 { 78 var hi: i64 = (val >> 12) & 0xFFFFF 79 var lo: i64 = val & 0xFFF 80 if lo >= 0x800 { lo = lo - 0x1000; hi = (hi + 1) & 0xFFFFF } 81 var o: i64 = k4_w32(buf, off, k4_lui(rd, hi)) 82 o = k4_w32(buf, o, k4_addi(rd, rd, lo)) 83 return o 84} 85func k4_li32u(buf: *u8, off: i64, rd: i64, val: i64) -> i64 { 86 var o: i64 = k4_li32(buf, off, rd, val) 87 o = k4_w32(buf, o, k4_slli(rd, rd, 32)) 88 o = k4_w32(buf, o, k4_srli(rd, rd, 32)) 89 return o 90} 91func k4_emit_str(buf: *u8, off: i64, s: *u8, n: i64) -> i64 { 92 var o: i64 = off 93 var i: i64 = 0 94 while i < n { o = k4_w32(buf, o, k4_addi(RV_T1, RV_X0, s[i] as i64)); o = k4_w32(buf, o, k4_store(RV_T1, RV_T0, 0, 0)); i = i + 1 } 95 return o 96} 97func k4_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 98func k4_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 } 99 100// pos_out[0]=CONT (S-mode), [1]=FAIL, [2]=HALT 101func k4_emit_image(buf: *u8, cont_off: i64, fail_off: i64, halt_off: i64, pos_out: *i64, l1_pte: i64) -> i64 { 102 var o: i64 = 0 103 o = k4_w32(buf, o, k4_lui(RV_T0, K4_UART >> 12)) // t0 = UART 104 // ---- lay the 3-level page table (M-mode, Bare -> stores are physical) ---- 105 o = k4_li32u(buf, o, RV_T5, K4_ROOT) 106 o = k4_li32(buf, o, RV_T1, K4_CODE_PTE) 107 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0x10)) // root[VPN2=2] = identity code gigapage 108 o = k4_li32(buf, o, RV_T1, K4_ROOT3_PTE) 109 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0x18)) // root[VPN2=3] = non-leaf -> L1 110 o = k4_li32u(buf, o, RV_T5, K4_L1) 111 o = k4_li32(buf, o, RV_T1, l1_pte) 112 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0)) // L1[VPN1=0] = non-leaf -> L0 (tamper-able) 113 o = k4_li32u(buf, o, RV_T5, K4_L0) 114 o = k4_li32(buf, o, RV_T1, K4_L0_LEAF) 115 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0x48)) // L0[VPN0=9] = 4KB leaf -> data (offset 9*8) 116 o = k4_li32u(buf, o, RV_T5, K4_DATA) // sentinel at the data page 117 o = k4_li32(buf, o, RV_T1, K4_SENT_VAL) 118 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0)) 119 o = k4_li32(buf, o, RV_T1, K4_SATP_PPN) // satp = Sv39 | root_ppn -> translation ON 120 o = k4_w32(buf, o, k4_addi(RV_T2, RV_X0, K4_SV39)) 121 o = k4_w32(buf, o, k4_slli(RV_T2, RV_T2, 60)) 122 o = k4_w32(buf, o, k4_or(RV_T1, RV_T1, RV_T2)) 123 o = k4_w32(buf, o, k4_csrrw(RV_X0, K4_CSR_SATP, RV_T1)) 124 o = k4_li32u(buf, o, RV_T1, K4_MEM_BASE + cont_off) // mepc = &cont (physical, identity-mapped) ; S-mode 125 o = k4_w32(buf, o, k4_csrrw(RV_X0, K4_CSR_MEPC, RV_T1)) 126 o = k4_li32(buf, o, RV_T1, K4_MPP_S) 127 o = k4_w32(buf, o, k4_csrrw(RV_X0, K4_CSR_MSTATUS, RV_T1)) 128 o = k4_w32(buf, o, K4_MRET) 129 pos_out[0] = o // CONT (S-mode) 130 o = k4_li32u(buf, o, RV_T5, K4_VA) // load VA 0xC0009000 -> full 3-level walk 131 o = k4_w32(buf, o, k4_load(RV_T3, RV_T5, 2, 0)) 132 o = k4_li32(buf, o, RV_T4, K4_SENT_VAL) 133 let pcb: i64 = o 134 o = k4_w32(buf, o, k4_branch(RV_T3, RV_T4, 1, fail_off - pcb)) // bne -> FAIL 135 o = k4_emit_str(buf, o, "K4OK" as *u8, 4) 136 o = k4_w32(buf, o, k4_jal(RV_X0, halt_off - o)) 137 pos_out[1] = o // FAIL 138 o = k4_emit_str(buf, o, "K4X" as *u8, 3) 139 pos_out[2] = o // HALT 140 o = k4_li32(buf, o, RV_T5, K4_FIN) 141 o = k4_li32(buf, o, RV_T1, K4_PASS) 142 o = k4_w32(buf, o, k4_store(RV_T1, RV_T5, 2, 0)) 143 o = k4_w32(buf, o, k4_jal(RV_X0, 0)) 144 return o 145} 146 147func k4_parse_num(s: *u8) -> i64 { 148 var q: i64 = 0; var val: i64 = 0 149 if s[0] == (48 as u8) { if s[1] == (120 as u8) { 150 q = 2 151 var go: i64 = 1 152 while go == 1 { let c: i64 = s[q] as i64; var d: i64 = 0-1; if c>=48 { if c<=57 { d=c-48 } } if c>=97 { if c<=102 { d=c-87 } } if c>=65 { if c<=70 { d=c-55 } } if d<0 { go=0 } else { val=val*16+d; q=q+1 } } 153 return val 154 }} 155 var go2: i64 = 1 156 while go2 == 1 { let c: i64 = s[q] as i64; if c>=48 { if c<=57 { val=val*10+(c-48); q=q+1 } else { go2=0 } } else { go2=0 } } 157 return val 158} 159 160func main(argc: i64, argv: *i64) -> i64 { 161 var l1_pte: i64 = K4_L1 * 0 + 0x20000C01 // default L1 PTE (-> L0 at 0x80003000, V) 162 var outp: *u8 = K4_OUT 163 if argc >= 2 { l1_pte = k4_parse_num(argv[1] as *u8) } 164 if argc >= 3 { outp = argv[2] as *u8 } 165 let scratch: *u8 = sys_mmap(K4_MAGIC_8192) 166 let pos: *i64 = sys_mmap(32) as *i64 167 k4_emit_image(scratch, 0, 0, 0, pos, l1_pte) 168 let cont_off: i64 = pos[0] 169 let fail_off: i64 = pos[1] 170 let halt_off: i64 = pos[2] 171 let buf: *u8 = sys_mmap(K4_MAGIC_8192) 172 let sz: i64 = k4_emit_image(buf, cont_off, fail_off, halt_off, pos, l1_pte) 173 let fd: i64 = sys_openat_wr(outp, 420) 174 if fd < 0 { k4_p("K4EMIT verdict=RED reason=out-unwritable\n" as *u8); return 1 } 175 sys_write(fd, buf, sz); sys_close(fd) 176 let gold: *u8 = sys_mmap(8) 177 gold[0]=75 as u8; gold[1]=52 as u8; gold[2]=79 as u8; gold[3]=75 as u8 // "K4OK" 178 let gfd: i64 = sys_openat_wr(K4_GOLD, 420) 179 if gfd >= 0 { sys_write(gfd, gold, 4); sys_close(gfd) } 180 k4_p("K4EMIT name=" as *u8); k4_p(outp); k4_p(" bytes=" as *u8); k4_fn(1, sz); k4_p(" (4KB-page full 3-level Sv39 walk)\n" as *u8) 181 let lf: i64 = sys_openat_append(K4_LOG, 420) 182 if lf >= 0 { var n: i64=0; let m: *u8="K4EMIT authored _4kpage_virt.bin golden=K4OK\n" as *u8; while m[n]!=(0 as u8){n=n+1} sys_write(lf,m,n); sys_close(lf) } 183 return 0 184}