ohmu
·
2026-10-15
1#include "gpu.h"
2
3#if FF_HAVE_DRM || __has_include(<drm/drm.h>)
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6
7 #include "common/io.h"
8 #include "common/mallocHelper.h"
9
10 #if FF_HAVE_DRM
11 #include <drm.h>
12 #include <radeon_drm.h>
13 #include <nouveau_drm.h>
14 #include <amdgpu_drm.h>
15 #else
16 #define FF_HAVE_DRM 1
17 #include <drm/drm.h>
18 #include <drm/radeon_drm.h>
19 #include <drm/nouveau_drm.h>
20 #include <drm/amdgpu_drm.h>
21 #endif
22 #include "intel_drm.h"
23 #include "asahi_drm.h"
24
25const char* ffDrmDetectRadeon(const FFGPUOptions* options, FFGPUResult* gpu, const char* renderPath) {
26 FF_AUTO_CLOSE_FD int fd = open(renderPath, O_RDONLY | O_CLOEXEC);
27 if (fd < 0) {
28 return "Failed to open DRM render device";
29 }
30
31 uint32_t value;
32
33 // https://github.com/torvalds/linux/blob/fb4d33ab452ea254e2c319bac5703d1b56d895bf/drivers/gpu/drm/radeon/radeon_kms.c#L231
34
35 if (ioctl(fd, DRM_IOCTL_RADEON_INFO, &(struct drm_radeon_info) {
36 .request = RADEON_INFO_ACTIVE_CU_COUNT,
37 .value = (uintptr_t) &value,
38 }) >= 0) {
39 gpu->coreCount = (int32_t) value;
40 }
41
42 if (options->temp) {
43 if (ioctl(fd, DRM_IOCTL_RADEON_INFO, &(struct drm_radeon_info) {
44 .request = RADEON_INFO_CURRENT_GPU_TEMP, // millidegrees C
45 .value = (uintptr_t) &value,
46 }) >= 0 &&
47 value != 0) { // 0 means unavailable
48 gpu->temperature = (double) value / 1000.0;
49 }
50 }
51
52 if (ioctl(fd, DRM_IOCTL_RADEON_INFO, &(struct drm_radeon_info) {
53 .request = RADEON_INFO_MAX_SCLK, // KHz
54 .value = (uintptr_t) &value,
55 }) >= 0) {
56 gpu->frequency = (uint32_t) (value / 1000u);
57 }
58
59 if (options->driverSpecific) {
60 struct drm_radeon_gem_info gemInfo;
61 if (ioctl(fd, DRM_IOCTL_RADEON_GEM_INFO, &gemInfo) >= 0) {
62 // vram_usage can be bigger than vram_size, so we use vram_size here
63 gpu->dedicated.total = gemInfo.vram_size;
64 gpu->shared.total = gemInfo.gart_size;
65
66 uint64_t memSize;
67 if (ioctl(fd, DRM_IOCTL_RADEON_INFO, &(struct drm_radeon_info) {
68 .request = RADEON_INFO_VRAM_USAGE, // uint64_t
69 .value = (uintptr_t) &memSize,
70 }) >= 0) {
71 gpu->dedicated.used = memSize;
72 }
73
74 if (ioctl(fd, DRM_IOCTL_RADEON_INFO, &(struct drm_radeon_info) {
75 .request = RADEON_INFO_GTT_USAGE, // uint64_t
76 .value = (uintptr_t) &memSize,
77 }) >= 0) {
78 gpu->shared.used = memSize;
79 }
80 }
81 }
82
83 return nullptr;
84}
85
86const char* ffDrmDetectAmdgpu(const FFGPUOptions* options, FFGPUResult* gpu, const char* renderPath) {
87 FF_AUTO_CLOSE_FD int fd = open(renderPath, O_RDONLY | O_CLOEXEC);
88 if (fd < 0) {
89 return "Failed to open DRM render device";
90 }
91
92 uint32_t value;
93
94 if (options->temp) {
95 if (ioctl(fd, DRM_IOCTL_AMDGPU_INFO,
96 &(struct drm_amdgpu_info) {
97 .return_pointer = (uintptr_t) &value,
98 .return_size = sizeof(value),
99 .query = AMDGPU_INFO_SENSOR,
100 .query_hw_ip.type = AMDGPU_INFO_SENSOR_GPU_TEMP,
101 }) >= 0 && value != 0) {
102 gpu->temperature = value / 1000.;
103 }
104 }
105
106 struct drm_amdgpu_info_device devInfo;
107 if (ioctl(fd, DRM_IOCTL_AMDGPU_INFO,
108 &(struct drm_amdgpu_info) {
109 .return_pointer = (uintptr_t) &devInfo,
110 .return_size = sizeof(devInfo),
111 .query = AMDGPU_INFO_DEV_INFO,
112 }) >= 0) {
113 gpu->coreCount = (int32_t) devInfo.cu_active_number;
114 gpu->frequency = (uint32_t) (devInfo.max_engine_clock / 1000u);
115 gpu->index = FF_GPU_INDEX_UNSET;
116 gpu->type = devInfo.ids_flags & AMDGPU_IDS_FLAGS_FUSION ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
117 // Was `_pad`. Introduced in commit https://github.com/sailfishos-mirror/drm/commit/22b698a5990292bce0eeb2782754d1eba3fe7a2e
118 #ifdef AMDGPU_FAMILY_GC_11_0_0
119 gpu->psMax.gen = (uint16_t) devInfo.pcie_gen;
120 gpu->psMax.lanes = (uint16_t) devInfo.pcie_num_lanes;
121 #else
122 gpu->psMax.gen = (uint16_t) devInfo._pad;
123 gpu->psMax.lanes = (uint16_t) devInfo._pad1;
124 #endif
125 #define FF_VRAM_CASE(name, value) \
126 case value /* AMDGPU_VRAM_TYPE_ ## name */: \
127 ffStrbufSetStatic(&gpu->memoryType, #name); \
128 break
129 switch (devInfo.vram_type) {
130 FF_VRAM_CASE(UNKNOWN, 0);
131 FF_VRAM_CASE(GDDR1, 1);
132 FF_VRAM_CASE(DDR2, 2);
133 FF_VRAM_CASE(GDDR3, 3);
134 FF_VRAM_CASE(GDDR4, 4);
135 FF_VRAM_CASE(GDDR5, 5);
136 FF_VRAM_CASE(HBM, 6);
137 FF_VRAM_CASE(DDR3, 7);
138 FF_VRAM_CASE(DDR4, 8);
139 FF_VRAM_CASE(GDDR6, 9);
140 FF_VRAM_CASE(DDR5, 10);
141 FF_VRAM_CASE(LPDDR4, 11);
142 FF_VRAM_CASE(LPDDR5, 12);
143 FF_VRAM_CASE(HBM3E, 13);
144 FF_VRAM_CASE(HBM4, 14);
145 default:
146 ffStrbufAppendF(&gpu->memoryType, "Unknown (%u)", devInfo.vram_type);
147 break;
148 }
149 #undef FF_VRAM_CASE
150 }
151
152 struct drm_amdgpu_memory_info memInfo;
153 if (ioctl(fd, DRM_IOCTL_AMDGPU_INFO,
154 &(struct drm_amdgpu_info) {
155 .return_pointer = (uintptr_t) &memInfo,
156 .return_size = sizeof(memInfo),
157 .query = AMDGPU_INFO_MEMORY,
158 }) >= 0) {
159 gpu->dedicated.total = memInfo.vram.total_heap_size;
160 gpu->dedicated.used = memInfo.vram.heap_usage;
161 gpu->shared.total = memInfo.gtt.total_heap_size;
162 gpu->shared.used = memInfo.gtt.heap_usage;
163 }
164
165 if (ioctl(fd, DRM_IOCTL_AMDGPU_INFO,
166 &(struct drm_amdgpu_info) {
167 .return_pointer = (uintptr_t) &value,
168 .return_size = sizeof(value),
169 .query = AMDGPU_INFO_SENSOR,
170 .query_hw_ip.type = AMDGPU_INFO_SENSOR_GPU_LOAD,
171 }) >= 0) {
172 gpu->coreUsage = value;
173 }
174
175 return nullptr;
176}
177
178const char* ffDrmDetectI915(FFGPUResult* gpu, int fd) {
179 {
180 int value;
181 drm_i915_getparam_t getparam = { .param = I915_PARAM_EU_TOTAL, .value = &value };
182 if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &getparam) >= 0) {
183 gpu->coreCount = value;
184 }
185 }
186 {
187 struct drm_i915_query_item queryItem = {
188 .query_id = DRM_I915_QUERY_MEMORY_REGIONS,
189 };
190 struct drm_i915_query query = {
191 .items_ptr = (uintptr_t) &queryItem,
192 .num_items = 1,
193 };
194 if (ioctl(fd, DRM_IOCTL_I915_QUERY, &query) >= 0 && queryItem.length > 0) { // #2259
195 FF_AUTO_FREE uint8_t* buffer = calloc(1, (size_t) queryItem.length);
196 queryItem.data_ptr = (uintptr_t) buffer;
197 if (ioctl(fd, DRM_IOCTL_I915_QUERY, &query) >= 0) {
198 gpu->dedicated.total = gpu->shared.total = gpu->dedicated.used = gpu->shared.used = 0;
199 struct drm_i915_query_memory_regions* regionInfo = (void*) buffer;
200 for (uint32_t i = 0; i < regionInfo->num_regions; i++) {
201 struct drm_i915_memory_region_info* region = regionInfo->regions + i;
202 switch (region->region.memory_class) {
203 case I915_MEMORY_CLASS_SYSTEM:
204 gpu->shared.total += region->probed_size;
205 gpu->shared.used += region->probed_size - region->unallocated_size;
206 break;
207 case I915_MEMORY_CLASS_DEVICE:
208 gpu->dedicated.total += region->probed_size;
209 gpu->dedicated.used += region->probed_size - region->unallocated_size;
210 break;
211 }
212 }
213 }
214 }
215 }
216 return nullptr;
217}
218
219static inline int popcountBytes(uint8_t* bytes, uint32_t length) {
220 int count = 0;
221 while (length >= 8) {
222 count += __builtin_popcountll(*(uint64_t*) bytes);
223 bytes += 8;
224 length -= 8;
225 }
226 if (length >= 4) {
227 count += __builtin_popcountl(*(uint32_t*) bytes);
228 bytes += 4;
229 length -= 4;
230 }
231 if (length >= 2) {
232 count += __builtin_popcount(*(uint16_t*) bytes);
233 bytes += 2;
234 length -= 2;
235 }
236 if (length) {
237 count += __builtin_popcount(*(uint8_t*) bytes);
238 }
239 return count;
240}
241
242const char* ffDrmDetectXe(FFGPUResult* gpu, int fd) {
243 bool flag = false;
244 {
245 struct drm_xe_device_query query = {
246 .query = DRM_XE_DEVICE_QUERY_GT_TOPOLOGY,
247 };
248 if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) >= 0) {
249 FF_AUTO_FREE uint8_t* buffer = malloc(query.size);
250 query.data = (uintptr_t) buffer;
251 if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) >= 0) {
252 int dssCount = 0, euPerDssCount = 0;
253 for (struct drm_xe_query_topology_mask* topo = (void*) buffer;
254 (uint8_t*) topo < buffer + query.size;
255 topo = (void*) (topo->mask + topo->num_bytes)) {
256 switch (topo->type) {
257 case DRM_XE_TOPO_DSS_COMPUTE:
258 case DRM_XE_TOPO_DSS_GEOMETRY:
259 dssCount += popcountBytes(topo->mask, topo->num_bytes);
260 break;
261 case DRM_XE_TOPO_EU_PER_DSS:
262 euPerDssCount += popcountBytes(topo->mask, topo->num_bytes);
263 break;
264 }
265 }
266 gpu->coreCount = dssCount * euPerDssCount;
267 flag = true;
268 }
269 }
270 }
271
272 {
273 struct drm_xe_device_query query = {
274 .query = DRM_XE_DEVICE_QUERY_MEM_REGIONS,
275 };
276 if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) >= 0) {
277 FF_AUTO_FREE uint8_t* buffer = malloc(query.size);
278 query.data = (uintptr_t) buffer;
279 if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) >= 0) {
280 gpu->dedicated.total = gpu->shared.total = gpu->dedicated.used = gpu->shared.used = 0;
281 struct drm_xe_query_mem_regions* regionInfo = (void*) buffer;
282 for (uint32_t i = 0; i < regionInfo->num_mem_regions; i++) {
283 struct drm_xe_mem_region* region = regionInfo->mem_regions + i;
284 switch (region->mem_class) {
285 case DRM_XE_MEM_REGION_CLASS_SYSMEM:
286 gpu->shared.total += region->total_size;
287 gpu->shared.used += region->used;
288 break;
289 case DRM_XE_MEM_REGION_CLASS_VRAM:
290 gpu->dedicated.total += region->total_size;
291 gpu->dedicated.used += region->used;
292 break;
293 }
294 }
295 flag = true;
296 }
297 }
298 }
299 return flag ? nullptr : "Failed to query Xe GPU information";
300}
301
302const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd) {
303 struct drm_asahi_params_global paramsGlobal = {};
304 if (ioctl(fd, DRM_IOCTL_ASAHI_GET_PARAMS, &(struct drm_asahi_get_params) {
305 .param_group = DRM_ASAHI_GET_PARAMS,
306 .pointer = (uintptr_t) ¶msGlobal,
307 .size = sizeof(paramsGlobal),
308 }) >= 0) {
309 // They removed `unstable_uabi_version` from the struct. Hopefully they won't introduce new ABI changes.
310 assert(paramsGlobal.num_clusters_total <= DRM_ASAHI_MAX_CLUSTERS);
311 gpu->coreCount = 0;
312 for (uint32_t i = 0; i < paramsGlobal.num_clusters_total; i++) {
313 gpu->coreCount += __builtin_popcountll(paramsGlobal.core_masks[i]);
314 }
315 gpu->frequency = paramsGlobal.max_frequency_khz / 1000;
316 gpu->deviceId = ffGPUGeneral2Id(paramsGlobal.chip_id);
317
318 if (!gpu->name.length) {
319 const char* variant = " Unknown";
320 switch (paramsGlobal.gpu_variant) {
321 case 'G':
322 variant = "";
323 break;
324 case 'S':
325 variant = " Pro";
326 break;
327 case 'C':
328 variant = " Max";
329 break;
330 case 'D':
331 variant = " Ultra";
332 break;
333 }
334 ffStrbufSetF(&gpu->name, "Apple M%d%s (G%d%c %02X)", paramsGlobal.gpu_generation - 12, variant, paramsGlobal.gpu_generation, paramsGlobal.gpu_variant, paramsGlobal.gpu_revision + 0xA0);
335 }
336
337 return nullptr;
338 }
339
340 return "Failed to query Asahi GPU information";
341}
342
343 #ifndef DRM_IOCTL_NOUVEAU_GETPARAM
344 #define DRM_IOCTL_NOUVEAU_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GETPARAM, struct drm_nouveau_getparam)
345 #endif
346
347const char* ffDrmDetectNouveau(FFGPUResult* gpu, int fd) {
348 struct drm_nouveau_getparam getparam = {};
349
350 getparam.param = NOUVEAU_GETPARAM_FB_SIZE;
351 if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0) {
352 gpu->dedicated.total = getparam.value;
353 }
354
355 getparam.param = NOUVEAU_GETPARAM_AGP_SIZE;
356 if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0) {
357 gpu->shared.total = getparam.value;
358 }
359
360 getparam.param = NOUVEAU_GETPARAM_GRAPH_UNITS;
361 if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0 && getparam.value < INT32_MAX) {
362 gpu->coreCount = (int32_t) getparam.value;
363 }
364
365 return nullptr;
366}
367
368#endif // FF_HAVE_DRM
369
370#include "gpu_driver_specific.h"
371
372const char* ffGPUDetectDriverSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFGpuDriverPciBusId pciBusId) {
373#if !__OpenBSD__
374 typeof(&ffDetectNvidiaGpuInfo) detectFn;
375 const char* soName;
376 if (getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &soName) && (options->temp || options->driverSpecific)) {
377 return detectFn(&(FFGpuDriverCondition) {
378 .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID,
379 .pciBusId = pciBusId,
380 },
381 (FFGpuDriverResult) {
382 .index = &gpu->index,
383 .temp = options->temp ? &gpu->temperature : nullptr,
384 .memory = options->driverSpecific ? &gpu->dedicated : nullptr,
385 .sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
386 .memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
387 .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
388 .coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
389 .type = &gpu->type,
390 .frequency = options->driverSpecific ? &gpu->frequency : nullptr,
391 .name = &gpu->name,
392 .psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
393 .psMax = options->driverSpecific ? &gpu->psMax : nullptr,
394 },
395 soName);
396 }
397#else
398 FF_UNUSED(options, gpu, pciBusId);
399#endif
400
401 return "No driver-specific detection function found for the GPU vendor";
402}