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}