code wiki / _hdl_build / nx_nishi_usb_longmode.nx

nx_nishi_usb_longmode.nx source

↩ module page · 258 lines · 15911 B

1// nx_nishi_usb_longmode.nx -- x86 ladder R9-HANDOFF: the loaded kernel boots THROUGH to LONG mode. 2// 3// The previous rung (nx_nishi_usb_image) proved a persisted multi-sector image boots MBR->loader-> 4// kernel, but every stage was 16-bit real-mode. THIS rung crosses the seam the OS ladder has been 5// stuck at: the kernel that the MBR loads OFF DISK now performs the real -> protected -> LONG mode 6// transition (GDT-less minimal model: CR0.PE, then EFER.LME via WRMSR + CR0.PG) and the emu tracks it. 7// 8// It fuses two previously-separate emus into ONE unified boot-disk emu: 9// * the 16-bit real-mode + BIOS INT 13h/INT 10h loader (from nx_emu_x86_stage2 / nx_nishi_usb_image) 10// * the CR0/EFER mode-transition decode (from nx_emu_x86_boot_kernel / nx_nishios_boot_gui) 11// The two opcode sets are disjoint (0xBE/0xB8/0xCD... vs 0x48 0xC7 / 0x0F 0x22 / 0x0F 0x30), so one 12// flat decoder runs both and reports the final CPU mode. 13// 14// Boot flow, executed off the PERSISTED on-disk bytes: 15// sector 0 (MBR) --INT 13h AH=02--> loads sector 1 (the kernel) to 0x8000, jmps to it 16// sector 1 (kernel) prints "NishiOS" over INT 10h, then: mov cr0,1 (->PROTECTED) 17// wrmsr EFER.LME=1 ; mov cr0,0x80000001 (PE|PG) -> LONG mode ; HLT 18// 19// KAT: (T1) persisted byte-faithful + 0x55AA; (T2) INT 13h loaded the kernel off disk (0x8000 0->0xBE); 20// (T3) console banner == 'NishiOS'; (T4) the LOADED kernel reached LONG mode (mode==2); (T5) clean HLT; 21// (T6) NEG/liar-kill: a sibling image whose MBR loads ZERO sectors never runs the kernel, so it NEVER 22// reaches long mode and prints no banner -- the long-mode result MUST come from the loaded kernel. 23// 24// HONEST SEAM (no overclaim, same as the boot-kernel organs): the emu EXECUTES the loader + the 25// CR0/EFER transition as real x86 and TRACKS the mode; it does not yet model a real GDT/IDT/paging 26// table walk, nor decode-width change by mode. The next rung is loading the real 64-bit desktop kernel 27// (nx_nishios_boot_gui) at the long-mode entry instead of a banner stub, then GDT/IDT/paging. 28// NEVER-BRICK (Rule 26): writes a FILE (knowledge/status/nishi_os_lm.img); models INT 13h *reads* only; 29// no /dev, no firmware. expect_exit: 0 license_tier: ORIGINAL 30import "nx_syscalls.nx" 31import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 32const IMG_MAGIC_200000: i64 = 200000 33const IMG_MAGIC_32767: i64 = 32767 34const IMG_MAGIC_65536: i64 = 65536 35 36func ul_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 37// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 38// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 39// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 40// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 41func ul_num(v: i64) -> i64 { nxi_out(v); return 0 } 42func ul_beq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i] { return 0 } i=i+1 } return 1 } 43func ul_contains(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 { 44 if nn==0 { return 1 } 45 var i: i64 = 0 46 while i + nn <= hn { 47 var j: i64 = 0 48 var ok: i64 = 1 49 while j < nn { if hay[i+j]!=ndl[j] { ok=0; j=nn } else { j=j+1 } } 50 if ok==1 { return 1 } 51 i=i+1 52 } 53 return 0 54} 55// CPU mode from CR0/EFER: 0=real, 1=protected, 2=long. (same rule as the boot-kernel organs) 56func ul_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 } 57 58// UNIFIED boot-disk emu: 16-bit real-mode + INT 13h/INT 10h PLUS 64-bit mov + CR0/EFER mode transition. 59// returns 0 = clean HLT, -1 = unknown opcode, -2 = ran away. st[0]=final cr0, st[1]=final mode. 60func emu_x86_boot_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64, st: *i64) -> i64 { 61 var ip: i64 = entry 62 var ax: i64 = 0 63 var bx: i64 = 0 64 var cx: i64 = 0 65 var dx: i64 = 0 66 var si: i64 = 0 67 var zf: i64 = 0 68 var rax: i64 = 0 // 64-bit reg used by the mode-transition setup (distinct from 16-bit ax) 69 var cr0: i64 = 0 70 var efer: i64 = 0 71 var guard: i64 = 0 72 st[0]=0; st[1]=0 73 while guard < IMG_MAGIC_200000 { 74 guard = guard + 1 75 let op: i64 = mem[ip] as i64 76 if op == 0xF4 { st[0]=cr0; st[1]=ul_mode(cr0,efer); return 0 } // hlt 77 var h: i64 = 0 78 // ---- 64-bit mov r64,imm32 (48 C7 modrm imm32) ---- 79 if h==0 { if op==0x48 { 80 if mem[ip+1]==(0xC7 as u8) { 81 let modrm: i64 = mem[ip+2] as i64 82 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) 83 if modrm==0xC0 { rax = imm } // mov rax,imm32 (the only target we use) 84 ip=ip+7; h=1 85 } 86 } } 87 // ---- two-byte 0F ops: mov cr0,rax / wrmsr / syscall ---- 88 if h==0 { if op==0x0F { 89 let b1: i64 = mem[ip+1] as i64 90 if b1==0x22 { let m: i64=mem[ip+2] as i64; if ((m>>3)&7)==0 { cr0=rax }; ip=ip+3; h=1 } // mov cr0,rax 91 if h==0 { if b1==0x30 { efer=rax; ip=ip+2; h=1 } } // wrmsr -> EFER=rax 92 if h==0 { if b1==0x05 { st[0]=cr0; st[1]=ul_mode(cr0,efer); return rax & 0xff } } // syscall (exit) 93 if h==0 { return 0 - 1 } 94 } } 95 // ---- 16-bit real-mode + BIOS INT services (from nx_nishi_usb_image / stage2) ---- 96 if h==0 { if op==0xBE { si = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov si,imm16 97 if h==0 { if op==0xB8 { ax = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov ax,imm16 98 if h==0 { if op==0xBB { bx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov bx,imm16 99 if h==0 { if op==0xB9 { cx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov cx,imm16 100 if h==0 { if op==0xBA { dx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov dx,imm16 101 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 } } // lodsb 102 if h==0 { if op==0x08 { let al2: i64 = ax & 0xFF; if al2==0 { zf=1 } else { zf=0 } ip=ip+2; h=1 } } // or al,al 103 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 } } // jz rel8 104 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } // mov ah,imm8 105 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 } } // jmp rel8 106 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 } } // jmp rel16 107 if h==0 { if op==0xCD { 108 let vec: i64 = mem[ip+1] as i64 109 let ah: i64 = (ax >> 8) & 0xFF 110 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } // BIOS teletype 111 if vec==0x13 { if ah==0x02 { // BIOS read sectors 112 let count: i64 = ax & 0xFF 113 let sector: i64 = cx & 0xFF 114 let lba: i64 = sector - 1 115 var s: i64 = 0 116 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 } 117 ax = ax & 0xFF 118 } } 119 ip=ip+2; h=1 120 } } 121 if h==0 { return 0 - 1 } 122 } 123 return 0 - 2 124} 125 126const IMG_SZ: i64 = 1024 // 2 sectors: MBR + the long-mode kernel 127 128// emit "mov rax,imm32" (48 C7 C0 + 4 LE bytes) at img[base+o]; returns next offset. 129func ul_movrax(img: *u8, base: i64, o: i64, b0: i64, b1: i64, b2: i64, b3: i64) -> i64 { 130 img[base+o]=0x48 as u8; img[base+o+1]=0xC7 as u8; img[base+o+2]=0xC0 as u8 131 img[base+o+3]=(b0&0xff) as u8; img[base+o+4]=(b1&0xff) as u8; img[base+o+5]=(b2&0xff) as u8; img[base+o+6]=(b3&0xff) as u8 132 return o+7 133} 134 135func build_image(img: *u8) -> i64 { 136 var z: i64=0 137 while z<IMG_SZ { img[z]=0 as u8; z=z+1 } 138 139 // ---- sector 0: MBR (loads at 0x7C00); loads sector 1 (kernel) to 0x8000 then jmps to it ---- 140 img[0]=0xB8 as u8; img[1]=0x01 as u8; img[2]=0x02 as u8 // mov ax,0x0201 (AH=02 read, AL=01 -> ONE sector) 141 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 // mov bx,0x8000 142 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 // mov cx,0x0002 (CL=2 -> LBA1 = sector1) 143 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 // mov dx,0x0080 144 img[12]=0xCD as u8; img[13]=0x13 as u8 // int 0x13 (load kernel) 145 let rel16: i64 = 0x8000 - (0x7C00 + 14 + 3) // jmp 0x8000 -> kernel 146 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 147 img[510]=0x55 as u8; img[511]=0xAA as u8 148 149 // ---- sector 1: the long-mode kernel (loads at 0x8000); offsets r relative to kernel start ---- 150 var r: i64 = 0 151 img[512+r]=0xBE as u8; let si_r: i64 = r+1; r=r+3 // mov si, imm16 (patched -> 0x8000+msg) 152 let loop_r: i64 = r 153 img[512+r]=0xAC as u8; r=r+1 // lodsb 154 img[512+r]=0x08 as u8; img[512+r+1]=0xC0 as u8; r=r+2 // or al,al 155 img[512+r]=0x74 as u8; let jz_r: i64 = r+1; r=r+2 // jz afterprint (patched) 156 img[512+r]=0xB4 as u8; img[512+r+1]=0x0E as u8; r=r+2 // mov ah,0x0E 157 img[512+r]=0xCD as u8; img[512+r+1]=0x10 as u8; r=r+2 // int 0x10 158 img[512+r]=0xEB as u8; img[512+r+1]=((loop_r-(r+2)) & 0xFF) as u8; r=r+2 // jmp loop 159 let after_r: i64 = r 160 // enter PROTECTED: mov rax,1 ; mov cr0,rax 161 r = ul_movrax(img, 512, r, 1, 0, 0, 0) 162 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 // mov cr0,rax 163 // enter LONG: EFER.LME=1 (rax=0x100 ; wrmsr) 164 r = ul_movrax(img, 512, r, 0x00, 0x01, 0, 0) 165 img[512+r]=0x0F as u8; img[512+r+1]=0x30 as u8; r=r+2 // wrmsr -> EFER=rax 166 // CR0 = PE|PG = 0x80000001 ; mov cr0,rax -> with EFER.LME = LONG mode 167 r = ul_movrax(img, 512, r, 0x01, 0x00, 0x00, 0x80) 168 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 // mov cr0,rax 169 img[512+r]=0xF4 as u8; r=r+1 // hlt 170 let msg_r: i64 = r 171 let msg: *u8 = "NishiOS\x0D\x0A\x00" 172 var mi: i64=0 173 while msg[mi]!=(0 as u8) { img[512+r]=msg[mi]; r=r+1; mi=mi+1 } 174 img[512+r]=0 as u8; r=r+1 175 img[512+jz_r] = ((after_r - (jz_r+1)) & 0xFF) as u8 // patch jz rel8 -> afterprint 176 let si_abs: i64 = 0x8000 + msg_r // patch si = 0x8000 + msg offset 177 img[512+si_r] = (si_abs & 0xFF) as u8 178 img[512+si_r+1] = ((si_abs>>8) & 0xFF) as u8 179 return 0 180} 181 182// boot an image: copy sector0 -> mem[0x7C00], run the on-disk bytes. loaded[0]=0x8000 before, [1]=after. 183func boot_image(img: *u8, console: *u8, clen: *i64, st: *i64, loaded: *i64) -> i64 { 184 let mem: *u8 = sys_mmap(IMG_MAGIC_65536) 185 var k: i64=0 186 while k<IMG_MAGIC_65536 { mem[k]=0 as u8; k=k+1 } 187 var j: i64=0 188 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 189 loaded[0] = mem[0x8000] as i64 190 clen[0]=0 191 let rc: i64 = emu_x86_boot_disk(mem, img, 0x7C00, console, clen, st) 192 loaded[1] = mem[0x8000] as i64 193 return rc 194} 195 196func ul_read(path: *u8, out: *u8, cap: i64) -> i64 { 197 let fd: i64 = sys_openat_rd(path) 198 if fd < 0 { return 0 - 1 } 199 var n: i64 = 0; var go: i64 = 1 200 while go==1 { 201 let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n) 202 if rr<=0 { go=0 } else { n=n+rr } 203 if n>=cap { go=0 } 204 } 205 sys_close(fd) 206 return n 207} 208 209func main() -> i64 { 210 ul_puts("x86 ladder R9-HANDOFF: the disk-loaded kernel boots real -> protected -> LONG mode\n" as *u8) 211 212 let img: *u8 = sys_mmap(IMG_SZ + 16) 213 build_image(img) 214 215 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os_lm.img\x00" as *u8, 0x1a4) 216 if fd<=0 { ul_puts("R9-HANDOFF RED: cannot write image\n" as *u8); sys_exit(1); return 1 } 217 sys_write(fd, img, IMG_SZ) 218 sys_close(fd) 219 ul_puts(" wrote knowledge/status/nishi_os_lm.img (" as *u8); ul_num(IMG_SZ); ul_puts(" bytes, 2 sectors)\n" as *u8) 220 221 let rd: *u8 = sys_mmap(IMG_SZ + 16) 222 let rn: i64 = ul_read("knowledge/status/nishi_os_lm.img\x00" as *u8, rd, IMG_SZ) 223 224 // GOOD boot (executes the ON-DISK bytes). 225 let con: *u8 = sys_mmap(256) 226 let clen: *i64 = sys_mmap(8) as *i64 227 let st: *i64 = sys_mmap(64) as *i64 228 let ld: *i64 = sys_mmap(64) as *i64 229 let rc: i64 = boot_image(rd, con, clen, st, ld) 230 ul_puts(" MBR loaded kernel (0x8000 " as *u8); ul_num(ld[0]); ul_puts("->" as *u8); ul_num(ld[1]); ul_puts("), kernel ran -> mode=" as *u8); ul_num(st[1]); ul_puts(" (0=real 1=protected 2=long) rc=" as *u8); ul_num(rc); ul_puts("\n" as *u8) 231 ul_puts(" SOVEREIGN-EMU console: " as *u8); sys_write(1, con, clen[0]); ul_puts(" [" as *u8); ul_num(clen[0]); ul_puts(" chars]\n" as *u8) 232 233 // NEG CONTROL: MBR loads ZERO sectors (AL=00) -> kernel never loaded/run -> never reaches long mode. 234 let bad: *u8 = sys_mmap(IMG_SZ + 16) 235 var c: i64=0 236 while c<IMG_SZ { bad[c]=rd[c]; c=c+1 } 237 bad[1]=0x00 as u8 // mov ax,0x0200 -> read 0 sectors 238 let con2: *u8 = sys_mmap(256) 239 let clen2: *i64 = sys_mmap(8) as *i64 240 let st2: *i64 = sys_mmap(64) as *i64 241 let ld2: *i64 = sys_mmap(64) as *i64 242 let rc2: i64 = boot_image(bad, con2, clen2, st2, ld2) 243 ul_puts(" NEG (MBR loads 0 sectors): kernel-slot 0x8000 after=" as *u8); ul_num(ld2[1]); ul_puts(" mode=" as *u8); ul_num(st2[1]); ul_puts(" rc=" as *u8); ul_num(rc2); ul_puts("\n" as *u8) 244 245 let banner: *u8 = "NishiOS" as *u8 246 var pass: i64=0 247 var ttl: i64=0 248 ttl=ttl+1; ul_puts(" T1 persisted byte-faithfully + 0x55AA signature: " as *u8); if rn==IMG_SZ { if ul_beq(rd, img, IMG_SZ)==1 { if rd[510]==(0x55 as u8) { if rd[511]==(0xAA as u8) { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } } else { ul_puts("FAIL\n" as *u8) } } else { ul_puts("FAIL\n" as *u8) } } else { ul_puts("FAIL\n" as *u8) } 249 ttl=ttl+1; ul_puts(" T2 INT 13h loaded the kernel off disk (0x8000: 0->0xBE): " as *u8); if ld[0]==0 { if ld[1]==0xBE { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } } else { ul_puts("FAIL\n" as *u8) } 250 ttl=ttl+1; ul_puts(" T3 console banner == 'NishiOS': " as *u8); if ul_contains(con, clen[0], banner, 7)==1 { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } 251 ttl=ttl+1; ul_puts(" T4 the LOADED kernel reached LONG mode (mode==2): " as *u8); if st[1]==2 { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } 252 ttl=ttl+1; ul_puts(" T5 clean HLT (rc=0): " as *u8); if rc==0 { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } 253 ttl=ttl+1; ul_puts(" T6 NEG: 0-sector load never reaches long mode + no banner (liar-kill): " as *u8); if st2[1]!=2 { if ul_contains(con2, clen2[0], banner, 7)==0 { pass=pass+1; ul_puts("PASS\n" as *u8) } else { ul_puts("FAIL\n" as *u8) } } else { ul_puts("FAIL\n" as *u8) } 254 255 ul_puts("X86-USB-LONGMODE-GATE passed " as *u8); ul_num(pass); ul_puts("/" as *u8); ul_num(ttl) 256 if pass==ttl { ul_puts(" verdict=GREEN (a disk-loaded kernel crosses real->protected->LONG mode by execution; loading the real 64-bit desktop kernel = next rung)\n" as *u8); sys_exit(0); return 0 } 257 ul_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 258}