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}