code wiki / _hdl_build / nx_dxg.nx
nx_dxg.nx source
↩ module page · 171 lines · 11918 B
1// nx_dxg.nx -- the /dev/dxg DEVICE-HAL SHIM (WSL2 GPU-PV backend), CONSOLIDATED.
2//
3// The dxg control-plane (enumerate adapters, query type, open device, create context/paging-queue, alloc,
4// map-VA, make-resident, hardware-queue create/submit) was COPY-PASTED across _gpu_dxg_r5a_sem,
5// _gpu_dxg_r6_hwq, and nx_device_probe. This lib is the single source of truth -- pure funcs, no main.
6// It is ONE per-OS device shim (mirrors nx_hal's per-target shim model); native-Linux /dev/dri = a sibling
7// shim implementing the same interface, so the per-device backends above it don't change. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9const DXG_MAGIC_4096: i64 = 4096
10const DXG_MAGIC_65536: i64 = 65536
11
12const DXG_ENUM2: i64 = 0xC0104714
13const DXG_QAI: i64 = 0xC0184709
14const DXG_OAFL: i64 = 0xC00C4701
15const DXG_CDEV: i64 = 0xC0404702
16const DXG_CCV: i64 = 0xC0284704
17const DXG_CPQ: i64 = 0xC0204707
18const DXG_CALLOC: i64 = 0xC0484706
19const DXG_MAPVA: i64 = 0xC068470C
20const DXG_MKRES: i64 = 0xC030470B
21const DXG_CHWQ: i64 = 0xC0304718
22const DXG_SUBHWQ: i64 = 0xC0384734
23// SYNC OBJECTS (folded from the R4g candidate gate 2026-09-02; ABI size-swept LIVE on the 5080):
24// CREATESYNCHRONIZATIONOBJECT size 96: device@0, info.type@8 (5 = MONITORED_FENCE), fence_cpu_va@24 OUT, sync_object@88 OUT
25// SIGNALSYNCHRONIZATIONOBJECTFROMCPU size 32: device@0, object_count@4, objects@8 ptr, fence_values@16 ptr
26// WAITFORSYNCHRONIZATIONOBJECTFROMCPU size 40: same layout, async_event@24 = 0 -> synchronous
27const DXG_CSO: i64 = 0xC0604710
28const DXG_SIGCPU: i64 = 0xC0204731
29const DXG_WAITCPU: i64 = 0xC028473A
30const DXG_SO_MONITORED_FENCE: i64 = 5
31// MEMORY BUDGET + EVICT (size-swept LIVE on the 5080, 2026-09-02, _gpu_dxg_mem_recon: exactly one size answered each):
32// QUERYVIDEOMEMORYINFO size 56: process@0 (0 = current), adapter@8, memory_segment_group@12 (0 LOCAL, 1 NON_LOCAL),
33// budget@16 current_usage@24 current_reservation@32 available_for_reservation@40 (u64 OUT), physical_adapter_index@48
34// -- measured local budget 15,974,006,784 B, non-local 18,424,386,109 B on the RTX 5080
35// EVICT size 32: device@0, alloc_count@4, allocations@8 (ptr), flags@16, num_bytes_to_trim@24 (u64 OUT) -- ret 0 on a
36// resident allocation, and the next MAKERESIDENT of it returns 259 (paging pending), which is the proof it was evicted
37const DXG_QVMI: i64 = 0xC038470A
38const DXG_EVICT: i64 = 0xC020471E
39const DXG_SEG_LOCAL: i64 = 0
40const DXG_SEG_NONLOCAL: i64 = 1
41const DXG_MKRES_PENDING: i64 = 259
42
43func dxg_rd32(buf: *u8, off: i64) -> i64 { return (buf[off] as i64)|((buf[off+1] as i64)<<8)|((buf[off+2] as i64)<<16)|((buf[off+3] as i64)<<24) }
44func dxg_rd64(buf: *u8, off: i64) -> i64 { let q: *i64 = (buf as i64 + off) as *i64; return q[0] }
45func dxg_setu32(buf: *u8, off: i64, val: i64) -> i64 { let w: *u8 = (buf as i64 + off) as *u8; w[0]=(val&0xff) as u8; w[1]=((val>>8)&0xff) as u8; w[2]=((val>>16)&0xff) as u8; w[3]=((val>>24)&0xff) as u8; return 0 }
46func dxg_setu64(buf: *u8, off: i64, val: i64) -> i64 { let q: *i64 = (buf as i64 + off) as *i64; q[0]=val; return 0 }
47
48func dxg_open() -> i64 { return sys_openat_rd("/dev/dxg" as *u8) }
49// enumerate adapters into caller-provided ainfo (>=4096 bytes); returns adapter count.
50func dxg_enum(fd: i64, ainfo: *u8) -> i64 {
51 var z: i64 = 0; while z < DXG_MAGIC_4096 { ainfo[z] = 0 as u8; z = z + 1 }
52 let ereq: *u8 = sys_mmap(64); ereq[0] = 8 as u8; let r8: *i64 = (ereq as i64 + 8) as *i64; r8[0] = ainfo as i64
53 sys_ioctl(fd, DXG_ENUM2, ereq as i64)
54 return dxg_rd32(ereq, 0)
55}
56func dxg_query_type(fd: i64, handle: i64, qtype: i64, psize: i64, outv: *i64) -> i64 {
57 let priv: *u8 = sys_mmap(64); var zz: i64 = 0; while zz < 64 { priv[zz] = 0 as u8; zz = zz + 1 }
58 let req: *u8 = sys_mmap(64); var y: i64 = 0; while y < 64 { req[y] = 0 as u8; y = y + 1 }
59 let h: *i64 = (req as i64 + 0) as *i64; h[0] = (handle & 0xffffffff) | ((qtype & 0xffffffff) << 32)
60 let pd: *i64 = (req as i64 + 8) as *i64; pd[0] = priv as i64
61 let pds: *i64 = (req as i64 + 16) as *i64; pds[0] = psize & 0xffffffff
62 let ret: i64 = sys_ioctl(fd, DXG_QAI, req as i64); outv[0] = dxg_rd32(priv, 0); return ret
63}
64func dxg_open_from_luid(fd: i64, luid_lo: i64, luid_hi: i64, outh: *i64) -> i64 {
65 let req: *u8 = sys_mmap(64); var y: i64 = 0; while y < 64 { req[y] = 0 as u8; y = y + 1 }
66 let lo: *i64 = (req as i64 + 0) as *i64; lo[0] = (luid_lo & 0xffffffff) | ((luid_hi & 0xffffffff) << 32)
67 let ret: i64 = sys_ioctl(fd, DXG_OAFL, req as i64); outh[0] = dxg_rd32(req, 8); return ret
68}
69func dxg_create_device(fd: i64, adapter: i64, outd: *i64) -> i64 {
70 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
71 dxg_setu32(buf, 0, adapter & 0xffffffff)
72 let ret: i64 = sys_ioctl(fd, DXG_CDEV, buf as i64); outd[0] = dxg_rd32(buf, 12); return ret
73}
74func dxg_create_ctx(fd: i64, device: i64, outc: *i64) -> i64 {
75 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
76 dxg_setu32(buf, 0, device & 0xffffffff)
77 let ret: i64 = sys_ioctl(fd, DXG_CCV, buf as i64); outc[0] = dxg_rd32(buf, 32); return ret
78}
79func dxg_create_pq(fd: i64, device: i64, outpq: *i64, outfva: *i64) -> i64 {
80 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
81 dxg_setu32(buf, 0, device & 0xffffffff)
82 let ret: i64 = sys_ioctl(fd, DXG_CPQ, buf as i64); outpq[0] = dxg_rd32(buf, 8); outfva[0] = dxg_rd64(buf, 16); return ret
83}
84// the 64 KiB EXISTINGHEAP allocation every R4 gate uses; the sized form below is the same recipe with the heap size a parameter
85func dxg_alloc_eh(fd: i64, device: i64, sysmem: *u8, outh: *i64) -> i64 { return dxg_alloc_eh_sz(fd, device, sysmem, DXG_MAGIC_65536, outh) }
86// EXISTINGHEAP allocation of `bytes` (page-aligned, sysmem page-aligned) -- GP6 heaps are built from this
87func dxg_alloc_eh_sz(fd: i64, device: i64, sysmem: *u8, bytes: i64, outh: *i64) -> i64 {
88 let desc: *u8 = sys_mmap(64); var dz: i64 = 0; while dz < 64 { desc[dz] = 0 as u8; dz = dz + 1 }
89 dxg_setu32(desc, 0, 1); dxg_setu64(desc, 8, bytes)
90 let entry: *u8 = sys_mmap(256); var ez: i64 = 0; while ez < 256 { entry[ez] = 0 as u8; ez = ez + 1 }
91 dxg_setu64(entry, 8, sysmem as i64)
92 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
93 dxg_setu32(buf, 0, device & 0xffffffff); dxg_setu64(buf, 32, desc as i64); dxg_setu32(buf, 40, 0)
94 dxg_setu32(buf, 44, 1); dxg_setu64(buf, 48, entry as i64); dxg_setu32(buf, 56, 0x10020)
95 let ret: i64 = sys_ioctl(fd, DXG_CALLOC, buf as i64); outh[0] = dxg_rd32(entry, 0); return ret
96}
97func dxg_map_va(fd: i64, pq: i64, alloc: i64, outva: *i64, outf: *i64) -> i64 {
98 let buf: *u8 = sys_mmap(256); var bz: i64 = 0; while bz < 256 { buf[bz] = 0 as u8; bz = bz + 1 }
99 dxg_setu32(buf, 0, pq & 0xffffffff); dxg_setu64(buf, 24, 0x10000000000)
100 dxg_setu32(buf, 32, alloc & 0xffffffff); dxg_setu64(buf, 48, 16); dxg_setu64(buf, 56, 1)
101 let ret: i64 = sys_ioctl(fd, DXG_MAPVA, buf as i64); outva[0] = dxg_rd64(buf, 88); outf[0] = dxg_rd64(buf, 96); return ret
102}
103func dxg_makeresident(fd: i64, pq: i64, alloc_handle: i64, outf: *i64) -> i64 {
104 let alist: *u8 = sys_mmap(64); dxg_setu32(alist, 0, alloc_handle & 0xffffffff)
105 let plist: *u8 = sys_mmap(64); dxg_setu32(plist, 0, 0)
106 let buf: *u8 = sys_mmap(256); var bz: i64 = 0; while bz < 256 { buf[bz] = 0 as u8; bz = bz + 1 }
107 dxg_setu32(buf, 0, pq & 0xffffffff); dxg_setu32(buf, 4, 1); dxg_setu64(buf, 8, alist as i64); dxg_setu64(buf, 16, plist as i64)
108 let ret: i64 = sys_ioctl(fd, DXG_MKRES, buf as i64); outf[0] = dxg_rd64(buf, 32); return ret
109}
110func dxg_create_hwqueue(fd: i64, context: i64, outq: *i64, outfgva: *i64, outfcva: *i64) -> i64 {
111 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
112 dxg_setu32(buf, 0, context & 0xffffffff)
113 let ret: i64 = sys_ioctl(fd, DXG_CHWQ, buf as i64)
114 outq[0] = dxg_rd32(buf, 24); outfcva[0] = dxg_rd64(buf, 32); outfgva[0] = dxg_rd64(buf, 40)
115 return ret
116}
117// the adapter's memory budget for a segment group, READ from the device: out[0]=budget out[1]=current_usage
118// out[2]=current_reservation out[3]=available_for_reservation; returns the ioctl ret (0 = the numbers are real)
119func dxg_query_vidmem(fd: i64, adapter: i64, group: i64, out: *i64) -> i64 {
120 let buf: *u8 = sys_mmap(256); var bz: i64 = 0; while bz < 256 { buf[bz] = 0 as u8; bz = bz + 1 }
121 dxg_setu32(buf, 8, adapter & 0xffffffff); dxg_setu32(buf, 12, group)
122 let ret: i64 = sys_ioctl(fd, DXG_QVMI, buf as i64)
123 out[0] = dxg_rd64(buf, 16); out[1] = dxg_rd64(buf, 24); out[2] = dxg_rd64(buf, 32); out[3] = dxg_rd64(buf, 40)
124 sys_munmap(buf, 256)
125 return ret
126}
127// evict one allocation from GPU-accessible memory; outtrim[0] = num_bytes_to_trim the device reports
128func dxg_evict(fd: i64, device: i64, alloc: i64, outtrim: *i64) -> i64 {
129 let alist: *u8 = sys_mmap(64); dxg_setu32(alist, 0, alloc & 0xffffffff)
130 let buf: *u8 = sys_mmap(256); var bz: i64 = 0; while bz < 256 { buf[bz] = 0 as u8; bz = bz + 1 }
131 dxg_setu32(buf, 0, device & 0xffffffff); dxg_setu32(buf, 4, 1); dxg_setu64(buf, 8, alist as i64)
132 let ret: i64 = sys_ioctl(fd, DXG_EVICT, buf as i64)
133 outtrim[0] = dxg_rd64(buf, 24)
134 sys_munmap(alist, 64); sys_munmap(buf, 256)
135 return ret
136}
137// create a sync object of `stype` on `device`: sync_object -> outso[0], fence_cpu_va -> outfva[0]; returns the ioctl ret
138func dxg_create_so(fd: i64, device: i64, stype: i64, outso: *i64, outfva: *i64) -> i64 {
139 let buf: *u8 = sys_mmap(512); var bz: i64 = 0; while bz < 512 { buf[bz] = 0 as u8; bz = bz + 1 }
140 dxg_setu32(buf, 0, device & 0xffffffff); dxg_setu32(buf, 8, stype)
141 let ret: i64 = sys_ioctl(fd, DXG_CSO, buf as i64); outso[0] = dxg_rd32(buf, 88); outfva[0] = dxg_rd64(buf, 24)
142 sys_munmap(buf, 512)
143 return ret
144}
145// signal `count` object(s) (one here) to `val` from the CPU; fire-and-forget on the host side
146func dxg_signal_cpu(fd: i64, device: i64, so: i64, count: i64, val: i64) -> i64 {
147 let objs: *u8 = sys_mmap(64); dxg_setu32(objs, 0, so & 0xffffffff)
148 let vals: *u8 = sys_mmap(64); dxg_setu64(vals, 0, val)
149 let buf: *u8 = sys_mmap(128); var bz: i64 = 0; while bz < 128 { buf[bz] = 0 as u8; bz = bz + 1 }
150 dxg_setu32(buf, 0, device & 0xffffffff); dxg_setu32(buf, 4, count); dxg_setu64(buf, 8, objs as i64); dxg_setu64(buf, 16, vals as i64)
151 let ret: i64 = sys_ioctl(fd, DXG_SIGCPU, buf as i64)
152 sys_munmap(objs, 64); sys_munmap(vals, 64); sys_munmap(buf, 128)
153 return ret
154}
155// synchronous CPU wait for `so` to reach `val` (async_event@24 = 0). Only ever call for a value a signal has reached
156// or will reach: the host validates the handle before it blocks, so a bogus handle returns negative instead of hanging.
157func dxg_wait_cpu(fd: i64, device: i64, so: i64, val: i64) -> i64 {
158 let objs: *u8 = sys_mmap(64); dxg_setu32(objs, 0, so & 0xffffffff)
159 let vals: *u8 = sys_mmap(64); dxg_setu64(vals, 0, val)
160 let buf: *u8 = sys_mmap(128); var bz: i64 = 0; while bz < 128 { buf[bz] = 0 as u8; bz = bz + 1 }
161 dxg_setu32(buf, 0, device & 0xffffffff); dxg_setu32(buf, 4, 1); dxg_setu64(buf, 8, objs as i64); dxg_setu64(buf, 16, vals as i64)
162 let ret: i64 = sys_ioctl(fd, DXG_WAITCPU, buf as i64)
163 sys_munmap(objs, 64); sys_munmap(vals, 64); sys_munmap(buf, 128)
164 return ret
165}
166func dxg_submit_hwq(fd: i64, hwqueue: i64, cmd_va: i64, cmd_len: i64, fence_id: i64) -> i64 {
167 let buf: *u8 = sys_mmap(256); var bz: i64 = 0; while bz < 256 { buf[bz] = 0 as u8; bz = bz + 1 }
168 dxg_setu32(buf, 0, hwqueue & 0xffffffff); dxg_setu64(buf, 8, fence_id); dxg_setu64(buf, 16, cmd_va); dxg_setu32(buf, 24, cmd_len & 0xffffffff)
169 let ret: i64 = sys_ioctl(fd, DXG_SUBHWQ, buf as i64)
170 return ret
171}