code wiki / _hdl_build / nx_nishi_usb_os.nx

nx_nishi_usb_os.nx source

↩ module page · 318 lines · 20581 B

1// nx_nishi_usb_os.nx -- x86 ladder R-KERN-4: INTEGRATION -- one booted kernel that runs a userland. 2// 3// The kernel-internals rungs proved GDT/IDT (R-KERN-1), paging (R-KERN-2), and preemption (R-KERN-3) 4// SEPARATELY. This fuses them into ONE kernel, loaded off the persisted image, that performs the full 5// OS bring-up and then runs userland tasks: 6// boot (MBR -> INT 13h load -> kernel) -> LONG mode -> lgdt (GDT) -> build the IDT in RAM with a TIMER 7// gate (0x20) AND a SYSCALL gate (0x80) -> lidt -> build page tables + load CR3 -> STI -> launch. 8// Then two userland tasks run preemptively (timer-driven, round-robin); each loops incrementing its own 9// counter and issues `int 0x80`, which VECTORS THROUGH THE KERNEL'S IDT to the syscall handler the kernel 10// installed -- the handler services it (increments a kernel-side syscall counter) and IRETs back. 11// 12// KAT: ONE kernel did it all -- (T1) booted off disk to LONG mode; (T2) lgdt loaded the GDT; (T3) lidt 13// loaded the IDT and the syscall gate points at the kernel's handler; (T4) CR3 + the page walk works; 14// (T5) BOTH userland tasks ran (preemptive multitasking); (T6) the tasks' syscalls were SERVICED by the 15// kernel's handler (kernel-side count > 0). NEG/liar-kill (T7): with the timer masked, task B never runs. 16// 17// HONEST SCOPE: like the other rungs, the context switch + the IF-gated timer are modeled (as 18// nx_kernel_sched), and the IDT here uses 8-byte handler slots (R-KERN-1 already proved the full 19// split-field gate); the syscall path (task -> IDT[0x80] -> kernel handler -> iret) is genuinely 20// vectored. The shared syscall counter is intentionally unlocked, so a preempt mid-increment can race 21// (a lower bound, and an honest demonstration of why kernels CLI/lock in handlers). NEVER-BRICK 22// (Rule 26): writes a FILE; INT 13h *reads* only; no /dev. expect_exit: 0 license_tier: ORIGINAL 23import "nx_syscalls.nx" 24import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 25const IMG_MAGIC_200000: i64 = 200000 26const IMG_MAGIC_200001: i64 = 200001 27const IMG_MAGIC_32767: i64 = 32767 28const IMG_MAGIC_65536: i64 = 65536 29const IMG_MAGIC_5000000: i64 = 5000000 30const IMG_MAGIC_5000001: i64 = 5000001 31 32func os_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 33// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 34// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 35// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 36// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 37func os_num(v: i64) -> i64 { nxi_out(v); return 0 } 38func os_mode(cr0: i64, efer: i64) -> i64 { let pe: i64=cr0&1; let pg: i64=(cr0>>31)&1; let lme: i64=(efer>>8)&1; if pe==0 { return 0 } if pg==1 { if lme==1 { return 2 } } return 1 } 39func os_st64(mem: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<8 { mem[a+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 } 40func os_ld64(mem: *u8, a: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((mem[a+i] as i64)<<(i*8)); i=i+1 } return v } 41func os_ld16(mem: *u8, a: i64) -> i64 { return (mem[a] as i64) | ((mem[a+1] as i64)<<8) } 42 43func pg_walk(mem: *u8, cr3: i64, va: i64) -> i64 { 44 let i1: i64=(va>>39)&0x1FF; let e1: i64=os_ld64(mem, cr3+i1*8); if (e1&1)==0 { return 0-1 } 45 let b2: i64=(e1>>12)<<12; let i2: i64=(va>>30)&0x1FF; let e2: i64=os_ld64(mem, b2+i2*8); if (e2&1)==0 { return 0-1 } 46 let b3: i64=(e2>>12)<<12; let i3: i64=(va>>21)&0x1FF; let e3: i64=os_ld64(mem, b3+i3*8); if (e3&1)==0 { return 0-1 } 47 let b4: i64=(e3>>12)<<12; let i4: i64=(va>>12)&0x1FF; let e4: i64=os_ld64(mem, b4+i4*8); if (e4&1)==0 { return 0-1 } 48 return ((e4>>12)<<12) + (va & 0xFFF) 49} 50 51const IMG_SZ: i64 = 1024 52const IDT_BASE: i64 = 0xD000 53const SYS_SLOT: i64 = 0xD400 // IDT_BASE + 0x80*8 54const SYSCOUNT: i64 = 0xC000 55const STACK_A: i64 = 0x6000 56const STACK_B: i64 = 0x6800 57 58// THE OS EMU. Phase 0 = the kernel (single context) boots + inits, until HLT (= launch). Phase 1 = 59// preemptive scheduling of 2 userland tasks with int-0x80 syscall vectoring through the kernel's IDT. 60// st[]: 1=mode 2=gdtr_base 3=gdtr_limit 4=idtr_base 7=cr3 8=syscall-gate 9=pgwalk(0x1000) 10=syscount 11=cA 12=cB. 61func emu_os(mem: *u8, disk: *u8, entry: i64, taskA: i64, taskB: i64, timer_on: i64, st: *i64, loaded: *i64) -> i64 { 62 let kreg: *i64 = sys_mmap(16 * 8) as *i64 63 var kpc: i64=entry 64 var ax: i64=0 65 var bx: i64=0 66 var cx: i64=0 67 var dx: i64=0 68 var cr0: i64=0 69 var efer: i64=0 70 var cr3: i64=0 71 var gdtr_base: i64=0 72 var gdtr_limit: i64=0 73 var idtr_base: i64=0 74 var idtr_limit: i64=0 75 var g0: i64=0 76 // ---- PHASE 0: the kernel boots + initializes ---- 77 while g0 < IMG_MAGIC_200000 { 78 g0 = g0 + 1 79 let op: i64 = mem[kpc] as i64 80 if op == 0xF4 { kpc=kpc+1; g0=IMG_MAGIC_200001 } // HLT = launch the scheduler (leave phase 0) 81 if g0 != IMG_MAGIC_200001 { 82 var h: i64 = 0 83 if h==0 { if op==0x48 { 84 let o2: i64 = mem[kpc+1] as i64 85 if o2==0xC7 { let m: i64=mem[kpc+2] as i64; let imm: i64=(mem[kpc+3] as i64)|((mem[kpc+4] as i64)<<8)|((mem[kpc+5] as i64)<<16)|((mem[kpc+6] as i64)<<24); kreg[m&7]=imm; kpc=kpc+7; h=1 } 86 if h==0 { if o2==0x89 { let m: i64=mem[kpc+2] as i64; if ((m>>6)&3)==3 { kreg[m&7]=kreg[(m>>3)&7] } else { os_st64(mem, kreg[m&7], kreg[(m>>3)&7]) } kpc=kpc+3; h=1 } } 87 if h==0 { return 0 - 11 } 88 } } 89 if h==0 { if op==0x0F { 90 let b1: i64 = mem[kpc+1] as i64 91 if b1==0x01 { let m: i64=mem[kpc+2] as i64; let addr: i64=kreg[m&7]; if ((m>>3)&7)==2 { gdtr_limit=os_ld16(mem,addr); gdtr_base=os_ld64(mem,addr+2) } if ((m>>3)&7)==3 { idtr_limit=os_ld16(mem,addr); idtr_base=os_ld64(mem,addr+2) } kpc=kpc+3; h=1 } 92 if h==0 { if b1==0x22 { let m: i64=mem[kpc+2] as i64; let cri: i64=(m>>3)&7; if cri==0 { cr0=kreg[0] } if cri==3 { cr3=kreg[0] }; kpc=kpc+3; h=1 } } 93 if h==0 { if b1==0x30 { efer=kreg[0]; kpc=kpc+2; h=1 } } 94 if h==0 { return 0 - 12 } 95 } } 96 if h==0 { if op==0xFB { kpc=kpc+1; h=1 } } // sti (kernel enables interrupts) 97 if h==0 { if op==0xB8 { ax=(mem[kpc+1] as i64)|((mem[kpc+2] as i64)<<8); kpc=kpc+3; h=1 } } 98 if h==0 { if op==0xBB { bx=(mem[kpc+1] as i64)|((mem[kpc+2] as i64)<<8); kpc=kpc+3; h=1 } } 99 if h==0 { if op==0xB9 { cx=(mem[kpc+1] as i64)|((mem[kpc+2] as i64)<<8); kpc=kpc+3; h=1 } } 100 if h==0 { if op==0xBA { dx=(mem[kpc+1] as i64)|((mem[kpc+2] as i64)<<8); kpc=kpc+3; h=1 } } 101 if h==0 { if op==0xE9 { var r3: i64=(mem[kpc+1] as i64)|((mem[kpc+2] as i64)<<8); if r3>IMG_MAGIC_32767 { r3=r3-IMG_MAGIC_65536 } kpc=kpc+3+r3; h=1 } } 102 if h==0 { if op==0xCD { let vec: i64=mem[kpc+1] as i64; if vec==0x13 { let count: i64=ax&0xFF; let lba: i64=(cx&0xFF)-1; var s: i64=0; while s<count*512 { mem[bx+s]=disk[lba*512+s]; s=s+1 } } kpc=kpc+2; h=1 } } 103 if h==0 { return 0 - 13 } 104 } 105 } 106 loaded[0] = mem[0x8000] as i64 107 st[1]=os_mode(cr0,efer); st[2]=gdtr_base; st[3]=gdtr_limit; st[4]=idtr_base; st[7]=cr3 108 109 // ---- PHASE 1: preemptive scheduling of 2 userland tasks ---- 110 let treg: *i64 = sys_mmap(2 * 16 * 8) as *i64 111 let tpc: *i64 = sys_mmap(2 * 8) as *i64 112 tpc[0]=taskA; tpc[1]=taskB 113 treg[0*16+4]=STACK_A; treg[1*16+4]=STACK_B 114 var cur: i64=0 115 var timer: i64=0 116 var ticks: i64=0 117 var g1: i64=0 118 while g1 < IMG_MAGIC_5000000 { 119 g1 = g1 + 1 120 if ticks >= 40 { g1=IMG_MAGIC_5000001 } 121 if g1 != IMG_MAGIC_5000001 { 122 timer=timer+1 123 if timer>=8 { timer=0; if timer_on==1 { if cur==0 { cur=1 } else { cur=0 } ticks=ticks+1 } } // TIMER PREEMPT 124 let base: i64=cur*16 125 let p: i64=tpc[cur] 126 let b: i64=mem[p] as i64 127 var h2: i64=0 128 if h2==0 { if b==0xCF { tpc[cur]=os_ld64(mem, treg[base+4]); treg[base+4]=treg[base+4]+8; h2=1 } } // iret 129 if h2==0 { if b==0xEB { var r: i64=mem[p+1] as i64; if r>127 { r=r-256 } tpc[cur]=p+2+r; h2=1 } } 130 if h2==0 { if b==0xCD { 131 let vec: i64=mem[p+1] as i64 132 if vec==0x80 { let hd: i64=os_ld64(mem, SYS_SLOT); treg[base+4]=treg[base+4]-8; os_st64(mem, treg[base+4], p+2); tpc[cur]=hd } else { tpc[cur]=p+2 } 133 h2=1 134 } } 135 if h2==0 { if b==0x48 { 136 let o2: i64=mem[p+1] as i64 137 if o2==0xC7 { let m: i64=mem[p+2] as i64; let imm: i64=(mem[p+3] as i64)|((mem[p+4] as i64)<<8)|((mem[p+5] as i64)<<16)|((mem[p+6] as i64)<<24); treg[base+(m&7)]=imm; tpc[cur]=p+7; h2=1 } 138 if h2==0 { if o2==0x8B { let m: i64=mem[p+2] as i64; if ((m>>6)&3)==3 { treg[base+((m>>3)&7)]=treg[base+(m&7)] } else { treg[base+((m>>3)&7)]=os_ld64(mem, treg[base+(m&7)]) } tpc[cur]=p+3; h2=1 } } 139 if h2==0 { if o2==0x89 { let m: i64=mem[p+2] as i64; if ((m>>6)&3)==3 { treg[base+(m&7)]=treg[base+((m>>3)&7)] } else { os_st64(mem, treg[base+(m&7)], treg[base+((m>>3)&7)]) } tpc[cur]=p+3; h2=1 } } 140 if h2==0 { if o2==0x01 { let m: i64=mem[p+2] as i64; treg[base+(m&7)]=treg[base+(m&7)]+treg[base+((m>>3)&7)]; tpc[cur]=p+3; h2=1 } } 141 if h2==0 { return 0 - 21 } 142 } } 143 if h2==0 { return 0 - 23 } 144 } 145 } 146 loaded[1] = ticks 147 st[8]=os_ld64(mem, SYS_SLOT); st[9]=pg_walk(mem, cr3, 0x1000); st[10]=os_ld64(mem, SYSCOUNT); st[11]=os_ld64(mem, 0xA000); st[12]=os_ld64(mem, 0xB000) 148 return 0 149} 150 151func uf_movr(img: *u8, base: i64, o: i64, modrm: i64, imm: i64) -> i64 { 152 img[base+o]=0x48 as u8; img[base+o+1]=0xC7 as u8; img[base+o+2]=(modrm&0xff) as u8 153 img[base+o+3]=(imm&0xff) as u8; img[base+o+4]=((imm>>8)&0xff) as u8; img[base+o+5]=((imm>>16)&0xff) as u8; img[base+o+6]=((imm>>24)&0xff) as u8 154 return o+7 155} 156func emit_pte(img: *u8, base: i64, o: i64, val: i64, addr: i64) -> i64 { 157 var r: i64 = uf_movr(img, base, o, 0xC0, val) 158 r = uf_movr(img, base, r, 0xC3, addr) 159 img[base+r]=0x48 as u8; img[base+r+1]=0x89 as u8; img[base+r+2]=0x03 as u8 160 return r+3 161} 162func wr16(img: *u8, a: i64, v: i64) -> i64 { img[a]=(v&0xff) as u8; img[a+1]=((v>>8)&0xff) as u8; return 0 } 163func wr64(img: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<8 { img[a+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 } 164func wr32(img: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<4 { img[a+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 } 165 166// info[]: 0=taskA_abs 1=taskB_abs 2=GDT_ABS 167func build_image(img: *u8, info: *i64) -> i64 { 168 var z: i64=0 169 while z<IMG_SZ { img[z]=0 as u8; z=z+1 } 170 img[0]=0xB8 as u8; img[1]=0x01 as u8; img[2]=0x02 as u8 171 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 172 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 173 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 174 img[12]=0xCD as u8; img[13]=0x13 as u8 175 let rel16: i64 = 0x8000 - (0x7C00 + 14 + 3) 176 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 177 img[510]=0x55 as u8; img[511]=0xAA as u8 178 179 var r: i64 = 0 180 // mode transition -> long 181 r = uf_movr(img, 512, r, 0xC0, 1); img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 182 r = uf_movr(img, 512, r, 0xC0, 0x100); img[512+r]=0x0F as u8; img[512+r+1]=0x30 as u8; r=r+2 183 r = uf_movr(img, 512, r, 0xC0, 0x80000001); img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 184 // lgdt [rax] 185 r = uf_movr(img, 512, r, 0xC0, 0); let gp_imm: i64 = r-4; img[512+r]=0x0F as u8; img[512+r+1]=0x01 as u8; img[512+r+2]=0x10 as u8; r=r+3 186 // install IDT[0x20] = timer handler, IDT[0x80] = syscall handler (8-byte slots in RAM at IDT_BASE) 187 r = uf_movr(img, 512, r, 0xC0, 0); let th_imm: i64 = r-4; r = uf_movr(img, 512, r, 0xC3, 0xD100); img[512+r]=0x48 as u8; img[512+r+1]=0x89 as u8; img[512+r+2]=0x03 as u8; r=r+3 188 r = uf_movr(img, 512, r, 0xC0, 0); let sh_imm: i64 = r-4; r = uf_movr(img, 512, r, 0xC3, 0xD400); img[512+r]=0x48 as u8; img[512+r+1]=0x89 as u8; img[512+r+2]=0x03 as u8; r=r+3 189 // lidt [rax] 190 r = uf_movr(img, 512, r, 0xC0, 0); let ip_imm: i64 = r-4; img[512+r]=0x0F as u8; img[512+r+1]=0x01 as u8; img[512+r+2]=0x18 as u8; r=r+3 191 // paging + CR3 192 r = emit_pte(img, 512, r, 0x2001, 0x1000) 193 r = emit_pte(img, 512, r, 0x3001, 0x2000) 194 r = emit_pte(img, 512, r, 0x4001, 0x3000) 195 r = emit_pte(img, 512, r, 0x40001, 0x4008) 196 r = emit_pte(img, 512, r, 0x50001, 0x4010) 197 r = uf_movr(img, 512, r, 0xC0, 0x1000); img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xD8 as u8; r=r+3 198 // launch 199 img[512+r]=0xFB as u8; r=r+1 // sti 200 img[512+r]=0xF4 as u8; r=r+1 // hlt = launch 201 // timer handler (installation proof; the switch is modeled by the emu) 202 let timer_h: i64 = r 203 img[512+r]=0xCF as u8; r=r+1 // iret 204 // syscall handler: SYSCOUNT++ via rsi/rdx/rdi (registers the tasks don't use) 205 let sys_h: i64 = r 206 r = uf_movr(img, 512, r, 0xC6, SYSCOUNT) // mov rsi, SYSCOUNT 207 img[512+r]=0x48 as u8; img[512+r+1]=0x8B as u8; img[512+r+2]=0x16 as u8; r=r+3 // mov rdx, [rsi] 208 r = uf_movr(img, 512, r, 0xC7, 1) // mov rdi, 1 209 img[512+r]=0x48 as u8; img[512+r+1]=0x01 as u8; img[512+r+2]=0xFA as u8; r=r+3 // add rdx, rdi 210 img[512+r]=0x48 as u8; img[512+r+1]=0x89 as u8; img[512+r+2]=0x16 as u8; r=r+3 // mov [rsi], rdx 211 img[512+r]=0xCF as u8; r=r+1 // iret 212 // task A 213 let taskA: i64 = r 214 r = uf_movr(img, 512, r, 0xC3, 0xA000) // mov rbx, CTR_A 215 r = uf_movr(img, 512, r, 0xC1, 1) // mov rcx, 1 216 let loopA: i64 = r 217 img[512+r]=0x48 as u8; img[512+r+1]=0x8B as u8; img[512+r+2]=0x03 as u8; r=r+3 // mov rax,[rbx] 218 img[512+r]=0x48 as u8; img[512+r+1]=0x01 as u8; img[512+r+2]=0xC8 as u8; r=r+3 // add rax,rcx 219 img[512+r]=0x48 as u8; img[512+r+1]=0x89 as u8; img[512+r+2]=0x03 as u8; r=r+3 // mov [rbx],rax 220 img[512+r]=0xCD as u8; img[512+r+1]=0x80 as u8; r=r+2 // int 0x80 221 img[512+r]=0xEB as u8; img[512+r+1]=((loopA-(r+2)) & 0xff) as u8; r=r+2 // jmp loopA 222 // task B 223 let taskB: i64 = r 224 r = uf_movr(img, 512, r, 0xC3, 0xB000) 225 r = uf_movr(img, 512, r, 0xC1, 1) 226 let loopB: i64 = r 227 img[512+r]=0x48 as u8; img[512+r+1]=0x8B as u8; img[512+r+2]=0x03 as u8; r=r+3 228 img[512+r]=0x48 as u8; img[512+r+1]=0x01 as u8; img[512+r+2]=0xC8 as u8; r=r+3 229 img[512+r]=0x48 as u8; img[512+r+1]=0x89 as u8; img[512+r+2]=0x03 as u8; r=r+3 230 img[512+r]=0xCD as u8; img[512+r+1]=0x80 as u8; r=r+2 231 img[512+r]=0xEB as u8; img[512+r+1]=((loopB-(r+2)) & 0xff) as u8; r=r+2 232 // GDT data 233 let gdt_r: i64 = r 234 img[512+gdt_r+8]=0xFF as u8; img[512+gdt_r+9]=0xFF as u8; img[512+gdt_r+13]=0x9A as u8; img[512+gdt_r+14]=0xAF as u8 235 img[512+gdt_r+16]=0xFF as u8; img[512+gdt_r+17]=0xFF as u8; img[512+gdt_r+21]=0x92 as u8; img[512+gdt_r+22]=0xCF as u8 236 r = gdt_r + 24 237 // pseudo-descriptors 238 let gdt_ps: i64 = r; wr16(img, 512+gdt_ps, 23); r=r+10 239 let idt_ps: i64 = r; wr16(img, 512+idt_ps, 0x407); wr64(img, 512+idt_ps+2, IDT_BASE); r=r+10 240 241 let GDT_ABS: i64 = 0x8000+gdt_r 242 let GDTPS_ABS: i64 = 0x8000+gdt_ps 243 let IDTPS_ABS: i64 = 0x8000+idt_ps 244 let TIMERH_ABS: i64 = 0x8000+timer_h 245 let SYSH_ABS: i64 = 0x8000+sys_h 246 wr32(img, 512+gp_imm, GDTPS_ABS) 247 wr32(img, 512+th_imm, TIMERH_ABS) 248 wr32(img, 512+sh_imm, SYSH_ABS) 249 wr32(img, 512+ip_imm, IDTPS_ABS) 250 wr64(img, 512+gdt_ps+2, GDT_ABS) 251 252 info[0]=0x8000+taskA; info[1]=0x8000+taskB; info[2]=GDT_ABS 253 return 0 254} 255 256func os_read(path: *u8, out: *u8, cap: i64) -> i64 { 257 let fd: i64 = sys_openat_rd(path) 258 if fd < 0 { return 0 - 1 } 259 var n: i64 = 0; var go: i64 = 1 260 while go==1 { let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n); if rr<=0 { go=0 } else { n=n+rr } if n>=cap { go=0 } } 261 sys_close(fd) 262 return n 263} 264 265func run(img: *u8, timer_on: i64, st: *i64, loaded: *i64) -> i64 { 266 let mem: *u8 = sys_mmap(IMG_MAGIC_65536) 267 var k: i64=0 268 while k<IMG_MAGIC_65536 { mem[k]=0 as u8; k=k+1 } 269 var j: i64=0 270 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 271 return emu_os(mem, img, 0x7C00, st[13], st[14], timer_on, st, loaded) 272} 273 274func main() -> i64 { 275 os_puts("x86 ladder R-KERN-4: INTEGRATION -- ONE booted kernel does GDT+IDT+paging+preempt+syscalls\n" as *u8) 276 277 let img: *u8 = sys_mmap(IMG_SZ + 16) 278 let info: *i64 = sys_mmap(64) as *i64 279 build_image(img, info) 280 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os_os.img\x00" as *u8, 0x1a4) 281 if fd<=0 { os_puts("R-KERN-4 RED: cannot write image\n" as *u8); sys_exit(1); return 1 } 282 sys_write(fd, img, IMG_SZ) 283 sys_close(fd) 284 let rd: *u8 = sys_mmap(IMG_SZ + 16) 285 os_read("knowledge/status/nishi_os_os.img\x00" as *u8, rd, IMG_SZ) 286 287 // GOOD run. 288 let st: *i64 = sys_mmap(256) as *i64 289 let ld: *i64 = sys_mmap(64) as *i64 290 st[13]=info[0]; st[14]=info[1] 291 let rc: i64 = run(rd, 1, st, ld) 292 os_puts(" kernel: boot->mode=" as *u8); os_num(st[1]); os_puts(" GDT@" as *u8); os_num(st[2]); os_puts(" IDT@" as *u8); os_num(st[4]); os_puts(" CR3=" as *u8); os_num(st[7]); os_puts(" (emu rc=" as *u8); os_num(rc); os_puts(")\n" as *u8) 293 os_puts(" syscall gate -> handler @" as *u8); os_num(st[8]); os_puts(" page walk 0x1000->" as *u8); os_num(st[9]); os_puts("\n" as *u8) 294 os_puts(" userland: task A ran " as *u8); os_num(st[11]); os_puts(", task B ran " as *u8); os_num(st[12]); os_puts(", syscalls serviced by kernel=" as *u8); os_num(st[10]); os_puts("\n" as *u8) 295 296 // NEG: masked timer -> task B starves. 297 let st2: *i64 = sys_mmap(256) as *i64 298 let ld2: *i64 = sys_mmap(64) as *i64 299 st2[13]=info[0]; st2[14]=info[1] 300 run(rd, 0, st2, ld2) 301 os_puts(" NEG (timer masked): task A=" as *u8); os_num(st2[11]); os_puts(" task B=" as *u8); os_num(st2[12]); os_puts(" (B starved)\n" as *u8) 302 303 let GDT_ABS: i64 = info[2] 304 let SYSH_ABS: i64 = st[8] 305 var pass: i64=0 306 var ttl: i64=0 307 ttl=ttl+1; os_puts(" T1 ONE kernel booted off disk to LONG mode (loaded + mode==2): " as *u8); if ld[0]!=0 { if st[1]==2 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 308 ttl=ttl+1; os_puts(" T2 the kernel loaded the GDT (lgdt, base==" as *u8); os_num(GDT_ABS); os_puts(", limit==23): " as *u8); if st[2]==GDT_ABS { if st[3]==23 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 309 ttl=ttl+1; os_puts(" T3 the kernel loaded the IDT + the syscall gate points at its handler (==" as *u8); os_num(SYSH_ABS); os_puts("): " as *u8); if st[4]==IDT_BASE { if st[8]==SYSH_ABS { if st[8]!=0 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 310 ttl=ttl+1; os_puts(" T4 the kernel set up paging (CR3==0x1000, VA 0x1000 -> PA 0x40000): " as *u8); if st[7]==0x1000 { if st[9]==0x40000 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 311 ttl=ttl+1; os_puts(" T5 BOTH userland tasks ran preemptively (A>5 & B>5): " as *u8); if st[11]>5 { if st[12]>5 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 312 ttl=ttl+1; os_puts(" T6 the tasks' syscalls were SERVICED by the kernel's handler (count>0): " as *u8); if st[10]>0 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } 313 ttl=ttl+1; os_puts(" T7 NEG: masked timer -> task B starves (B==0, A>0) (liar-kill): " as *u8); if st2[12]==0 { if st2[11]>0 { pass=pass+1; os_puts("PASS\n" as *u8) } else { os_puts("FAIL\n" as *u8) } } else { os_puts("FAIL\n" as *u8) } 314 315 os_puts("X86-USB-OS-GATE passed " as *u8); os_num(pass); os_puts("/" as *u8); os_num(ttl) 316 if pass==ttl { os_puts(" verdict=GREEN (ONE booted kernel: GDT + IDT + paging + preemptive scheduling + serviced syscalls = a real OS kernel running a userland)\n" as *u8); sys_exit(0); return 0 } 317 os_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 318}