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}