code wiki / _hdl_build / nx_paging.nx
nx_paging.nx source
↩ module page · 83 lines · 5835 B
1// nx_paging.nx -- SOVEREIGN VIRTUAL-MEMORY MODEL (deepens MEMORY-MGMT beyond the nx_alloc heap). Page table
2// (vpn->pfn), address translation, PAGE FAULTS, a physical-frame allocator, DEMAND paging + LRU eviction, and
3// per-page protection bits. This is the VM POLICY/data-structure.
4// ★ NEVER-BRICK (cardinal 26): this is a MODEL only -- it does NOT touch the real hardware MMU (no CR3/TLB/ring0,
5// no firmware/page-table writes). By construction it cannot brick anything. The real-MMU binding is a separate,
6// guarded hardware-axis capability.
7// T1 map + translate (hit). T2 unmapped page -> FAULT. T3 demand page-in. T4 eviction (LRU; evicted page faults).
8// T5 teeth: write to a read-only page is DENIED (protection).
9// expect_exit: 0 Sovereign: nx_syscalls.
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12import "nx_g_puts_lib.nx"
13
14// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
15// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
16// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
17// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
18func g_pn(v: i64) -> i64 { nxi_out(v); return 0 }
19func 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 }
20
21const PS: i64 = 4096 // page size
22const NV: i64 = 16 // virtual pages
23const NF: i64 = 4 // physical frames (small -> exercises eviction)
24
25func vm_translate(ptab: *i64, vaddr: i64) -> i64 { let vpn: i64=vaddr/PS; if ptab[vpn]>=0 { return ptab[vpn]*PS + (vaddr%PS) } return 0-1 }
26// access vpn at time `clk`: hit updates LRU; fault allocates a free frame or EVICTS the LRU page. returns pfn.
27func vm_access(ptab: *i64, frown: *i64, lastu: *i64, vpn: i64, clk: i64) -> i64 {
28 if ptab[vpn]>=0 { lastu[vpn]=clk; return ptab[vpn] }
29 var pf: i64=0-1; var f: i64=0; while f<NF { if frown[f]==(0-1) { pf=f; f=NF } else { f=f+1 } }
30 if pf==(0-1) {
31 var victim: i64=0-1; var oldest: i64=1<<60; var v: i64=0
32 while v<NV { if ptab[v]>=0 { if lastu[v]<oldest { oldest=lastu[v]; victim=v } } v=v+1 }
33 pf=ptab[victim]; ptab[victim]=0-1
34 }
35 ptab[vpn]=pf; frown[pf]=vpn; lastu[vpn]=clk; return pf
36}
37func vm_can_write(prot: *i64, vpn: i64) -> i64 { return (prot[vpn]>>1)&1 } // prot bit1 = write
38
39func main() -> i64 {
40 g_puts("nx_paging (SOVEREIGN VM model: page table + translation + demand-paging + LRU eviction; MODEL, not the hw MMU)\n" as *u8)
41 var pass: i64=0; var total: i64=0
42 let ptab: *i64=sys_mmap(NV*8) as *i64; let frown: *i64=sys_mmap(NF*8) as *i64; let lastu: *i64=sys_mmap(NV*8) as *i64; let prot: *i64=sys_mmap(NV*8) as *i64
43 var i: i64=0; while i<NV { ptab[i]=0-1; lastu[i]=0; prot[i]=3; i=i+1 } // prot=3 (R+W) default
44 i=0; while i<NF { frown[i]=0-1; i=i+1 }
45
46 // T1: map vpn 5 (access faults it in) then translate a vaddr in page 5
47 let pf5: i64=vm_access(ptab,frown,lastu,5,1)
48 let pa: i64=vm_translate(ptab, 5*PS+100)
49 var t1: i64=0; if pf5>=0 { if pa==(pf5*PS+100) { t1=1 } }
50 g_puts(" T1 vpn5 -> frame "); g_pn(pf5); g_puts("; translate(5*PS+100)="); g_pn(pa); g_puts("\n" as *u8)
51 pass=pass+ck("T1: map + translate -- vaddr in a mapped page resolves to frame*PS+offset" as *u8, t1); total=total+1
52
53 // T2: an unmapped page -> PAGE FAULT
54 var t2: i64=0; if vm_translate(ptab, 9*PS)==(0-1) { t2=1 }
55 pass=pass+ck("T2: an unmapped page translates to PAGE FAULT (-1)" as *u8, t2); total=total+1
56
57 // T3: demand page-in -- fault then access maps it, then it translates
58 let before: i64=vm_translate(ptab, 7*PS); vm_access(ptab,frown,lastu,7,2); let after: i64=vm_translate(ptab, 7*PS)
59 var t3: i64=0; if before==(0-1) { if after>=0 { t3=1 } }
60 pass=pass+ck("T3: demand paging -- a faulting page is paged in on access, then translates" as *u8, t3); total=total+1
61
62 // T4: eviction -- reset, fill NF frames (vpn 1..4), access vpn 5 -> evicts LRU (vpn1)
63 i=0; while i<NV { ptab[i]=0-1; lastu[i]=0; i=i+1 } i=0; while i<NF { frown[i]=0-1; i=i+1 }
64 vm_access(ptab,frown,lastu,1,1); vm_access(ptab,frown,lastu,2,2); vm_access(ptab,frown,lastu,3,3); vm_access(ptab,frown,lastu,4,4)
65 vm_access(ptab,frown,lastu,5,5) // frames full -> evict LRU = vpn1
66 var t4: i64=0; if ptab[1]==(0-1) { if ptab[5]>=0 { if ptab[2]>=0 { t4=1 } } }
67 g_puts(" T4 after filling 4 frames + access vpn5: ptab[1](evicted)="); g_pn(ptab[1]); g_puts(" ptab[5](mapped)="); g_pn(ptab[5]); g_puts("\n" as *u8)
68 pass=pass+ck("T4: LRU eviction -- new access evicts the least-recently-used page; evicted page now faults" as *u8, t4); total=total+1
69
70 // T5 teeth: a read-only page denies write
71 prot[3]=1 // R only (bit0), no write bit
72 var t5: i64=0; if vm_can_write(prot,3)==0 { if vm_can_write(prot,2)==1 { t5=1 } }
73 pass=pass+ck("T5 (teeth): write to a read-only page is DENIED (protection bits enforced)" as *u8, t5); total=total+1
74
75 var okall: i64=0; if pass==total { okall=1 }
76 g_puts("---- nx_paging: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8)
77 if okall==1 {
78 let logf: i64=sys_openat_append("knowledge/status/paging.log" as *u8, 420)
79 if logf>=0 { let z: i64=sys_write(logf,"NXPAGING GREEN: VM model -- page table/translate/fault/demand-paging/LRU-eviction/protection (MODEL, not hw MMU)\n" as *u8,108); sys_close(logf) }
80 g_puts("verdict=GREEN (sovereign VM model: page table + translation + demand-paging + LRU eviction + protection; deepens MEMORY-MGMT; MODEL only, never-brick)\n" as *u8); sys_exit(0); return 0
81 }
82 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
83}