code wiki / _hdl_build / nx_nishi_usb_image.nx

nx_nishi_usb_image.nx source

↩ module page · 249 lines · 15698 B

1// nx_nishi_usb_image.nx -- x86 ladder R9-FUSE: the REAL bootable Nishi USB image, proven by execution. 2// 3// Until now three proofs sat SEPARATE: nx_nishi_bootimg (R9, a 512B MBR that only prints+halts), 4// nx_emu_x86_stage2_test (R9-4, an INT 13h multi-stage loader, but only as an IN-MEMORY test), and 5// the desktop kernel (which lived in no bootable image at all). This organ FUSES them into ONE 6// persisted multi-sector artifact -- knowledge/status/nishi_os.img, the file you would 7// dd if=nishi_os.img of=/dev/sdX to make a bootable USB -- and PROVES it boots end-to-end by 8// EXECUTING THE ON-DISK BYTES on the sovereign 16-bit real-mode + BIOS-INT emu: 9// sector 0 (MBR) --INT 13h AH=02--> loads sectors 1+2 to 0x8000, then jumps to stage2 10// sector 1 (stage2 loader) prints the NishiOS banner over INT 10h, then transfers to the kernel 11// sector 2 (kernel) prints 'NISHIOS KERNEL' over INT 10h, then HLT 12// A 3-stage chain (MBR -> loader -> kernel) is a genuine step past the 2-stage stage2 proof, and it 13// is the REAL PERSISTED artifact: written to disk, re-read, then executed -- not an in-memory image. 14// 15// KAT: (T1) persisted byte-faithfully + 0x55AA signature; (T2) INT 13h actually copied BOTH stage2 16// and the kernel off disk (0x8000 and 0x8200 went 0 -> 0xBE); (T3) the LOADED kernel ran (console 17// contains 'NISHIOS KERNEL'); (T4) clean HLT. NEG CONTROL (T5, liar-kill): a sibling image whose MBR 18// loads only ONE sector never reaches the kernel banner -- so the banner in the good case MUST have 19// come from the sector that was loaded off disk, not from the emu or a hardcode. 20// 21// HONEST SCOPE: every stage here is 16-bit real-mode. The next rung is the mode-transition handoff -- 22// stage2/kernel set up GDT + CR0.PE + PAE + EFER.LME, enter long mode, and load the real 64-bit 23// desktop kernel (nx_nishios_boot_gui) instead of a banner printer. This rung delivers the loader 24// chassis + the real flashable artifact that that kernel will ride. 25// NEVER-BRICK (Rule 26): writes a FILE (knowledge/status/nishi_os.img); models INT 13h *reads* only; 26// touches no /dev and no host firmware -- by construction, not by promise. 27// The 16-bit + BIOS-INT13h/INT10h emu below is inlined verbatim from the proven nx_emu_x86_stage2_test 28// (a _test's main() blocks `import`, so we inline -- the same DRY exception the codebase already uses). 29// 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 35const IMG_MAGIC_1024: i64 = 1024 36 37func ui_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 38// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 39// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 40// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 41// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 42func ui_num(v: i64) -> i64 { nxi_out(v); return 0 } 43func ui_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 } 44// does haystack[0,hn) contain needle[0,nn) ? 1/0. 45func ui_contains(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 { 46 if nn==0 { return 1 } 47 var i: i64 = 0 48 while i + nn <= hn { 49 var j: i64 = 0 50 var ok: i64 = 1 51 while j < nn { if hay[i+j]!=ndl[j] { ok=0; j=nn } else { j=j+1 } } 52 if ok==1 { return 1 } 53 i=i+1 54 } 55 return 0 56} 57 58// 16-bit real-mode emu WITH a disk + BIOS INT 13h (read sectors) + INT 10h (teletype). 59// mem = RAM, disk = the disk image. returns 0 = clean HLT, -1 = unknown opcode, -2 = ran away. 60// (inlined verbatim from the proven nx_emu_x86_stage2_test.nx) 61func emu_x86_real16_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64) -> 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 guard: i64 = 0 70 while guard < IMG_MAGIC_200000 { 71 guard = guard + 1 72 let op: i64 = mem[ip] as i64 73 if op == 0xF4 { return 0 } 74 var h: i64 = 0 75 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 76 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 77 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 78 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 79 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 80 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 81 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 82 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 83 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } // mov ah,imm8 84 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 85 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 86 if h==0 { if op==0xCD { 87 let vec: i64 = mem[ip+1] as i64 88 let ah: i64 = (ax >> 8) & 0xFF 89 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } // BIOS teletype 90 if vec==0x13 { if ah==0x02 { // BIOS read sectors 91 let count: i64 = ax & 0xFF 92 let sector: i64 = cx & 0xFF 93 let lba: i64 = sector - 1 94 var s: i64 = 0 95 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 } 96 ax = ax & 0xFF // AH=0 = success 97 } } 98 ip=ip+2; h=1 99 } } 100 if h==0 { return 0 - 1 } 101 } 102 return 0 - 2 103} 104 105const IMG_SZ: i64 = 1536 // 3 sectors: MBR + stage2 + kernel 106 107// author the 3-sector image into img[0,IMG_SZ). returns 0. 108func build_image(img: *u8) -> i64 { 109 var z: i64=0 110 while z<IMG_SZ { img[z]=0 as u8; z=z+1 } 111 112 // ---- sector 0: stage1 MBR (loads at 0x7C00) ---- 113 img[0]=0xB8 as u8; img[1]=0x02 as u8; img[2]=0x02 as u8 // mov ax,0x0202 (AH=02 read, AL=02 -> TWO sectors) 114 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 // mov bx,0x8000 (dest) 115 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 // mov cx,0x0002 (CL=2 -> LBA 1 = sector 1) 116 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 // mov dx,0x0080 (DL=0x80 first disk) 117 img[12]=0xCD as u8; img[13]=0x13 as u8 // int 0x13 (load sectors 1+2 to 0x8000) 118 let e9off: i64 = 14 119 let rel16: i64 = 0x8000 - (0x7C00 + e9off + 3) // jmp 0x8000 (E9 rel16) -> stage2 120 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 121 img[510]=0x55 as u8; img[511]=0xAA as u8 122 123 // ---- sector 1: stage2 loader (loads at 0x8000); offsets q relative to stage2 start ---- 124 var q: i64 = 0 125 img[512+q]=0xBE as u8; let si_q: i64 = q+1; q=q+3 // mov si, imm16 (patched -> 0x8000+msg) 126 let loop_q: i64 = q 127 img[512+q]=0xAC as u8; q=q+1 // lodsb 128 img[512+q]=0x08 as u8; img[512+q+1]=0xC0 as u8; q=q+2 // or al,al 129 img[512+q]=0x74 as u8; let jz_q: i64 = q+1; q=q+2 // jz tokernel (patched) 130 img[512+q]=0xB4 as u8; img[512+q+1]=0x0E as u8; q=q+2 // mov ah,0x0E 131 img[512+q]=0xCD as u8; img[512+q+1]=0x10 as u8; q=q+2 // int 0x10 132 img[512+q]=0xEB as u8; img[512+q+1]=((loop_q-(q+2)) & 0xFF) as u8; q=q+2 // jmp loop 133 let tok_q: i64 = q 134 let relK: i64 = 0x8200 - (0x8000 + tok_q + 3) // jmp 0x8200 (E9 rel16) -> kernel 135 img[512+q]=0xE9 as u8; img[512+q+1]=(relK & 0xFF) as u8; img[512+q+2]=((relK>>8) & 0xFF) as u8; q=q+3 136 let msg2_q: i64 = q 137 let msg2: *u8 = "NishiOS booting...\x0D\x0A\x00" 138 var mi: i64=0 139 while msg2[mi]!=(0 as u8) { img[512+q]=msg2[mi]; q=q+1; mi=mi+1 } 140 img[512+q]=0 as u8; q=q+1 141 img[512+jz_q] = ((tok_q - (jz_q+1)) & 0xFF) as u8 // patch jz rel8 -> tokernel 142 let si2_abs: i64 = 0x8000 + msg2_q // patch si = 0x8000 + msg2 offset 143 img[512+si_q] = (si2_abs & 0xFF) as u8 144 img[512+si_q+1] = ((si2_abs>>8) & 0xFF) as u8 145 146 // ---- sector 2: kernel (loads at 0x8200); offsets r relative to kernel start ---- 147 var r0: i64 = 0 148 img[IMG_MAGIC_1024+r0]=0xBE as u8; let si_r: i64 = r0+1; r0=r0+3 // mov si, imm16 (patched -> 0x8200+msg) 149 let loop_r: i64 = r0 150 img[IMG_MAGIC_1024+r0]=0xAC as u8; r0=r0+1 // lodsb 151 img[IMG_MAGIC_1024+r0]=0x08 as u8; img[IMG_MAGIC_1024+r0+1]=0xC0 as u8; r0=r0+2 // or al,al 152 img[IMG_MAGIC_1024+r0]=0x74 as u8; let jz_r: i64 = r0+1; r0=r0+2 // jz hang (patched) 153 img[IMG_MAGIC_1024+r0]=0xB4 as u8; img[IMG_MAGIC_1024+r0+1]=0x0E as u8; r0=r0+2 // mov ah,0x0E 154 img[IMG_MAGIC_1024+r0]=0xCD as u8; img[IMG_MAGIC_1024+r0+1]=0x10 as u8; r0=r0+2 // int 0x10 155 img[IMG_MAGIC_1024+r0]=0xEB as u8; img[IMG_MAGIC_1024+r0+1]=((loop_r-(r0+2)) & 0xFF) as u8; r0=r0+2 // jmp loop 156 let hang_r: i64 = r0 157 img[IMG_MAGIC_1024+r0]=0xF4 as u8; r0=r0+1 // hlt 158 let msgK_r: i64 = r0 159 let msgK: *u8 = "NISHIOS KERNEL\x0D\x0A\x00" 160 var ki: i64=0 161 while msgK[ki]!=(0 as u8) { img[IMG_MAGIC_1024+r0]=msgK[ki]; r0=r0+1; ki=ki+1 } 162 img[IMG_MAGIC_1024+r0]=0 as u8; r0=r0+1 163 img[IMG_MAGIC_1024+jz_r] = ((hang_r - (jz_r+1)) & 0xFF) as u8 // patch jz rel8 -> hang 164 let siK_abs: i64 = 0x8200 + msgK_r // patch si = 0x8200 + msgK offset 165 img[IMG_MAGIC_1024+si_r] = (siK_abs & 0xFF) as u8 166 img[IMG_MAGIC_1024+si_r+1] = ((siK_abs>>8) & 0xFF) as u8 167 return 0 168} 169 170// boot an image: copy sector 0 -> mem[0x7C00], run the on-disk bytes; report what got loaded + the console. 171// pre0/pre2 = bytes at 0x8000/0x8200 before (expect 0); the caller checks they became 0xBE after. 172func boot_image(img: *u8, console: *u8, clen: *i64, loaded: *i64) -> i64 { 173 let mem: *u8 = sys_mmap(IMG_MAGIC_65536) 174 var k: i64=0 175 while k<IMG_MAGIC_65536 { mem[k]=0 as u8; k=k+1 } 176 var j: i64=0 177 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 178 loaded[0] = mem[0x8000] as i64 // before load (expect 0) 179 loaded[1] = mem[0x8200] as i64 180 clen[0]=0 181 let rc: i64 = emu_x86_real16_disk(mem, img, 0x7C00, console, clen) 182 loaded[2] = mem[0x8000] as i64 // after load (expect 0xBE if stage2 copied off disk) 183 loaded[3] = mem[0x8200] as i64 // after load (expect 0xBE if kernel copied off disk) 184 return rc 185} 186 187func ui_read(path: *u8, out: *u8, cap: i64) -> i64 { 188 let fd: i64 = sys_openat_rd(path) 189 if fd < 0 { return 0 - 1 } 190 var n: i64 = 0; var go: i64 = 1 191 while go==1 { 192 let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n) 193 if rr<=0 { go=0 } else { n=n+rr } 194 if n>=cap { go=0 } 195 } 196 sys_close(fd) 197 return n 198} 199 200func main() -> i64 { 201 ui_puts("x86 ladder R9-FUSE: authoring + booting a REAL multi-sector Nishi USB image (MBR -> loader -> kernel)\n" as *u8) 202 203 let img: *u8 = sys_mmap(IMG_SZ + 16) 204 build_image(img) 205 206 // PERSIST the real artifact. 207 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os.img\x00" as *u8, 0x1a4) 208 if fd<=0 { ui_puts("R9-FUSE RED: cannot write image\n" as *u8); sys_exit(1); return 1 } 209 sys_write(fd, img, IMG_SZ) 210 sys_close(fd) 211 ui_puts(" wrote knowledge/status/nishi_os.img (" as *u8); ui_num(IMG_SZ); ui_puts(" bytes, 3 sectors) -- dd to a USB + boot on x86 = R10\n" as *u8) 212 213 // RE-READ it -- everything below executes the ON-DISK bytes, not the in-memory buffer. 214 let rd: *u8 = sys_mmap(IMG_SZ + 16) 215 let rn: i64 = ui_read("knowledge/status/nishi_os.img\x00" as *u8, rd, IMG_SZ) 216 217 // GOOD boot. 218 let con: *u8 = sys_mmap(256) 219 let clen: *i64 = sys_mmap(8) as *i64 220 let ld: *i64 = sys_mmap(64) as *i64 221 let rc: i64 = boot_image(rd, con, clen, ld) 222 ui_puts(" MBR ran INT 13h, jumped to stage2 -> kernel -> halt rc=" as *u8); ui_num(rc); ui_puts("\n" as *u8) 223 ui_puts(" 0x8000 (stage2) load: " as *u8); ui_num(ld[0]); ui_puts("->" as *u8); ui_num(ld[2]); ui_puts(" 0x8200 (kernel) load: " as *u8); ui_num(ld[1]); ui_puts("->" as *u8); ui_num(ld[3]); ui_puts(" (0xBE=190 = copied off disk)\n" as *u8) 224 ui_puts(" SOVEREIGN-EMU console: " as *u8); sys_write(1, con, clen[0]); ui_puts(" [" as *u8); ui_num(clen[0]); ui_puts(" chars]\n" as *u8) 225 226 // NEG CONTROL: a sibling image whose MBR loads only ONE sector (AL=01) -> kernel never loaded. 227 let bad: *u8 = sys_mmap(IMG_SZ + 16) 228 var c: i64=0 229 while c<IMG_SZ { bad[c]=rd[c]; c=c+1 } 230 bad[1]=0x01 as u8 // mov ax,0x0201 -> read only 1 sector 231 let con2: *u8 = sys_mmap(256) 232 let clen2: *i64 = sys_mmap(8) as *i64 233 let ld2: *i64 = sys_mmap(64) as *i64 234 let rc2: i64 = boot_image(bad, con2, clen2, ld2) 235 ui_puts(" NEG (MBR loads 1 sector): kernel-slot 0x8200 after=" as *u8); ui_num(ld2[3]); ui_puts(" rc=" as *u8); ui_num(rc2); ui_puts("\n" as *u8) 236 237 let kbanner: *u8 = "NISHIOS KERNEL" as *u8 238 var pass: i64=0 239 var ttl: i64=0 240 ttl=ttl+1; ui_puts(" T1 persisted byte-faithfully + 0x55AA signature: " as *u8); if rn==IMG_SZ { if ui_beq(rd, img, IMG_SZ)==1 { if rd[510]==(0x55 as u8) { if rd[511]==(0xAA as u8) { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 241 ttl=ttl+1; ui_puts(" T2 INT 13h loaded BOTH stage2+kernel off disk (0x8000 & 0x8200: 0->0xBE): " as *u8); if ld[0]==0 { if ld[1]==0 { if ld[2]==0xBE { if ld[3]==0xBE { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 242 ttl=ttl+1; ui_puts(" T3 the LOADED kernel ran (console contains 'NISHIOS KERNEL'): " as *u8); if ui_contains(con, clen[0], kbanner, 14)==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } 243 ttl=ttl+1; ui_puts(" T4 clean HLT (rc=0): " as *u8); if rc==0 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } 244 ttl=ttl+1; ui_puts(" T5 NEG: 1-sector load never reaches the kernel banner (liar-kill): " as *u8); if ui_contains(con2, clen2[0], kbanner, 14)==0 { if ld2[3]==0 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } 245 246 ui_puts("X86-USB-IMAGE-GATE passed " as *u8); ui_num(pass); ui_puts("/" as *u8); ui_num(ttl) 247 if pass==ttl { ui_puts(" verdict=GREEN (a REAL persisted multi-sector Nishi USB image boots MBR->loader->kernel by execution; long-mode + 64-bit desktop kernel = next rung)\n" as *u8); sys_exit(0); return 0 } 248 ui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 249}