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}