code wiki / _hdl_build / nx_emu_uefi.nx
nx_emu_uefi.nx source
↩ module page · 1332 lines · 74523 B
1// nx_emu_uefi.nx -- the sovereign UEFI EXECUTION-PROOF harness (NOS execution gate).
2//
3// A purpose-built minimal x86-64 interpreter WITH a flat memory model + stack + a hooked
4// UEFI SystemTable, just enough to EXECUTE the entry of an emitted nx_boot_uefi*.efi and
5// PROVE it does what it claims -- the in-house analog of running the RV64 boot stub on
6// rv64im_min_sim (no qemu, no real laptop). It sets up a mock SystemTable/ConOut whose
7// OutputString is a sentinel HOOK; when the executed code calls it, we CAPTURE the UTF-16
8// string it asked to print. A correct nx_boot_uefi prints "NISHI"; the buggy off-by-one
9// variant would capture garbage -- so this is a real gate, not a structural rubber-stamp.
10//
11// Memory is the flat image space with ImageBase=0: .text loaded at its RVA (0x1000), so
12// RIP-relative leas resolve to the real string RVAs. Mock UEFI placed elsewhere in MEM.
13//
14// Decoded forms (exactly what the nx_boot_uefi* / nx_gop_efi emitters produce):
15// 50+r push r64 | 58+r pop r64 | C3 ret | 31 /r xor r/m,r (reg-direct)
16// 48 83 grp1 (add/sub/and/cmp imm8) | 48 C7 mov r64,imm32 | 48 C1/D3 shr | 48 01/29/39 add/sub/cmp
17// 48 31 /r xor r64,r64 (reg-direct) | 48 89 /r mov store | 48 8B /r mov load (disp8/SIB+disp8/disp32/reg)
18// 48 8D lea [rip+disp32] (mod00) or [SIB-base+disp8] (mod01) | B8+rd mov r32,imm32 (zero-extended)
19// FC cld | F3 AB rep stosd (one interpreter step, bounds-refused -34)
20// FF /2 call [base+disp8] (mod01) or call r64 (mod11) -- HOOK-aware
21// EB FE jmp $ -> distinct HOLD return: a painting app KEEPS the screen; the verdict is then
22// decided by MEASURING the modeled framebuffer (color read from the artifact, never assumed):
23// nonzero color AND 100% fill -> PAINTED-AND-HOLDING, anything less -> RED.
24// Mock UEFI hooks: ConOut->OutputString (capture) | BootServices->GetMemoryMap (writes 0x1800) |
25// BootServices->LocateProtocol (GUID-CHECKED: only the GOP GUID is served, wrong GUID -> -33)
26// -> modeled GOP: gop->Mode->FrameBufferBase/Size = a real 1280x800x32 in-MEM framebuffer
27// (mirrors the OVMF oracle mode, so sovereign + foreign engines judge the same geometry).
28// Usage: nx_emu_uefi <path.efi> [expect-prefix] | nx_emu_uefi selftest (4 teeth incl. negative controls)
29// Gate: captured console output begins "NISHI" -> GREEN. Log -> knowledge/status/nishi_os.log
30// Sovereign: syscalls only, no gcc/qemu/.sh. license_tier: ORIGINAL
31import "nx_syscalls.nx"
32const RET_MAGIC_200000: i64 = 200000
33
34const MEMSZ: i64 = 0x800000 // raised for AI2: the served AI blob sits above the kernel window
35const HOOK: i64 = 0x9000
36const RET_SENTINEL: i64 = 0xC0DE
37const ST_ADDR: i64 = 0x4000
38const CO_ADDR: i64 = 0x4800
39const FV_ADDR: i64 = 0x4900
40const STACK_TOP: i64 = 0x20000 // RAISED: the loader's virtual image (code + reserves) now ends
41 // near 0x8DC0, and a stack growing down from 0x8000 would have
42 // grown straight into the assistant reserve. A stack that shares
43 // an address with the data it is loading is the modelled-
44 // peripheral bug wearing different clothes -- moved, not shrunk.
45const BS_ADDR: i64 = 0x4C00
46const HOOK2: i64 = 0x9100
47const HOOK3: i64 = 0x9200
48const HOOK4: i64 = 0x9300
49const HOOK5: i64 = 0x9400
50const CI_ADDR: i64 = 0x4A00
51const KEYQ_ADDR: i64 = 0x4B00
52const EFI_NOT_READY: i64 = 6
53const EFI_NOT_FOUND: i64 = 14 // what real firmware returns for a file that is not there --
54 // the ABSENT assistant file must look exactly like this
55const EFI_INVALID_PARAM: i64 = 2
56const EFI_BUFFER_TOO_SMALL: i64 = 5
57// N4: the firmware handoff is modeled with REAL protocol teeth, not a rubber stamp --
58// GetMemoryMap is a genuine two-call sequence and ExitBootServices REFUSES a stale MapKey,
59// which is the classic UEFI mistake. EBS_STATE tracks whether firmware has been surrendered;
60// after that, every firmware service must FAIL, because that is what real hardware does.
61const MM_REQ_BYTES: i64 = 0x1800 // required map size the modeled firmware reports
62const MM_DESC_SIZE: i64 = 0x30 // EFI_MEMORY_DESCRIPTOR stride
63const MM_MAPKEY: i64 = 0x5AFEC0DE // the key a SUCCESSFUL GetMemoryMap issues
64const EBS_STATE: i64 = 0x4D00 // [0]=exited? [8]=issued map key
65// N1-full: model the UEFI file-read chain the shim must use to load its OWN kernel off the ESP.
66// Every step is GUID- or handle-checked so a wrong protocol is REFUSED rather than served.
67const HOOK6: i64 = 0x9500 // BootServices->HandleProtocol
68const HOOK7: i64 = 0x9600 // SimpleFileSystem->OpenVolume
69const HOOK8: i64 = 0x9700 // File->Open
70const HOOK9: i64 = 0x9800 // File->Read
71// FS-2: BootServices->LocateHandleBuffer. The loader must stop ASSUMING the optional file is on the
72// volume it booted from and start SEARCHING every filesystem the firmware knows about. The model
73// therefore serves TWO volumes and puts the assistant file ONLY on the second one -- a loader that
74// checks just its own volume fails this harness by construction, which is the whole point.
75const HOOK10: i64 = 0x9900 // BootServices->LocateHandleBuffer (BS + 0x138)
76const DEV2_HANDLE: i64 = 0x501100 // the DATA partition's device handle
77const FS2_ADDR: i64 = 0x502100 // its EFI_SIMPLE_FILE_SYSTEM
78const ROOT2_ADDR: i64 = 0x503100 // its root EFI_FILE
79const HBUF_ADDR: i64 = 0x509000 // the handle array firmware hands back
80const HCNT_ADDR: i64 = 0x50A000 // and its count
81// ⚠These MUST live outside the guest image. First placement (0x5200-0x5700) landed INSIDE the
82// loaded .text -- directly on the embedded KERNEL.NXE at 0x5280 -- so wiring the vtables silently
83// corrupted the payload, its checksum failed, and the shim took its refusal path to a black
84// screen. ★★★★★A MODELED PERIPHERAL PLACED INSIDE THE GUEST'S ADDRESS SPACE IS NOT A PERIPHERAL,
85// IT IS MEMORY CORRUPTION WEARING A DEVICE'S NAME. Image ~0x1000-0x6400, framebuffer to 0x4E8000.
86const LI_ADDR: i64 = 0x500000 // EFI_LOADED_IMAGE_PROTOCOL (+0x18 DeviceHandle)
87const DEV_HANDLE: i64 = 0x501000 // the ESP device handle we hand back
88const FS_ADDR: i64 = 0x502000 // EFI_SIMPLE_FILE_SYSTEM (+0x08 OpenVolume)
89const ROOT_ADDR: i64 = 0x503000 // root EFI_FILE_PROTOCOL (+0x08 Open)
90const FILE_ADDR: i64 = 0x504000 // opened EFI_FILE_PROTOCOL (+0x20 Read)
91const KFILE_LEN: i64 = 0x505000 // [0] = served file length
92const KFILE_DATA: i64 = 0x580000
93// AI2: the OPTIONAL assistant file is a SECOND named file on the same modeled ESP. Modeling it as a
94// distinct handle+buffer (not a second read of the same one) is what lets Open() be NAME-AWARE, which
95// is the only way the harness can exercise "the file is absent" -- the default state of the toggle.
96const AIFILE_ADDR: i64 = 0x507000 // opened EFI_FILE_PROTOCOL for the AI blob
97const AIFILE_LEN: i64 = 0x508000 // [0] = served AI blob length (0 = ABSENT, Open must refuse)
98const AIFILE_DATA: i64 = 0x600000 // served AI bytes (above the kernel window, inside MEMSZ)
99// HW1 (A16 spore): a modeled PCI config-space window. CONFIG_ADDRESS (0xCF8) is a SELECTOR --
100// writing it chooses which register a subsequent read returns, and changes no device state.
101// CONFIG_DATA (0xCFC) is READ ONLY here: a write to it would mutate a device's configuration,
102// so it is refused by construction. That is the whole never-brick rule for bus enumeration.
103const PCI_ADDR_PORT: i64 = 0xCF8
104const PCI_DATA_PORT: i64 = 0xCFC
105const PCI_LATCH: i64 = 0x506000 // last value written to CONFIG_ADDRESS
106const PCI_NDEV: i64 = 3 // modeled devices on bus 0, functions 0 // served file bytes (well clear of the framebuffer)
107// N4b: a modeled PS/2 controller. After ExitBootServices there is no ConIn, so a real OS talks
108// to the 8042 directly: port 0x64 is status (bit0 = a byte is waiting), port 0x60 is the data
109// register. Both are READS -- nothing here writes hardware state.
110const PS2_STATE: i64 = 0x4E00 // [0]=count [8]=index [16+8i]=scancodes
111const PS2_PORT_STATUS: i64 = 0x64
112const PS2_PORT_DATA: i64 = 0x60
113const GOP_ADDR: i64 = 0x5000
114const MODE_ADDR: i64 = 0x5100
115const FB_ADDR: i64 = 0x100000
116const FB_SIZE: i64 = 0x3E8000
117const FB_W_PIX: i64 = 1280
118const FB_H_PIX: i64 = 800
119
120// reg indices: 0 rax 1 rcx 2 rdx 3 rbx 4 rsp 5 rbp 6 rsi 7 rdi
121const R_RAX: i64 = 0
122const R_RCX: i64 = 1
123const R_RDX: i64 = 2
124const R_RSP: i64 = 4
125const R_RDI: i64 = 7
126
127// ===== flat-memory access ========================================================
128func m_r8(mem: *u8, a: i64) -> i64 { return mem[a] as i64 }
129func m_r64(mem: *u8, a: i64) -> i64 {
130 var v: i64 = 0; var i: i64 = 0
131 while i < 8 { v = v | ((mem[a + i] as i64) << (8 * i)); i = i + 1 }
132 return v
133}
134func m_w64(mem: *u8, a: i64, v: i64) -> i64 {
135 var i: i64 = 0
136 while i < 8 { mem[a + i] = ((v >> (8 * i)) & 0xff) as u8; i = i + 1 }
137 return 0
138}
139func m_w16(mem: *u8, a: i64, v: i64) -> i64 {
140 mem[a] = (v & 0xff) as u8; mem[a + 1] = ((v >> 8) & 0xff) as u8; return 0
141}
142// little-endian imm32, sign-extended (operands live in MEM alongside code)
143func m_i32(mem: *u8, off: i64) -> i64 {
144 var v: i64 = (mem[off] as i64) | ((mem[off+1] as i64) << 8) | ((mem[off+2] as i64) << 16) | ((mem[off+3] as i64) << 24)
145 if (v & 0x80000000) != 0 { v = v - (1 << 32) }
146 return v
147}
148// unsigned 32 from a file buffer (PE header fields)
149func f_u32(fb: *u8, off: i64) -> i64 {
150 return (fb[off] as i64) | ((fb[off+1] as i64) << 8) | ((fb[off+2] as i64) << 16) | ((fb[off+3] as i64) << 24)
151}
152func sx8(b: i64) -> i64 { if b >= 128 { return b - 256 } return b }
153
154// capture the UTF-16LE string at mem[straddr] (low bytes) into out[]; return new outn
155func cap_str(mem: *u8, straddr: i64, out: *u8, outn: i64) -> i64 {
156 var a: i64 = straddr; var n: i64 = outn; var go: i64 = 1
157 while go == 1 {
158 let lo: i64 = mem[a] as i64
159 let hi: i64 = mem[a + 1] as i64
160 if (lo | (hi << 8)) == 0 { go = 0 } else { out[n] = lo as u8; n = n + 1; a = a + 2 }
161 }
162 return n
163}
164
165// EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID 9042a9de-23dc-4a38-96fb-7aded080516a (UEFI-spec bytes, mixed-endian).
166// Restated from the spec here so the reader never borrows the writer's table -- LocateProtocol REFUSES (-33)
167// any other GUID instead of handing out the modeled GOP for whatever is asked.
168// EFI_LOADED_IMAGE_PROTOCOL_GUID 5B1B31A1-9562-11d2-8E3F-00A0C969723B
169func li_guid_b(i: i64) -> i64 {
170 if i==0 { return 0xA1 } if i==1 { return 0x31 } if i==2 { return 0x1B } if i==3 { return 0x5B }
171 if i==4 { return 0x62 } if i==5 { return 0x95 } if i==6 { return 0xD2 } if i==7 { return 0x11 }
172 if i==8 { return 0x8E } if i==9 { return 0x3F } if i==10 { return 0x00 } if i==11 { return 0xA0 }
173 if i==12 { return 0xC9 } if i==13 { return 0x69 } if i==14 { return 0x72 } return 0x3B
174}
175// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID 964E5B22-6459-11D2-8E39-00A0C969723B
176func fs_guid_b(i: i64) -> i64 {
177 if i==0 { return 0x22 } if i==1 { return 0x5B } if i==2 { return 0x4E } if i==3 { return 0x96 }
178 if i==4 { return 0x59 } if i==5 { return 0x64 } if i==6 { return 0xD2 } if i==7 { return 0x11 }
179 if i==8 { return 0x8E } if i==9 { return 0x39 } if i==10 { return 0x00 } if i==11 { return 0xA0 }
180 if i==12 { return 0xC9 } if i==13 { return 0x69 } if i==14 { return 0x72 } return 0x3B
181}
182// Compare the UTF-16LE filename at mem[at] against one of the two names this modeled ESP serves.
183// which: 0 = "KERNEL.NXE", 1 = "NISHI.AI". The table is restated here rather than shared with the
184// loader's table -- a reader that borrows the writer's bytes cannot catch the writer's typo.
185func esp_name_ch(which: i64, i: i64) -> i64 {
186 if which == 0 {
187 if i==0 { return 0x4B } if i==1 { return 0x45 } if i==2 { return 0x52 } if i==3 { return 0x4E }
188 if i==4 { return 0x45 } if i==5 { return 0x4C } if i==6 { return 0x2E } if i==7 { return 0x4E }
189 if i==8 { return 0x58 } if i==9 { return 0x45 } return 0
190 }
191 if i==0 { return 0x4E } if i==1 { return 0x49 } if i==2 { return 0x53 } if i==3 { return 0x48 }
192 if i==4 { return 0x49 } if i==5 { return 0x2E } if i==6 { return 0x41 } if i==7 { return 0x49 }
193 return 0
194}
195func u16_name_is(mem: *u8, at: i64, which: i64) -> i64 {
196 var i: i64 = 0
197 var go: i64 = 1
198 while go == 1 {
199 let want: i64 = esp_name_ch(which, i)
200 let lo: i64 = mem[at + i * 2] as i64
201 let hi: i64 = mem[at + i * 2 + 1] as i64
202 if hi != 0 { return 0 }
203 if lo != want { return 0 }
204 if want == 0 { go = 0 }
205 i = i + 1
206 }
207 return 1
208}
209func guid_eq(mem: *u8, at: i64, which: i64) -> i64 {
210 var i: i64 = 0
211 while i < 16 {
212 var want: i64 = 0
213 if which == 0 { want = li_guid_b(i) } else { want = fs_guid_b(i) }
214 if (mem[at + i] as i64) != want { return 0 }
215 i = i + 1
216 }
217 return 1
218}
219
220
221// The N1-full file-chain hooks, in ONE place so the selftest exercises exactly what the
222// interpreter runs. Returns 0 = handled OK, negative = refusal sentinel, 1 = not my hook.
223// The modeled bus: three devices at slots 0,1,2. Everything else reads back all-ones, which is
224// exactly how real hardware says "nothing here" -- so the kernel's enumeration must treat
225// 0xFFFFFFFF as absence rather than as a device, or it will invent hardware.
226func pci_cfg_read(latch: i64) -> i64 {
227 if (latch & 0x80000000) == 0 { return 0xFFFFFFFF } // enable bit clear
228 let bus: i64 = (latch >> 16) & 0xFF
229 let dev: i64 = (latch >> 11) & 0x1F
230 let fun: i64 = (latch >> 8) & 0x07
231 let off: i64 = latch & 0xFC
232 if bus != 0 { return 0xFFFFFFFF }
233 if fun != 0 { return 0xFFFFFFFF }
234 if dev >= PCI_NDEV { return 0xFFFFFFFF }
235 if off == 0 {
236 if dev == 0 { return 0x12378086 } // host bridge (vendor 8086)
237 if dev == 1 { return 0x100E8086 } // network (vendor 8086)
238 return 0x00281AF4 // virtio block (vendor 1AF4)
239 }
240 if off == 8 {
241 if dev == 0 { return 0x06000001 } // class 06 bridge
242 if dev == 1 { return 0x02000001 } // class 02 network
243 return 0x01000001 // class 01 storage
244 }
245 return 0
246}
247
248func emu_hook_call(mem: *u8, tgt: i64, reg: *i64) -> i64 {
249 if tgt == HOOK6 { // HandleProtocol(rcx=handle, rdx=&GUID, r8=&iface)
250 if guid_eq(mem, reg[R_RDX], 0) == 1 {
251 m_w64(mem, reg[8], LI_ADDR); reg[R_RAX] = 0; return 0
252 }
253 if guid_eq(mem, reg[R_RDX], 1) == 1 {
254 // Two volumes now: the boot ESP and the DATA partition. Serve each its OWN filesystem,
255 // and refuse anything else -- a handle we did not hand out must never resolve.
256 if reg[R_RCX] == DEV_HANDLE { m_w64(mem, reg[8], FS_ADDR); reg[R_RAX] = 0; return 0 }
257 if reg[R_RCX] == DEV2_HANDLE { m_w64(mem, reg[8], FS2_ADDR); reg[R_RAX] = 0; return 0 }
258 return 0 - 39
259 }
260 return 0 - 33 // any other protocol: REFUSED
261 }
262 if tgt == HOOK7 { // OpenVolume(rcx=This, rdx=&root)
263 if reg[R_RCX] == FS_ADDR { m_w64(mem, reg[R_RDX], ROOT_ADDR); reg[R_RAX] = 0; return 0 }
264 if reg[R_RCX] == FS2_ADDR { m_w64(mem, reg[R_RDX], ROOT2_ADDR); reg[R_RAX] = 0; return 0 }
265 return 0 - 39
266 }
267 if tgt == HOOK10 { // LocateHandleBuffer(rcx=SearchType, rdx=&GUID,
268 // r8=SearchKey, r9=&count, [rsp+0x20]=&buffer)
269 if reg[R_RCX] != 2 { return 0 - 43 } // ByProtocol only -- AllHandles would
270 // hand back handles we never checked
271 if guid_eq(mem, reg[R_RDX], 1) == 0 { return 0 - 33 }
272 m_w64(mem, HBUF_ADDR + 0, DEV_HANDLE)
273 m_w64(mem, HBUF_ADDR + 8, DEV2_HANDLE)
274 m_w64(mem, reg[9], 2)
275 m_w64(mem, m_r64(mem, reg[R_RSP] + 0x20), HBUF_ADDR)
276 reg[R_RAX] = 0
277 return 0
278 }
279 if tgt == HOOK8 { // Open(rcx=This, rdx=&new, r8=name, r9=mode)
280 if reg[R_RCX] != ROOT_ADDR { if reg[R_RCX] != ROOT2_ADDR { return 0 - 39 } }
281 if reg[9] != 1 { return 0 - 40 } // READ only -- a writable open of the boot volume is
282 // refused by construction (never-brick posture)
283 // NAME-AWARE. Serving whatever is asked for would make "the optional file is missing" an
284 // UNTESTABLE state, and that state is the DEFAULT of the assistant toggle -- the one the
285 // harness most needs to exercise. Unknown name -> EFI_NOT_FOUND, exactly like real firmware.
286 if u16_name_is(mem, reg[8], 1) == 1 { // L"NISHI.AI"
287 // MODELLED DELIBERATELY: the assistant file lives ONLY on the DATA volume. A loader that
288 // opens it on its own boot volume gets NOT_FOUND here and must go looking.
289 if reg[R_RCX] != ROOT2_ADDR { reg[R_RAX] = EFI_NOT_FOUND; return 0 }
290 if m_r64(mem, AIFILE_LEN) <= 0 { reg[R_RAX] = EFI_NOT_FOUND; return 0 }
291 m_w64(mem, reg[R_RDX], AIFILE_ADDR); reg[R_RAX] = 0; return 0
292 }
293 if u16_name_is(mem, reg[8], 0) == 1 { // L"KERNEL.NXE" -- boot volume only
294 if reg[R_RCX] != ROOT_ADDR { reg[R_RAX] = EFI_NOT_FOUND; return 0 }
295 if m_r64(mem, KFILE_LEN) <= 0 { reg[R_RAX] = EFI_NOT_FOUND; return 0 }
296 m_w64(mem, reg[R_RDX], FILE_ADDR); reg[R_RAX] = 0; return 0
297 }
298 reg[R_RAX] = EFI_NOT_FOUND; return 0
299 }
300 if tgt == HOOK9 { // Read(rcx=This, rdx=&size, r8=buf)
301 if reg[R_RCX] == AIFILE_ADDR { // the optional assistant blob
302 let ahave: i64 = m_r64(mem, AIFILE_LEN)
303 var awant: i64 = m_r64(mem, reg[R_RDX])
304 if awant > ahave { awant = ahave }
305 var ai: i64 = 0
306 while ai < awant { mem[reg[8] + ai] = mem[AIFILE_DATA + ai]; ai = ai + 1 }
307 m_w64(mem, reg[R_RDX], awant)
308 reg[R_RAX] = 0
309 return 0
310 }
311 if reg[R_RCX] != FILE_ADDR { return 0 - 39 }
312 let have: i64 = m_r64(mem, KFILE_LEN)
313 var want: i64 = m_r64(mem, reg[R_RDX])
314 if want > have { want = have }
315 var ci: i64 = 0
316 while ci < want { mem[reg[8] + ci] = mem[KFILE_DATA + ci]; ci = ci + 1 }
317 m_w64(mem, reg[R_RDX], want)
318 reg[R_RAX] = 0
319 return 0
320 }
321 return 1
322}
323
324func gop_guid_b(i: i64) -> i64 {
325 if i==0 { return 0xDE } if i==1 { return 0xA9 } if i==2 { return 0x42 } if i==3 { return 0x90 }
326 if i==4 { return 0xDC } if i==5 { return 0x23 } if i==6 { return 0x38 } if i==7 { return 0x4A }
327 if i==8 { return 0x96 } if i==9 { return 0xFB } if i==10 { return 0x7A } if i==11 { return 0xDE }
328 if i==12 { return 0xD0 } if i==13 { return 0x80 } if i==14 { return 0x51 } return 0x6A
329}
330
331// ===== the interpreter: run from entry RVA; capture ConOut output into out; return outn or -err =====
332func emu_run(mem: *u8, entry: i64, out: *u8, trace: i64) -> i64 {
333 let reg: *i64 = sys_mmap(8 * 16) as *i64
334 var pc: i64 = entry
335 var outn: i64 = 0
336 reg[R_RSP] = STACK_TOP
337 reg[R_RSP] = reg[R_RSP] - 8
338 m_w64(mem, reg[R_RSP], RET_SENTINEL) // so the entry's final ret stops us
339 reg[R_RCX] = 0x1234 // ImageHandle
340 reg[R_RDX] = ST_ADDR // SystemTable*
341 var steps: i64 = 0
342 var flagv: i64 = 0 // last cmp/sub result (for jcc); ZF=(==0) SF=(<0)
343 while steps < RET_MAGIC_200000 {
344 steps = steps + 1
345 // An emulator must never fault on guest behaviour -- a wild pc is the GUEST's bug and must
346 // be REFUSED with evidence, not turned into a host segfault that says nothing.
347 if pc < 0 { e_p("EMU-WILDPC negative\n" as *u8); return 0 - 36 }
348 if (pc + 16) > MEMSZ {
349 e_p("EMU-WILDPC pc=0x" as *u8); e_fx(1, pc); e_p(" step=" as *u8); e_fn(1, steps); e_p("\n" as *u8)
350 return 0 - 36
351 }
352 if trace == 1 {
353 e_p("T pc=0x" as *u8); e_fx(1, pc); e_p(" op=0x" as *u8); e_fx(1, mem[pc] as i64)
354 e_p(" rdi=0x" as *u8); e_fx(1, reg[7]); e_p(" rcx=0x" as *u8); e_fx(1, reg[R_RCX])
355 e_p(" rsi=0x" as *u8); e_fx(1, reg[6]); e_p("\n" as *u8)
356 }
357 let b: i64 = mem[pc] as i64
358 var rexR: i64 = 0
359 var rexB: i64 = 0
360 var is_rexw: i64 = 0
361 if b == 0x48 { is_rexw = 1 }
362 if b == 0x4C { is_rexw = 1; rexR = 1 } // REX.WR -> r8/r9 as lea destinations
363 if b == 0x49 { is_rexw = 1; rexB = 1 } // REX.WB -> r8/r9 as r/m dest. N1-full adds
364 // EXACTLY mov r9,imm32 (49 C7 C1): Open()'s
365 // READ mode rides r9 and lea cannot carry an
366 // immediate. Every other 49-prefixed op is
367 // REFUSED below, not silently run as its
368 // 48-prefixed twin.
369 var is_jcc: i64 = 0
370 if b >= 0x74 { if b <= 0x7F { is_jcc = 1 } } // conditional jumps
371 if b == 0xC3 { // ret
372 let tgt: i64 = m_r64(mem, reg[R_RSP])
373 reg[R_RSP] = reg[R_RSP] + 8
374 if tgt == RET_SENTINEL { return outn } // clean program return
375 pc = tgt
376 } else { if b == 0xFF { // call: [base+disp8] (/2 mod01) or r64 (/2 mod11)
377 let m: i64 = mem[pc + 1] as i64
378 let ffdig: i64 = (m >> 3) & 7
379 if ffdig != 2 { if ffdig != 4 { return 0 - 21 } }
380 let ffmod: i64 = (m >> 6) & 3
381 if ffdig == 4 { // FF /4: jmp r64 (FF E0 = jmp rax) -- the shim->NXE handoff
382 if ffmod != 3 { return 0 - 21 }
383 pc = reg[m & 7]
384 } else {
385 // ONE hook dispatch for BOTH call forms. They were split once, and a firmware service
386 // reachable via `call [mem]` was then INVISIBLE via `call rax` -- the emulator jumped
387 // into the hook's address as if it were code (sentinel -29).
388 // ★A HOOK REGISTERED ON ONE CALL FORM IS NOT REGISTERED ON THE MECHANISM.
389 var tgt: i64 = 0
390 var ilen: i64 = 0
391 if ffmod == 3 { tgt = reg[m & 7]; ilen = 2 } // call r64
392 if ffmod == 1 { // call [base+disp8]
393 tgt = m_r64(mem, reg[m & 7] + sx8(mem[pc + 2] as i64)); ilen = 3
394 }
395 if ilen == 0 { return 0 - 22 }
396 var hooked: i64 = 1
397 // Once boot services are exited, the firmware is GONE. Any further service call is a
398 // use-after-free on real hardware; the model refuses it loudly instead of pretending.
399 if m_r64(mem, EBS_STATE) == 1 {
400 if tgt == HOOK { return 0 - 37 }
401 if tgt == HOOK2 { return 0 - 37 }
402 if tgt == HOOK3 { return 0 - 37 }
403 if tgt == HOOK4 { return 0 - 37 }
404 if tgt == HOOK5 { return 0 - 37 }
405 if tgt == HOOK10 { return 0 - 37 }
406 }
407 if tgt == HOOK { // ConOut->OutputString(rcx=This, rdx=CHAR16*)
408 outn = cap_str(mem, reg[R_RDX], out, outn)
409 reg[R_RAX] = 0 // EFI_SUCCESS
410 } else { if tgt == HOOK2 {
411 // GetMemoryMap(rcx=&MapSize, rdx=MapBuf, r8=&MapKey, r9=&DescSize, [rsp+0x20]=&Ver)
412 // Real two-call protocol: too small (or NULL buffer) -> BUFFER_TOO_SMALL + the
413 // required size; big enough -> SUCCESS + a MapKey that ExitBootServices will check.
414 let want: i64 = m_r64(mem, reg[R_RCX])
415 if reg[R_RDX] == 0 { m_w64(mem, reg[R_RCX], MM_REQ_BYTES); reg[R_RAX] = EFI_BUFFER_TOO_SMALL }
416 else { if want < MM_REQ_BYTES { m_w64(mem, reg[R_RCX], MM_REQ_BYTES); reg[R_RAX] = EFI_BUFFER_TOO_SMALL }
417 else {
418 m_w64(mem, reg[R_RCX], MM_REQ_BYTES) // actual bytes written
419 if reg[8] != 0 { m_w64(mem, reg[8], MM_MAPKEY) }
420 if reg[9] != 0 { m_w64(mem, reg[9], MM_DESC_SIZE) }
421 m_w64(mem, EBS_STATE + 8, MM_MAPKEY) // the key now outstanding
422 reg[R_RAX] = 0
423 } }
424 } else { if tgt == HOOK5 { // ExitBootServices(rcx=ImageHandle, rdx=MapKey)
425 let issued: i64 = m_r64(mem, EBS_STATE + 8)
426 if issued == 0 { reg[R_RAX] = EFI_INVALID_PARAM } // never got a map
427 else { if reg[R_RDX] != issued { reg[R_RAX] = EFI_INVALID_PARAM } // STALE key refused
428 else {
429 m_w64(mem, EBS_STATE, 1) // firmware surrendered; the kernel owns the machine
430 reg[R_RAX] = 0
431 } }
432 } else { if tgt == HOOK10 { hooked = 2 } else { if tgt == HOOK6 { hooked = 2 } else { if tgt == HOOK7 { hooked = 2
433 } else { if tgt == HOOK8 { hooked = 2 } else { if tgt == HOOK9 { hooked = 2
434 } else { if tgt == HOOK3 { // BootServices->LocateProtocol(rcx=&GUID, r8=&iface)
435 var gi: i64 = 0 // the GUID is CHECKED: wrong protocol -> refused
436 while gi < 16 { if (mem[reg[R_RCX] + gi] as i64) != gop_guid_b(gi) { return 0 - 33 } gi = gi + 1 }
437 m_w64(mem, reg[8], GOP_ADDR) // *r8 = the modeled GOP interface
438 reg[R_RAX] = 0
439 } else { if tgt == HOOK4 { // ConIn->ReadKeyStroke(rcx=This, rdx=EFI_INPUT_KEY*)
440 let qn: i64 = m_r64(mem, KEYQ_ADDR) // scripted keys drain in order, then NOT_READY
441 let qi: i64 = m_r64(mem, KEYQ_ADDR + 8)
442 if qi < qn {
443 m_w16(mem, reg[R_RDX], m_r64(mem, KEYQ_ADDR + 16 + qi * 16)) // ScanCode
444 m_w16(mem, reg[R_RDX] + 2, m_r64(mem, KEYQ_ADDR + 16 + qi * 16 + 8)) // UnicodeChar
445 m_w64(mem, KEYQ_ADDR + 8, qi + 1)
446 reg[R_RAX] = 0 // EFI_SUCCESS
447 } else {
448 reg[R_RAX] = EFI_NOT_READY // an empty queue is NOT-READY, never a fake key
449 }
450 } else { hooked = 0 } } } } } } } } } }
451 if hooked == 2 {
452 let hr: i64 = emu_hook_call(mem, tgt, reg)
453 if hr < 0 { return hr }
454 pc = pc + ilen
455 } else {
456 if hooked == 1 { pc = pc + ilen } else { // a real call: push the return address
457 reg[R_RSP] = reg[R_RSP] - 8
458 m_w64(mem, reg[R_RSP], pc + ilen)
459 pc = tgt
460 } } }
461 } else { if is_rexw == 1 { // REX.W (0x48) or REX.WR (0x4C) -- flat dispatch
462 let op: i64 = mem[pc + 1] as i64
463 var hd: i64 = 0
464 // REX.WB is implemented for ONE opcode. Letting the other handlers fire with rexB set
465 // would silently write the WRONG register -- the /digit lesson applied to prefixes:
466 // ★AN OPCODE IMPLEMENTED FOR ONE PREFIX IS NOT IMPLEMENTED, IT IS MIS-EXECUTED.
467 if rexB == 1 { if op != 0xC7 {
468 e_p("EMU-UNDECODED REX.WB op=0x" as *u8); e_fx(1, op)
469 e_p(" at pc=0x" as *u8); e_fx(1, pc); e_p("\n" as *u8)
470 return 0 - 28
471 } }
472 if op == 0x83 { // grp1 r/m64,imm8 (reg-direct): add/sub/and/cmp
473 let m: i64 = mem[pc + 2] as i64
474 let imm: i64 = sx8(mem[pc + 3] as i64)
475 let dig: i64 = (m >> 3) & 7
476 if dig == 0 { reg[m & 7] = reg[m & 7] + imm }
477 if dig == 5 { reg[m & 7] = reg[m & 7] - imm }
478 if dig == 4 { reg[m & 7] = reg[m & 7] & imm }
479 if dig == 7 { flagv = reg[m & 7] - imm }
480 pc = pc + 4; hd = 1
481 }
482 if op == 0x81 { // grp1 r/m64,imm32 -- a stride that outgrew imm8
483 let m81: i64 = mem[pc + 2] as i64
484 let imm81: i64 = m_i32(mem, pc + 3)
485 let dig81: i64 = (m81 >> 3) & 7
486 if dig81 == 0 { reg[m81 & 7] = reg[m81 & 7] + imm81 }
487 if dig81 == 5 { reg[m81 & 7] = reg[m81 & 7] - imm81 }
488 if dig81 == 4 { reg[m81 & 7] = reg[m81 & 7] & imm81 }
489 if dig81 == 7 { flagv = reg[m81 & 7] - imm81 }
490 pc = pc + 7; hd = 1
491 }
492 if op == 0xC7 { reg[(rexB * 8) + ((mem[pc + 2] as i64) & 7)] = m_i32(mem, pc + 3); pc = pc + 7; hd = 1 } // mov r/m64,imm32 (rexB -> r8/r9)
493 if op == 0xC1 { // grp2 r/m64, imm8 -- /4 = shl, /5 = shr
494 // ⚠This decoded EVERY 0xC1 as shr, ignoring the /digit, so `shl rax,11` silently
495 // executed as a RIGHT shift and the PCI slot never entered the selector (the latch
496 // sat at 0x80000000 for all 32 slots). ★★★★★AN OPCODE IMPLEMENTED FOR ONE /DIGIT
497 // IS NOT IMPLEMENTED -- IT IS MIS-EXECUTED FOR THE OTHERS.
498 let m: i64 = mem[pc + 2] as i64
499 let dig: i64 = (m >> 3) & 7
500 let amt: i64 = mem[pc + 3] as i64
501 if dig == 4 { reg[m & 7] = reg[m & 7] << amt; pc = pc + 4; hd = 1 }
502 if dig == 5 { reg[m & 7] = reg[m & 7] >> amt; pc = pc + 4; hd = 1 }
503 }
504 if op == 0xD3 { // grp2 r/m64, CL -- /4 = shl, /5 = shr
505 let m: i64 = mem[pc + 2] as i64
506 let dig3: i64 = (m >> 3) & 7
507 if dig3 == 4 { reg[m & 7] = reg[m & 7] << (reg[1] & 63); pc = pc + 3; hd = 1 }
508 if dig3 == 5 { reg[m & 7] = reg[m & 7] >> (reg[1] & 63); pc = pc + 3; hd = 1 }
509 }
510 if op == 0x31 { let m: i64 = mem[pc + 2] as i64; reg[m & 7] = reg[m & 7] ^ reg[(m >> 3) & 7]; pc = pc + 3; hd = 1 } // xor r64,r64 (reg-direct)
511 if op == 0x0F { // two-byte opcode: 0F AF /r = imul r64,r/m64
512 if (mem[pc + 2] as i64) == 0xAF { // signed multiply -- the AI1 multiply-accumulate
513 let m: i64 = mem[pc + 3] as i64
514 if ((m >> 6) & 3) == 3 { reg[(m >> 3) & 7] = reg[(m >> 3) & 7] * reg[m & 7]; pc = pc + 4; hd = 1 }
515 }
516 }
517 if op == 0x01 { let m: i64 = mem[pc + 2] as i64; reg[m & 7] = reg[m & 7] + reg[(m >> 3) & 7]; pc = pc + 3; hd = 1 }
518 if op == 0x29 { let m: i64 = mem[pc + 2] as i64; reg[m & 7] = reg[m & 7] - reg[(m >> 3) & 7]; pc = pc + 3; hd = 1 }
519 if op == 0x39 { let m: i64 = mem[pc + 2] as i64; flagv = reg[m & 7] - reg[(m >> 3) & 7]; pc = pc + 3; hd = 1 }
520 if op == 0x21 { let m: i64 = mem[pc + 2] as i64; reg[m & 7] = reg[m & 7] & reg[(m >> 3) & 7]; pc = pc + 3; hd = 1 }
521 if op == 0x89 { // mov r/m64,r64 (reg-direct / [reg+disp8] store)
522 let m: i64 = mem[pc + 2] as i64
523 let mod89: i64 = (m >> 6) & 3
524 let src89: i64 = (rexR * 8) + ((m >> 3) & 7)
525 if mod89 == 3 { reg[m & 7] = reg[src89]; pc = pc + 3; hd = 1 }
526 if mod89 == 1 {
527 if (m & 7) == 4 { m_w64(mem, reg[R_RSP] + sx8(mem[pc + 4] as i64), reg[src89]); pc = pc + 5; hd = 1 }
528 else { m_w64(mem, reg[m & 7] + sx8(mem[pc + 3] as i64), reg[src89]); pc = pc + 4; hd = 1 }
529 }
530 }
531 if op == 0x8B { // mov r64,[base+disp8|SIB+disp8|base+disp32] or reg-direct
532 let m: i64 = mem[pc + 2] as i64
533 let mod: i64 = (m >> 6) & 3
534 let dst: i64 = (m >> 3) & 7
535 if mod == 1 {
536 if (m & 7) == 4 { // SIB form (no-index assumed): base from the SIB byte
537 reg[dst] = m_r64(mem, reg[(mem[pc + 3] as i64) & 7] + sx8(mem[pc + 4] as i64)); pc = pc + 5; hd = 1
538 } else {
539 reg[dst] = m_r64(mem, reg[m & 7] + sx8(mem[pc + 3] as i64)); pc = pc + 4; hd = 1
540 }
541 }
542 if mod == 2 { if (m & 7) != 4 { reg[dst] = m_r64(mem, reg[m & 7] + m_i32(mem, pc + 3)); pc = pc + 7; hd = 1 } }
543 if mod == 0 { // mov r64,[base] -- no disp (AI1 vector walk)
544 if (m & 7) != 4 { if (m & 7) != 5 { reg[dst] = m_r64(mem, reg[m & 7]); pc = pc + 3; hd = 1 } }
545 }
546 if mod == 3 { reg[dst] = reg[m & 7]; pc = pc + 3; hd = 1 }
547 }
548 if op == 0x8D { // lea r64,[rip+disp32] (mod00) | [SIB-base+disp8] (mod01)
549 let m: i64 = mem[pc + 2] as i64
550 let lmod: i64 = (m >> 6) & 3
551 if lmod == 0 { reg[(rexR * 8) + ((m >> 3) & 7)] = (pc + 7) + m_i32(mem, pc + 3); pc = pc + 7; hd = 1 }
552 if lmod == 1 {
553 if (m & 7) == 4 { // lea r64,[rsp+disp8] shape (no-index SIB)
554 reg[(rexR * 8) + ((m >> 3) & 7)] = reg[(mem[pc + 3] as i64) & 7] + sx8(mem[pc + 4] as i64); pc = pc + 5; hd = 1
555 } else { // lea r64,[base+disp8]
556 reg[(rexR * 8) + ((m >> 3) & 7)] = reg[m & 7] + sx8(mem[pc + 3] as i64); pc = pc + 4; hd = 1
557 }
558 }
559 }
560 if hd == 0 {
561 e_p("EMU-UNDECODED rex=0x" as *u8); e_fx(1, b); e_p(" op=0x" as *u8); e_fx(1, op)
562 e_p(" modrm=0x" as *u8); e_fx(1, mem[pc + 2] as i64)
563 e_p(" at pc=0x" as *u8); e_fx(1, pc); e_p("\n" as *u8)
564 return 0 - 28
565 }
566 } else { if b == 0x31 { // xor r/m,r (reg-direct)
567 let m: i64 = mem[pc + 1] as i64
568 reg[m & 7] = reg[m & 7] ^ reg[(m >> 3) & 7]
569 pc = pc + 2
570 } else { if is_jcc == 1 { // jcc rel8 (74 jz/75 jnz/7C jl/7D jge/7E jle/7F jg)
571 let rel: i64 = sx8(mem[pc + 1] as i64)
572 var taken: i64 = 0
573 if b == 0x74 { if flagv == 0 { taken = 1 } }
574 if b == 0x75 { if flagv != 0 { taken = 1 } }
575 if b == 0x7C { if flagv < 0 { taken = 1 } }
576 if b == 0x7D { if flagv >= 0 { taken = 1 } }
577 if b == 0x7E { if flagv <= 0 { taken = 1 } }
578 if b == 0x7F { if flagv > 0 { taken = 1 } }
579 if taken == 1 { pc = (pc + 2) + rel } else { pc = pc + 2 }
580 } else { if b == 0xEB { // jmp rel8; EB FE self-jump = the app deliberately HOLDS
581 let rel: i64 = sx8(mem[pc + 1] as i64)
582 if rel == (0 - 2) { return 0 - 90 } // distinct HOLD return -- judged by MEASURING the framebuffer, not an error
583 pc = (pc + 2) + rel
584 } else { if b == 0xE9 { // jmp rel32 (a loop body >127B needs it)
585 pc = (pc + 5) + m_i32(mem, pc + 1)
586 } else { if b == 0x0F { // 0F 85 = jnz rel32 (loops >127B)
587 let two: i64 = mem[pc + 1] as i64
588 if two == 0x85 { // jnz rel32
589 if flagv != 0 { pc = (pc + 6) + m_i32(mem, pc + 2) } else { pc = pc + 6 }
590 } else { if two == 0x84 { // jz rel32
591 if flagv == 0 { pc = (pc + 6) + m_i32(mem, pc + 2) } else { pc = pc + 6 }
592 } else { return 0 - 42 } }
593 } else { if b == 0xBA { // mov edx,imm32 (the port selector)
594 var pv: i64 = m_i32(mem, pc + 1)
595 if pv < 0 { pv = pv + (1 << 32) }
596 reg[R_RDX] = pv
597 pc = pc + 5
598 } else { if b == 0xEF { // OUT dx,eax -- CONFIG_ADDRESS ONLY
599 if reg[R_RDX] != PCI_ADDR_PORT {
600 // A port WRITE anywhere else can mutate device or firmware state. Refused, and
601 // named, rather than quietly permitted. (never-brick, rule 26)
602 e_p("EMU-PORT refused OUT to port 0x" as *u8); e_fx(1, reg[R_RDX]); e_p("
603" as *u8)
604 return 0 - 41
605 }
606 m_w64(mem, PCI_LATCH, reg[R_RAX] & 0xFFFFFFFF)
607 pc = pc + 1
608 } else { if b == 0xED { // IN eax,dx -- CONFIG_DATA ONLY
609 if reg[R_RDX] != PCI_DATA_PORT {
610 e_p("EMU-PORT refused IN from port 0x" as *u8); e_fx(1, reg[R_RDX]); e_p("
611" as *u8)
612 return 0 - 41
613 }
614 let plat: i64 = m_r64(mem, PCI_LATCH)
615 reg[R_RAX] = pci_cfg_read(plat)
616 pc = pc + 1
617 } else { if b == 0xE4 { // IN al,imm8 -- modeled PS/2 only
618 let port: i64 = mem[pc + 1] as i64
619 if port == PS2_PORT_STATUS {
620 let n: i64 = m_r64(mem, PS2_STATE)
621 let ix: i64 = m_r64(mem, PS2_STATE + 8)
622 if ix < n { reg[R_RAX] = 1 } else { reg[R_RAX] = 0 } // bit0 = byte waiting
623 } else { if port == PS2_PORT_DATA {
624 let n2: i64 = m_r64(mem, PS2_STATE)
625 let ix2: i64 = m_r64(mem, PS2_STATE + 8)
626 if ix2 < n2 {
627 reg[R_RAX] = m_r64(mem, PS2_STATE + 16 + ix2 * 8)
628 m_w64(mem, PS2_STATE + 8, ix2 + 1)
629 } else { reg[R_RAX] = 0 }
630 } else {
631 // Any other port is REFUSED. An emulator that invents a value for an unmodeled
632 // port teaches the kernel that made-up hardware works.
633 e_p("EMU-PORT refused unmodeled port 0x" as *u8); e_fx(1, port); e_p("
634" as *u8)
635 return 0 - 38
636 } }
637 pc = pc + 2
638 } else { if b == 0xFC { // cld (this model is always forward)
639 pc = pc + 1
640 } else { if b == 0xF3 { // F3 AB rep stosd: eax -> rcx dwords at [rdi], ONE interpreter step
641 if (mem[pc + 1] as i64) != 0xAB { return 0 - 27 }
642 let cval: i64 = reg[R_RAX] & 0xFFFFFFFF
643 while reg[R_RCX] > 0 {
644 if reg[R_RDI] < 0 { return 0 - 34 } // store outside modeled memory = refused, never wrapped
645 if (reg[R_RDI] + 4) > MEMSZ { return 0 - 34 }
646 mem[reg[R_RDI]] = (cval & 0xff) as u8
647 mem[reg[R_RDI] + 1] = ((cval >> 8) & 0xff) as u8
648 mem[reg[R_RDI] + 2] = ((cval >> 16) & 0xff) as u8
649 mem[reg[R_RDI] + 3] = ((cval >> 24) & 0xff) as u8
650 reg[R_RDI] = reg[R_RDI] + 4
651 reg[R_RCX] = reg[R_RCX] - 1
652 }
653 pc = pc + 2
654 } else { if b >= 0xB8 {
655 if b <= 0xBF { // mov r32,imm32 (B8+rd, zero-extends into r64)
656 var iv: i64 = m_i32(mem, pc + 1)
657 if iv < 0 { iv = iv + (1 << 32) }
658 reg[b - 0xB8] = iv
659 pc = pc + 5
660 } else { return 0 - 29 }
661 } else { if b >= 0x50 { // push/pop r64 (0x50..0x5F)
662 if b <= 0x57 {
663 reg[R_RSP] = reg[R_RSP] - 8
664 m_w64(mem, reg[R_RSP], reg[b - 0x50])
665 pc = pc + 1
666 } else { if b <= 0x5F {
667 reg[b - 0x58] = m_r64(mem, reg[R_RSP])
668 reg[R_RSP] = reg[R_RSP] + 8
669 pc = pc + 1
670 } else { return 0 - 20 } }
671 } else {
672 // an undecoded opcode must say WHICH one and WHERE -- a bare sentinel costs a build
673 // cycle every time. (Learned twice today: name the failing tooth, name the failing byte.)
674 e_p("EMU-UNDECODED opcode=0x" as *u8); e_fx(1, b)
675 e_p(" next=0x" as *u8); e_fx(1, mem[pc + 1] as i64)
676 e_p(" at pc=0x" as *u8); e_fx(1, pc); e_p(" step=" as *u8); e_fn(1, steps); e_p("\n" as *u8)
677 return 0 - 29
678 } } } } } } } } } } } } } } } } // <- closes the dispatch chain ONLY, back to loop depth
679 // ⚠ONE closer per `} else { if …` link. This pile has now been wrong three times while
680 // adding opcodes; ALWAYS re-run the brace-depth counter after touching the dispatch.
681 } // <- closes `while`
682 // ⚠The pile above was ONE closer too long for a day: it ended the FUNCTION, so the next line
683 // closed the loop and everything below fell OUTSIDE emu_run -- silently, because a stray
684 // statement at module level is not an error. Symptom: segfault with ZERO output (the exit
685 // block got a synthesized return of param0, main read that pointer as a length and wrote far
686 // past a 512-byte buffer). ★★★★★A DEEP ELSE-CHAIN HIDES ITS OWN MIS-NESTING -- COUNT BRACE
687 // DEPTH MECHANICALLY, NEVER BY EYE. I blamed the compiler first; the compiler was innocent.
688 return 0 - 30 // step cap: a runaway, OR a live event loop
689}
690
691// ===== I/O + log =================================================================
692func e_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
693func e_fp(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
694func e_fn(fd: i64, v: i64) -> i64 {
695 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m }
696 let t: *u8 = sys_mmap(28); var k: i64 = 0
697 if m == 0 { t[0] = 48; k = 1 }
698 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
699 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
700 sys_write(fd, bb, k); return 0
701}
702
703// hex print (8 digits) -- the measured fill color is reported in the artifact's own terms
704func e_fx(fd: i64, v: i64) -> i64 {
705 let hx: *u8 = "0123456789ABCDEF" as *u8
706 let bb: *u8 = sys_mmap(16)
707 var i: i64 = 0
708 while i < 8 { bb[i] = hx[(v >> (28 - 4 * i)) & 15]; i = i + 1 }
709 sys_write(fd, bb, 8); return 0
710}
711
712// Dump the modeled framebuffer as a 24-bit BMP so the operator SEES what the sovereign lane
713// measured -- the picture and the verdict come from the SAME bytes (no second render path).
714func fb_dump_bmp(mem: *u8, base: i64, w: i64, h: i64, path: *u8) -> i64 {
715 let rowb: i64 = w * 3
716 var pad: i64 = 0
717 while ((rowb + pad) % 4) != 0 { pad = pad + 1 }
718 let imgsz: i64 = (rowb + pad) * h
719 let total: i64 = 54 + imgsz
720 let bm: *u8 = sys_mmap(total + 4096)
721 bm[0] = 66 as u8; bm[1] = 77 as u8 // "BM"
722 m_w32b(bm, 2, total); m_w32b(bm, 10, 54)
723 m_w32b(bm, 14, 40); m_w32b(bm, 18, w); m_w32b(bm, 22, h)
724 bm[26] = 1 as u8; bm[28] = 24 as u8 // planes=1, bpp=24
725 m_w32b(bm, 34, imgsz)
726 var y: i64 = 0
727 while y < h {
728 let srow: i64 = base + (h - 1 - y) * w * 4 // BMP rows are bottom-up
729 var d: i64 = 54 + y * (rowb + pad)
730 var x: i64 = 0
731 while x < w {
732 bm[d] = mem[srow + x * 4] // B
733 bm[d + 1] = mem[srow + x * 4 + 1] // G
734 bm[d + 2] = mem[srow + x * 4 + 2] // R
735 d = d + 3
736 x = x + 1
737 }
738 y = y + 1
739 }
740 let fd: i64 = sys_openat_wr(path, 0x1a4)
741 if fd < 0 { return 0 - 1 }
742 sys_write(fd, bm, total)
743 sys_close(fd)
744 return total
745}
746func m_w32b(b: *u8, o: i64, v: i64) -> i64 {
747 b[o] = (v & 0xff) as u8; b[o+1] = ((v >> 8) & 0xff) as u8
748 b[o+2] = ((v >> 16) & 0xff) as u8; b[o+3] = ((v >> 24) & 0xff) as u8
749 return 0
750}
751
752// atoi for the small positive coordinates the pixel probe takes
753func s_atoi(s: *u8) -> i64 {
754 var v: i64 = 0; var i: i64 = 0
755 while s[i] != (0 as u8) {
756 let d: i64 = (s[i] as i64) - 48
757 if d < 0 { return 0 - 1 }
758 if d > 9 { return 0 - 1 }
759 v = v * 10 + d; i = i + 1
760 }
761 return v
762}
763
764// --px <x> <y> : print the exact dword at one pixel. Generic harness verb -- the emulator learns
765// no kernel geometry, the CALLER states the coordinate, so a rung can assert a computed value
766// without the emulator being taught what that value means.
767func px_probe(mem: *u8, base: i64, argc: i64, argv: *i64) -> i64 {
768 var i: i64 = 2
769 while i < (argc - 2) {
770 if s_eq(argv[i] as *u8, "--px" as *u8) == 1 {
771 let x: i64 = s_atoi(argv[i + 1] as *u8)
772 let y: i64 = s_atoi(argv[i + 2] as *u8)
773 if x < 0 { return 0 }
774 if y < 0 { return 0 }
775 let a: i64 = base + (y * FB_W_PIX + x) * 4
776 let d: i64 = (mem[a] as i64) | ((mem[a+1] as i64) << 8) | ((mem[a+2] as i64) << 16) | ((mem[a+3] as i64) << 24)
777 e_p("NOS-PX x=" as *u8); e_fn(1, x); e_p(" y=" as *u8); e_fn(1, y)
778 e_p(" value=" as *u8); e_fn(1, d); e_p(" hex=0x" as *u8); e_fx(1, d); e_p("\n" as *u8)
779 return d
780 }
781 i = i + 1
782 }
783 return 0
784}
785
786func s_eq(a: *u8, b: *u8) -> i64 {
787 var i: i64 = 0
788 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
789 if b[i] != (0 as u8) { return 0 }
790 return 1
791}
792
793// MEASURE the modeled framebuffer: color := first dword (never assumed from any emitter constant),
794// outp[0]=color outp[1]=dwords-matching-color outp[2]=total-dwords outp[3]=zero-dwords
795// outp[4]=adjacent-transitions (a SCENE has many; a wash has 0; an unpainted hole shows in zeros).
796func fb_measure(mem: *u8, base: i64, size: i64, outp: *i64) -> i64 {
797 let ndw: i64 = size / 4
798 let color: i64 = (mem[base] as i64) | ((mem[base+1] as i64) << 8) | ((mem[base+2] as i64) << 16) | ((mem[base+3] as i64) << 24)
799 var matches: i64 = 0
800 var zeros: i64 = 0
801 var trans: i64 = 0
802 var prev: i64 = color
803 var i: i64 = 0
804 while i < ndw {
805 let o: i64 = base + i * 4
806 let d: i64 = (mem[o] as i64) | ((mem[o+1] as i64) << 8) | ((mem[o+2] as i64) << 16) | ((mem[o+3] as i64) << 24)
807 if d == color { matches = matches + 1 }
808 if d == 0 { zeros = zeros + 1 }
809 if d != prev { trans = trans + 1 }
810 prev = d
811 i = i + 1
812 }
813 outp[0] = color; outp[1] = matches; outp[2] = ndw; outp[3] = zeros; outp[4] = trans
814 return matches
815}
816
817// PURE verdict for a HOLDING app (testable without waiting on a real image):
818// 1 = PAINTED-AND-HOLDING (uniform nonzero wash covers 100%)
819// 2 = SCENE-PAINTED-AND-HOLDING (every dword nonzero AND >=4 transitions = a composed scene)
820// 0 = not painted (any zero dword, or a black wash)
821func hold_verdict(color: i64, matches: i64, ndw: i64, zeros: i64, trans: i64) -> i64 {
822 if ndw <= 0 { return 0 }
823 if color != 0 { if matches == ndw { return 1 } }
824 if zeros == 0 { if trans >= 4 { return 2 } }
825 return 0
826}
827
828// selftest byte writers
829func st_w(mem: *u8, o: i64, v: i64) -> i64 { mem[o] = (v & 0xff) as u8; return o + 1 }
830func st_i32(mem: *u8, o: i64, v: i64) -> i64 {
831 st_w(mem, o, v); st_w(mem, o + 1, v >> 8); st_w(mem, o + 2, v >> 16); st_w(mem, o + 3, v >> 24); return o + 4
832}
833
834// ===== in-process teeth (every selftest run refutable; negative controls are the point) ======
835// T1 hold-without-paint must NOT read as painted. T2 full paint + hold IS painted. T3 partial
836// paint must NOT read as painted (the 1000-permil bar has a bite). T4 wrong GUID is refused -33.
837func emu_selftest() -> i64 {
838 let out: *u8 = sys_mmap(512)
839 let mm: *i64 = sys_mmap(48) as *i64
840 var pass: i64 = 0
841
842 // T1: EB FE only -- HOLDS, framebuffer untouched (zero) -> not painted
843 var mem: *u8 = sys_mmap(MEMSZ)
844 var o: i64 = 0x1000
845 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
846 var r: i64 = emu_run(mem, 0x1000, out, 0)
847 var c: i64 = 0
848 if r == (0 - 90) { fb_measure(mem, 0x2000, 0x400, mm); if mm[0] == 0 { c = 1 } }
849 if c == 1 { pass = pass + 1; e_p("EMU-T1 hold-unpainted GREEN\n" as *u8) } else { e_p("EMU-T1 RED\n" as *u8) }
850
851 // T2: mov rdi/rcx/eax + cld + rep stosd + hold -> painted 256/256, color as written
852 mem = sys_mmap(MEMSZ)
853 o = 0x1000
854 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC7); o = st_i32(mem, o, 0x2000)
855 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, 0x100)
856 o = st_w(mem, o, 0xB8); o = st_i32(mem, o, 0x00AA8844)
857 o = st_w(mem, o, 0xFC)
858 o = st_w(mem, o, 0xF3); o = st_w(mem, o, 0xAB)
859 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
860 r = emu_run(mem, 0x1000, out, 0)
861 c = 0
862 if r == (0 - 90) {
863 fb_measure(mem, 0x2000, 0x400, mm)
864 if mm[0] == 0x00AA8844 { if mm[1] == 256 { if mm[2] == 256 { c = 1 } } }
865 }
866 if c == 1 { pass = pass + 1; e_p("EMU-T2 painted-and-holding GREEN\n" as *u8) } else { e_p("EMU-T2 RED\n" as *u8) }
867
868 // T3: same but only half the dwords -> 128/256, must NOT be full
869 mem = sys_mmap(MEMSZ)
870 o = 0x1000
871 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC7); o = st_i32(mem, o, 0x2000)
872 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, 0x80)
873 o = st_w(mem, o, 0xB8); o = st_i32(mem, o, 0x00AA8844)
874 o = st_w(mem, o, 0xFC)
875 o = st_w(mem, o, 0xF3); o = st_w(mem, o, 0xAB)
876 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
877 r = emu_run(mem, 0x1000, out, 0)
878 c = 0
879 if r == (0 - 90) { fb_measure(mem, 0x2000, 0x400, mm); if mm[1] == 128 { if mm[2] == 256 { c = 1 } } }
880 if c == 1 { pass = pass + 1; e_p("EMU-T3 partial-refused GREEN\n" as *u8) } else { e_p("EMU-T3 RED\n" as *u8) }
881
882 // T4: LocateProtocol with a WRONG (all-zero) GUID -> refused with -33
883 mem = sys_mmap(MEMSZ)
884 o = 0x1000
885 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC0); o = st_i32(mem, o, HOOK3)
886 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, 0x3000)
887 o = st_w(mem, o, 0xFF); o = st_w(mem, o, 0xD0)
888 r = emu_run(mem, 0x1000, out, 0)
889 if r == (0 - 33) { pass = pass + 1; e_p("EMU-T4 guid-refused GREEN\n" as *u8) } else { e_p("EMU-T4 RED\n" as *u8) }
890
891 // T5: five distinct bands covering the whole region -> SCENE verdict (2), not uniform, not red
892 mem = sys_mmap(MEMSZ)
893 o = 0x1000
894 o = st_w(mem, o, 0xFC)
895 var bi: i64 = 0
896 while bi < 5 {
897 var bcnt: i64 = 51
898 if bi == 4 { bcnt = 52 }
899 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC7); o = st_i32(mem, o, 0x2000 + bi * 204)
900 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, bcnt)
901 o = st_w(mem, o, 0xB8); o = st_i32(mem, o, 0x11111111 * (bi + 1))
902 o = st_w(mem, o, 0xF3); o = st_w(mem, o, 0xAB)
903 bi = bi + 1
904 }
905 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
906 r = emu_run(mem, 0x1000, out, 0)
907 c = 0
908 if r == (0 - 90) {
909 fb_measure(mem, 0x2000, 0x400, mm)
910 if hold_verdict(mm[0], mm[1], mm[2], mm[3], mm[4]) == 2 { c = 1 }
911 }
912 if c == 1 { pass = pass + 1; e_p("EMU-T5 scene-verdict GREEN\n" as *u8) } else { e_p("EMU-T5 RED\n" as *u8) }
913
914 // T6: full-region fill EXCEPT one zero dword -> a hole means NOT painted (verdict 0)
915 mem = sys_mmap(MEMSZ)
916 o = 0x1000
917 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC7); o = st_i32(mem, o, 0x2000)
918 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, 255)
919 o = st_w(mem, o, 0xB8); o = st_i32(mem, o, 0x66666666)
920 o = st_w(mem, o, 0xFC)
921 o = st_w(mem, o, 0xF3); o = st_w(mem, o, 0xAB)
922 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
923 r = emu_run(mem, 0x1000, out, 0)
924 c = 0
925 if r == (0 - 90) {
926 fb_measure(mem, 0x2000, 0x400, mm)
927 if hold_verdict(mm[0], mm[1], mm[2], mm[3], mm[4]) == 0 { c = 1 }
928 }
929 if c == 1 { pass = pass + 1; e_p("EMU-T6 hole-refused GREEN\n" as *u8) } else { e_p("EMU-T6 RED\n" as *u8) }
930
931 // T7: the ConIn hook DRAINS a scripted queue exactly once per key and then reports NOT-READY.
932 // Queue of 2; the program reads 3 times -> 2 successes then a refusal, and rax proves which.
933 mem = sys_mmap(MEMSZ)
934 m_w64(mem, CI_ADDR + 0x08, HOOK4)
935 m_w64(mem, KEYQ_ADDR, 2)
936 m_w64(mem, KEYQ_ADDR + 8, 0)
937 m_w64(mem, KEYQ_ADDR + 16 + 8, 65) // 'A'
938 m_w64(mem, KEYQ_ADDR + 32 + 8, 66) // 'B'
939 o = 0x1000
940 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC1); o = st_i32(mem, o, CI_ADDR)
941 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0xC7); o = st_w(mem, o, 0xC2); o = st_i32(mem, o, 0x3000)
942 var kk: i64 = 0
943 while kk < 3 { // 3 reads against a 2-key queue
944 o = st_w(mem, o, 0x48); o = st_w(mem, o, 0x8B); o = st_w(mem, o, 0x41); o = st_w(mem, o, 0x08)
945 o = st_w(mem, o, 0xFF); o = st_w(mem, o, 0xD0)
946 kk = kk + 1
947 }
948 o = st_w(mem, o, 0xEB); o = st_w(mem, o, 0xFE)
949 r = emu_run(mem, 0x1000, out, 0)
950 c = 0
951 if r == (0 - 90) {
952 if m_r64(mem, KEYQ_ADDR + 8) == 2 { // exactly 2 drained, never 3
953 if (mem[0x3002] as i64) == 66 { c = 1 } // the LAST delivered key was 'B'
954 }
955 }
956 if c == 1 { pass = pass + 1; e_p("EMU-T7 conin-drain GREEN\n" as *u8) } else { e_p("EMU-T7 RED\n" as *u8) }
957
958 // T8: the N1-full file chain end to end THROUGH THE SAME emu_hook_call the interpreter uses,
959 // plus its refusals. A hook nobody has exercised is not a hook.
960 mem = sys_mmap(MEMSZ)
961 m_w64(mem, LI_ADDR + 0x18, DEV_HANDLE)
962 m_w64(mem, KFILE_LEN, 8)
963 var gi2: i64 = 0
964 while gi2 < 16 { mem[0x3000 + gi2] = li_guid_b(gi2) as u8; mem[0x3100 + gi2] = fs_guid_b(gi2) as u8; gi2 = gi2 + 1 }
965 var kb2: i64 = 0
966 while kb2 < 8 { mem[KFILE_DATA + kb2] = (0x41 + kb2) as u8; kb2 = kb2 + 1 }
967 let rg: *i64 = sys_mmap(8 * 16) as *i64
968 var c8: i64 = 1
969 rg[R_RCX] = 0x1234; rg[R_RDX] = 0x3000; rg[8] = 0x3200
970 if emu_hook_call(mem, HOOK6, rg) != 0 { c8 = 0 }
971 if m_r64(mem, 0x3200) != LI_ADDR { c8 = 0 }
972 rg[R_RCX] = m_r64(mem, LI_ADDR + 0x18); rg[R_RDX] = 0x3100; rg[8] = 0x3208
973 if emu_hook_call(mem, HOOK6, rg) != 0 { c8 = 0 }
974 if m_r64(mem, 0x3208) != FS_ADDR { c8 = 0 }
975 rg[R_RCX] = 0xDEAD; rg[R_RDX] = 0x3100; rg[8] = 0x3210
976 if emu_hook_call(mem, HOOK6, rg) != (0 - 39) { c8 = 0 }
977 rg[R_RCX] = FS_ADDR; rg[R_RDX] = 0x3218
978 if emu_hook_call(mem, HOOK7, rg) != 0 { c8 = 0 }
979 if m_r64(mem, 0x3218) != ROOT_ADDR { c8 = 0 }
980 // Open() is NAME-AWARE now, so the test must present a real UTF-16LE name. The names are
981 // RESTATED here as literals rather than read from esp_name_ch -- a test that borrows the
982 // implementation's table cannot catch the implementation's typo.
983 let knm: *u8 = "KERNEL.NXE" as *u8
984 var ni: i64 = 0
985 while knm[ni] != (0 as u8) { m_w16(mem, 0x3300 + ni * 2, knm[ni] as i64); ni = ni + 1 }
986 m_w16(mem, 0x3300 + ni * 2, 0)
987 let anm: *u8 = "NISHI.AI" as *u8
988 var ai2: i64 = 0
989 while anm[ai2] != (0 as u8) { m_w16(mem, 0x3340 + ai2 * 2, anm[ai2] as i64); ai2 = ai2 + 1 }
990 m_w16(mem, 0x3340 + ai2 * 2, 0)
991 let xnm: *u8 = "PASSWD.TXT" as *u8
992 var xi: i64 = 0
993 while xnm[xi] != (0 as u8) { m_w16(mem, 0x3380 + xi * 2, xnm[xi] as i64); xi = xi + 1 }
994 m_w16(mem, 0x3380 + xi * 2, 0)
995 rg[R_RCX] = ROOT_ADDR; rg[R_RDX] = 0x3220; rg[8] = 0x3300; rg[9] = 3
996 if emu_hook_call(mem, HOOK8, rg) != (0 - 40) { c8 = 0 }
997 rg[9] = 1
998 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
999 if m_r64(mem, 0x3220) != FILE_ADDR { c8 = 0 }
1000 // NEG: a file this ESP does not serve is EFI_NOT_FOUND, never a silently-served handle.
1001 rg[R_RDX] = 0x3230; rg[8] = 0x3380
1002 m_w64(mem, 0x3230, 0)
1003 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1004 if rg[R_RAX] != EFI_NOT_FOUND { c8 = 0 }
1005 if m_r64(mem, 0x3230) != 0 { c8 = 0 }
1006 // The OPTIONAL assistant file: ABSENT (len 0) must be NOT_FOUND -- this is the DEFAULT state of
1007 // the toggle, and the state the loader must survive. Then present -> its own distinct handle.
1008 rg[R_RDX] = 0x3238; rg[8] = 0x3340
1009 m_w64(mem, 0x3238, 0)
1010 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1011 if rg[R_RAX] != EFI_NOT_FOUND { c8 = 0 }
1012 if m_r64(mem, 0x3238) != 0 { c8 = 0 }
1013 m_w64(mem, AIFILE_LEN, 4)
1014 var ab2: i64 = 0
1015 while ab2 < 4 { mem[AIFILE_DATA + ab2] = (0x61 + ab2) as u8; ab2 = ab2 + 1 }
1016 // FS-2: even WITH the file present, the BOOT volume must still refuse it -- the assistant file
1017 // lives on the DATA volume, and this is the check that makes "search every volume" testable.
1018 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1019 if rg[R_RAX] != EFI_NOT_FOUND { c8 = 0 }
1020 if m_r64(mem, 0x3238) != 0 { c8 = 0 }
1021 rg[R_RCX] = ROOT2_ADDR
1022 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1023 if m_r64(mem, 0x3238) != AIFILE_ADDR { c8 = 0 }
1024 // and the DATA volume must NOT serve the kernel: one file, one home.
1025 rg[R_RDX] = 0x3248; rg[8] = 0x3300
1026 m_w64(mem, 0x3248, 0)
1027 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1028 if rg[R_RAX] != EFI_NOT_FOUND { c8 = 0 }
1029 // LocateHandleBuffer hands back BOTH device handles, by protocol, with a real count
1030 rg[R_RSP] = 0x7000
1031 m_w64(mem, 0x7020, 0x7100)
1032 rg[R_RCX] = 2; rg[R_RDX] = 0x3100; rg[8] = 0; rg[9] = 0x7108
1033 if emu_hook_call(mem, HOOK10, rg) != 0 { c8 = 0 }
1034 if m_r64(mem, 0x7108) != 2 { c8 = 0 }
1035 let hb: i64 = m_r64(mem, 0x7100)
1036 if m_r64(mem, hb) != DEV_HANDLE { c8 = 0 }
1037 if m_r64(mem, hb + 8) != DEV2_HANDLE { c8 = 0 }
1038 rg[R_RCX] = 1 // AllHandles must be REFUSED, not served
1039 if emu_hook_call(mem, HOOK10, rg) != (0 - 43) { c8 = 0 }
1040 rg[R_RCX] = ROOT_ADDR
1041 // and its Read serves the AI bytes, NOT the kernel's
1042 m_w64(mem, 0x3240, 64)
1043 rg[R_RCX] = AIFILE_ADDR; rg[R_RDX] = 0x3240; rg[8] = 0x3500
1044 if emu_hook_call(mem, HOOK9, rg) != 0 { c8 = 0 }
1045 if m_r64(mem, 0x3240) != 4 { c8 = 0 }
1046 var ax2: i64 = 0
1047 while ax2 < 4 { if (mem[0x3500 + ax2] as i64) != (0x61 + ax2) { c8 = 0 } ax2 = ax2 + 1 }
1048 m_w64(mem, AIFILE_LEN, 0)
1049 rg[R_RCX] = ROOT_ADDR; rg[R_RDX] = 0x3220; rg[8] = 0x3300; rg[9] = 1
1050 if emu_hook_call(mem, HOOK8, rg) != 0 { c8 = 0 }
1051 m_w64(mem, 0x3228, 64)
1052 rg[R_RCX] = FILE_ADDR; rg[R_RDX] = 0x3228; rg[8] = 0x3400
1053 if emu_hook_call(mem, HOOK9, rg) != 0 { c8 = 0 }
1054 if m_r64(mem, 0x3228) != 8 { c8 = 0 }
1055 var rb2: i64 = 0
1056 while rb2 < 8 { if (mem[0x3400 + rb2] as i64) != (0x41 + rb2) { c8 = 0 } rb2 = rb2 + 1 }
1057 if c8 == 1 { pass = pass + 1; e_p("EMU-T8 esp-file-chain GREEN
1058" as *u8) } else { e_p("EMU-T8 RED
1059" as *u8) }
1060
1061 let lf: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1062 if lf >= 0 {
1063 e_fp(lf, "NOSEMU selftest teeth=" as *u8); e_fn(lf, pass); e_fp(lf, "of8 verdict=" as *u8)
1064 if pass == 8 { e_fp(lf, "GREEN\n" as *u8) } else { e_fp(lf, "RED\n" as *u8) }
1065 sys_close(lf)
1066 }
1067 if pass == 8 {
1068 e_p("EMU-SELFTEST GREEN 8/8\n" as *u8)
1069 sys_exit(0); return 0
1070 }
1071 e_p("EMU-SELFTEST RED\n" as *u8)
1072 sys_exit(1); return 1
1073}
1074
1075func main(argc: i64, argv: *i64) -> i64 {
1076 var path: *u8 = "_offc/nx_boot_uefi.efi" as *u8
1077 if argc >= 2 { path = argv[1] as *u8 }
1078 if argc >= 2 { if s_eq(path, "selftest" as *u8) == 1 { return emu_selftest() } }
1079
1080 // load the .efi
1081 let lenp: *i64 = sys_mmap(16) as *i64
1082 let fb: *u8 = sys_read_file(path, lenp)
1083 let flen: i64 = lenp[0]
1084 if flen <= 0 { e_p("NOS-EXEC REFUSED: cannot read " as *u8); e_p(path); e_p("\n" as *u8); sys_exit(2); return 2 }
1085
1086 // parse the PE: entry RVA + first section (.text) load
1087 let entry: i64 = f_u32(fb, 0x98 + 16)
1088 let vaddr: i64 = f_u32(fb, 0x188 + 12)
1089 let rawsz: i64 = f_u32(fb, 0x188 + 16)
1090 let rawptr: i64 = f_u32(fb, 0x188 + 20)
1091
1092 let mem: *u8 = sys_mmap(MEMSZ)
1093 var i: i64 = 0
1094 while i < rawsz { mem[vaddr + i] = fb[rawptr + i]; i = i + 1 }
1095
1096 // mock UEFI: SystemTable + ConOut + FirmwareVendor string, OutputString = HOOK sentinel
1097 m_w64(mem, ST_ADDR + 0x18, FV_ADDR) // SystemTable->FirmwareVendor
1098 m_w64(mem, ST_ADDR + 0x40, CO_ADDR) // SystemTable->ConOut
1099 m_w64(mem, CO_ADDR + 0x08, HOOK) // ConOut->OutputString
1100 m_w64(mem, ST_ADDR + 0x30, CI_ADDR) // SystemTable->ConIn
1101 m_w64(mem, CI_ADDR + 0x08, HOOK4) // ConIn->ReadKeyStroke (hook, scripted queue)
1102 m_w64(mem, ST_ADDR + 0x60, BS_ADDR) // SystemTable->BootServices
1103 m_w64(mem, BS_ADDR + 0x38, HOOK2) // BootServices->GetMemoryMap (hook)
1104 m_w64(mem, BS_ADDR + 0xE8, HOOK5) // BootServices->ExitBootServices (UEFI spec offset)
1105 m_w64(mem, BS_ADDR + 0x98, HOOK6) // BootServices->HandleProtocol
1106 m_w64(mem, BS_ADDR + 0x138, HOOK10) // BootServices->LocateHandleBuffer (FS-2 volume search)
1107 m_w64(mem, FS2_ADDR + 0x08, HOOK7) // the DATA volume's OpenVolume
1108 m_w64(mem, ROOT2_ADDR + 0x08, HOOK8) // its root Open
1109 m_w64(mem, LI_ADDR + 0x18, DEV_HANDLE) // LoadedImage->DeviceHandle (the ESP we booted from)
1110 m_w64(mem, FS_ADDR + 0x08, HOOK7) // SimpleFileSystem->OpenVolume
1111 m_w64(mem, ROOT_ADDR + 0x08, HOOK8) // EFI_FILE->Open
1112 m_w64(mem, FILE_ADDR + 0x20, HOOK9) // EFI_FILE->Read
1113 m_w64(mem, AIFILE_ADDR + 0x20, HOOK9) // the OPTIONAL assistant file needs its OWN Read slot.
1114 // Omitting it made [rcx+0x20] read 0 and the loader
1115 // `call rax` jumped to address 0 -- the emulator refused
1116 // with EMU-UNDECODED at pc=0 rather than pretending.
1117 // A SECOND HANDLE IS A SECOND VTABLE, NOT A SECOND NAME.
1118 m_w64(mem, BS_ADDR + 0x140, HOOK3) // BootServices->LocateProtocol (hook, GUID-checked)
1119 m_w64(mem, GOP_ADDR + 0x18, MODE_ADDR) // gop->Mode
1120 m_w64(mem, MODE_ADDR + 0x18, FB_ADDR) // Mode->FrameBufferBase (a real in-MEM framebuffer)
1121 m_w64(mem, MODE_ADDR + 0x20, FB_SIZE) // Mode->FrameBufferSize = 1280x800x4 (mirrors the OVMF oracle mode)
1122 // FirmwareVendor = UTF-16 "NISHI-FW"
1123 m_w16(mem, FV_ADDR + 0, 0x4E); m_w16(mem, FV_ADDR + 2, 0x49); m_w16(mem, FV_ADDR + 4, 0x53)
1124 m_w16(mem, FV_ADDR + 6, 0x48); m_w16(mem, FV_ADDR + 8, 0x49); m_w16(mem, FV_ADDR + 10, 0x2D)
1125 m_w16(mem, FV_ADDR + 12, 0x46); m_w16(mem, FV_ADDR + 14, 0x57); m_w16(mem, FV_ADDR + 16, 0x00)
1126
1127 // scripted keyboard: nx_emu_uefi <img> --keys <ascii-string> (each char = one ReadKeyStroke
1128 // SUCCESS in order; the queue then reports NOT-READY forever, exactly like idle firmware)
1129 var nkeys: i64 = 0
1130 if argc >= 4 {
1131 if s_eq(argv[2] as *u8, "--keys" as *u8) == 1 {
1132 let ks: *u8 = argv[3] as *u8
1133 while ks[nkeys] != (0 as u8) {
1134 m_w64(mem, KEYQ_ADDR + 16 + nkeys * 16, 0) // ScanCode 0 = a unicode key
1135 m_w64(mem, KEYQ_ADDR + 16 + nkeys * 16 + 8, ks[nkeys] as i64)
1136 nkeys = nkeys + 1
1137 }
1138 m_w64(mem, KEYQ_ADDR, nkeys)
1139 m_w64(mem, KEYQ_ADDR + 8, 0)
1140 var pk: i64 = 0
1141 while pk < nkeys {
1142 m_w64(mem, PS2_STATE + 16 + pk * 8, ks[pk] as i64)
1143 pk = pk + 1
1144 }
1145 m_w64(mem, PS2_STATE, nkeys)
1146 m_w64(mem, PS2_STATE + 8, 0)
1147 }
1148 }
1149
1150 // N1-full: --kernel <file> makes the modeled ESP serve REAL bytes -- a harness that invents
1151 // file contents proves nothing. Scanned at ANY argv position (the --keys positional trap is
1152 // not being extended to a second flag). Without --kernel the served length is 0, Read hands
1153 // back nothing, and the shim's magic check takes its NXE! refusal path -- so the missing-file
1154 // negative control costs nothing to run.
1155 var ka: i64 = 2
1156 while (ka + 1) < argc {
1157 if s_eq(argv[ka] as *u8, "--aifile" as *u8) == 1 {
1158 let alp: *i64 = sys_mmap(16) as *i64
1159 let ab: *u8 = sys_read_file(argv[ka + 1] as *u8, alp)
1160 let alen: i64 = alp[0]
1161 if alen <= 0 { e_p("NOS-EXEC REFUSED: cannot read --aifile\n" as *u8); sys_exit(2); return 2 }
1162 if alen > 0x100000 { e_p("NOS-EXEC REFUSED: --aifile exceeds the modeled window (0x100000)\n" as *u8); sys_exit(2); return 2 }
1163 var ac: i64 = 0
1164 while ac < alen { mem[AIFILE_DATA + ac] = ab[ac]; ac = ac + 1 }
1165 m_w64(mem, AIFILE_LEN, alen)
1166 }
1167 if s_eq(argv[ka] as *u8, "--kernel" as *u8) == 1 {
1168 let klp: *i64 = sys_mmap(16) as *i64
1169 let kb: *u8 = sys_read_file(argv[ka + 1] as *u8, klp)
1170 let klen: i64 = klp[0]
1171 if klen <= 0 { e_p("NOS-EXEC REFUSED: cannot read --kernel file\n" as *u8); sys_exit(2); return 2 }
1172 if klen > 0x60000 { e_p("NOS-EXEC REFUSED: --kernel exceeds the modeled ESP window (0x60000)\n" as *u8); sys_exit(2); return 2 }
1173 var kc: i64 = 0
1174 while kc < klen { mem[KFILE_DATA + kc] = kb[kc]; kc = kc + 1 }
1175 m_w64(mem, KFILE_LEN, klen)
1176 }
1177 ka = ka + 1
1178 }
1179
1180 let out: *u8 = sys_mmap(512)
1181 var tracen: i64 = 0
1182 if argc >= 3 { if s_eq(argv[2] as *u8, "--trace" as *u8) == 1 { tracen = 1 } }
1183 let outn: i64 = emu_run(mem, entry, out, tracen)
1184
1185 // -30 = the step cap. For an EVENT-LOOP kernel that is NOT an error: a live poll loop is what
1186 // an OS does. Judge it the same way as a hold -- by MEASURING the screen it owns.
1187 if outn == (0 - 90) { } else { if outn == (0 - 30) { } else { if outn < 0 {
1188 e_p("NOS-EXEC RED: interpreter sentinel " as *u8); e_fn(1, outn); e_p(" on " as *u8); e_p(path); e_p("\n" as *u8)
1189 let lf3: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1190 if lf3 >= 0 { e_fp(lf3, "NOSEXEC name=" as *u8); e_fp(lf3, path); e_fp(lf3, " err=" as *u8); e_fn(lf3, outn); e_fp(lf3, " verdict=RED\n" as *u8); sys_close(lf3) }
1191 sys_exit(1); return 1
1192 } } }
1193
1194 if outn == (0 - 30) { // live event loop: measure, report keys consumed
1195 let fbb2: i64 = m_r64(mem, MODE_ADDR + 0x18)
1196 let fbs2: i64 = m_r64(mem, MODE_ADDR + 0x20)
1197 let m2: *i64 = sys_mmap(48) as *i64
1198 fb_measure(mem, fbb2, fbs2, m2)
1199 let drained: i64 = m_r64(mem, KEYQ_ADDR + 8)
1200 px_probe(mem, fbb2, argc, argv)
1201 let hv2: i64 = hold_verdict(m2[0], m2[1], m2[2], m2[3], m2[4])
1202 if argc >= 6 {
1203 if s_eq(argv[4] as *u8, "--shot" as *u8) == 1 { fb_dump_bmp(mem, fbb2, FB_W_PIX, FB_H_PIX, argv[5] as *u8) }
1204 }
1205 let ebs: i64 = m_r64(mem, EBS_STATE)
1206 e_p("NOS-EXEC: " as *u8); e_p(path); e_p(" RUNS an event loop; keys_consumed=" as *u8)
1207 e_fn(1, drained); e_p("/" as *u8); e_fn(1, nkeys); e_p(" zeros=" as *u8); e_fn(1, m2[3])
1208 e_p(" transitions=" as *u8); e_fn(1, m2[4])
1209 e_p(" firmware_exited=" as *u8); e_fn(1, ebs); e_p("\n" as *u8)
1210 // every scripted key must actually have been READ. Scripting NO keys is not a failure --
1211 // it proves the screen, just not the input; say exactly that instead of crying RED.
1212 var kok: i64 = 0
1213 if drained == nkeys { kok = 1 }
1214 let lf4: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1215 if lf4 >= 0 {
1216 e_fp(lf4, "NOSRUN name=" as *u8); e_fp(lf4, path)
1217 e_fp(lf4, " executed=sovereign-x86-emu loop=1 keys_consumed=" as *u8); e_fn(lf4, drained)
1218 e_fp(lf4, " of=" as *u8); e_fn(lf4, nkeys)
1219 e_fp(lf4, " zeros=" as *u8); e_fn(lf4, m2[3])
1220 e_fp(lf4, " transitions=" as *u8); e_fn(lf4, m2[4])
1221 e_fp(lf4, " firmware_exited=" as *u8); e_fn(lf4, ebs)
1222 e_fp(lf4, " verdict=" as *u8)
1223 if hv2 > 0 { if kok == 1 { e_fp(lf4, "RUNNING-AND-PAINTED\n" as *u8) } else { e_fp(lf4, "RED\n" as *u8) } }
1224 else { e_fp(lf4, "RED\n" as *u8) }
1225 sys_close(lf4)
1226 }
1227 if hv2 > 0 { if kok == 1 {
1228 e_p("NOS-EXEC GREEN: RUNNING-AND-PAINTED -- the kernel owns the screen" as *u8)
1229 if nkeys > 0 { e_p(" AND consumed every scripted keystroke" as *u8) } else { e_p(" (no keys scripted: input not exercised)" as *u8) }
1230 e_p(" (sovereign, no qemu)\n" as *u8)
1231 sys_exit(0); return 0
1232 } }
1233 e_p("NOS-EXEC RED: event loop ran but the screen or the key trace did not check out\n" as *u8)
1234 sys_exit(1); return 1
1235 }
1236
1237 if outn == (0 - 90) { // EB FE: the app parks HOLDING the screen -- measure, don't error
1238 let fbbase: i64 = m_r64(mem, MODE_ADDR + 0x18)
1239 let fbsize: i64 = m_r64(mem, MODE_ADDR + 0x20)
1240 let mm: *i64 = sys_mmap(48) as *i64
1241 fb_measure(mem, fbbase, fbsize, mm)
1242 let color: i64 = mm[0]
1243 let matches: i64 = mm[1]
1244 let ndw: i64 = mm[2]
1245 let zeros: i64 = mm[3]
1246 let trans: i64 = mm[4]
1247 var permil: i64 = 0
1248 if ndw > 0 { permil = (matches * 1000) / ndw }
1249 px_probe(mem, fbbase, argc, argv)
1250 let hv: i64 = hold_verdict(color, matches, ndw, zeros, trans)
1251 if argc >= 4 { // nx_emu_uefi <img> --shot <out.bmp>
1252 if s_eq(argv[2] as *u8, "--shot" as *u8) == 1 {
1253 let bw: i64 = fb_dump_bmp(mem, fbbase, FB_W_PIX, FB_H_PIX, argv[3] as *u8)
1254 e_p("NOS-SHOT: wrote " as *u8); e_fn(1, bw); e_p("B BMP -> " as *u8)
1255 e_p(argv[3] as *u8); e_p("\n" as *u8)
1256 }
1257 }
1258 e_p("NOS-EXEC: " as *u8); e_p(path); e_p(" HOLDS (EB FE); modeled-GOP fill " as *u8)
1259 e_fn(1, matches); e_p("/" as *u8); e_fn(1, ndw); e_p(" dwords " as *u8); e_fn(1, permil)
1260 e_p(" permil zeros=" as *u8); e_fn(1, zeros); e_p(" transitions=" as *u8); e_fn(1, trans)
1261 e_p(" firmware_exited=" as *u8); e_fn(1, m_r64(mem, EBS_STATE))
1262 e_p(" ps2_consumed=" as *u8); e_fn(1, m_r64(mem, PS2_STATE + 8))
1263 e_p("/" as *u8); e_fn(1, m_r64(mem, PS2_STATE))
1264 e_p(" color=0x" as *u8); e_fx(1, color); e_p("\n" as *u8)
1265 let lf2: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1266 if lf2 >= 0 {
1267 e_fp(lf2, "NOSEXEC name=" as *u8); e_fp(lf2, path)
1268 e_fp(lf2, " executed=sovereign-x86-emu hold=1 fb_dwords=" as *u8); e_fn(lf2, ndw)
1269 e_fp(lf2, " match_dwords=" as *u8); e_fn(lf2, matches)
1270 e_fp(lf2, " fill_permil=" as *u8); e_fn(lf2, permil)
1271 e_fp(lf2, " zeros=" as *u8); e_fn(lf2, zeros)
1272 e_fp(lf2, " transitions=" as *u8); e_fn(lf2, trans)
1273 e_fp(lf2, " firmware_exited=" as *u8); e_fn(lf2, m_r64(mem, EBS_STATE))
1274 e_fp(lf2, " ps2_consumed=" as *u8); e_fn(lf2, m_r64(mem, PS2_STATE + 8))
1275 e_fp(lf2, " color=0x" as *u8); e_fx(lf2, color)
1276 e_fp(lf2, " verdict=" as *u8)
1277 if hv == 1 { e_fp(lf2, "PAINTED-AND-HOLDING\n" as *u8) }
1278 if hv == 2 { e_fp(lf2, "SCENE-PAINTED-AND-HOLDING\n" as *u8) }
1279 if hv == 0 { e_fp(lf2, "RED\n" as *u8) }
1280 sys_close(lf2)
1281 }
1282 if hv == 1 {
1283 e_p("NOS-EXEC GREEN: PAINTED-AND-HOLDING -- the app filled 100.0% of the modeled framebuffer and holds the screen (sovereign, no qemu)\n" as *u8)
1284 sys_exit(0); return 0
1285 }
1286 if hv == 2 {
1287 e_p("NOS-EXEC GREEN: SCENE-PAINTED-AND-HOLDING -- every pixel written, composed multi-color scene, screen held (sovereign, no qemu)\n" as *u8)
1288 sys_exit(0); return 0
1289 }
1290 e_p("NOS-EXEC RED: the app holds but the modeled framebuffer is not fully painted\n" as *u8)
1291 sys_exit(1); return 1
1292 }
1293
1294 if outn < 0 {
1295 e_p("NOS-EXEC RED: interpreter sentinel " as *u8); e_fn(1, outn); e_p(" on " as *u8); e_p(path); e_p("\n" as *u8)
1296 let lf: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1297 if lf >= 0 { e_fp(lf, "NOSEXEC name=" as *u8); e_fp(lf, path); e_fp(lf, " err=" as *u8); e_fn(lf, outn); e_fp(lf, " verdict=RED\n" as *u8); sys_close(lf) }
1298 sys_exit(1); return 1
1299 }
1300
1301 // assert: captured console output begins with the expected prefix (default "NISHI",
1302 // override via argv[2] so the gate is reusable per rung -- exact match keeps its teeth)
1303 out[outn] = 0 as u8
1304 var exp: *u8 = "NISHI" as *u8
1305 if argc >= 3 { exp = argv[2] as *u8 }
1306 var ok: i64 = 1
1307 var j: i64 = 0
1308 while exp[j] != (0 as u8) {
1309 if j >= outn { ok = 0 } else { if out[j] != exp[j] { ok = 0 } }
1310 j = j + 1
1311 }
1312 if j == 0 { ok = 0 }
1313
1314 e_p("NOS-EXEC: " as *u8); e_p(path); e_p(" entry=0x" as *u8); e_fn(1, entry)
1315 e_p(" captured-console=[" as *u8); sys_write(1, out, outn); e_p("] chars=" as *u8); e_fn(1, outn); e_p("\n" as *u8)
1316
1317 let lf: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
1318 if lf >= 0 {
1319 e_fp(lf, "NOSEXEC name=" as *u8); e_fp(lf, path)
1320 e_fp(lf, " executed=sovereign-x86-emu console_chars=" as *u8); e_fn(lf, outn)
1321 e_fp(lf, " expect=" as *u8); e_fp(lf, exp); e_fp(lf, " verdict=" as *u8)
1322 if ok == 1 { e_fp(lf, "GREEN\n" as *u8) } else { e_fp(lf, "RED\n" as *u8) }
1323 sys_close(lf)
1324 }
1325
1326 if ok == 1 {
1327 e_p("NOS-EXEC GREEN: emitted .efi EXECUTES on the sovereign x86 emu and prints the expected output (boot-proof, no qemu/laptop)\n" as *u8)
1328 sys_exit(0); return 0
1329 }
1330 e_p("NOS-EXEC RED: console output did not begin with the expected prefix\n" as *u8)
1331 sys_exit(1); return 1
1332}