code wiki / (root) / nx_hal.nx

nx_hal.nx source

↩ module page · 554 lines · 25006 B

1// nx_hal.nx -- substrate Hardware Abstraction Layer. 2// 3// Cardinal 2026-05-21 ("bits up eliminate and for the rest we want to 4// be hardware agnostic as in the future nishi silicon will run all 5// of this"): every substrate primitive above this layer expresses 6// hardware-level operations in HAL terms, NOT Linux-syscall terms. 7// The HAL is the substrate-defined contract that ANY backing 8// implementation (Linux today, bare-metal Nishi kernel tomorrow, 9// future Nishi silicon eventually) must provide. 10// 11// Substrate's bottom-up sovereignty story: 12// - Top of stack: Elder AI services + UI -- can run on whatever OS 13// the substrate currently runs on (Win-native + bash-launched 14// today; future substrate-hosted on a NishiLang container) 15// - Middle: substrate primitives (HAL clients) -- hardware-agnostic; 16// compile + run unchanged on any HAL implementation 17// - Bottom: HAL implementation (per-target shim) -- the thin 18// adapter that turns hardware-level HAL ops into actual machine 19// behavior. Today: Linux syscall ABI. Tomorrow: bare-metal 20// MMIO + page-table walks. Eventually: future Nishi silicon. 21// 22// The win: as we eliminate third parties layer by layer (Docker, 23// Linux, Win32, NVIDIA), the substrate primitives DO NOT CHANGE. 24// Only the HAL shim changes. The investment in shipped substrate 25// is preserved. 26// 27// V1 operations (the hardware-level concepts any silicon must 28// provide; bare-metal Nishi kernel + future Nishi silicon both 29// implement these): 30// nx_hal_clock_now_ns monotonic nanosecond timestamp 31// nx_hal_alloc_pages contiguous page allocation 32// nx_hal_release_pages return pages to substrate 33// nx_hal_console_write debug-channel byte output 34// nx_hal_random_bytes hardware-quality entropy 35// nx_hal_yield cooperative scheduling hint 36// nx_hal_exit process termination 37// nx_hal_spawn fork + exec; substrate becomes parent 38// nx_hal_wait reap child + read exit code 39// nx_hal_kill send signal to child 40// 41// V1.5 dispatcher (2026-05-26): each public op is a one-branch 42// dispatcher over NX_HAL_ACTIVE_SHIM_KIND. Each shim has its own 43// implementation function (nx_hal_<op>_<shim>); shims with no real 44// backing return NO_BACKING honestly rather than silently doing the 45// wrong thing. Today only NX_HAL_SHIM_LINUX has real implementations; 46// NX_HAL_SHIM_BARE_METAL and NX_HAL_SHIM_NISHI_SILICON are TODO_SILICON 47// stubs that document what the real implementation must do. 48// 49// Switching the active shim is a one-line constant edit at the top 50// of this file (NX_HAL_ACTIVE_SHIM_KIND). A bare-metal or silicon 51// build flips that constant; until each stub is filled in, calls 52// route through NO_BACKING which is loud at runtime. 53// 54// (Future: when the NishiLang preprocessor grows general @ifdef 55// support beyond the hardcoded TARGET_X86_64 dimension, the runtime 56// dispatcher gets replaced with compile-time @ifdef so that wrong- 57// target builds fail at compile rather than runtime. Today the 58// preprocessor only knows ONE macro so the runtime dispatcher is 59// the honest mechanism.) 60// 61// V2+ scope (for the next layers down the stack): 62// - nx_hal_mmio_read/write (bare-metal direct register access) 63// - nx_hal_intr_register (interrupt vector hookup) 64// - nx_hal_dma_buffer (DMA-coherent allocator) 65// - nx_hal_cpu_pin (affinity for the Nishi-silicon clusters) 66// 67// genealogy_id: linux_syscall_abi_1991 + microkernel_minimality_1969 + 68// cardinal_2026-05-21_bits_up_silicon_to_top + 69// cardinal_2026-05-21_hardware_agnostic_future_nishi_silicon 70// lineage_id: substrate_hal_v1_dispatcher 71// 72// nx_capability_manifest: 73// variant_class: hal 74// variant_id: hal_v1_5_dispatcher 75// requires_isa: [rv32i, rv64imac, x86_64, cortex_m, armv7a, aarch64, wasm32] 76// requires_syscalls: [shim_dependent] 77// requires_ram_min_b: 4096 78// tier_floor: NX_TIER_INF_EDGE 79// tier_ceiling: NX_TIER_INF_HPC 80// adversary_class: THREAT_AI_ADVERSARY 81// 82// nx_safety_envelope: 83// intended_use: "Hardware Abstraction Layer; substrate primitives 84// call public HAL ops; dispatcher routes to active 85// shim's implementation; shims provide per-target 86// backing or honest NO_BACKING" 87// sil_target: SIL2 88// evidence: [sealed_enum_op_kinds, sealed_enum_shim_kinds, 89// dispatcher_branch_per_shim, no_backing_loud_runtime, 90// audit_lint_for_direct_sys_calls] 91// verdict: NOT_YET_EVALUATED 92 93import "nx_syscalls.nx" 94const NX_MAGIC_1000000000: i64 = 1000000000 95 96// ===== HAL operation kinds (sealed enum) ================================================= 97// 98// Every HAL operation has a kind code. The enum is the SUBSTRATE 99// contract -- every shim (Linux today, Nishi-silicon tomorrow) must 100// implement all kinds < NX_HAL_N_KINDS. 101const NX_HAL_OP_CLOCK_NOW_NS: i64 = 0 102const NX_HAL_OP_ALLOC_PAGES: i64 = 1 103const NX_HAL_OP_RELEASE_PAGES: i64 = 2 104const NX_HAL_OP_CONSOLE_WRITE: i64 = 3 105const NX_HAL_OP_RANDOM_BYTES: i64 = 4 106const NX_HAL_OP_YIELD: i64 = 5 107const NX_HAL_OP_EXIT: i64 = 6 108const NX_HAL_OP_SPAWN: i64 = 7 // fork+exec; substrate becomes parent 109const NX_HAL_OP_WAIT: i64 = 8 // reap child + read exit code 110const NX_HAL_OP_KILL: i64 = 9 // send signal to child 111const NX_HAL_OP_N_KINDS: i64 = 10 112 113func nx_hal_op_is_valid(op: i64) -> i64 { 114 if op < 0 { return 0 } 115 if op >= NX_HAL_OP_N_KINDS { return 0 } 116 return 1 117} 118 119// ===== Backing-shim kinds (sealed enum) ================================================= 120const NX_HAL_SHIM_LINUX: i64 = 0 121const NX_HAL_SHIM_BARE_METAL: i64 = 1 // future: substrate kernel on bare hw 122const NX_HAL_SHIM_NISHI_SILICON: i64 = 2 // future: custom Nishi ASIC 123const NX_HAL_SHIM_N_KINDS: i64 = 3 124 125func nx_hal_shim_is_valid(s: i64) -> i64 { 126 if s < 0 { return 0 } 127 if s >= NX_HAL_SHIM_N_KINDS { return 0 } 128 return 1 129} 130 131// ===== Verdicts ================================================= 132const NX_HAL_OK: i64 = 0 133const NX_HAL_BAD_INPUT: i64 = 1 134const NX_HAL_NO_BACKING: i64 = 2 // shim not implemented for this op 135const NX_HAL_FAULT: i64 = 3 // hardware/syscall returned error 136const NX_HAL_N_VERDICTS: i64 = 4 137 138func nx_hal_verdict_is_valid(v: i64) -> i64 { 139 if v < 0 { return 0 } 140 if v >= NX_HAL_N_VERDICTS { return 0 } 141 return 1 142} 143 144// ===== Active shim selector ================================================= 145// THE seam between substrate and silicon target. Edit this single 146// constant to switch the build's backing shim; the dispatchers below 147// route every op through the matching implementation function. 148// 149// LINUX -- today; real implementations via Linux syscall ABI. 150// BARE_METAL -- future; substrate runs on Nishi-OS bare-metal kernel. 151// Stubs return NO_BACKING until the kernel HAL shim 152// lands per task K-7+. 153// NISHI_SILICON -- future; substrate runs on Nishi-fabbed silicon 154// with no general-purpose OS. Stubs return NO_BACKING 155// until the silicon RTL exposes the documented MMIO 156// contract (see TODO_SILICON comments per op). 157const NX_HAL_ACTIVE_SHIM_KIND: i64 = NX_HAL_SHIM_LINUX 158 159func nx_hal_active_shim() -> i64 { 160 return NX_HAL_ACTIVE_SHIM_KIND 161} 162 163// ===== Per-target syscall numbers needed by the Linux shim ================================================= 164// (Some primitives below need syscalls that aren't already in 165// nx_syscalls.nx; we declare them here so the substrate stays 166// out of nx_syscalls.nx's stable surface.) 167@ifdef TARGET_X86_64 168const NX_HAL_SYS_GETRANDOM: i64 = 318 169const NX_HAL_SYS_SCHED_YIELD: i64 = 24 170const NX_HAL_SYS_KILL: i64 = 62 171@endif 172 173@ifndef TARGET_X86_64 174const NX_HAL_SYS_GETRANDOM: i64 = 278 175const NX_HAL_SYS_SCHED_YIELD: i64 = 124 176const NX_HAL_SYS_KILL: i64 = 129 177@endif 178 179// ===== nx_hal_clock_now_ns ================================================= 180// Returns nanoseconds since some monotonic epoch. Substrate primitives 181// that need durations or scheduling timestamps call THIS, not 182// sys_clock_gettime directly. 183func nx_hal_clock_now_ns_linux() -> i64 { 184 // CLOCK_MONOTONIC is id 1; timespec is two i64s. 185 let ts: *i64 = (sys_mmap(16)) as *i64 186 ts[0] = 0 187 ts[1] = 0 188 let rc: i64 = __syscall(SYS_CLOCK_GETTIME, 1, ts as i64, 0, 0, 0, 0) 189 if rc != 0 { return 0 - NX_HAL_FAULT } 190 return ts[0] * NX_MAGIC_1000000000 + ts[1] 191} 192 193func nx_hal_clock_now_ns_bare_metal() -> i64 { 194 // TODO_SILICON: on Nishi-OS bare metal, read CLINT MTIME 195 // (RV64 virt: 0x0200BFF8) and scale by fabric clock period. 196 // Kernel's clint.nx already exposes this; HAL shim plugs into 197 // sched_now_ns once K-7 (bare-metal HAL shim task) lands. 198 return 0 - NX_HAL_NO_BACKING 199} 200 201func nx_hal_clock_now_ns_nishi_silicon() -> i64 { 202 // TODO_SILICON: dedicated time-stamp counter. For Nishi RV64 203 // silicon: mcycle CSR scaled by fabric clock period (10 MHz on 204 // FPGA Tier A; per-process for MPW Tier B). See 205 // nishi-silicon/hdl/README.md once Tier A bring-up writes it. 206 return 0 - NX_HAL_NO_BACKING 207} 208 209func nx_hal_clock_now_ns() -> i64 { 210 let s: i64 = nx_hal_active_shim() 211 if s == NX_HAL_SHIM_LINUX { return nx_hal_clock_now_ns_linux() } 212 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_clock_now_ns_bare_metal() } 213 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_clock_now_ns_nishi_silicon() } 214 return 0 - NX_HAL_NO_BACKING 215} 216 217// ===== nx_hal_alloc_pages ================================================= 218// Returns pointer to n_bytes of zero-initialised anonymous memory. 219// Page-rounded by the shim. Substrate primitives that today call 220// sys_mmap(size) should migrate to this. 221func nx_hal_alloc_pages_linux(n_bytes: i64) -> *u8 { 222 return (sys_mmap(n_bytes)) as *u8 223} 224 225func nx_hal_alloc_pages_bare_metal(n_bytes: i64) -> *u8 { 226 // TODO_SILICON: bare-metal page allocator. Kernel vm.nx already 227 // implements a bump allocator over the physical RAM range starting 228 // at kernel_end. HAL shim calls into that allocator once K-7 229 // exposes it as kvm_alloc_pages(n_bytes). 230 return (0 as i64) as *u8 231} 232 233func nx_hal_alloc_pages_nishi_silicon(n_bytes: i64) -> *u8 { 234 // TODO_SILICON: silicon-fabbed page allocator. On Nishi RV64 235 // (Tier A FPGA: no MMU; Tier B MPW: optional MMU), pages come 236 // from the static RAM region documented by silicon's memory-map 237 // header. Bump allocator suffices for Tier A; ring allocator 238 // when MMU lands in Tier B+. 239 return (0 as i64) as *u8 240} 241 242func nx_hal_alloc_pages(n_bytes: i64) -> *u8 { 243 if n_bytes <= 0 { return (0 as i64) as *u8 } 244 let s: i64 = nx_hal_active_shim() 245 if s == NX_HAL_SHIM_LINUX { return nx_hal_alloc_pages_linux(n_bytes) } 246 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_alloc_pages_bare_metal(n_bytes) } 247 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_alloc_pages_nishi_silicon(n_bytes) } 248 return (0 as i64) as *u8 249} 250 251// ===== nx_hal_release_pages ================================================= 252// Releases pages obtained via nx_hal_alloc_pages. V1 Linux shim is 253// a no-op (the substrate hasn't shipped munmap wrappers yet; sys_mmap 254// allocations are released only at process exit). Stub here so the 255// CONTRACT is published; future shims can implement actual freeing. 256func nx_hal_release_pages_linux(ptr: *u8, n_bytes: i64) -> i64 { 257 // No-op until sys_munmap wrapper ships. Return NO_BACKING so 258 // callers know to expect leak at process boundary. 259 return 0 - NX_HAL_NO_BACKING 260} 261 262func nx_hal_release_pages_bare_metal(ptr: *u8, n_bytes: i64) -> i64 { 263 // TODO_SILICON: bare-metal page release. Kernel vm.nx bump 264 // allocator does not free today; ring allocator would on K-8+. 265 return 0 - NX_HAL_NO_BACKING 266} 267 268func nx_hal_release_pages_nishi_silicon(ptr: *u8, n_bytes: i64) -> i64 { 269 // TODO_SILICON: silicon-fabbed page release. Tier A bump 270 // allocator does not free; Tier B+ ring allocator does. 271 return 0 - NX_HAL_NO_BACKING 272} 273 274func nx_hal_release_pages(ptr: *u8, n_bytes: i64) -> i64 { 275 if (ptr as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 276 if n_bytes <= 0 { return 0 - NX_HAL_BAD_INPUT } 277 let s: i64 = nx_hal_active_shim() 278 if s == NX_HAL_SHIM_LINUX { return nx_hal_release_pages_linux(ptr, n_bytes) } 279 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_release_pages_bare_metal(ptr, n_bytes) } 280 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_release_pages_nishi_silicon(ptr, n_bytes) } 281 return 0 - NX_HAL_NO_BACKING 282} 283 284// ===== nx_hal_console_write ================================================= 285// Writes bytes to the debug console. V1 Linux shim writes to fd 2 286// (stderr). Future bare-metal shim writes to UART or framebuffer 287// console; future Nishi-silicon shim writes to dedicated debug MMIO. 288func nx_hal_console_write_linux(buf: *u8, len: i64) -> i64 { 289 return sys_write(2, buf, len) 290} 291 292func nx_hal_console_write_bare_metal(buf: *u8, len: i64) -> i64 { 293 // TODO_SILICON: bare-metal UART write. Kernel uart.nx implements 294 // the 16550 polled-THRE protocol at 0x10000000 with MIE masking 295 // for atomicity. HAL shim calls uart_print_bytes(buf, len) once 296 // K-7 (bare-metal HAL shim task) lands. 297 return 0 - NX_HAL_NO_BACKING 298} 299 300func nx_hal_console_write_nishi_silicon(buf: *u8, len: i64) -> i64 { 301 // TODO_SILICON: silicon-fabbed debug MMIO write. Tier A FPGA: 302 // 16550-compatible UART at 0x10000000 (kernel-compatible). 303 // Tier B MPW: same plus optional second console for printf-debug 304 // separation. Tier C home-fab: TBD per fab/README.md. 305 return 0 - NX_HAL_NO_BACKING 306} 307 308func nx_hal_console_write(buf: *u8, len: i64) -> i64 { 309 if (buf as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 310 if len < 0 { return 0 - NX_HAL_BAD_INPUT } 311 if len == 0 { return NX_HAL_OK } 312 let s: i64 = nx_hal_active_shim() 313 if s == NX_HAL_SHIM_LINUX { return nx_hal_console_write_linux(buf, len) } 314 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_console_write_bare_metal(buf, len) } 315 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_console_write_nishi_silicon(buf, len) } 316 return 0 - NX_HAL_NO_BACKING 317} 318 319// ===== nx_hal_random_bytes ================================================= 320// Fills buf with len bytes of hardware-quality entropy. V1 Linux 321// shim uses getrandom(). Future bare-metal shim taps the silicon 322// RNG. Future Nishi-silicon shim taps the dedicated entropy source. 323func nx_hal_random_bytes_linux(buf: *u8, len: i64) -> i64 { 324 let rc: i64 = __syscall(NX_HAL_SYS_GETRANDOM, buf as i64, len, 0, 0, 0, 0) 325 if rc < 0 { return 0 - NX_HAL_FAULT } 326 if rc != len { return 0 - NX_HAL_FAULT } 327 return NX_HAL_OK 328} 329 330func nx_hal_random_bytes_bare_metal(buf: *u8, len: i64) -> i64 { 331 // TODO_SILICON: bare-metal entropy source. No silicon RNG yet 332 // on QEMU virt; bare-metal HAL shim must either (a) seed from 333 // boot-time entropy + chacha20 stretch, or (b) wait for silicon 334 // RNG block. Stub returns NO_BACKING so callers cannot accept 335 // weak entropy by accident. 336 return 0 - NX_HAL_NO_BACKING 337} 338 339func nx_hal_random_bytes_nishi_silicon(buf: *u8, len: i64) -> i64 { 340 // TODO_SILICON: silicon entropy block. Tier A FPGA: ring- 341 // oscillator TRNG (well-studied design, ~100 Kbps). Tier B MPW: 342 // dedicated CMOS thermal-noise TRNG (~1 Mbps). Tier C home-fab: 343 // TBD. HAL shim reads from MMIO entropy FIFO once silicon 344 // exposes one. 345 return 0 - NX_HAL_NO_BACKING 346} 347 348func nx_hal_random_bytes(buf: *u8, len: i64) -> i64 { 349 if (buf as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 350 if len < 0 { return 0 - NX_HAL_BAD_INPUT } 351 if len == 0 { return NX_HAL_OK } 352 let s: i64 = nx_hal_active_shim() 353 if s == NX_HAL_SHIM_LINUX { return nx_hal_random_bytes_linux(buf, len) } 354 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_random_bytes_bare_metal(buf, len) } 355 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_random_bytes_nishi_silicon(buf, len) } 356 return 0 - NX_HAL_NO_BACKING 357} 358 359// ===== nx_hal_yield ================================================= 360// Cooperative scheduler hint -- substrate releases its time slice. 361// Used by busy-wait loops + actor cardinals. 362func nx_hal_yield_linux() -> i64 { 363 let rc: i64 = __syscall(NX_HAL_SYS_SCHED_YIELD, 0, 0, 0, 0, 0, 0) 364 if rc != 0 { return 0 - NX_HAL_FAULT } 365 return NX_HAL_OK 366} 367 368func nx_hal_yield_bare_metal() -> i64 { 369 // TODO_SILICON: bare-metal cooperative yield. Kernel sched.nx 370 // sched_yield_now() is exactly this; HAL shim calls it once 371 // K-7 exposes the kernel ABI to substrate. 372 return 0 - NX_HAL_NO_BACKING 373} 374 375func nx_hal_yield_nishi_silicon() -> i64 { 376 // TODO_SILICON: on a single-tasking Tier A FPGA bring-up there 377 // is nothing to yield to (busy-wait suffices). Tier B MPW with 378 // multi-tasking kernel routes through the same sched_yield_now() 379 // path as bare-metal once kernel HAL shim lands. 380 return 0 - NX_HAL_NO_BACKING 381} 382 383func nx_hal_yield() -> i64 { 384 let s: i64 = nx_hal_active_shim() 385 if s == NX_HAL_SHIM_LINUX { return nx_hal_yield_linux() } 386 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_yield_bare_metal() } 387 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_yield_nishi_silicon() } 388 return 0 - NX_HAL_NO_BACKING 389} 390 391// ===== nx_hal_exit ================================================= 392// Terminates the substrate process with the given code. All shims 393// implement this. Linux: exit_group syscall. Bare metal: jump to 394// reset vector. Nishi silicon: assert HALT line. 395func nx_hal_exit_linux(code: i64) -> i64 { 396 __syscall(SYS_EXIT, code, 0, 0, 0, 0, 0) 397 return 0 - NX_HAL_FAULT // unreachable 398} 399 400func nx_hal_exit_bare_metal(code: i64) -> i64 { 401 // TODO_SILICON: bare-metal exit. On QEMU virt this writes 402 // 0x5555 to SiFive finisher at 0x100000 for clean poweroff 403 // (kernel tasks.nx already does this). HAL shim calls into 404 // kernel finisher path once K-7 exposes it. 405 return 0 - NX_HAL_NO_BACKING 406} 407 408func nx_hal_exit_nishi_silicon(code: i64) -> i64 { 409 // TODO_SILICON: silicon HALT line assertion. Tier A FPGA: write 410 // 1 to a HALT MMIO register that the test-harness FPGA bitstream 411 // exposes (loops back via JTAG). Tier B MPW: dedicated HALT pin 412 // + optional exit-code MMIO read-back. Tier C home-fab: TBD. 413 return 0 - NX_HAL_NO_BACKING 414} 415 416func nx_hal_exit(code: i64) -> i64 { 417 let s: i64 = nx_hal_active_shim() 418 if s == NX_HAL_SHIM_LINUX { return nx_hal_exit_linux(code) } 419 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_exit_bare_metal(code) } 420 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_exit_nishi_silicon(code) } 421 return 0 - NX_HAL_NO_BACKING 422} 423 424// ===== nx_hal_spawn ================================================= 425// Substrate becomes parent of a new child process. Linux shim: 426// sys_fork + sys_execve. Bare-metal/Nishi-silicon shims: schedule 427// a new task on the substrate kernel scheduler. 428// 429// Returns: 430// > 0 child PID (parent path) 431// == 0 CHILD PATH -- only reached if execve fails; child should 432// immediately nx_hal_exit(). V1 substrate does this dance 433// internally so the caller always sees parent-path return 434// values. 435// < 0 negated verdict on parent-side failure 436// 437// argv must be a *i64 array where each slot is a *u8 string pointer; 438// the array is null-terminated (the terminator slot holds the 439// integer 0, not a string). envp same shape, or null to inherit. 440func nx_hal_spawn_linux(exec_path: *u8, argv: *i64, envp: *i64) -> i64 { 441 let pid: i64 = sys_fork() 442 if pid < 0 { return 0 - NX_HAL_FAULT } 443 if pid == 0 { 444 // CHILD path -- execve replaces image. If it returns, 445 // execve failed; the child exits non-zero so the parent 446 // sees the error via wait. 447 sys_execve(exec_path, argv, envp) 448 nx_hal_exit(127) 449 return 0 // unreachable 450 } 451 // PARENT path. 452 return pid 453} 454 455func nx_hal_spawn_bare_metal(exec_path: *u8, argv: *i64, envp: *i64) -> i64 { 456 // TODO_SILICON: bare-metal spawn = kernel sched_spawn(entry_addr, 457 // stack_top, brane_caps). No filesystem on bare metal yet, so 458 // exec_path resolves via an embedded module registry (kernel 459 // tasks.nx already enumerates task_entry_addr_a/_perf/_vm). 460 // HAL shim calls into kernel once K-7 exposes the spawn ABI. 461 return 0 - NX_HAL_NO_BACKING 462} 463 464func nx_hal_spawn_nishi_silicon(exec_path: *u8, argv: *i64, envp: *i64) -> i64 { 465 // TODO_SILICON: Tier A FPGA single-tasking has no spawn (return 466 // BAD_INPUT-style honest verdict). Tier B MPW with kernel routes 467 // through bare-metal path. Tier C home-fab: TBD. 468 return 0 - NX_HAL_NO_BACKING 469} 470 471func nx_hal_spawn(exec_path: *u8, argv: *i64, envp: *i64) -> i64 { 472 if (exec_path as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 473 if (argv as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 474 let s: i64 = nx_hal_active_shim() 475 if s == NX_HAL_SHIM_LINUX { return nx_hal_spawn_linux(exec_path, argv, envp) } 476 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_spawn_bare_metal(exec_path, argv, envp) } 477 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_spawn_nishi_silicon(exec_path, argv, envp) } 478 return 0 - NX_HAL_NO_BACKING 479} 480 481// ===== nx_hal_wait ================================================= 482// Wait for the named child to exit. Returns exit code via the 483// out-pointer, returns OK / verdict on the call itself. 484// pid: > 0 means wait for that specific child; -1 means ANY child. 485// exit_code_out: *i64; on OK, this is filled with the child's 486// exit code (0..255 typical). Must be caller-allocated. 487func nx_hal_wait_linux(pid: i64, exit_code_out: *i64) -> i64 { 488 let status: *i64 = (sys_mmap(8)) as *i64 489 status[0] = 0 490 let reaped: i64 = sys_wait4(pid, status, 0) 491 if reaped < 0 { return 0 - NX_HAL_FAULT } 492 exit_code_out[0] = wait_exit_code(status[0]) 493 return NX_HAL_OK 494} 495 496func nx_hal_wait_bare_metal(pid: i64, exit_code_out: *i64) -> i64 { 497 // TODO_SILICON: bare-metal wait = kernel sched_wait(pid, &exit). 498 // Kernel sched.nx already has task-exit notification via the 499 // IPC mailbox; HAL shim wraps it once K-7 exposes the ABI. 500 return 0 - NX_HAL_NO_BACKING 501} 502 503func nx_hal_wait_nishi_silicon(pid: i64, exit_code_out: *i64) -> i64 { 504 // TODO_SILICON: Tier A FPGA single-tasking has no wait. Tier B 505 // MPW routes through bare-metal path. Tier C home-fab: TBD. 506 return 0 - NX_HAL_NO_BACKING 507} 508 509func nx_hal_wait(pid: i64, exit_code_out: *i64) -> i64 { 510 if (exit_code_out as i64) == 0 { return 0 - NX_HAL_BAD_INPUT } 511 let s: i64 = nx_hal_active_shim() 512 if s == NX_HAL_SHIM_LINUX { return nx_hal_wait_linux(pid, exit_code_out) } 513 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_wait_bare_metal(pid, exit_code_out) } 514 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_wait_nishi_silicon(pid, exit_code_out) } 515 return 0 - NX_HAL_NO_BACKING 516} 517 518// ===== nx_hal_kill ================================================= 519// Send a signal to a child. Linux shim: sys_kill. Substrate 520// supervisor uses this for the BACKING_OFF -> TERMINATED transition 521// when escalate is required. 522// pid: child PID (must be > 0; substrate refuses to signal-broadcast) 523// signal: SIGTERM=15 / SIGKILL=9 etc. 524func nx_hal_kill_linux(pid: i64, signal: i64) -> i64 { 525 let rc: i64 = __syscall(NX_HAL_SYS_KILL, pid, signal, 0, 0, 0, 0) 526 if rc != 0 { return 0 - NX_HAL_FAULT } 527 return NX_HAL_OK 528} 529 530func nx_hal_kill_bare_metal(pid: i64, signal: i64) -> i64 { 531 // TODO_SILICON: bare-metal signal delivery = kernel 532 // sched_signal(pid, signal). Kernel sched.nx does not yet have 533 // a signal abstraction; SIGTERM-equivalent is "set exit-pending 534 // flag, next preempt drops the task." HAL shim wraps that path 535 // once K-7 exposes it. 536 return 0 - NX_HAL_NO_BACKING 537} 538 539func nx_hal_kill_nishi_silicon(pid: i64, signal: i64) -> i64 { 540 // TODO_SILICON: Tier A FPGA single-tasking has no kill. Tier B 541 // MPW routes through bare-metal path. Tier C home-fab: TBD. 542 return 0 - NX_HAL_NO_BACKING 543} 544 545func nx_hal_kill(pid: i64, signal: i64) -> i64 { 546 if pid <= 0 { return 0 - NX_HAL_BAD_INPUT } 547 if signal <= 0 { return 0 - NX_HAL_BAD_INPUT } 548 if signal > 64 { return 0 - NX_HAL_BAD_INPUT } 549 let s: i64 = nx_hal_active_shim() 550 if s == NX_HAL_SHIM_LINUX { return nx_hal_kill_linux(pid, signal) } 551 if s == NX_HAL_SHIM_BARE_METAL { return nx_hal_kill_bare_metal(pid, signal) } 552 if s == NX_HAL_SHIM_NISHI_SILICON { return nx_hal_kill_nishi_silicon(pid, signal) } 553 return 0 - NX_HAL_NO_BACKING 554}