code wiki / (root) / nx_emu_x86_stage2_test.nx

nx_emu_x86_stage2_test.nx source

↩ module page · 132 lines · 8748 B

1// nx_emu_x86_stage2_test.nx -- x86 ladder R9 rung-4: a REAL MULTI-STAGE LOADER, executed end-to-end. 2// A 512-byte boot sector cannot BE an OS -- it must LOAD one. This builds a 2-sector disk (sector 0 = 3// stage-1 MBR, sector 1 = stage-2), and the emu now models BIOS INT 13h (AH=0x02 read-sectors). The 4// real boot flow runs on the sovereign machine: stage-1 issues INT 13h to read sector 1 into memory at 5// 0x8000, then E9-jumps to it; stage-2 prints "STAGE2 OK" over INT 10h and halts. This is the 6// multi-stage loader the real-OS ladder needs -- proven by EXECUTION, not structure. 7// KAT: stage-2 was actually copied off disk by INT 13h (0x8000 was empty, now holds stage-2), the 8// console shows stage-2's output, and the cpu halted cleanly. 9// HONEST SCOPE: stage-2 here is a tiny printer. The rungs above: stage-2 -> protected/long mode -> 10// load the real kernel -> GDT/IDT/paging/MM/scheduler/syscalls -> userspace. No hw writes (Rule 26). 11// expect_exit: 0 license_tier: ORIGINAL 12import "nx_syscalls.nx" 13 14func s2_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 15func s2_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 } 16func s2_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 } 17 18// 16-bit real-mode emu WITH a disk + BIOS INT 13h (read sectors). mem = RAM, disk = the disk image. 19// returns 0 = clean HLT, -1 = unknown opcode, -2 = ran away. 20func emu_x86_real16_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64) -> i64 { 21 var ip: i64 = entry 22 var ax: i64 = 0 23 var bx: i64 = 0 24 var cx: i64 = 0 25 var dx: i64 = 0 26 var si: i64 = 0 27 var zf: i64 = 0 28 var guard: i64 = 0 29 while guard < 200000 { 30 guard = guard + 1 31 let op: i64 = mem[ip] as i64 32 if op == 0xF4 { return 0 } 33 var h: i64 = 0 34 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 35 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 36 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 37 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 38 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 39 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 40 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 41 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 42 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } // mov ah,imm8 43 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 44 if h==0 { if op==0xE9 { var r3: i64 = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); if r3>32767 { r3=r3-65536 } ip=ip+3+r3; h=1 } } // jmp rel16 45 if h==0 { if op==0xCD { 46 let vec: i64 = mem[ip+1] as i64 47 let ah: i64 = (ax >> 8) & 0xFF 48 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } // BIOS teletype 49 if vec==0x13 { if ah==0x02 { // BIOS read sectors 50 let count: i64 = ax & 0xFF 51 let sector: i64 = cx & 0xFF 52 let lba: i64 = sector - 1 53 var s: i64 = 0 54 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 } 55 ax = ax & 0xFF // AH=0 = success 56 } } 57 ip=ip+2; h=1 58 } } 59 if h==0 { return 0 - 1 } 60 } 61 return 0 - 2 62} 63 64func main() -> i64 { 65 s2_puts("x86 ladder R9 rung-4: REAL MULTI-STAGE LOADER (stage1 --INT13h--> loads stage2 --> runs it)\n" as *u8) 66 let img: *u8 = sys_mmap(2048) 67 var z: i64=0 68 while z<2048 { img[z]=0 as u8; z=z+1 } 69 70 // ---- stage-1 (sector 0, loads at 0x7C00) ---- 71 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) 72 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 // mov bx,0x8000 (dest) 73 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 // mov cx,0x0002 (CL=2 -> LBA 1 = sector 1) 74 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 // mov dx,0x0080 (DL=0x80 first disk) 75 img[12]=0xCD as u8; img[13]=0x13 as u8 // int 0x13 (load stage2) 76 let e9off: i64 = 14 77 let rel16: i64 = 0x8000 - (0x7C00 + e9off + 3) // jmp 0x8000 78 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 79 img[510]=0x55 as u8; img[511]=0xAA as u8 80 81 // ---- stage-2 (sector 1, loads at 0x8000); offsets q are relative to stage-2 start ---- 82 var q: i64 = 0 83 img[512+q]=0xBE as u8; let si_q: i64 = q+1; q=q+3 // mov si, imm16 (patched to 0x8000+msg) 84 let loop_q: i64 = q 85 img[512+q]=0xAC as u8; q=q+1 // lodsb 86 img[512+q]=0x08 as u8; img[512+q+1]=0xC0 as u8; q=q+2 // or al,al 87 img[512+q]=0x74 as u8; let jz_q: i64 = q+1; q=q+2 // jz hang (patched) 88 img[512+q]=0xB4 as u8; img[512+q+1]=0x0E as u8; q=q+2 // mov ah,0x0E 89 img[512+q]=0xCD as u8; img[512+q+1]=0x10 as u8; q=q+2 // int 0x10 90 img[512+q]=0xEB as u8; img[512+q+1]=((loop_q-(q+2)) & 0xFF) as u8; q=q+2 // jmp loop 91 let hang_q: i64 = q 92 img[512+q]=0xF4 as u8; q=q+1 // hlt 93 let msg_q: i64 = q 94 let msg: *u8 = "STAGE2 OK\x0D\x0A\x00" 95 var mi: i64=0 96 while msg[mi]!=(0 as u8) { img[512+q]=msg[mi]; q=q+1; mi=mi+1 } 97 img[512+q]=0 as u8; q=q+1 98 // patch jz rel8 = hang_q - (jz_q+1) 99 img[512+jz_q] = ((hang_q - (jz_q+1)) & 0xFF) as u8 100 // patch si = 0x8000 + msg_q 101 let si_abs: i64 = 0x8000 + msg_q 102 img[512+si_q] = (si_abs & 0xFF) as u8 103 img[512+si_q+1] = ((si_abs>>8) & 0xFF) as u8 104 105 // ---- the machine: RAM with ONLY sector 0 loaded at 0x7C00 (stage2 must be pulled off disk by INT 13h) ---- 106 let mem: *u8 = sys_mmap(65536) 107 var k: i64=0 108 while k<65536 { mem[k]=0 as u8; k=k+1 } 109 var j: i64=0 110 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 111 let pre: i64 = mem[0x8000] as i64 // should be 0 (stage2 not loaded yet) 112 113 let console: *u8 = sys_mmap(256) 114 let clen: *i64 = sys_mmap(8) as *i64 115 clen[0]=0 116 let rc: i64 = emu_x86_real16_disk(mem, img, 0x7C00, console, clen) 117 let post: i64 = mem[0x8000] as i64 // should now be stage2's first byte (0xBE) 118 119 s2_puts(" stage1 ran INT 13h, jumped to 0x8000 -> halt rc=" as *u8); s2_num(rc); s2_puts("\n" as *u8) 120 s2_puts(" 0x8000 before load=" as *u8); s2_num(pre); s2_puts(" after load=" as *u8); s2_num(post); s2_puts(" (0xBE=190 means stage2 was copied off disk)\n" as *u8) 121 s2_puts(" SOVEREIGN-EMU console: " as *u8); sys_write(1, console, clen[0]); s2_puts(" [" as *u8); s2_num(clen[0]); s2_puts(" chars]\n" as *u8) 122 123 var pass: i64=0 124 var ttl: i64=0 125 ttl=ttl+1; s2_puts(" T1 INT 13h actually loaded stage2 off disk (0x8000: 0->0xBE): " as *u8); if pre==0 { if post==0xBE { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) } } else { s2_puts("FAIL\n" as *u8) } 126 ttl=ttl+1; s2_puts(" T2 loaded stage2 executed (console == 'STAGE2 OK\\r\\n'): " as *u8); if clen[0]==11 { if s2_beq(console, "STAGE2 OK\x0D\x0A\x00" as *u8, 11)==1 { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) } } else { s2_puts("FAIL\n" as *u8) } 127 ttl=ttl+1; s2_puts(" T3 cpu halted cleanly (rc=0): " as *u8); if rc==0 { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) } 128 129 s2_puts("X86-STAGE2-LOADER-GATE passed " as *u8); s2_num(pass); s2_puts("/" as *u8); s2_num(ttl) 130 if pass==ttl { s2_puts(" verdict=GREEN (a REAL multi-stage loader runs end-to-end on the sovereign machine; stage2->kernel->OS = the ladder above)\n" as *u8); sys_exit(0); return 0 } 131 s2_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 132}