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}