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}