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}