code wiki / _hdl_build / nx_gop_efi.nx

nx_gop_efi.nx source

↩ module page · 177 lines · 11133 B

1// nx_gop_efi.nx -- GX2: emit a real bootable UEFI app that draws the games' pixels on a real x86 screen via 2// the Graphics Output Protocol. The x86-on-metal companion to the silicon display rungs (SF1/SF2/SF4) and the 3// proven GOP blit logic (GX1, nx_gop_present). Mirrors nx_boot_uefi (subsystem-10 PE32+, hand-emitted PIC 4// x86-64, structural self-gate) -- its accepted scope is EMIT-proven here, BOOT-proven on OVMF/metal next rung. 5// 6// THE EFI APP (.text, MS x64 ABI; on entry RCX=ImageHandle, RDX=SystemTable): 7// BootServices = [SystemTable + 0x60] 8// LocateProtocol(&GOP_GUID, NULL, &gop) -- LocateProtocol = [BootServices + 0x140] (read-only discovery) 9// Mode = [gop + 0x18]; FrameBufferBase = [Mode + 0x18]; FrameBufferSize = [Mode + 0x20] 10// rep stosd: fill FrameBufferSize/4 dwords at FrameBufferBase with a solid color -- the canonical first 11// GOP app; a non-black screen on metal proves: valid EFI image + SystemTable/BootServices/GOP navigated + 12// the linear framebuffer written. (Richer gradient/game-frame blit = GX1's nx_gop_present, proven; a later 13// rung emits its loop.) 14// 15// NEVER-BRICK (#26): the app's ONLY persistent-relevant action is a VOLATILE-VRAM write (rep stosd to the GOP 16// FrameBufferBase). It calls LocateProtocol (read-only protocol discovery) and NOTHING ELSE -- no SetMode, no 17// Blt, no Set-Variable, no firmware/flash. So it cannot corrupt firmware/CMOS/NVRAM BY CONSTRUCTION, asserted 18// mechanically by gop_efi_verify (exactly one `call rax`, the FB-write present, no gop->SetMode/Blt call). 19// license_tier: ORIGINAL 20import "nx_syscalls.nx" 21 22// ===== PE/COFF + UEFI constants (mirror nx_boot_uefi) ============================ 23const PE_FILE_SIZE: i64 = 0x400 24const PE_MACHINE_AMD64: i64 = 0x8664 25const PE_OH_MAGIC_PEPLUS: i64 = 0x020B 26const PE_SUBSYSTEM_EFI_APP: i64 = 10 27const PE_CHAR_EXEC: i64 = 0x0002 28const PE_CHAR_LARGE_ADDR: i64 = 0x0020 29const PE_SECT_CODE_X_R: i64 = 0x60000020 30const FOFF_PE_SIG: i64 = 0x80 31const FOFF_COFF: i64 = 0x84 32const FOFF_OPT: i64 = 0x98 33const FOFF_SECT_TBL: i64 = 0x188 34const FOFF_TEXT: i64 = 0x200 35const RVA_TEXT: i64 = 0x1000 36const OPT_SUBSYS: i64 = 0x98 + 68 37const OPT_ENTRY: i64 = 0x98 + 16 38const IMG_BASE: i64 = 0x10000000 39const GOP_TEXT_VSIZE: i64 = 0x56 // 0x46 code (incl. the persist spin) + 16 GUID bytes 40const GOP_FILL_COLOR: i64 = 0x00AA8844 // a distinctive non-black solid (BGRX on metal) 41 42func _w8(buf: *u8, off: i64, v: i64) -> i64 { buf[off] = (v & 0xff) as u8; return off + 1 } 43func _w16(buf: *u8, off: i64, v: i64) -> i64 { _w8(buf, off, v); _w8(buf, off + 1, v >> 8); return off + 2 } 44func _w32(buf: *u8, off: i64, v: i64) -> i64 { _w8(buf, off, v); _w8(buf, off+1, v>>8); _w8(buf, off+2, v>>16); _w8(buf, off+3, v>>24); return off + 4 } 45func _w64(buf: *u8, off: i64, v: i64) -> i64 { _w32(buf, off, v); _w32(buf, off + 4, v >> 32); return off + 8 } 46func _r16(buf: *u8, off: i64) -> i64 { return (buf[off] as i64) | ((buf[off + 1] as i64) << 8) } 47func _r32(buf: *u8, off: i64) -> i64 { return (buf[off] as i64) | ((buf[off+1] as i64)<<8) | ((buf[off+2] as i64)<<16) | ((buf[off+3] as i64)<<24) } 48 49// EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = 9042a9de-23dc-4a38-96fb-7aded080516a (mixed-endian EFI GUID bytes) 50func gop_guid_byte(i: i64) -> i64 { 51 if i==0 { return 0xDE } if i==1 { return 0xA9 } if i==2 { return 0x42 } if i==3 { return 0x90 } 52 if i==4 { return 0xDC } if i==5 { return 0x23 } if i==6 { return 0x38 } if i==7 { return 0x4A } 53 if i==8 { return 0x96 } if i==9 { return 0xFB } if i==10 { return 0x7A } if i==11 { return 0xDE } 54 if i==12 { return 0xD0 } if i==13 { return 0x80 } if i==14 { return 0x51 } return 0x6A 55} 56 57// ===== the GOP struct-walk + fill ALGORITHM (the offset-sensitive logic, gate-verifiable vs a modeled GOP) == 58// Reads Mode->FrameBufferBase (+0x18) + Mode->FrameBufferSize (+0x20) and fills the framebuffer with `color` 59// (FrameBufferSize/4 dwords) -- byte-for-byte what the emitted `rep stosd` does. Returns dwords written. 60func gop_fill_mode(mode_ptr: i64, color: i64) -> i64 { 61 let mp: *i64 = mode_ptr as *i64 62 let fbbase: i64 = mp[3] // +0x18 FrameBufferBase 63 let fbsize: i64 = mp[4] // +0x20 FrameBufferSize 64 let ndw: i64 = fbsize / 4 65 let lp: *u8 = fbbase as *u8 66 let c0: i64 = color & 0xFF; let c1: i64 = (color>>8)&0xFF; let c2: i64 = (color>>16)&0xFF; let c3: i64 = (color>>24)&0xFF 67 var i: i64 = 0 68 while i < ndw { 69 let o: i64 = i*4 70 lp[o]=c0 as u8; lp[o+1]=c1 as u8; lp[o+2]=c2 as u8; lp[o+3]=c3 as u8 71 i = i + 1 72 } 73 return ndw 74} 75 76// ===== emit the EFI image (fills buf, returns PE_FILE_SIZE) ====================== 77func gop_efi_emit(buf: *u8) -> i64 { 78 _w16(buf, 0, 0x5A4D) // 'MZ' 79 _w32(buf, 0x3C, FOFF_PE_SIG) 80 _w32(buf, FOFF_PE_SIG, 0x00004550) // 'PE\0\0' 81 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64) 82 _w16(buf, FOFF_COFF + 2, 1) 83 _w16(buf, FOFF_COFF + 16, 0xF0) 84 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 85 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS) 86 _w8(buf, FOFF_OPT + 2, 1) 87 _w32(buf, FOFF_OPT + 4, 0x200) // SizeOfCode 88 _w32(buf, FOFF_OPT + 16, RVA_TEXT) // AddressOfEntryPoint 89 _w32(buf, FOFF_OPT + 20, RVA_TEXT) // BaseOfCode 90 _w64(buf, FOFF_OPT + 24, IMG_BASE) 91 _w32(buf, FOFF_OPT + 32, 0x1000) // SectionAlignment 92 _w32(buf, FOFF_OPT + 36, 0x200) // FileAlignment 93 _w32(buf, FOFF_OPT + 56, 0x2000) // SizeOfImage 94 _w32(buf, FOFF_OPT + 60, 0x200) // SizeOfHeaders 95 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_EFI_APP) // Subsystem = 10 96 _w64(buf, FOFF_OPT + 72, 0x100000) 97 _w64(buf, FOFF_OPT + 80, 0x1000) 98 _w64(buf, FOFF_OPT + 88, 0x100000) 99 _w64(buf, FOFF_OPT + 96, 0x1000) 100 _w32(buf, FOFF_OPT + 108, 16) // NumberOfRvaAndSizes 101 // .text section header 102 _w8(buf, FOFF_SECT_TBL+0, 46); _w8(buf, FOFF_SECT_TBL+1, 116); _w8(buf, FOFF_SECT_TBL+2, 101) 103 _w8(buf, FOFF_SECT_TBL+3, 120); _w8(buf, FOFF_SECT_TBL+4, 116) // ".text" 104 _w32(buf, FOFF_SECT_TBL + 8, GOP_TEXT_VSIZE) // VirtualSize 105 _w32(buf, FOFF_SECT_TBL + 12, RVA_TEXT) 106 _w32(buf, FOFF_SECT_TBL + 16, 0x200) 107 _w32(buf, FOFF_SECT_TBL + 20, FOFF_TEXT) 108 _w32(buf, FOFF_SECT_TBL + 36, PE_SECT_CODE_X_R) 109 110 // ----- .text: PIC GOP solid-fill (each instruction byte-commented; offsets verified) ----- 111 var o: i64 = FOFF_TEXT 112 o=_w8(buf,o,0x48); o=_w8(buf,o,0x83); o=_w8(buf,o,0xEC); o=_w8(buf,o,0x48) // 00 sub rsp,0x48 113 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x42); o=_w8(buf,o,0x60) // 04 mov rax,[rdx+0x60] BootServices 114 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8D); o=_w8(buf,o,0x0D); o=_w32(buf,o,0x37) // 08 lea rcx,[rip+0x37] &GOP_GUID (GUID moved +2 by the spin) 115 o=_w8(buf,o,0x48); o=_w8(buf,o,0x31); o=_w8(buf,o,0xD2) // 0F xor rdx,rdx (Registration=NULL) 116 o=_w8(buf,o,0x4C); o=_w8(buf,o,0x8D); o=_w8(buf,o,0x44); o=_w8(buf,o,0x24); o=_w8(buf,o,0x30) // 12 lea r8,[rsp+0x30] &gop 117 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x80); o=_w32(buf,o,0x140) // 17 mov rax,[rax+0x140] LocateProtocol 118 o=_w8(buf,o,0xFF); o=_w8(buf,o,0xD0) // 1E call rax 119 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x44); o=_w8(buf,o,0x24); o=_w8(buf,o,0x30) // 20 mov rax,[rsp+0x30] gop 120 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x40); o=_w8(buf,o,0x18) // 25 mov rax,[rax+0x18] gop->Mode 121 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x78); o=_w8(buf,o,0x18) // 29 mov rdi,[rax+0x18] FrameBufferBase 122 o=_w8(buf,o,0x48); o=_w8(buf,o,0x8B); o=_w8(buf,o,0x48); o=_w8(buf,o,0x20) // 2D mov rcx,[rax+0x20] FrameBufferSize 123 o=_w8(buf,o,0x48); o=_w8(buf,o,0xC1); o=_w8(buf,o,0xE9); o=_w8(buf,o,0x02) // 31 shr rcx,2 (dword count) 124 o=_w8(buf,o,0xB8); o=_w32(buf,o,GOP_FILL_COLOR) // 35 mov eax,color 125 o=_w8(buf,o,0xFC) // 3A cld 126 o=_w8(buf,o,0xF3); o=_w8(buf,o,0xAB) // 3B rep stosd (fill framebuffer) 127 // 3D jmp $ -- HOLD the painted screen. Returning EFI_SUCCESS hands control straight back to 128 // BdsDxe, which starts UiApp and REPAINTS within milliseconds: the fill was real but unobservable 129 // (57 OVMF screendumps across two runs caught it in zero frames). A boot app that paints must keep 130 // the screen to be seen -- and holding is also what a kernel taking the display does. 131 o=_w8(buf,o,0xEB); o=_w8(buf,o,0xFE) 132 o=_w8(buf,o,0x31); o=_w8(buf,o,0xC0) // 3D xor eax,eax EFI_SUCCESS 133 o=_w8(buf,o,0x48); o=_w8(buf,o,0x83); o=_w8(buf,o,0xC4); o=_w8(buf,o,0x48) // 3F add rsp,0x48 134 o=_w8(buf,o,0xC3) // 43 ret 135 // 46: GOP GUID (16 bytes) -- lea rcx,[rip+0x37] resolves here 136 var g: i64 = 0 137 while g < 16 { o=_w8(buf, o, gop_guid_byte(g)); g = g + 1 } 138 return PE_FILE_SIZE 139} 140 141// ===== structural + GUID + NEVER-BRICK self-gate ================================ 142func gop_efi_verify(buf: *u8) -> i64 { 143 if buf[0] != (0x4D as u8) { return 0 } 144 if buf[1] != (0x5A as u8) { return 0 } 145 if _r32(buf, 0x3C) != FOFF_PE_SIG { return 0 } 146 if _r32(buf, FOFF_PE_SIG) != 0x00004550 { return 0 } 147 if _r16(buf, FOFF_COFF) != PE_MACHINE_AMD64 { return 0 } 148 if _r16(buf, FOFF_OPT) != PE_OH_MAGIC_PEPLUS { return 0 } 149 if _r16(buf, OPT_SUBSYS) != PE_SUBSYSTEM_EFI_APP { return 0 } 150 if _r32(buf, OPT_ENTRY) != RVA_TEXT { return 0 } 151 if buf[FOFF_TEXT] != (0x48 as u8) { return 0 } // entry = sub rsp prefix 152 // GOP GUID present at .text+0x44 153 var g: i64 = 0 154 while g < 16 { if (buf[FOFF_TEXT + 0x46 + g] as i64) != gop_guid_byte(g) { return 0 } g = g + 1 } 155 return 1 156} 157 158// NEVER-BRICK structural check: the emitted .text writes ONLY volatile VRAM (rep stosd) + calls ONLY 159// LocateProtocol (read-only discovery). Returns 1 iff: exactly one `call rax` (FF D0), `rep stosd` (F3 AB) 160// present, and NO gop->SetMode/Blt indirect call (FF 50 08 / FF 50 10). Scans the 0x44 code bytes only. 161func gop_efi_never_brick(buf: *u8) -> i64 { 162 var calls: i64 = 0; var has_fill: i64 = 0; var bad_call: i64 = 0 163 var i: i64 = FOFF_TEXT 164 let end: i64 = FOFF_TEXT + 0x46 165 while i < end { 166 if buf[i] == (0xFF as u8) { 167 if buf[i+1] == (0xD0 as u8) { calls = calls + 1 } // call rax (LocateProtocol) 168 if buf[i+1] == (0x50 as u8) { bad_call = bad_call + 1 } // call [rax+disp8] (a protocol method = SetMode/Blt) 169 } 170 if buf[i] == (0xF3 as u8) { if buf[i+1] == (0xAB as u8) { has_fill = 1 } } // rep stosd (FB write) 171 i = i + 1 172 } 173 if calls != 1 { return 0 } 174 if has_fill != 1 { return 0 } 175 if bad_call != 0 { return 0 } 176 return 1 177}