code wiki / _hdl_build / nx_bootcap.nx
nx_bootcap.nx source
↩ module page · 113 lines · 6369 B
1// nx_bootcap.nx -- ONE boot-and-capture primitive for every organ that needs to know what the
2// live kernel actually SAID.
3//
4// WHY: measuring a kernel means booting the ARTIFACT and reading its serial -- not parsing a log
5// ABOUT the artifact, which can be stale, written by a different build, or absent entirely. That
6// boot-and-capture block had already been copy-pasted into nx_nishios_kernel_gate and
7// nx_kernel_adoption and was about to land in nx_kernel_census as a third copy; three copies of a
8// measurement primitive is three chances for the rulers to silently disagree about what booted.
9//
10// Also owns the TWO-ROOTS resolution: the buildroot keeps the image under runtime/_hdl_build/
11// while the live serving root keeps it at ./ -- one name, two roots. Callers get the resolved
12// path back so they can PRINT which artifact they actually measured; a ruler that will not say
13// which artifact it read is not reporting a measurement.
14// license_tier: ORIGINAL
15import "rv64im_min_sim.nx"
16import "rv64im_min_csr.nx"
17import "rv64im_min_clint.nx"
18import "rv64im_min_uart.nx"
19import "rv64im_min_virtio.nx"
20import "rv64im_min_nvme.nx"
21
22const BOOTCAP_MEM_BASE: i64 = 0x80000000
23const BOOTCAP_MEM_SIZE: i64 = 65536
24const BOOTCAP_MAX_STEPS: i64 = 100000
25// res[] slots the caller reads back
26const BOOTCAP_R_HALTED: i64 = 0
27const BOOTCAP_R_CODE: i64 = 1
28const BOOTCAP_R_STEPS: i64 = 2
29const BOOTCAP_R_TXN: i64 = 3
30const BOOTCAP_R_N: i64 = 4
31
32// Build a FRESH machine (every device re-allocated, so machine N cannot contaminate machine N+1 --
33// the negative-control runs depend on that isolation), load `img` at BOOTCAP_MEM_BASE, and return
34// the sim STOPPED at reset -- not run. Split out of bootcap_run on 2026-08-23 for the GDB stub
35// (nx_gdbstub), which must hold the machine stopped until a debugger resumes it: the stub needs the
36// SAME device set the boot rulers measure against, and a second hand-rolled copy of this block is
37// exactly the three-rulers-disagree defect the file header describes. bootcap_run composes this.
38// The sim is allocated with NX_RV64IM_SIM_BYTES -- the struct's own size constant; rv64im_min_sim.nx
39// documents why a literal byte count here was the out-of-bounds landmine.
40// bootcap_machine keeps the original 4-arg signature (every boot ruler + nx_gdbstub call it) and
41// delegates to the memory-size-parameterised form. ADDED 2026-08-23 (nishios NO3, nx_rvc_run_suite):
42// the RISC-V architectural branch tests (jal/beq/bne/blt/bge/bltu/bgeu-01) link their signature region
43// up to ~586 KB above mem_base, so a fixed 64 KB RAM cannot hold them. Parameterising the ONE
44// composition -- rather than a second hand-rolled copy in the conformance runner -- is exactly what
45// this file's header demands (a second copy is the three-rulers-disagree defect). mem_size is the
46// caller's derived need (the suite passes round_up(_end - mem_base, page)); existing callers pass
47// BOOTCAP_MEM_SIZE and are byte-for-byte unchanged.
48func bootcap_machine(img: *u8, ilen: i64, tx_buf: *u8, txcap: i64) -> *NxRv64imSim {
49 return bootcap_machine_mem(img, ilen, tx_buf, txcap, BOOTCAP_MEM_SIZE)
50}
51func bootcap_machine_mem(img: *u8, ilen: i64, tx_buf: *u8, txcap: i64, mem_size: i64) -> *NxRv64imSim {
52 let rf_storage: *i64 = (sys_mmap(8 * NX_RV64IM_RF_N_REGS)) as *i64
53 let csr_storage: *i64 = (sys_mmap(8 * NX_CSR_SLOT_N)) as *i64
54 let clint_storage: *i64 = (sys_mmap(8 * NX_CLINT_SLOT_N)) as *i64
55 let uart_storage: *i64 = (sys_mmap(8 * NX_UART_SLOT_N)) as *i64
56 let virtio_storage:*i64 = (sys_mmap(8 * NX_VIRTIO_SLOT_N)) as *i64
57 let vnet_storage: *i64 = (sys_mmap(8 * NX_VIRTIO_SLOT_N)) as *i64
58 let nvme_storage: *i64 = (sys_mmap(8 * NX_NVME_SLOT_N)) as *i64
59 let nndev_storage: *i64 = (sys_mmap(8 * NX_NNDEV_SLOT_N)) as *i64
60 let mmu_storage: *i64 = (sys_mmap(8 * NX_MMU_SLOT_N)) as *i64
61 let mem: *u8 = sys_mmap(mem_size)
62 let rf: *NxRv64imRegfile = (sys_mmap(64)) as *NxRv64imRegfile
63 let csr: *NxRv64imCsrFile = (sys_mmap(64)) as *NxRv64imCsrFile
64 let clint: *NxClint = (sys_mmap(64)) as *NxClint
65 let uart: *NxUart = (sys_mmap(64)) as *NxUart
66 let virtio:*NxVirtioMmio = (sys_mmap(64)) as *NxVirtioMmio
67 let vnet: *NxVirtioMmio = (sys_mmap(64)) as *NxVirtioMmio
68 let nvme: *NxNvmeCtrl = (sys_mmap(64)) as *NxNvmeCtrl
69 let nndev: *NxNnDev = (sys_mmap(64)) as *NxNnDev
70 let mmu: *NxMmu = (sys_mmap(64)) as *NxMmu
71 let sim: *NxRv64imSim = (sys_mmap(NX_RV64IM_SIM_BYTES)) as *NxRv64imSim
72 nx_rv64im_rf_init(rf, rf_storage)
73 nx_rv64im_csr_init(csr, csr_storage, 0)
74 nx_clint_init(clint, clint_storage)
75 nx_uart_init(uart, uart_storage, tx_buf, txcap)
76 nx_virtio_init(virtio, virtio_storage)
77 nx_virtio_init_net(vnet, vnet_storage)
78 nx_nvme_init(nvme, nvme_storage)
79 nx_nndev_init(nndev, nndev_storage)
80 nx_mmu_init(mmu, mmu_storage)
81 nx_rv64im_sim_init(sim, rf, csr, clint, uart, BOOTCAP_MEM_BASE, mem, mem_size, 0)
82 nx_rv64im_sim_attach_virtio(sim, virtio)
83 nx_rv64im_sim_attach_virtio_net(sim, vnet)
84 nx_rv64im_sim_attach_nvme(sim, nvme)
85 nx_rv64im_sim_attach_nndev(sim, nndev)
86 nx_rv64im_sim_attach_mmu(sim, mmu)
87 var i: i64 = 0
88 while i < ilen { mem[i] = img[i]; i = i + 1 }
89 return sim
90}
91
92// Boot `img` on a FRESH machine (bootcap_machine) and capture the UART. Returns tx count.
93func bootcap_run(img: *u8, ilen: i64, tx_buf: *u8, txcap: i64, res: *i64) -> i64 {
94 let sim: *NxRv64imSim = bootcap_machine(img, ilen, tx_buf, txcap)
95 nx_rv64im_sim_run(sim, BOOTCAP_MAX_STEPS)
96 let cnt: i64 = nx_uart_tx_count(sim.uart)
97 res[BOOTCAP_R_HALTED] = sim.halted
98 res[BOOTCAP_R_CODE] = sim.halt_code
99 res[BOOTCAP_R_STEPS] = sim.steps
100 res[BOOTCAP_R_TXN] = cnt
101 return cnt
102}
103
104// Resolve the kernel image across the two roots. Returns the buffer (0 if neither exists) and
105// writes the byte length to lenp[0]; `outpath` receives which path actually answered.
106func bootcap_load(primary: *u8, alt: *u8, lenp: *i64, outpath: *i64) -> *u8 {
107 var img: *u8 = sys_read_file(primary, lenp)
108 if lenp[0] > 0 { outpath[0] = primary as i64; return img }
109 img = sys_read_file(alt, lenp)
110 if lenp[0] > 0 { outpath[0] = alt as i64; return img }
111 outpath[0] = primary as i64
112 return 0 as *u8
113}