code wiki / _hdl_build / nx_fw_safeflash.nx

nx_fw_safeflash.nx source

↩ module page · 224 lines · 10774 B

1// nx_fw_safeflash.nx -- THE NEVER-BRICK firmware flash, proven in the sandbox. 2// 3// module: nishi-core.genealogy.fw_safeflash 4// capability: CORE_COMPUTE (model + PROVE the never-brick guarantee for firmware flashing) 5// 6// THE LAW (operator 2026-06-16, CLAUDE.md #26): no Nishi capability may EVER brick the 7// electronics. Flashing firmware is the canonical brick risk. This organ models the SAME 8// mechanism real motherboards use to make a flash un-brickable -- DUAL-IMAGE GOLDEN RECOVERY 9// (dual-BIOS / BIOS-flashback) -- and PROVES the guarantee against deliberate failures: 10// * a firmware image = magic "NXFW" + payload_len + payload + sha256(payload); it is BOOTABLE 11// iff the magic is intact AND the sha256 matches (a corrupt/zeroed image is NOT bootable); 12// * fw_safe_flash(active, golden, new, corrupt): 13// 0) REFUSE any path not under /tmp/ (the emu sandbox -- this organ CANNOT touch the host 14// firmware/dev; never-brick by construction, not by promise); 15// 1) PRE-VALIDATE the new image -- a bad source image is REJECTED, active untouched; 16// 2) REQUIRE a bootable GOLDEN anchor -- with no recovery copy, REFUSE to flash; 17// 3) write active (optionally inject mid-write corruption to simulate a bad/interrupted flash); 18// 4) POST-VERIFY active; if not bootable, AUTO-RESTORE from golden and re-verify. 19// INVARIANT (the never-brick property): given a bootable golden anchor, `active` is BOOTABLE 20// after EVERY safe_flash, on every path. A "brick" costs at most a restore, never the device. 21// Sandboxed: operates ONLY on /tmp/ files; touches no real /dev or host firmware. Sovereign: 22// imports nx_syscalls + the sovereign nx_sha256 (no gcc/.sh). license_tier: ORIGINAL 23 24import "nx_syscalls.nx" 25import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 26import "nx_sha256.nx" 27const FW_MAGIC_65536: i64 = 65536 28const FW_MAGIC_16777216: i64 = 16777216 29 30const FW_OK: i64 = 0 // flashed + verified bootable 31const FW_REJECTED_PRE: i64 = 1 // new image was itself bad -> active untouched 32const FW_RECOVERED: i64 = 2 // active went bad mid-flash -> auto-restored from golden 33const FW_REFUSED_NO_ANCHOR: i64 = 3 // no bootable golden -> refused to flash (never flash blind) 34const FW_SANDBOX_VIOLATION: i64 = 4 // a path was not under /tmp/ -> refused (cannot touch host) 35const FW_FATAL: i64 = 5 // unreachable while a golden anchor exists (the proof) 36 37func fw_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } sys_write(1, s, n); return 0 } 38// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 39// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 40// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 41// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 42func fw_putn(v: i64) -> i64 { nxi_out(v); return 0 } 43 44// every firmware path MUST be under /tmp/ -- the organ literally cannot address host firmware. 45func fw_is_sandbox(p: *u8) -> i64 { 46 let pre: *u8 = "/tmp/" 47 var i: i64 = 0 48 while pre[i] != 0 as u8 { if p[i] != pre[i] { return 0 } i = i + 1 } 49 return 1 50} 51 52func fw_wr_u32(buf: *u8, off: i64, v: i64) -> i64 { 53 buf[off + 0] = (v & 0xff) as u8 54 buf[off + 1] = ((v >> 8) & 0xff) as u8 55 buf[off + 2] = ((v >> 16) & 0xff) as u8 56 buf[off + 3] = ((v >> 24) & 0xff) as u8 57 return 0 58} 59func fw_rd_u32(buf: *u8, off: i64) -> i64 { 60 var v: i64 = buf[off + 0] as i64 61 v = v + (buf[off + 1] as i64) * 256 62 v = v + (buf[off + 2] as i64) * FW_MAGIC_65536 63 v = v + (buf[off + 3] as i64) * FW_MAGIC_16777216 64 return v 65} 66// integrity is now a 32-byte sovereign sha256 over the payload (see fw_make / fw_bootable). 67const FW_DIGEST: i64 = 32 68func fw_dig_eq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while i < FW_DIGEST { if a[i] != b[i] { return 0 } i = i + 1 } return 1 } 69 70func fw_write_bytes(path: *u8, buf: *u8, n: i64) -> i64 { 71 let fd: i64 = sys_openat_wr(path, 0x1a4) 72 if fd < 0 { return 0 - 1 } 73 sys_write(fd, buf, n) 74 sys_close(fd) 75 return 0 76} 77 78// BOUNDED file read (16 MB cap). sys_read_file reserves 4 GB virtual PER CALL; the safe-flash path 79// reads ~10 images, and on a fork-bearing gate that ~40 GB of committed VMAs makes fork() fail 80// with ENOMEM. Firmware images are tiny, so a bounded reader keeps every fork cheap. lb[0]=bytes. 81const FW_MAXIMG: i64 = 16777216 82func fw_read(path: *u8, lb: *i64) -> *u8 { 83 let fd: i64 = sys_openat_rd(path) 84 if fd < 0 { lb[0] = 0; return 0 as *u8 } 85 let buf: *u8 = sys_mmap(FW_MAXIMG + 16) 86 var n: i64 = 0; var go: i64 = 1 87 while go == 1 { 88 let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, FW_MAXIMG - n) 89 if r <= 0 { go = 0 } else { n = n + r } 90 if n >= FW_MAXIMG { go = 0 } 91 } 92 sys_close(fd) 93 buf[n] = 0 as u8 94 lb[0] = n 95 return buf 96} 97 98// author a fresh BOOTABLE image at path from payload[0,plen). "NXFW" + len + payload + checksum. 99func fw_make(path: *u8, payload: *u8, plen: i64) -> i64 { 100 let buf: *u8 = sys_mmap(plen + 48) 101 buf[0] = 78 as u8; buf[1] = 88 as u8; buf[2] = 70 as u8; buf[3] = 87 as u8 // 'N''X''F''W' 102 fw_wr_u32(buf, 4, plen) 103 var i: i64 = 0 104 while i < plen { buf[8 + i] = payload[i]; i = i + 1 } 105 sha256_digest(((buf as i64) + 8) as *u8, plen, ((buf as i64) + 8 + plen) as *u8) // sha256(payload) trailer 106 fw_write_bytes(path, buf, 8 + plen + FW_DIGEST) 107 return 0 108} 109 110// BOOTABLE iff magic intact AND length consistent AND checksum matches. 1/0. 111func fw_bootable(path: *u8) -> i64 { 112 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 113 let buf: *u8 = fw_read(path, lb) 114 if (buf as i64) == 0 { return 0 } 115 let n: i64 = lb[0] 116 if n < (8 + FW_DIGEST) { return 0 } 117 if buf[0] != 78 as u8 { return 0 } 118 if buf[1] != 88 as u8 { return 0 } 119 if buf[2] != 70 as u8 { return 0 } 120 if buf[3] != 87 as u8 { return 0 } 121 let plen: i64 = fw_rd_u32(buf, 4) 122 if plen < 0 { return 0 } 123 if (8 + plen + FW_DIGEST) > n { return 0 } 124 let calc: *u8 = sys_mmap(FW_DIGEST) 125 sha256_digest(((buf as i64) + 8) as *u8, plen, calc) 126 if fw_dig_eq(calc, ((buf as i64) + 8 + plen) as *u8) == 0 { return 0 } 127 return 1 128} 129 130func fw_copy(src: *u8, dst: *u8) -> i64 { 131 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 132 let buf: *u8 = fw_read(src, lb) 133 if (buf as i64) == 0 { return 0 - 1 } 134 fw_write_bytes(dst, buf, lb[0]) 135 return 0 136} 137 138// extract the raw payload (e.g. the real .efi bytes) from an NXFW image into a raw file, so it 139// can be handed to nx_emu_uefi for an EXECUTION boot-proof. Sandboxed (both paths under /tmp). 140func fw_unwrap(nxfw: *u8, rawout: *u8) -> i64 { 141 if fw_is_sandbox(nxfw) == 0 { return 0 - 1 } 142 if fw_is_sandbox(rawout) == 0 { return 0 - 1 } 143 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 144 let buf: *u8 = fw_read(nxfw, lb) 145 if (buf as i64) == 0 { return 0 - 1 } 146 let n: i64 = lb[0] 147 if n < (8 + FW_DIGEST) { return 0 - 1 } 148 let plen: i64 = fw_rd_u32(buf, 4) 149 if plen < 0 { return 0 - 1 } 150 if (8 + plen + FW_DIGEST) > n { return 0 - 1 } 151 fw_write_bytes(rawout, ((buf as i64) + 8) as *u8, plen) 152 return 0 153} 154 155// simulate a bad/interrupted flash: garble one payload byte so the checksum no longer matches. 156func fw_corrupt_byte(path: *u8) -> i64 { 157 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 158 let buf: *u8 = fw_read(path, lb) 159 if (buf as i64) == 0 { return 0 - 1 } 160 let n: i64 = lb[0] 161 if n > 8 { buf[8] = ((buf[8] as i64) + 1) as u8 } 162 fw_write_bytes(path, buf, n) 163 return 0 164} 165 166// THE NEVER-BRICK FLASH. corrupt: 0=clean write, 1=inject mid-write corruption (bad flash). 167func fw_safe_flash(active: *u8, golden: *u8, newimg: *u8, corrupt: i64) -> i64 { 168 if fw_is_sandbox(active) == 0 { return FW_SANDBOX_VIOLATION } 169 if fw_is_sandbox(golden) == 0 { return FW_SANDBOX_VIOLATION } 170 if fw_is_sandbox(newimg) == 0 { return FW_SANDBOX_VIOLATION } 171 if fw_bootable(newimg) == 0 { return FW_REJECTED_PRE } // bad source -> never write it 172 if fw_bootable(golden) == 0 { return FW_REFUSED_NO_ANCHOR } // no recovery anchor -> refuse 173 fw_copy(newimg, active) 174 if corrupt == 1 { fw_corrupt_byte(active) } // bad/interrupted flash 175 if fw_bootable(active) == 1 { return FW_OK } 176 fw_copy(golden, active) // AUTO-RESTORE from golden 177 if fw_bootable(active) == 1 { return FW_RECOVERED } 178 return FW_FATAL 179} 180 181func fw_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { dst[off + i] = s[i]; i = i + 1 } return off + i } 182func fw_catn(dst: *u8, off: i64, v: i64) -> i64 { 183 var m: i64 = v; var o: i64 = off 184 if m < 0 { m = 0 - m } 185 let t: *u8 = sys_mmap(28); var k: i64 = 0 186 if m == 0 { t[0] = 48 as u8; k = 1 } 187 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 188 var i: i64 = 0 189 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 190 return o + k 191} 192func fw_path(out: *u8, stem: *u8, epoch: i64, pid: i64) -> i64 { 193 var o: i64 = 0 194 o = fw_cat(out, o, stem) 195 o = fw_catn(out, o, epoch) 196 o = fw_cat(out, o, "." as *u8) 197 o = fw_catn(out, o, pid) 198 o = fw_cat(out, o, ".img" as *u8) 199 out[o] = 0 as u8 200 return o 201} 202 203func main() -> i64 { 204 fw_puts("=== NISHI FIRMWARE SAFE-FLASH (never-brick: dual-image golden recovery, sandboxed) ===\n") 205 let epoch: i64 = sys_now_realtime_sec() 206 let pid: i64 = __syscall(39, 0, 0, 0, 0, 0, 0) 207 // sandbox paths (epoch.pid suffixed, all under /tmp) 208 let g: *u8 = sys_mmap(256); let a: *u8 = sys_mmap(256); let nw: *u8 = sys_mmap(256) 209 fw_path(g, "/tmp/nxfw_golden." as *u8, epoch, pid) 210 fw_path(a, "/tmp/nxfw_active." as *u8, epoch, pid) 211 fw_path(nw, "/tmp/nxfw_new." as *u8, epoch, pid) 212 213 fw_make(g, "NISHI-UEFI-GOLDEN-v1 boot services stub (immutable recovery anchor)" as *u8, 65) 214 fw_make(a, "NISHI-UEFI-GOLDEN-v1 boot services stub (immutable recovery anchor)" as *u8, 65) 215 fw_make(nw, "NISHI-UEFI-v2 boot services stub (the new firmware we are flashing)" as *u8, 66) 216 217 fw_puts(" active bootable before any flash = "); fw_putn(fw_bootable(a)); fw_puts("\n") 218 let r1: i64 = fw_safe_flash(a, g, nw, 0) 219 fw_puts(" clean flash -> status="); fw_putn(r1); fw_puts(" active bootable="); fw_putn(fw_bootable(a)); fw_puts("\n") 220 let r2: i64 = fw_safe_flash(a, g, nw, 1) 221 fw_puts(" bad flash -> status="); fw_putn(r2); fw_puts(" active bootable="); fw_putn(fw_bootable(a)); fw_puts(" (2=auto-recovered from golden)\n") 222 fw_puts(" INVARIANT: active is bootable after every flash -> NEVER BRICKED\n") 223 sys_exit(0); return 0 224}