code wiki / _hdl_build / nx_boot_uefi.nx
nx_boot_uefi.nx source
↩ module page · 228 lines · 13253 B
1// nx_boot_uefi.nx -- NOS-R0 of the NISHI OS ladder: the sovereign UEFI boot stub emitter.
2//
3// Mirrors nx_boot_stub_emit (the RV64 K-R0 boot stub) but for the x86_64 / UEFI climate:
4// it AUTHORS, byte-reproducibly, a subsystem-10 (EFI_APPLICATION) PE32+ executable
5// (_offc/nx_boot_uefi.efi) whose position-independent entry walks the UEFI SystemTable
6// and prints a banner via ConOut->OutputString. This is the x86_64 twin of the RV64
7// boot stub already GREEN on rv64im_min_sim, and the literal first slot in the nx_spore
8// design (boot/nx_boot_uefi.efi). When firmware loads it, ConOut output proves: (a) our
9// sovereign PE32+ is a valid EFI image, (b) we navigated the real SystemTable correctly --
10// the seed of hardware auto-discovery (GetMemoryMap / firmware-vendor read come next rung).
11//
12// Reuses the PE32+ layout proven in runtime/nx_pe_writer.nx, with EXACTLY TWO deltas vs the
13// Windows console emitter: Subsystem 3 -> 10, and NO imports (UEFI services arrive via the
14// SystemTable pointer in RDX, not the PE IAT). Code is fully position-independent (entry
15// reads everything RIP-relative or from RDX), so the image needs no base relocations.
16//
17// Build (sovereign): ./_offc/nx_sov_build_run.elf nx_boot_uefi (nx_cc -> nxasm, no gcc)
18// Self-gate (no mocks): emit twice (byte-reproducible) + structural verify (machine=0x8664,
19// subsystem=10, PE/MZ sigs, entry resolves into .text) + tamper control (flip subsystem
20// to 3 -> verify MUST reject = liar-kill). VERDICT log -> knowledge/status/nishi_os.log.
21// Scope (honest): EMIT-proven this rung. BOOT-proven (OVMF diff-lane / real laptop USB) is
22// NOS-R0.1 -- the structural GREEN here is the emit-proof, not yet the run-proof.
23// Sovereign: syscalls only, no gcc/.sh. license_tier: ORIGINAL
24import "nx_syscalls.nx"
25const PE_MAGIC_4096: i64 = 4096
26
27// ===== PE/COFF + UEFI constants ==================================================
28const PE_FILE_SIZE: i64 = 0x400 // headers(0x200) + .text raw(0x200)
29const PE_MACHINE_AMD64: i64 = 0x8664
30const PE_OH_MAGIC_PEPLUS: i64 = 0x020B
31const PE_SUBSYSTEM_EFI_APP: i64 = 10 // <-- the delta that makes this UEFI, not Windows
32const PE_CHAR_EXEC: i64 = 0x0002
33const PE_CHAR_LARGE_ADDR: i64 = 0x0020
34const PE_SECT_CODE_X_R: i64 = 0x60000020 // CODE | EXECUTE | READ
35
36// file offsets (mirror nx_pe_writer)
37const FOFF_PE_SIG: i64 = 0x80
38const FOFF_COFF: i64 = 0x84
39const FOFF_OPT: i64 = 0x98
40const FOFF_SECT_TBL: i64 = 0x188
41const FOFF_TEXT: i64 = 0x200
42const RVA_TEXT: i64 = 0x1000
43const OPT_SUBSYS: i64 = 0x98 + 68 // Subsystem field absolute offset (0xDC)
44const OPT_ENTRY: i64 = 0x98 + 16 // AddressOfEntryPoint absolute offset
45
46// UEFI app preferred load base (code is PIC, so the loader may place it anywhere)
47const IMG_BASE: i64 = 0x10000000
48
49// ===== little-endian byte writers ===============================================
50func _w8(buf: *u8, off: i64, v: i64) -> i64 { buf[off] = (v & 0xff) as u8; return off + 1 }
51func _w16(buf: *u8, off: i64, v: i64) -> i64 { _w8(buf, off, v); _w8(buf, off + 1, v >> 8); return off + 2 }
52func _w32(buf: *u8, off: i64, v: i64) -> i64 {
53 _w8(buf, off, v); _w8(buf, off + 1, v >> 8); _w8(buf, off + 2, v >> 16); _w8(buf, off + 3, v >> 24)
54 return off + 4
55}
56func _w64(buf: *u8, off: i64, v: i64) -> i64 { _w32(buf, off, v); _w32(buf, off + 4, v >> 32); return off + 8 }
57
58// ===== little-endian readers (for the self-gate) ================================
59func _r16(buf: *u8, off: i64) -> i64 { return (buf[off] as i64) | ((buf[off + 1] as i64) << 8) }
60func _r32(buf: *u8, off: i64) -> i64 {
61 return (buf[off] as i64) | ((buf[off + 1] as i64) << 8) | ((buf[off + 2] as i64) << 16) | ((buf[off + 3] as i64) << 24)
62}
63
64// ===== the EFI image author: fills buf, returns PE_FILE_SIZE =====================
65func uefi_emit(buf: *u8) -> i64 {
66 // ----- DOS header -----
67 _w16(buf, 0, 0x5A4D) // 'MZ'
68 _w32(buf, 0x3C, FOFF_PE_SIG) // e_lfanew -> PE signature
69
70 // ----- PE signature -----
71 _w32(buf, FOFF_PE_SIG, 0x00004550) // 'PE\0\0'
72
73 // ----- COFF file header (20 bytes) -----
74 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64)
75 _w16(buf, FOFF_COFF + 2, 1) // NumberOfSections (.text only)
76 _w32(buf, FOFF_COFF + 4, 0) // TimeDateStamp (0 = reproducible)
77 _w32(buf, FOFF_COFF + 8, 0) // PointerToSymbolTable
78 _w32(buf, FOFF_COFF + 12, 0) // NumberOfSymbols
79 _w16(buf, FOFF_COFF + 16, 0xF0) // SizeOfOptionalHeader (240)
80 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) // NOT relocs-stripped (PIC, relocatable)
81
82 // ----- Optional header (PE32+, 240 bytes) -----
83 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS)
84 _w8(buf, FOFF_OPT + 2, 1) // MajorLinkerVersion
85 _w8(buf, FOFF_OPT + 3, 0)
86 _w32(buf, FOFF_OPT + 4, 0x200) // SizeOfCode
87 _w32(buf, FOFF_OPT + 8, 0) // SizeOfInitializedData
88 _w32(buf, FOFF_OPT + 12, 0) // SizeOfUninitializedData
89 _w32(buf, FOFF_OPT + 16, RVA_TEXT) // AddressOfEntryPoint
90 _w32(buf, FOFF_OPT + 20, RVA_TEXT) // BaseOfCode
91 _w64(buf, FOFF_OPT + 24, IMG_BASE) // ImageBase
92 _w32(buf, FOFF_OPT + 32, 0x1000) // SectionAlignment
93 _w32(buf, FOFF_OPT + 36, 0x200) // FileAlignment
94 _w16(buf, FOFF_OPT + 40, 0) // MajorOSVersion
95 _w16(buf, FOFF_OPT + 42, 0)
96 _w16(buf, FOFF_OPT + 44, 0) // MajorImageVersion
97 _w16(buf, FOFF_OPT + 46, 0)
98 _w16(buf, FOFF_OPT + 48, 0) // MajorSubsystemVersion
99 _w16(buf, FOFF_OPT + 50, 0)
100 _w32(buf, FOFF_OPT + 52, 0) // Win32VersionValue
101 _w32(buf, FOFF_OPT + 56, 0x2000) // SizeOfImage (headers + .text, 0x1000 each)
102 _w32(buf, FOFF_OPT + 60, 0x200) // SizeOfHeaders
103 _w32(buf, FOFF_OPT + 64, 0) // CheckSum
104 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_EFI_APP) // Subsystem = 10 <-- KEY
105 _w16(buf, FOFF_OPT + 70, 0) // DllCharacteristics
106 _w64(buf, FOFF_OPT + 72, 0x100000) // SizeOfStackReserve
107 _w64(buf, FOFF_OPT + 80, 0x1000) // SizeOfStackCommit
108 _w64(buf, FOFF_OPT + 88, 0x100000) // SizeOfHeapReserve
109 _w64(buf, FOFF_OPT + 96, 0x1000) // SizeOfHeapCommit
110 _w32(buf, FOFF_OPT + 104, 0) // LoaderFlags
111 _w32(buf, FOFF_OPT + 108, 16) // NumberOfRvaAndSizes
112 // DataDirectories[16] at FOFF_OPT+112 .. +240 : all zero (no imports, no relocs)
113
114 // ----- section header: .text -----
115 _w8(buf, FOFF_SECT_TBL + 0, 46) // '.'
116 _w8(buf, FOFF_SECT_TBL + 1, 116) // 't'
117 _w8(buf, FOFF_SECT_TBL + 2, 101) // 'e'
118 _w8(buf, FOFF_SECT_TBL + 3, 120) // 'x'
119 _w8(buf, FOFF_SECT_TBL + 4, 116) // 't'
120 _w32(buf, FOFF_SECT_TBL + 8, 0x2C) // VirtualSize (44 bytes of code+banner)
121 _w32(buf, FOFF_SECT_TBL + 12, RVA_TEXT) // VirtualAddress
122 _w32(buf, FOFF_SECT_TBL + 16, 0x200) // SizeOfRawData
123 _w32(buf, FOFF_SECT_TBL + 20, FOFF_TEXT) // PointerToRawData
124 _w32(buf, FOFF_SECT_TBL + 36, PE_SECT_CODE_X_R) // Characteristics
125
126 // ----- .text: position-independent UEFI entry -----
127 // On entry (MS x64 ABI): RCX = ImageHandle, RDX = SystemTable*.
128 // SystemTable->ConOut is at +0x40; ConOut->OutputString is at +0x08.
129 // OutputString(This=ConOut, String=CHAR16*) prints a UTF-16 string.
130 var o: i64 = FOFF_TEXT
131 o = _w8(buf, o, 0x48); o = _w8(buf, o, 0x83); o = _w8(buf, o, 0xEC); o = _w8(buf, o, 0x28) // sub rsp,0x28
132 o = _w8(buf, o, 0x48); o = _w8(buf, o, 0x8B); o = _w8(buf, o, 0x42); o = _w8(buf, o, 0x40) // mov rax,[rdx+0x40] (ConOut)
133 o = _w8(buf, o, 0x48); o = _w8(buf, o, 0x89); o = _w8(buf, o, 0xC1) // mov rcx,rax (This)
134 o = _w8(buf, o, 0x48); o = _w8(buf, o, 0x8D); o = _w8(buf, o, 0x15) // lea rdx,[rip+disp] (banner)
135 o = _w32(buf, o, 0x0A) // disp32 = 0x0A
136 o = _w8(buf, o, 0xFF); o = _w8(buf, o, 0x50); o = _w8(buf, o, 0x08) // call [rax+0x08] (OutputString)
137 o = _w8(buf, o, 0x31); o = _w8(buf, o, 0xC0) // xor eax,eax (EFI_SUCCESS)
138 o = _w8(buf, o, 0x48); o = _w8(buf, o, 0x83); o = _w8(buf, o, 0xC4); o = _w8(buf, o, 0x28) // add rsp,0x28
139 o = _w8(buf, o, 0xC3) // ret
140 // banner: UTF-16LE "NISHI\r\n\0" (must sit at .text+0x1C so lea disp 0x0A resolves)
141 o = _w16(buf, o, 0x4E); o = _w16(buf, o, 0x49); o = _w16(buf, o, 0x53) // N I S
142 o = _w16(buf, o, 0x48); o = _w16(buf, o, 0x49) // H I
143 o = _w16(buf, o, 0x0D); o = _w16(buf, o, 0x0A); o = _w16(buf, o, 0x00) // CR LF NUL
144
145 return PE_FILE_SIZE
146}
147
148// ===== structural self-gate: 1 iff buf is a valid subsystem-10 EFI PE32+ =========
149func uefi_verify(buf: *u8) -> i64 {
150 if buf[0] != (0x4D as u8) { return 0 } // 'M'
151 if buf[1] != (0x5A as u8) { return 0 } // 'Z'
152 if _r32(buf, 0x3C) != FOFF_PE_SIG { return 0 } // e_lfanew
153 if _r32(buf, FOFF_PE_SIG) != 0x00004550 { return 0 } // 'PE\0\0'
154 if _r16(buf, FOFF_COFF) != PE_MACHINE_AMD64 { return 0 }
155 if _r16(buf, FOFF_OPT) != PE_OH_MAGIC_PEPLUS { return 0 }
156 if _r16(buf, OPT_SUBSYS) != PE_SUBSYSTEM_EFI_APP { return 0 } // the UEFI assertion
157 if _r32(buf, OPT_ENTRY) != RVA_TEXT { return 0 } // entry points at .text
158 if buf[FOFF_TEXT] != (0x48 as u8) { return 0 } // first text byte = sub rsp prefix
159 return 1
160}
161
162// ===== I/O + log helpers (mirror nx_boot_stub_emit) =============================
163func u_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
164func u_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 }
165func u_fn(fd: i64, v: i64) -> i64 {
166 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m }
167 let t: *u8 = sys_mmap(28); var k: i64 = 0
168 if m == 0 { t[0] = 48; k = 1 }
169 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
170 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
171 sys_write(fd, bb, k); return 0
172}
173func u_log(sz: i64, repro: i64, structural: i64, tamper: i64, verdict: *u8) -> i64 {
174 let lfd: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4)
175 if lfd < 0 { return 0 - 1 }
176 u_fp(lfd, "NOSR0 name=nx_boot_uefi.efi subsystem=10 climate=x86_64-uefi bytes=" as *u8); u_fn(lfd, sz)
177 u_fp(lfd, " repro=" as *u8); u_fn(lfd, repro)
178 u_fp(lfd, " structural=" as *u8); u_fn(lfd, structural)
179 u_fp(lfd, " tamper_caught=" as *u8); u_fn(lfd, tamper)
180 u_fp(lfd, " scope=emit-proven(boot-proof=NOS-R0.1) verdict=" as *u8); u_fp(lfd, verdict); u_fp(lfd, "\n" as *u8)
181 sys_close(lfd); return 0
182}
183
184func main(argc: i64, argv: *i64) -> i64 {
185 let buf: *u8 = sys_mmap(PE_MAGIC_4096)
186 let buf2: *u8 = sys_mmap(PE_MAGIC_4096)
187 let sz: i64 = uefi_emit(buf)
188 let sz2: i64 = uefi_emit(buf2)
189
190 // (1) byte-reproducibility: two independent emits must be identical
191 var repro: i64 = 1
192 if sz != sz2 { repro = 0 }
193 var i: i64 = 0
194 while i < sz { if buf[i] != buf2[i] { repro = 0 } i = i + 1 }
195
196 // (2) structural verify of the clean image
197 let structural: i64 = uefi_verify(buf)
198
199 // (3) tamper control: corrupt subsystem 10 -> 3 in buf2; verify MUST now reject (liar-kill)
200 _w16(buf2, OPT_SUBSYS, 3)
201 var tamper_caught: i64 = 0
202 if uefi_verify(buf2) == 0 { tamper_caught = 1 }
203
204 var green: i64 = 0
205 if repro == 1 { if structural == 1 { if tamper_caught == 1 { green = 1 } } }
206
207 if green == 1 {
208 let ofd: i64 = sys_openat_wr("_offc/nx_boot_uefi.efi" as *u8, 0x1a4)
209 if ofd < 0 {
210 u_p("NOS-R0 RED: cannot write _offc/nx_boot_uefi.efi\n" as *u8)
211 u_log(sz, repro, structural, tamper_caught, "RED" as *u8)
212 sys_exit(1); return 1
213 }
214 sys_write(ofd, buf, sz)
215 sys_close(ofd)
216 u_p("NOS-R0 GREEN: authored _offc/nx_boot_uefi.efi (subsystem-10 EFI_APPLICATION) bytes=" as *u8)
217 u_fn(1, sz)
218 u_p(" repro+structural+tamper all pass\n" as *u8)
219 u_log(sz, repro, structural, tamper_caught, "GREEN" as *u8)
220 sys_exit(0); return 0
221 }
222
223 u_p("NOS-R0 RED: repro=" as *u8); u_fn(1, repro)
224 u_p(" structural=" as *u8); u_fn(1, structural)
225 u_p(" tamper_caught=" as *u8); u_fn(1, tamper_caught); u_p("\n" as *u8)
226 u_log(sz, repro, structural, tamper_caught, "RED" as *u8)
227 sys_exit(1); return 1
228}