code wiki / _hdl_build / nx_nishi_usb_paging.nx

nx_nishi_usb_paging.nx source

↩ module page · 271 lines · 16100 B

1// nx_nishi_usb_paging.nx -- x86 ladder R-KERN-2: the long-mode kernel sets up PAGING (CR3 + 4-level walk). 2// 3// Second OS-internals rung at the long-mode entry. The kernel LOADED OFF the persisted image, after it 4// reaches long mode, BUILDS a 4-level page-table tree IN MEMORY with its own x86 stores (PML4 -> PDPT -> 5// PD -> PT), loads CR3 with `mov cr3, rax`, and HLTs. The emu then performs the long-mode 4-level page 6// walk (VA[47:39]->PML4, [38:30]->PDPT, [29:21]->PD, [20:12]->PT, +[11:0] offset; bit0=present) over the 7// tables the kernel built, and reports the translations -- proving the kernel's page tables are real and 8// walkable. pg_walk is reused verbatim from the proven nx_emu_x86_paging_test (the x86 twin of our rv64 9// SV39 MMU); the unified emu is extended with `mov cr3` (0F 22 /3). 10// 11// KAT: (T1) booted off disk; (T2) LONG mode; (T3) CR3 loaded with the kernel's PML4 base; (T4) VA 0x1000 12// translates to PA 0x40000 through the kernel-built tables; (T5) offset preserved (0x1123->0x40123); 13// (T6) a second mapping (0x2000->0x50000); (T7) an unmapped VA faults. NEG/liar-kill (T8): a sibling 14// image whose CR3 is loaded with a WRONG base translates nothing -- so the translations depend on the 15// kernel having built the tables AND loaded the right CR3, not on a constant. 16// 17// HONEST SCOPE: 4KB pages + present bit; long mode is modeled flat so the emu walks the kernel's tables 18// on demand but does not yet route every instruction fetch through the TLB (large pages / A-D bits / 19// permission faults / full MMU routing = refinements). NEVER-BRICK (Rule 26): writes a FILE; INT 13h 20// *reads* only; no /dev. expect_exit: 0 license_tier: ORIGINAL 21import "nx_syscalls.nx" 22import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 23const IMG_MAGIC_2000000: i64 = 2000000 24const IMG_MAGIC_32767: i64 = 32767 25const IMG_MAGIC_65536: i64 = 65536 26 27func pg_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 pg_num(v: i64) -> i64 { nxi_out(v); return 0 } 33func pg_mode(cr0: i64, efer: i64) -> i64 { let pe: i64=cr0&1; let pg: i64=(cr0>>31)&1; let lme: i64=(efer>>8)&1; if pe==0 { return 0 } if pg==1 { if lme==1 { return 2 } } return 1 } 34func pg_st64(mem: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<8 { mem[a+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 } 35func pg_rd(mem: *u8, a: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((mem[a+i] as i64)<<(i*8)); i=i+1 } return v } 36 37// 4-level long-mode page walk (verbatim from nx_emu_x86_paging_test); PA, or -1 on a not-present fault. 38func pg_walk(mem: *u8, cr3: i64, va: i64) -> i64 { 39 let i1: i64 = (va >> 39) & 0x1FF 40 let e1: i64 = pg_rd(mem, cr3 + i1*8) 41 if (e1 & 1)==0 { return 0 - 1 } 42 let b2: i64 = (e1 >> 12) << 12 43 let i2: i64 = (va >> 30) & 0x1FF 44 let e2: i64 = pg_rd(mem, b2 + i2*8) 45 if (e2 & 1)==0 { return 0 - 1 } 46 let b3: i64 = (e2 >> 12) << 12 47 let i3: i64 = (va >> 21) & 0x1FF 48 let e3: i64 = pg_rd(mem, b3 + i3*8) 49 if (e3 & 1)==0 { return 0 - 1 } 50 let b4: i64 = (e3 >> 12) << 12 51 let i4: i64 = (va >> 12) & 0x1FF 52 let e4: i64 = pg_rd(mem, b4 + i4*8) 53 if (e4 & 1)==0 { return 0 - 1 } 54 let page: i64 = (e4 >> 12) << 12 55 return page + (va & 0xFFF) 56} 57 58// UNIFIED boot emu: 16-bit loader + CR0/EFER/CR3 + 64-bit store/arith. On HLT, walk the kernel's tables. 59// st[]: 0=cr0 1=mode 7=cr3 8..11 = pg_walk(0x1000,0x1123,0x2000,0x3000). 60func emu_x86_boot_page(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64, st: *i64) -> i64 { 61 let reg: *i64 = sys_mmap(8 * 16) as *i64 62 var ip: i64=entry 63 var ax: i64=0 64 var bx: i64=0 65 var cx: i64=0 66 var dx: i64=0 67 var si: i64=0 68 var zf: i64=0 69 var cr0: i64=0 70 var efer: i64=0 71 var cr3: i64=0 72 var guard: i64=0 73 st[0]=0; st[1]=0; st[7]=0; st[8]=0; st[9]=0; st[10]=0; st[11]=0 74 while guard < IMG_MAGIC_2000000 { 75 guard = guard + 1 76 let op: i64 = mem[ip] as i64 77 if op == 0xF4 { 78 st[0]=cr0; st[1]=pg_mode(cr0,efer); st[7]=cr3 79 st[8]=pg_walk(mem, cr3, 0x1000); st[9]=pg_walk(mem, cr3, 0x1123); st[10]=pg_walk(mem, cr3, 0x2000); st[11]=pg_walk(mem, cr3, 0x3000) 80 return 0 81 } 82 var h: i64 = 0 83 if h==0 { if op==0x48 { 84 let o2: i64 = mem[ip+1] as i64 85 if o2==0xC7 { let m: i64=mem[ip+2] as i64; let imm: i64=(mem[ip+3] as i64)|((mem[ip+4] as i64)<<8)|((mem[ip+5] as i64)<<16)|((mem[ip+6] as i64)<<24); reg[m & 7]=imm; ip=ip+7; h=1 } 86 if h==0 { if o2==0x89 { let m: i64=mem[ip+2] as i64; if ((m>>6)&3)==3 { reg[m & 7]=reg[(m>>3) & 7] } else { pg_st64(mem, reg[m & 7], reg[(m>>3) & 7]) } ip=ip+3; h=1 } } 87 if h==0 { if o2==0x01 { let m: i64=mem[ip+2] as i64; reg[m & 7]=reg[m & 7]+reg[(m>>3) & 7]; ip=ip+3; h=1 } } 88 if h==0 { if o2==0x39 { let m: i64=mem[ip+2] as i64; let t: i64=reg[m & 7]-reg[(m>>3) & 7]; if t==0 { zf=1 } else { zf=0 } ip=ip+3; h=1 } } 89 if h==0 { return 0 - 1 } 90 } } 91 if h==0 { if op==0x0F { 92 let b1: i64 = mem[ip+1] as i64 93 if b1==0x22 { let m: i64=mem[ip+2] as i64; let cri: i64=(m>>3)&7; if cri==0 { cr0=reg[0] } if cri==3 { cr3=reg[0] }; ip=ip+3; h=1 } // mov cr0/cr3, rax 94 if h==0 { if b1==0x30 { efer=reg[0]; ip=ip+2; h=1 } } 95 if h==0 { if b1==0x05 { st[0]=cr0; st[1]=pg_mode(cr0,efer); return reg[7] & 0xff } } 96 if h==0 { return 0 - 1 } 97 } } 98 if h==0 { if op==0x75 { var r: i64=mem[ip+1] as i64; if r>127 { r=r-256 } if zf==0 { ip=ip+2+r } else { ip=ip+2 } h=1 } } 99 if h==0 { if op==0xBE { si = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 100 if h==0 { if op==0xB8 { ax = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 101 if h==0 { if op==0xBB { bx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 102 if h==0 { if op==0xB9 { cx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 103 if h==0 { if op==0xBA { dx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 104 if h==0 { if op==0xAC { let al: i64 = mem[si] as i64; ax = (ax & 0xFF00) | al; si=si+1; ip=ip+1; h=1 } } 105 if h==0 { if op==0x08 { let al2: i64 = ax & 0xFF; if al2==0 { zf=1 } else { zf=0 } ip=ip+2; h=1 } } 106 if h==0 { if op==0x74 { var r: i64 = mem[ip+1] as i64; if r>127 { r=r-256 } if zf==1 { ip=ip+2+r } else { ip=ip+2 } h=1 } } 107 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } 108 if h==0 { if op==0xEB { var r2: i64 = mem[ip+1] as i64; if r2>127 { r2=r2-256 } ip=ip+2+r2; h=1 } } 109 if h==0 { if op==0xE9 { var r3: i64 = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); if r3>IMG_MAGIC_32767 { r3=r3-IMG_MAGIC_65536 } ip=ip+3+r3; h=1 } } 110 if h==0 { if op==0xCD { 111 let vec: i64 = mem[ip+1] as i64 112 let ah: i64 = (ax >> 8) & 0xFF 113 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } 114 if vec==0x13 { if ah==0x02 { 115 let count: i64 = ax & 0xFF 116 let sector: i64 = cx & 0xFF 117 let lba: i64 = sector - 1 118 var s: i64 = 0 119 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 } 120 ax = ax & 0xFF 121 } } 122 ip=ip+2; h=1 123 } } 124 if h==0 { return 0 - 1 } 125 } 126 return 0 - 2 127} 128 129const IMG_SZ: i64 = 1024 130 131func uf_movr(img: *u8, base: i64, o: i64, modrm: i64, imm: i64) -> i64 { 132 img[base+o]=0x48 as u8; img[base+o+1]=0xC7 as u8; img[base+o+2]=(modrm&0xff) as u8 133 img[base+o+3]=(imm&0xff) as u8; img[base+o+4]=((imm>>8)&0xff) as u8; img[base+o+5]=((imm>>16)&0xff) as u8; img[base+o+6]=((imm>>24)&0xff) as u8 134 return o+7 135} 136 137// emit "mov rax,val ; mov rbx,addr ; mov [rbx],rax" (write a page-table entry). returns next offset. 138func emit_pte(img: *u8, base: i64, o: i64, val: i64, addr: i64) -> i64 { 139 var r: i64 = o 140 r = uf_movr(img, base, r, 0xC0, val) // mov rax, val 141 r = uf_movr(img, base, r, 0xC3, addr) // mov rbx, addr 142 img[base+r]=0x48 as u8; img[base+r+1]=0x89 as u8; img[base+r+2]=0x03 as u8; r=r+3 // mov [rbx], rax 143 return r 144} 145 146// info[0] = in-sector offset of the CR3-source mov-imm (so the NEG control can corrupt it). 147func build_image(img: *u8, info: *i64) -> i64 { 148 var z: i64=0 149 while z<IMG_SZ { img[z]=0 as u8; z=z+1 } 150 img[0]=0xB8 as u8; img[1]=0x01 as u8; img[2]=0x02 as u8 151 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 152 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 153 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 154 img[12]=0xCD as u8; img[13]=0x13 as u8 155 let rel16: i64 = 0x8000 - (0x7C00 + 14 + 3) 156 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 157 img[510]=0x55 as u8; img[511]=0xAA as u8 158 159 var r: i64 = 0 160 // banner 161 img[512+r]=0xBE as u8; let si_r: i64 = r+1; r=r+3 162 let loop_r: i64 = r 163 img[512+r]=0xAC as u8; r=r+1 164 img[512+r]=0x08 as u8; img[512+r+1]=0xC0 as u8; r=r+2 165 img[512+r]=0x74 as u8; let jz_r: i64 = r+1; r=r+2 166 img[512+r]=0xB4 as u8; img[512+r+1]=0x0E as u8; r=r+2 167 img[512+r]=0xCD as u8; img[512+r+1]=0x10 as u8; r=r+2 168 img[512+r]=0xEB as u8; img[512+r+1]=((loop_r-(r+2)) & 0xFF) as u8; r=r+2 169 let after_r: i64 = r 170 // mode transition -> long 171 r = uf_movr(img, 512, r, 0xC0, 1) 172 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 173 r = uf_movr(img, 512, r, 0xC0, 0x100) 174 img[512+r]=0x0F as u8; img[512+r+1]=0x30 as u8; r=r+2 175 r = uf_movr(img, 512, r, 0xC0, 0x80000001) 176 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 177 // kernel builds a 4-level page table tree: 178 // PML4@0x1000[0]->PDPT@0x2000 ; PDPT[0]->PD@0x3000 ; PD[0]->PT@0x4000 ; PT[1]->0x40000 ; PT[2]->0x50000 179 r = emit_pte(img, 512, r, 0x2001, 0x1000) // PML4[0] = 0x2000|present 180 r = emit_pte(img, 512, r, 0x3001, 0x2000) // PDPT[0] = 0x3000|present 181 r = emit_pte(img, 512, r, 0x4001, 0x3000) // PD[0] = 0x4000|present 182 r = emit_pte(img, 512, r, 0x40001, 0x4008) // PT[1] = 0x40000|present (maps VA 0x1000) 183 r = emit_pte(img, 512, r, 0x50001, 0x4010) // PT[2] = 0x50000|present (maps VA 0x2000) 184 // load CR3 with the PML4 base 185 r = uf_movr(img, 512, r, 0xC0, 0x1000); let cr3_imm: i64 = r-4 // mov rax, 0x1000 (PML4 base) 186 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xD8 as u8; r=r+3 // mov cr3, rax 187 img[512+r]=0xF4 as u8; r=r+1 // hlt 188 let msg_r: i64 = r 189 let msg: *u8 = "NishiOS\x0D\x0A\x00" 190 var mi: i64=0 191 while msg[mi]!=(0 as u8) { img[512+r]=msg[mi]; r=r+1; mi=mi+1 } 192 img[512+r]=0 as u8; r=r+1 193 img[512+jz_r] = ((after_r - (jz_r+1)) & 0xFF) as u8 194 let si_abs: i64 = 0x8000 + msg_r 195 img[512+si_r] = (si_abs & 0xFF) as u8 196 img[512+si_r+1] = ((si_abs>>8) & 0xFF) as u8 197 info[0] = cr3_imm 198 return 0 199} 200 201func boot_image(img: *u8, console: *u8, clen: *i64, st: *i64, loaded: *i64) -> i64 { 202 let mem: *u8 = sys_mmap(IMG_MAGIC_65536) 203 var k: i64=0 204 while k<IMG_MAGIC_65536 { mem[k]=0 as u8; k=k+1 } 205 var j: i64=0 206 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 207 loaded[0] = mem[0x8000] as i64 208 clen[0]=0 209 let rc: i64 = emu_x86_boot_page(mem, img, 0x7C00, console, clen, st) 210 loaded[1] = mem[0x8000] as i64 211 return rc 212} 213 214func pg_read(path: *u8, out: *u8, cap: i64) -> i64 { 215 let fd: i64 = sys_openat_rd(path) 216 if fd < 0 { return 0 - 1 } 217 var n: i64 = 0; var go: i64 = 1 218 while go==1 { let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n); if rr<=0 { go=0 } else { n=n+rr } if n>=cap { go=0 } } 219 sys_close(fd) 220 return n 221} 222 223func main() -> i64 { 224 pg_puts("x86 ladder R-KERN-2: the long-mode kernel builds page tables, loads CR3, the emu walks them\n" as *u8) 225 226 let img: *u8 = sys_mmap(IMG_SZ + 16) 227 let info: *i64 = sys_mmap(64) as *i64 228 build_image(img, info) 229 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os_paging.img\x00" as *u8, 0x1a4) 230 if fd<=0 { pg_puts("R-KERN-2 RED: cannot write image\n" as *u8); sys_exit(1); return 1 } 231 sys_write(fd, img, IMG_SZ) 232 sys_close(fd) 233 let rd: *u8 = sys_mmap(IMG_SZ + 16) 234 pg_read("knowledge/status/nishi_os_paging.img\x00" as *u8, rd, IMG_SZ) 235 236 let con: *u8 = sys_mmap(256) 237 let clen: *i64 = sys_mmap(8) as *i64 238 let st: *i64 = sys_mmap(128) as *i64 239 let ld: *i64 = sys_mmap(64) as *i64 240 let rc: i64 = boot_image(rd, con, clen, st, ld) 241 pg_puts(" boot: 0x8000 " as *u8); pg_num(ld[0]); pg_puts("->" as *u8); pg_num(ld[1]); pg_puts(" mode=" as *u8); pg_num(st[1]); pg_puts(" CR3=" as *u8); pg_num(st[7]); pg_puts(" rc=" as *u8); pg_num(rc); pg_puts("\n" as *u8) 242 pg_puts(" page walks: 0x1000->" as *u8); pg_num(st[8]); pg_puts(" 0x1123->" as *u8); pg_num(st[9]); pg_puts(" 0x2000->" as *u8); pg_num(st[10]); pg_puts(" 0x3000->" as *u8); pg_num(st[11]); pg_puts("\n" as *u8) 243 244 // NEG CONTROL: load CR3 with a WRONG base -> nothing translates. 245 let bad: *u8 = sys_mmap(IMG_SZ + 16) 246 var c: i64=0 247 while c<IMG_SZ { bad[c]=rd[c]; c=c+1 } 248 let cb: i64 = 512 + info[0] 249 bad[cb]=0x99 as u8; bad[cb+1]=0x99 as u8; bad[cb+2]=0x00 as u8; bad[cb+3]=0x00 as u8 // CR3 = 0x9999 (no table there) 250 let con2: *u8 = sys_mmap(256) 251 let clen2: *i64 = sys_mmap(8) as *i64 252 let st2: *i64 = sys_mmap(128) as *i64 253 let ld2: *i64 = sys_mmap(64) as *i64 254 boot_image(bad, con2, clen2, st2, ld2) 255 pg_puts(" NEG (CR3=0x9999): 0x1000 walk -> " as *u8); pg_num(st2[8]); pg_puts(" (want <0 fault)\n" as *u8) 256 257 var pass: i64=0 258 var ttl: i64=0 259 ttl=ttl+1; pg_puts(" T1 booted off the persisted image (0x8000: 0->0xBE): " as *u8); if ld[0]==0 { if ld[1]==0xBE { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } } else { pg_puts("FAIL\n" as *u8) } 260 ttl=ttl+1; pg_puts(" T2 disk-loaded kernel reached LONG mode (mode==2): " as *u8); if st[1]==2 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 261 ttl=ttl+1; pg_puts(" T3 kernel loaded CR3 with its PML4 base (CR3==0x1000): " as *u8); if st[7]==0x1000 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 262 ttl=ttl+1; pg_puts(" T4 VA 0x1000 -> PA 0x40000 through the kernel's tables: " as *u8); if st[8]==0x40000 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 263 ttl=ttl+1; pg_puts(" T5 offset preserved (VA 0x1123 -> 0x40123): " as *u8); if st[9]==0x40123 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 264 ttl=ttl+1; pg_puts(" T6 second mapping (VA 0x2000 -> 0x50000): " as *u8); if st[10]==0x50000 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 265 ttl=ttl+1; pg_puts(" T7 unmapped VA 0x3000 -> page fault (<0): " as *u8); if st[11] < 0 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 266 ttl=ttl+1; pg_puts(" T8 NEG: wrong CR3 -> VA 0x1000 faults (translations depend on the kernel's real CR3): " as *u8); if st2[8] < 0 { pass=pass+1; pg_puts("PASS\n" as *u8) } else { pg_puts("FAIL\n" as *u8) } 267 268 pg_puts("X86-USB-PAGING-GATE passed " as *u8); pg_num(pass); pg_puts("/" as *u8); pg_num(ttl) 269 if pass==ttl { pg_puts(" verdict=GREEN (the disk-loaded long-mode kernel builds page tables + loads CR3; the 4-level walk is correct; PIC/IRQ0 preemption = next rung)\n" as *u8); sys_exit(0); return 0 } 270 pg_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 271}