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}