code wiki / _hdl_build / _gpu_dxg_r1_recon.nx
_gpu_dxg_r1_recon.nx source
↩ module page · 205 lines · 11048 B
1// _gpu_dxg_r1_recon.nx -- SCOPE/RECON probe for SOVEREIGN-GPU rung R1: adapter DESCRIPTION +
2// positive RTX 5080 ID via LX_DXQUERYADAPTERINFO(KMTQAITYPE_ADAPTERREGISTRYINFO). RAW syscalls
3// ONLY to /dev/dxg (no libvulkan/libcuda/libdxcore/libd3d12/libc). Reuses the R0 enum mechanism.
4//
5// What this recon PINS LIVE (the kernel rejects a wrong code with -ENOTTY=-25 and a wrong
6// size/type with -EINVAL=-22, so we probe before claiming):
7// 1. The exact LX_DXQUERYADAPTERINFO ioctl code. Encoding (same _IOWR(0x47,...) family as R0):
8// code = (3<<30) | (24<<16) | (0x47<<8) | nr for a 24-byte struct.
9// We sweep nr over a small range and report which nr the kernel ACCEPTS (ret==0) vs -ENOTTY.
10// 2. struct d3dkmt_queryadapterinfo (24B): adapter_handle u32@0 / type u32@4 / private_data u64@8 /
11// private_data_size u32@16 / reserved u32@20.
12// 3. KMTQAITYPE_ADAPTERREGISTRYINFO type value (expected 8) -- we sweep a few type values and
13// report which returns a real UTF-16 string.
14// 4. Whether QueryAdapterInfo works on the ENUM handle directly, or needs LX_DXOPENADAPTERFROMLUID
15// first. We try BOTH paths and report which path produced the real string.
16// 5. The ACTUAL UTF-16LE adapter_string each adapter returned (decoded to ASCII for printing) so
17// we can positively distinguish the RTX 5080 from the WSL/Microsoft software render adapter.
18//
19// NO-WAVE: reach + identify only. No throughput/exceed.
20// AUTHOR=ORGAN / NO-FALSE-GREEN: every printed string is the REAL bytes the kernel wrote into
21// private_data, never a literal. A wrong handle/type goes to a real errno, reported as-is.
22// license_tier: ORIGINAL
23import "nx_syscalls.nx"
24
25const DXG_LX_DXENUMADAPTERS2: i64 = 0xC0104714
26
27// d3dkmt_adapterregistryinfo layout (UTF-16, MAX_PATH=260):
28// adapter_string u16[260] @0 (520B) <- the device name e.g. "NVIDIA GeForce RTX 5080..."
29// bios_major u32 @520
30// bios_minor u32 @524
31// dac_type u16[260]@528 (520B)
32// chip_type u16[260]@1048 (520B)
33// total = 1568 bytes. We mmap a page (4096) for it, plenty.
34const QAI_PRIV_SIZE: i64 = 1568
35
36func p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
37func n(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
38func x(v: i64) -> i64 { p("0x" as *u8); let bb:*u8=sys_mmap(20); var k:i64=0; var m:i64=v; if m==0{bb[0]=48;k=1}; while m>0{ let d:i64=m&15; if d<10{bb[k]=(48+d) as u8}else{bb[k]=(87+d) as u8}; m=(m>>4); k=k+1 } var i:i64=0; let o:*u8=sys_mmap(20); while i<k{o[i]=bb[k-1-i];i=i+1} sys_write(1,o,k); return 0 }
39
40// Build a query ioctl code for a given nr (24-byte struct, _IOWR(0x47,nr,..)).
41func qai_code(nr: i64) -> i64 { return (3<<30) | (24<<16) | (0x47<<8) | nr }
42
43// Decode a UTF-16LE string (NUL-terminated) at base+off into ASCII printed to stdout.
44// Prints '?' for any code unit > 0x7e. Returns the number of code units printed.
45func print_utf16(base: *u8, off: i64) -> i64 {
46 let out: *u8 = sys_mmap(600)
47 var i: i64 = 0
48 var cnt: i64 = 0
49 while i < 260 {
50 let lo: i64 = base[off + i*2] as i64
51 let hi: i64 = base[off + i*2 + 1] as i64
52 let cu: i64 = lo | (hi << 8)
53 if cu == 0 { i = 260 }
54 else {
55 if cu >= 0x20 { if cu <= 0x7e { out[cnt] = cu as u8 } else { out[cnt] = 63 as u8 } }
56 else { out[cnt] = 63 as u8 }
57 cnt = cnt + 1
58 i = i + 1
59 }
60 }
61 sys_write(1, out, cnt)
62 return cnt
63}
64
65// Issue LX_DXQUERYADAPTERINFO(type) on `handle` via fd. priv must be a page-sized zeroed buffer.
66// Returns the ioctl ret (0 = kernel accepted).
67func query_adapter_info(fd: i64, code: i64, handle: i64, qtype: i64, priv: *u8) -> i64 {
68 // zero priv first 1600 bytes
69 var z: i64 = 0; while z < 1600 { priv[z] = 0 as u8; z = z + 1 }
70 // struct d3dkmt_queryadapterinfo (24B)
71 let req: *u8 = sys_mmap(64)
72 var y: i64 = 0; while y < 64 { req[y] = 0 as u8; y = y + 1 }
73 let h: *i64 = (req as i64 + 0) as *i64
74 // adapter_handle u32@0 + type u32@4 packed as one i64 @0
75 let lo32: i64 = handle & 0xffffffff
76 let hi32: i64 = qtype & 0xffffffff
77 h[0] = lo32 | (hi32 << 32)
78 let pd: *i64 = (req as i64 + 8) as *i64
79 pd[0] = priv as i64 // private_data ptr @8
80 let pds: *i64 = (req as i64 + 16) as *i64
81 pds[0] = QAI_PRIV_SIZE // private_data_size u32@16 (reserved u32@20 = 0)
82 return sys_ioctl(fd, code, req as i64)
83}
84
85// LX_DXOPENADAPTERFROMLUID: open an adapter handle from a LUID.
86// struct d3dkmt_openadapterfromluid { winluid luid(8); d3dkmthandle adapter_handle(4) [OUT]; } 16B
87// _IOWR(0x47, nr, 16B) = (3<<30)|(16<<16)|(0x47<<8)|nr
88// Returns the OUT handle (>0) on success, or -errno (negative). We sweep nr to find the accepted one.
89func open_from_luid(fd: i64, nr: i64, luid_a: i64, luid_b: i64, out_ret: *i64) -> i64 {
90 let code: i64 = (3<<30) | (16<<16) | (0x47<<8) | nr
91 let req: *u8 = sys_mmap(64)
92 var y: i64 = 0; while y < 64 { req[y] = 0 as u8; y = y + 1 }
93 let la: *i64 = (req as i64 + 0) as *i64
94 la[0] = (luid_a & 0xffffffff) | ((luid_b & 0xffffffff) << 32) // winluid @0 (8B)
95 let ret: i64 = sys_ioctl(fd, code, req as i64)
96 out_ret[0] = ret
97 // OUT handle at offset 8 (after 8-byte luid)
98 let h: *i64 = (req as i64 + 8) as *i64
99 return h[0] & 0xffffffff
100}
101
102func main() -> i64 {
103 p("=== R1 RECON: LX_DXQUERYADAPTERINFO adapter-description probe (raw /dev/dxg) ===\n" as *u8)
104
105 let fd: i64 = sys_openat_rd("/dev/dxg" as *u8)
106 p("open(/dev/dxg) fd=" as *u8); n(fd); p("\n" as *u8)
107 if fd < 0 { p("RECON ABORT: open failed\n" as *u8); sys_exit(1); return 1 }
108
109 // ---- (A) Reuse R0 enum to get adapter handles + LUIDs ----
110 let ainfo: *u8 = sys_mmap(4096)
111 var z: i64 = 0; while z < 4096 { ainfo[z] = 0 as u8; z = z + 1 }
112 let ereq: *u8 = sys_mmap(64)
113 var y: i64 = 0; while y < 64 { ereq[y] = 0 as u8; y = y + 1 }
114 ereq[0] = 8 as u8 // capacity = 8
115 let r8: *i64 = (ereq as i64 + 8) as *i64
116 r8[0] = ainfo as i64
117 let eret: i64 = sys_ioctl(fd, DXG_LX_DXENUMADAPTERS2, ereq as i64)
118 let nc: i64 = (ereq[0] as i64)|((ereq[1] as i64)<<8)|((ereq[2] as i64)<<16)|((ereq[3] as i64)<<24)
119 p("ENUM2 ret=" as *u8); n(eret); p(" num_adapters=" as *u8); n(nc); p("\n" as *u8)
120 if eret != 0 { p("RECON ABORT: enum failed\n" as *u8); sys_exit(1); return 1 }
121
122 // adapterinfo stride 20B: handle@0(u32) luid(a,b)@4(8B) num_sources@12 present_move@16
123 var ai: i64 = 0
124 while ai < nc {
125 let base: i64 = ai * 20
126 let h: i64 = (ainfo[base] as i64)|((ainfo[base+1] as i64)<<8)|((ainfo[base+2] as i64)<<16)|((ainfo[base+3] as i64)<<24)
127 let la: i64 = (ainfo[base+4] as i64)|((ainfo[base+5] as i64)<<8)|((ainfo[base+6] as i64)<<16)|((ainfo[base+7] as i64)<<24)
128 let lb: i64 = (ainfo[base+8] as i64)|((ainfo[base+9] as i64)<<8)|((ainfo[base+10] as i64)<<16)|((ainfo[base+11] as i64)<<24)
129 p(" adapter[" as *u8); n(ai); p("] handle=" as *u8); x(h); p(" luid.a=" as *u8); x(la); p(" luid.b=" as *u8); x(lb); p("\n" as *u8)
130 ai = ai + 1
131 }
132
133 // ---- (B) PIN the QueryAdapterInfo nr: sweep nr 0x01..0x14, type=8, on adapter[0] enum handle ----
134 let h0: i64 = (ainfo[0] as i64)|((ainfo[1] as i64)<<8)|((ainfo[2] as i64)<<16)|((ainfo[3] as i64)<<24)
135 let priv: *u8 = sys_mmap(4096)
136 p("\n-- nr sweep (type=8 KMTQAITYPE_ADAPTERREGISTRYINFO, enum handle=" as *u8); x(h0); p(") --\n" as *u8)
137 var nr: i64 = 1
138 var accepted_nr: i64 = -1
139 while nr <= 0x14 {
140 let code: i64 = qai_code(nr)
141 let qret: i64 = query_adapter_info(fd, code, h0, 8, priv)
142 // only print the interesting ones (not -ENOTTY=-25) to keep output readable, but print all
143 p(" nr=" as *u8); x(nr); p(" code=" as *u8); x(code); p(" ret=" as *u8); n(qret)
144 if qret == 0 { p(" <== ACCEPTED" as *u8); if accepted_nr < 0 { accepted_nr = nr } }
145 if qret == -22 { p(" (EINVAL: right nr, wrong handle/type?)" as *u8); if accepted_nr < 0 { accepted_nr = nr } }
146 p("\n" as *u8)
147 nr = nr + 1
148 }
149
150 p("\nACCEPTED_QUERY_NR=" as *u8); x(accepted_nr); p("\n" as *u8)
151
152 // ---- (C) With the accepted nr, query each adapter's registry info, decode the UTF-16 string ----
153 if accepted_nr >= 0 {
154 let code: i64 = qai_code(accepted_nr)
155 p("\n-- adapter description strings (code=" as *u8); x(code); p(", type=8) --\n" as *u8)
156 var a2: i64 = 0
157 while a2 < nc {
158 let base: i64 = a2 * 20
159 let hh: i64 = (ainfo[base] as i64)|((ainfo[base+1] as i64)<<8)|((ainfo[base+2] as i64)<<16)|((ainfo[base+3] as i64)<<24)
160 let qret: i64 = query_adapter_info(fd, code, hh, 8, priv)
161 p(" adapter[" as *u8); n(a2); p("] (enum handle=" as *u8); x(hh); p(") query ret=" as *u8); n(qret)
162 if qret == 0 {
163 p(" string=\"" as *u8); print_utf16(priv, 0); p("\"" as *u8)
164 }
165 p("\n" as *u8)
166 a2 = a2 + 1
167 }
168 } else {
169 p("\nNO nr accepted on enum handle directly -- will need OpenAdapterFromLuid path.\n" as *u8)
170 }
171
172 // ---- (D) ALSO probe OpenAdapterFromLuid path (sweep nr), then query the opened handle ----
173 p("\n-- OpenAdapterFromLuid nr sweep (adapter[0] luid) --\n" as *u8)
174 let la0: i64 = (ainfo[4] as i64)|((ainfo[5] as i64)<<8)|((ainfo[6] as i64)<<16)|((ainfo[7] as i64)<<24)
175 let lb0: i64 = (ainfo[8] as i64)|((ainfo[9] as i64)<<8)|((ainfo[10] as i64)<<16)|((ainfo[11] as i64)<<24)
176 let oret_box: *i64 = sys_mmap(16)
177 var onr: i64 = 1
178 var opened_handle: i64 = 0
179 var opened_via_nr: i64 = -1
180 while onr <= 0x14 {
181 let oh: i64 = open_from_luid(fd, onr, la0, lb0, oret_box)
182 let oret: i64 = oret_box[0]
183 if oret == 0 {
184 p(" open nr=" as *u8); x(onr); p(" ret=0 OUT_handle=" as *u8); x(oh); p(" <== ACCEPTED\n" as *u8)
185 if opened_via_nr < 0 { opened_via_nr = onr; opened_handle = oh }
186 }
187 onr = onr + 1
188 }
189 if opened_via_nr >= 0 {
190 p("OPEN_FROM_LUID_NR=" as *u8); x(opened_via_nr); p(" handle=" as *u8); x(opened_handle); p("\n" as *u8)
191 if accepted_nr >= 0 {
192 let code: i64 = qai_code(accepted_nr)
193 let qret: i64 = query_adapter_info(fd, code, opened_handle, 8, priv)
194 p(" query via OPENED handle ret=" as *u8); n(qret)
195 if qret == 0 { p(" string=\"" as *u8); print_utf16(priv, 0); p("\"" as *u8) }
196 p("\n" as *u8)
197 }
198 } else {
199 p("OPEN_FROM_LUID: no nr accepted (enum handle path may be the only one).\n" as *u8)
200 }
201
202 sys_close(fd)
203 p("\n=== R1 RECON DONE ===\n" as *u8)
204 return 0
205}