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}