code wiki / _hdl_build / nx_gpu_sync.nx

nx_gpu_sync.nx source

↩ module page · 81 lines · 4438 B

1// nx_gpu_sync.nx -- the sovereign GPU FENCE over the dxg door (gpu GP7, the fence half of "fences and barriers"). 2// 3// A monotonic timeline fence the CPU can signal and wait on, as ONE composed primitive over the nx_dxg shim: 4// gpu_sync_fence creates a MONITORED_FENCE sync object on the device, signals it to 1 and then to 2, and waits 5// for 2 -- a round trip that only completes when the signals advanced the real fence. It returns 0 on the 6// round trip, a NEGATIVE code naming the stage that refused, and leaves the sync-object handle in outso[0] so 7// a caller that wants the fence for real work keeps it. 8// 9// PROVENANCE: the ABI and the round-trip were measured LIVE on the RTX 5080 by the R4g candidate gate 10// (_offc/cand__gpu_dxg_syncobj_gate, size-swept ioctl codes, type-99 refused, three tampers rejected) and sat 11// unpromoted for weeks. Folded here so the estate has ONE fence primitive and ONE gate over it 12// (nx_gpu_sync_gate). The BARRIER half of GP7 (a barrier a recorded list carries between a write and a read) 13// is NOT here: it needs a command list the dxg door can execute, which the raw channel cannot carry without 14// a vendor UMD (gpu.plan GP5 lesson row, 2026-09-02). This file is the fence; the board says which half. 15// SCOPE: /dev/dxg only (WSL2 GPU-PV). No third party in the run path: raw ioctls through nx_syscalls. 16// license_tier: ORIGINAL No hw writes (Rule 26): sync objects are per-process kernel state, never persistent. 17import "nx_syscalls.nx" 18import "nx_dxg.nx" 19 20const GS_ERR_CREATE: i64 = 0 - 1 // the create ioctl refused or returned no object 21const GS_ERR_SIGNAL1: i64 = 0 - 2 // the first CPU signal refused 22const GS_ERR_SIGNAL2: i64 = 0 - 3 // the second CPU signal refused 23const GS_ERR_WAIT: i64 = 0 - 4 // the synchronous wait did not complete cleanly 24const GS_FENCE_STEP1: i64 = 1 25const GS_FENCE_STEP2: i64 = 2 26 27// the fence round trip on an already-opened device; outso[0] keeps the sync object, outfva[0] its CPU-mapped page 28func gpu_sync_fence(fd: i64, device: i64, outso: *i64, outfva: *i64) -> i64 { 29 let cr: i64 = dxg_create_so(fd, device, DXG_SO_MONITORED_FENCE, outso, outfva) 30 if cr != 0 { return GS_ERR_CREATE } 31 if outso[0] == 0 { return GS_ERR_CREATE } 32 if dxg_signal_cpu(fd, device, outso[0], 1, GS_FENCE_STEP1) != 0 { return GS_ERR_SIGNAL1 } 33 if dxg_signal_cpu(fd, device, outso[0], 1, GS_FENCE_STEP2) != 0 { return GS_ERR_SIGNAL2 } 34 if dxg_wait_cpu(fd, device, outso[0], GS_FENCE_STEP2) != 0 { return GS_ERR_WAIT } 35 return 0 36} 37 38// resolve the discrete adapter to a device handle: enum -> the discrete (non-integrated, non-software) adapter -> 39// open by LUID -> device. Returns 0 with outdev[0] set, or a negative stage code. Composes the shim; no copies. 40const GS_ERR_ENUM: i64 = 0 - 11 41const GS_ERR_NODISC: i64 = 0 - 12 42const GS_ERR_OPEN: i64 = 0 - 13 43const GS_ERR_DEVICE: i64 = 0 - 14 44const GS_ADAPTER_ROW: i64 = 20 45const GS_QTYPE_FLAGS: i64 = 15 46// the discrete adapter handle too (the budget query is per ADAPTER, the sync objects per DEVICE): outah[0] = adapter, outdev[0] = device 47func gpu_sync_open(fd: i64, outah: *i64, outdev: *i64) -> i64 { 48 let ainfo: *u8 = sys_mmap(DXG_MAGIC_4096) 49 let nc: i64 = dxg_enum(fd, ainfo) 50 if nc <= 0 { sys_munmap(ainfo, DXG_MAGIC_4096); return GS_ERR_ENUM } 51 let v: *i64 = sys_mmap(16) as *i64 52 var n_discrete: i64 = 0 53 var disc_lo: i64 = 0 54 var disc_hi: i64 = 0 55 var ai: i64 = 0 56 while ai < nc { 57 let base: i64 = ai * GS_ADAPTER_ROW 58 let eh: i64 = dxg_rd32(ainfo, base) 59 let qret: i64 = dxg_query_type(fd, eh, GS_QTYPE_FLAGS, 4, v) 60 let t: i64 = v[0] 61 if qret == 0 { if ((t >> 4) & 1) == 1 { if ((t >> 2) & 1) == 0 { 62 n_discrete = n_discrete + 1 63 disc_lo = dxg_rd32(ainfo, base + 4) 64 disc_hi = dxg_rd32(ainfo, base + 8) 65 } } } 66 ai = ai + 1 67 } 68 sys_munmap(ainfo, DXG_MAGIC_4096) 69 sys_munmap(v as *u8, 16) 70 if n_discrete != 1 { return GS_ERR_NODISC } 71 if dxg_open_from_luid(fd, disc_lo, disc_hi, outah) != 0 { return GS_ERR_OPEN } 72 if dxg_create_device(fd, outah[0], outdev) != 0 { return GS_ERR_DEVICE } 73 if outdev[0] == 0 { return GS_ERR_DEVICE } 74 return 0 75} 76func gpu_sync_device(fd: i64, outdev: *i64) -> i64 { 77 let ah: *i64 = sys_mmap(16) as *i64 78 let r: i64 = gpu_sync_open(fd, ah, outdev) 79 sys_munmap(ah as *u8, 16) 80 return r 81}