code wiki / _hdl_build / nx_emu_uefi.nx

nx_emu_uefi.nx source

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