master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_drm.c
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) &paramsGlobal,
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}