code wiki / _hdl_build / nx_nishi_usb_parttable.nx
nx_nishi_usb_parttable.nx source
↩ module page · 282 lines · 17096 B
1// nx_nishi_usb_parttable.nx -- x86 ladder C1 (installer/disk side): a REAL MBR PARTITION TABLE on
2// the bootable Nishi USB image -- the disk-layout brick the installer needs.
3//
4// Until now nishi_os.img was RAW sectors (MBR boot code + stage2 + kernel) with NO partition table,
5// so it was a bootable blob, not a real disk an installer/OS can describe + install onto. This organ
6// ADDS a standard MBR partition table (4x 16-byte entries at offset 446 / 0x1BE) with ONE bootable
7// Nishi partition that describes where the OS payload lives, keeps the 0x55AA signature, and -- the
8// point of the rung -- PROVES the table is ADDITIVE: the image STILL boots MBR->loader->kernel by
9// EXECUTION on the sovereign 16-bit real-mode + BIOS-INT emu, AND the partition entry parses back to
10// exactly what we wrote.
11//
12// Why it matters (the "Nishi Publisher / USB installer" arc): a real installer partitions a target
13// disk, writes a partition table, marks a partition bootable, and installs the bootloader into it.
14// This rung builds + proves the partition-table half (author + parse + boot-still-works) -- the brick
15// the installer PROGRAM (next rung) will write onto a target, never-brick, refusing the system disk.
16//
17// KAT: (T1) partition 1 boot flag == 0x80 (bootable); (T2) type == Nishi (0x9E); (T3) LBA start==1 &
18// count==2 (covers the stage2+kernel payload); (T4) 0x55AA intact AND the image STILL boots to
19// 'NISHIOS KERNEL' (the table did NOT break boot -- additive, Rule 25); (T5) find_bootable() locates
20// entry 0; (T6, liar-kill) a sibling with ALL boot flags cleared -> find_bootable()==-1, so T1/T5
21// prove a REAL bootable flag read off disk, not a hardcode.
22//
23// NEVER-BRICK (Rule 26): writes a FILE (knowledge/status/nishi_os_part.img); the emu models INT 13h
24// *reads* only; touches no /dev and no host firmware -- by construction, not by promise.
25// The 16-bit + BIOS-INT emu + image author + boot harness are inlined verbatim from the proven
26// nx_nishi_usb_image.nx (a DRY exception the codebase already uses: a standalone main() blocks import).
27// expect_exit: 0 license_tier: ORIGINAL
28import "nx_syscalls.nx"
29const IMG_MAGIC_200000: i64 = 200000
30const IMG_MAGIC_32767: i64 = 32767
31const IMG_MAGIC_65536: i64 = 65536
32const IMG_MAGIC_1024: i64 = 1024
33
34func 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 }
35func ui_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
36func 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 }
37func ui_contains(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 {
38 if nn==0 { return 1 }
39 var i: i64 = 0
40 while i + nn <= hn {
41 var j: i64 = 0
42 var ok: i64 = 1
43 while j < nn { if hay[i+j]!=ndl[j] { ok=0; j=nn } else { j=j+1 } }
44 if ok==1 { return 1 }
45 i=i+1
46 }
47 return 0
48}
49
50// little-endian u32 read/write into a byte image (MBR partition fields are LE u32).
51func wr_u32_le(img: *u8, off: i64, v: i64) -> i64 {
52 img[off] = (v & 0xFF) as u8
53 img[off+1] = ((v>>8) & 0xFF) as u8
54 img[off+2] = ((v>>16) & 0xFF) as u8
55 img[off+3] = ((v>>24) & 0xFF) as u8
56 return 0
57}
58func rd_u32_le(img: *u8, off: i64) -> i64 {
59 return (img[off] as i64) | ((img[off+1] as i64)<<8) | ((img[off+2] as i64)<<16) | ((img[off+3] as i64)<<24)
60}
61// scan the 4 MBR partition entries (offset 446, stride 16) for the bootable (0x80) one. index or -1.
62func find_bootable(img: *u8) -> i64 {
63 var e: i64 = 0
64 while e < 4 {
65 let off: i64 = 446 + e*16
66 if (img[off] as i64) == 0x80 { return e }
67 e = e + 1
68 }
69 return 0 - 1
70}
71
72// 16-bit real-mode emu WITH a disk + BIOS INT 13h (read sectors) + INT 10h (teletype).
73// (inlined verbatim from the proven nx_nishi_usb_image.nx / nx_emu_x86_stage2_test.nx)
74func emu_x86_real16_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64) -> i64 {
75 var ip: i64 = entry
76 var ax: i64 = 0
77 var bx: i64 = 0
78 var cx: i64 = 0
79 var dx: i64 = 0
80 var si: i64 = 0
81 var zf: i64 = 0
82 var guard: i64 = 0
83 while guard < IMG_MAGIC_200000 {
84 guard = guard + 1
85 let op: i64 = mem[ip] as i64
86 if op == 0xF4 { return 0 }
87 var h: i64 = 0
88 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
89 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
90 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
91 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
92 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
93 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
94 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
95 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
96 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } // mov ah,imm8
97 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
98 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
99 if h==0 { if op==0xCD {
100 let vec: i64 = mem[ip+1] as i64
101 let ah: i64 = (ax >> 8) & 0xFF
102 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } // BIOS teletype
103 if vec==0x13 { if ah==0x02 { // BIOS read sectors
104 let count: i64 = ax & 0xFF
105 let sector: i64 = cx & 0xFF
106 let lba: i64 = sector - 1
107 var s: i64 = 0
108 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 }
109 ax = ax & 0xFF // AH=0 = success
110 } }
111 ip=ip+2; h=1
112 } }
113 if h==0 { return 0 - 1 }
114 }
115 return 0 - 2
116}
117
118const IMG_SZ: i64 = 1536 // 3 sectors: MBR + stage2 + kernel
119const PART_TYPE_NISHI: i64 = 0x9E // Nishi partition type (informational; legacy BIOS boots the MBR code, not the type)
120
121// author the 3-sector boot image (MBR -> loader -> kernel) PLUS a real MBR partition table.
122func build_image(img: *u8) -> i64 {
123 var z: i64=0
124 while z<IMG_SZ { img[z]=0 as u8; z=z+1 }
125
126 // ---- sector 0: stage1 MBR (loads at 0x7C00) ----
127 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)
128 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 // mov bx,0x8000 (dest)
129 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 // mov cx,0x0002 (CL=2 -> LBA 1 = sector 1)
130 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 // mov dx,0x0080 (DL=0x80 first disk)
131 img[12]=0xCD as u8; img[13]=0x13 as u8 // int 0x13 (load sectors 1+2 to 0x8000)
132 let e9off: i64 = 14
133 let rel16: i64 = 0x8000 - (0x7C00 + e9off + 3) // jmp 0x8000 (E9 rel16) -> stage2
134 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8
135 img[510]=0x55 as u8; img[511]=0xAA as u8
136
137 // ---- sector 1: stage2 loader (loads at 0x8000); offsets q relative to stage2 start ----
138 var q: i64 = 0
139 img[512+q]=0xBE as u8; let si_q: i64 = q+1; q=q+3 // mov si, imm16 (patched -> 0x8000+msg)
140 let loop_q: i64 = q
141 img[512+q]=0xAC as u8; q=q+1 // lodsb
142 img[512+q]=0x08 as u8; img[512+q+1]=0xC0 as u8; q=q+2 // or al,al
143 img[512+q]=0x74 as u8; let jz_q: i64 = q+1; q=q+2 // jz tokernel (patched)
144 img[512+q]=0xB4 as u8; img[512+q+1]=0x0E as u8; q=q+2 // mov ah,0x0E
145 img[512+q]=0xCD as u8; img[512+q+1]=0x10 as u8; q=q+2 // int 0x10
146 img[512+q]=0xEB as u8; img[512+q+1]=((loop_q-(q+2)) & 0xFF) as u8; q=q+2 // jmp loop
147 let tok_q: i64 = q
148 let relK: i64 = 0x8200 - (0x8000 + tok_q + 3) // jmp 0x8200 (E9 rel16) -> kernel
149 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
150 let msg2_q: i64 = q
151 let msg2: *u8 = "NishiOS booting...\x0D\x0A\x00"
152 var mi: i64=0
153 while msg2[mi]!=(0 as u8) { img[512+q]=msg2[mi]; q=q+1; mi=mi+1 }
154 img[512+q]=0 as u8; q=q+1
155 img[512+jz_q] = ((tok_q - (jz_q+1)) & 0xFF) as u8 // patch jz rel8 -> tokernel
156 let si2_abs: i64 = 0x8000 + msg2_q // patch si = 0x8000 + msg2 offset
157 img[512+si_q] = (si2_abs & 0xFF) as u8
158 img[512+si_q+1] = ((si2_abs>>8) & 0xFF) as u8
159
160 // ---- sector 2: kernel (loads at 0x8200); offsets r relative to kernel start ----
161 var r0: i64 = 0
162 img[IMG_MAGIC_1024+r0]=0xBE as u8; let si_r: i64 = r0+1; r0=r0+3 // mov si, imm16 (patched -> 0x8200+msg)
163 let loop_r: i64 = r0
164 img[IMG_MAGIC_1024+r0]=0xAC as u8; r0=r0+1 // lodsb
165 img[IMG_MAGIC_1024+r0]=0x08 as u8; img[IMG_MAGIC_1024+r0+1]=0xC0 as u8; r0=r0+2 // or al,al
166 img[IMG_MAGIC_1024+r0]=0x74 as u8; let jz_r: i64 = r0+1; r0=r0+2 // jz hang (patched)
167 img[IMG_MAGIC_1024+r0]=0xB4 as u8; img[IMG_MAGIC_1024+r0+1]=0x0E as u8; r0=r0+2 // mov ah,0x0E
168 img[IMG_MAGIC_1024+r0]=0xCD as u8; img[IMG_MAGIC_1024+r0+1]=0x10 as u8; r0=r0+2 // int 0x10
169 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
170 let hang_r: i64 = r0
171 img[IMG_MAGIC_1024+r0]=0xF4 as u8; r0=r0+1 // hlt
172 let msgK_r: i64 = r0
173 let msgK: *u8 = "NISHIOS KERNEL\x0D\x0A\x00"
174 var ki: i64=0
175 while msgK[ki]!=(0 as u8) { img[IMG_MAGIC_1024+r0]=msgK[ki]; r0=r0+1; ki=ki+1 }
176 img[IMG_MAGIC_1024+r0]=0 as u8; r0=r0+1
177 img[IMG_MAGIC_1024+jz_r] = ((hang_r - (jz_r+1)) & 0xFF) as u8 // patch jz rel8 -> hang
178 let siK_abs: i64 = 0x8200 + msgK_r // patch si = 0x8200 + msgK offset
179 img[IMG_MAGIC_1024+si_r] = (siK_abs & 0xFF) as u8
180 img[IMG_MAGIC_1024+si_r+1] = ((siK_abs>>8) & 0xFF) as u8
181
182 // ---- MBR partition table at offset 446 (0x1BE): one bootable Nishi partition ----
183 // 16-byte entry: status, CHS-first(3), type, CHS-last(3), LBA-start(u32 LE), sector-count(u32 LE).
184 // CHS is legacy/approximate (0xFE/0xFF/0xFF = the "use LBA" overflow marker); the LBA fields are
185 // authoritative. Partition covers sectors 1..2 = the stage2+kernel payload (sector 0 = the MBR).
186 let p: i64 = 446
187 img[p+0] = 0x80 as u8 // status: bootable (active)
188 img[p+1] = 0x00 as u8 // CHS first head
189 img[p+2] = 0x02 as u8 // CHS first sector (LBA 1)
190 img[p+3] = 0x00 as u8 // CHS first cylinder
191 img[p+4] = PART_TYPE_NISHI as u8 // partition type = Nishi
192 img[p+5] = 0xFE as u8 // CHS last head (0xFE/0xFF/0xFF = use-LBA marker)
193 img[p+6] = 0xFF as u8 // CHS last sector
194 img[p+7] = 0xFF as u8 // CHS last cylinder
195 wr_u32_le(img, p+8, 1) // LBA first sector = 1 (OS payload begins right after the MBR)
196 wr_u32_le(img, p+12, 2) // sector count = 2 (stage2 + kernel)
197 return 0
198}
199
200// boot an image: copy sector 0 -> mem[0x7C00], run the on-disk bytes; report what got loaded + console.
201func boot_image(img: *u8, console: *u8, clen: *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 // before load (expect 0)
208 loaded[1] = mem[0x8200] as i64
209 clen[0]=0
210 let rc: i64 = emu_x86_real16_disk(mem, img, 0x7C00, console, clen)
211 loaded[2] = mem[0x8000] as i64 // after load (expect 0xBE if stage2 copied off disk)
212 loaded[3] = mem[0x8200] as i64 // after load (expect 0xBE if kernel copied off disk)
213 return rc
214}
215
216func ui_read(path: *u8, out: *u8, cap: i64) -> i64 {
217 let fd: i64 = sys_openat_rd(path)
218 if fd < 0 { return 0 - 1 }
219 var n: i64 = 0; var go: i64 = 1
220 while go==1 {
221 let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n)
222 if rr<=0 { go=0 } else { n=n+rr }
223 if n>=cap { go=0 }
224 }
225 sys_close(fd)
226 return n
227}
228
229func main() -> i64 {
230 ui_puts("x86 ladder C1: authoring a REAL MBR PARTITION TABLE on the bootable Nishi USB image\n" as *u8)
231
232 let img: *u8 = sys_mmap(IMG_SZ + 16)
233 build_image(img)
234
235 // PERSIST the real artifact.
236 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os_part.img\x00" as *u8, 0x1a4)
237 if fd<=0 { ui_puts("C1 RED: cannot write image\n" as *u8); sys_exit(1); return 1 }
238 sys_write(fd, img, IMG_SZ)
239 sys_close(fd)
240 ui_puts(" wrote knowledge/status/nishi_os_part.img (" as *u8); ui_num(IMG_SZ); ui_puts(" bytes, partitioned) -- dd to USB + boot = R10\n" as *u8)
241
242 // RE-READ -- everything below parses/executes the ON-DISK bytes, not the in-memory buffer.
243 let rd: *u8 = sys_mmap(IMG_SZ + 16)
244 let rn: i64 = ui_read("knowledge/status/nishi_os_part.img\x00" as *u8, rd, IMG_SZ)
245
246 // PARSE the partition table off disk.
247 let boot_flag: i64 = rd[446] as i64
248 let ptype: i64 = rd[446+4] as i64
249 let lba: i64 = rd_u32_le(rd, 446+8)
250 let cnt: i64 = rd_u32_le(rd, 446+12)
251 let bootidx: i64 = find_bootable(rd)
252 ui_puts(" partition 1 (decimal): status=" as *u8); ui_num(boot_flag); ui_puts(" (0x80=128) type=" as *u8); ui_num(ptype); ui_puts(" (0x9E=158)" as *u8); ui_puts(" lba=" as *u8); ui_num(lba); ui_puts(" count=" as *u8); ui_num(cnt); ui_puts(" find_bootable=" as *u8); ui_num(bootidx); ui_puts("\n" as *u8)
253
254 // BOOT the partitioned image -- prove the table is ADDITIVE (boot still reaches the kernel).
255 let con: *u8 = sys_mmap(256)
256 let clen: *i64 = sys_mmap(8) as *i64
257 let ld: *i64 = sys_mmap(64) as *i64
258 let rc: i64 = boot_image(rd, con, clen, ld)
259 ui_puts(" booted partitioned image -> rc=" as *u8); ui_num(rc); ui_puts(" console: " as *u8); sys_write(1, con, clen[0]); ui_puts("\n" as *u8)
260
261 // NEG CONTROL: a sibling with ALL boot flags cleared -> no bootable partition.
262 let bad: *u8 = sys_mmap(IMG_SZ + 16)
263 var c: i64=0
264 while c<IMG_SZ { bad[c]=rd[c]; c=c+1 }
265 bad[446]=0 as u8; bad[462]=0 as u8; bad[478]=0 as u8; bad[494]=0 as u8
266 let badidx: i64 = find_bootable(bad)
267 ui_puts(" NEG (boot flags cleared): find_bootable=" as *u8); ui_num(badidx); ui_puts("\n" as *u8)
268
269 let kbanner: *u8 = "NISHIOS KERNEL" as *u8
270 var pass: i64=0
271 var ttl: i64=0
272 ttl=ttl+1; ui_puts(" T1 partition 1 is bootable (status==0x80): " as *u8); if boot_flag==0x80 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
273 ttl=ttl+1; ui_puts(" T2 partition type == Nishi (0x9E): " as *u8); if ptype==PART_TYPE_NISHI { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
274 ttl=ttl+1; ui_puts(" T3 LBA start==1 & count==2 (covers stage2+kernel): " as *u8); if lba==1 { if cnt==2 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) }
275 ttl=ttl+1; ui_puts(" T4 0x55AA intact AND still boots to 'NISHIOS KERNEL' (additive): " as *u8); if rd[510]==(0x55 as u8) { if rd[511]==(0xAA as u8) { if rc==0 { 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) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) }
276 ttl=ttl+1; ui_puts(" T5 find_bootable() locates entry 0: " as *u8); if bootidx==0 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
277 ttl=ttl+1; ui_puts(" T6 NEG: cleared boot flags -> find_bootable()==-1 (liar-kill): " as *u8); if badidx==(0-1) { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
278
279 ui_puts("X86-USB-PARTTABLE-GATE passed " as *u8); ui_num(pass); ui_puts("/" as *u8); ui_num(ttl)
280 if pass==ttl { ui_puts(" verdict=GREEN (a REAL MBR partition table on the Nishi USB image: bootable Nishi partition, parses off disk, ADDITIVE -- still boots; NishiFS + installer = next rungs)\n" as *u8); sys_exit(0); return 0 }
281 ui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
282}