Files
fastfetch/src/detection/gpu/gpu_linux.c
T

659 lines
23 KiB
C
Raw Normal View History

2023-02-14 17:50:53 +08:00
#include "detection/gpu/gpu.h"
2026-01-06 09:48:38 +08:00
#include "common/io.h"
#include "common/FFstrbuf.h"
2026-05-28 15:58:08 +08:00
#include "common/strutil.h"
2026-01-05 16:19:47 +08:00
#include "modules/gpu/option.h"
2022-09-05 17:42:42 +02:00
#include <inttypes.h>
#include <stdint.h>
2022-09-05 17:42:42 +02:00
2026-04-09 16:20:59 +08:00
static bool pciDetectDriver(FFstrbuf* result, FFstrbuf* pciDir, FFstrbuf* buffer, FF_A_UNUSED const char* drmKey) {
uint32_t pciDirLength = pciDir->length;
ffStrbufAppendS(pciDir, "/driver");
char pathBuf[PATH_MAX];
2024-10-18 16:15:48 +08:00
ssize_t resultLength = readlink(pciDir->chars, pathBuf, ARRAY_SIZE(pathBuf));
2026-03-29 09:28:49 +08:00
if (resultLength <= 0) {
return false;
}
const char* slash = memrchr(pathBuf, '/', (size_t) resultLength);
2026-03-29 09:28:49 +08:00
if (slash) {
slash++;
ffStrbufSetNS(result, (uint32_t) (resultLength - (slash - pathBuf)), slash);
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(result, "nvidia")) {
if (ffReadFileBuffer("/proc/driver/nvidia/version", buffer)) {
if (ffStrbufContainS(buffer, " Open ")) {
ffStrbufAppendS(result, " (open source)");
2026-03-29 09:28:49 +08:00
} else {
ffStrbufAppendS(result, " (proprietary)");
2026-03-29 09:28:49 +08:00
}
}
}
2026-03-29 09:28:49 +08:00
if (instance.config.general.detectVersion) {
ffStrbufAppendS(pciDir, "/module/version");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
ffStrbufTrimRightSpace(buffer);
ffStrbufAppendC(result, ' ');
ffStrbufAppend(result, buffer);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(result, "zx")) {
ffStrbufSubstrBefore(pciDir, pciDirLength);
ffStrbufAppendS(pciDir, "/zx_info/driver_version");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
ffStrbufTrimRightSpace(buffer);
ffStrbufAppendC(result, ' ');
ffStrbufAppend(result, buffer);
}
}
}
2024-05-19 19:34:48 +08:00
return true;
2023-01-12 11:41:07 +01:00
}
2026-04-09 16:20:59 +08:00
FF_A_UNUSED static const char* drmFindRenderFromCard(const char* drmCardKey, FFstrbuf* result) {
char path[PATH_MAX];
sprintf(path, "/sys/class/drm/%s/device/drm", drmCardKey);
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(path);
2026-03-29 09:28:49 +08:00
if (!dirp) {
return "Failed to open `/sys/class/drm/{drmCardKey}/device/drm`";
}
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (ffStrStartsWith(entry->d_name, "render")) {
ffStrbufSetS(result, "/dev/dri/");
ffStrbufAppendS(result, entry->d_name);
return NULL;
}
}
return "Failed to find render device";
}
2026-03-29 09:28:49 +08:00
static const char* drmDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult* gpu, const char* drmKey, FFstrbuf* buffer) {
const char* error = drmFindRenderFromCard(drmKey, buffer);
2026-03-29 09:28:49 +08:00
if (error) {
return error;
}
if (ffStrbufEqualS(&gpu->driver, "radeon")) {
return ffDrmDetectRadeon(options, gpu, buffer->chars);
2026-03-29 09:28:49 +08:00
} else {
return ffDrmDetectAmdgpu(options, gpu, buffer->chars);
}
}
2026-03-29 09:28:49 +08:00
static void pciDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) {
// https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#mem-info-vis-vram-total
const uint32_t pciDirLen = pciDir->length;
ffStrbufAppendS(pciDir, "/hwmon/");
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars);
2026-03-29 09:28:49 +08:00
if (!dirp) {
return;
}
2024-05-03 08:28:20 +08:00
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
continue;
}
break;
}
2026-03-29 09:28:49 +08:00
if (!entry) {
return;
}
ffStrbufAppendS(pciDir, entry->d_name);
ffStrbufAppendC(pciDir, '/');
const uint32_t hwmonLen = pciDir->length;
uint64_t value = 0;
2026-03-29 09:28:49 +08:00
if (options->temp) {
ffStrbufAppendS(pciDir, "temp1_input"); // The on die GPU temperature in millidegrees Celsius
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) {
2024-04-29 20:43:19 +08:00
gpu->temperature = (double) value / 1000;
2026-03-29 09:28:49 +08:00
}
}
if (ffStrbufEqualS(&gpu->driver, "amdgpu")) // Ancient radeon drivers don't have these files
{
ffStrbufSubstrBefore(pciDir, hwmonLen);
ffStrbufAppendS(pciDir, "in1_input"); // Northbridge voltage in millivolts (APUs only)
2026-03-29 09:28:49 +08:00
if (ffPathExists(pciDir->chars, FF_PATHTYPE_ANY)) {
gpu->type = FF_GPU_TYPE_INTEGRATED;
2026-03-29 09:28:49 +08:00
} else {
gpu->type = FF_GPU_TYPE_DISCRETE;
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (options->driverSpecific) {
ffStrbufSubstrBefore(pciDir, pciDirLen);
ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) {
if (gpu->type == FF_GPU_TYPE_DISCRETE) {
gpu->dedicated.total = value;
2026-03-29 09:28:49 +08:00
} else {
gpu->shared.total = value;
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDir->length - (uint32_t) strlen("/mem_info_vis_vram_total"));
ffStrbufAppendS(pciDir, "/mem_info_vis_vram_used");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) {
if (gpu->type == FF_GPU_TYPE_DISCRETE) {
gpu->dedicated.used = value;
2026-03-29 09:28:49 +08:00
} else {
gpu->shared.used = value;
2026-03-29 09:28:49 +08:00
}
}
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
ffStrbufAppendS(pciDir, "/gpu_busy_percent");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) {
gpu->coreUsage = (double) value;
2026-03-29 09:28:49 +08:00
}
}
}
}
2026-03-29 09:28:49 +08:00
static void pciDetectIntelSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer, const char* drmKey) {
// Works for Intel GPUs
// https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/
2024-09-23 20:40:36 +08:00
// 0000:00:02.0 is reserved for Intel integrated graphics
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
2026-03-29 09:28:49 +08:00
if (!drmKey) {
return;
}
const uint32_t pciDirLen = pciDir->length;
bool isXE = ffStrbufEqualS(&gpu->driver, "xe");
2026-03-29 09:28:49 +08:00
if (isXE) {
2024-07-12 19:24:55 +08:00
ffStrbufAppendS(pciDir, "/tile0/gt0/freq0/max_freq");
2026-03-29 09:28:49 +08:00
} else {
ffStrbufAppendF(pciDir, "/drm/%s/gt_max_freq_mhz", drmKey);
2026-03-29 09:28:49 +08:00
}
if (ffReadFileBuffer(pciDir->chars, buffer)) {
2024-07-17 14:52:35 +08:00
gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, 0);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
2026-03-29 09:28:49 +08:00
if (options->temp) {
ffStrbufAppendS(pciDir, "/hwmon/");
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars);
2026-03-29 09:28:49 +08:00
if (dirp) {
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
continue;
}
ffStrbufSubstrBefore(pciDir, pciDirLen + strlen("/hwmon/"));
ffStrbufAppendS(pciDir, entry->d_name);
// https://github.com/Syllo/nvtop/blob/73291884d926445e499d6b9b71cb7a9bdbc7c393/src/extract_gpuinfo_intel.c#L279-L281
ffStrbufAppendS(pciDir, isXE ? "/temp2_input" : "/temp1_input");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
uint64_t value = ffStrbufToUInt(buffer, 0);
2026-03-29 09:28:49 +08:00
if (value > 0) {
gpu->temperature = (double) value / 1000;
break;
}
}
}
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
}
}
2026-03-29 09:28:49 +08:00
static const char* drmDetectIntelSpecific(FFGPUResult* gpu, const char* drmKey, FFstrbuf* buffer) {
ffStrbufSetS(buffer, "/dev/dri/");
ffStrbufAppendS(buffer, drmKey);
2025-06-19 18:11:54 +08:00
FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (fd < 0) {
return "Failed to open drm device";
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(&gpu->driver, "xe")) {
return ffDrmDetectXe(gpu, fd);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&gpu->driver, "i915")) {
return ffDrmDetectI915(gpu, fd);
2026-03-29 09:28:49 +08:00
}
return "Unknown Intel GPU driver";
}
2026-03-29 09:28:49 +08:00
static const char* pciDetectTempGeneral(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) {
if (options->temp) {
const uint32_t pciDirLen = pciDir->length;
ffStrbufAppendS(pciDir, "/hwmon/");
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars);
2026-03-29 09:28:49 +08:00
if (dirp) {
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp))) {
if (entry->d_name[0] == '.') {
continue;
}
ffStrbufAppendS(pciDir, entry->d_name);
ffStrbufAppendS(pciDir, "/temp1_input");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
uint64_t value = ffStrbufToUInt(buffer, 0);
2026-03-29 09:28:49 +08:00
if (value > 0) {
gpu->temperature = (double) value / 1000.0;
}
}
break;
}
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
}
return NULL;
}
2026-03-29 09:28:49 +08:00
static const char* drmDetectNouveauSpecific(FFGPUResult* gpu, const char* drmKey, FFstrbuf* buffer) {
ffStrbufSetS(buffer, "/dev/dri/");
ffStrbufAppendS(buffer, drmKey);
FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (fd < 0) {
return "Failed to open drm device";
}
return ffDrmDetectNouveau(gpu, fd);
}
2026-03-29 09:28:49 +08:00
static const char* pciDetectZxSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) {
gpu->type = FF_GPU_TYPE_INTEGRATED;
const uint32_t pciDirLen = pciDir->length;
ffStrbufAppendS(pciDir, "/zx_info/eclk");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, FF_GPU_FREQUENCY_UNSET);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
2026-03-29 09:28:49 +08:00
if (options->driverSpecific) {
ffStrbufAppendS(pciDir, "/zx_info/engine_3d_usage");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
gpu->coreUsage = ffStrbufToDouble(buffer, FF_GPU_CORE_USAGE_UNSET);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
ffStrbufAppendS(pciDir, "/zx_info/fb_size");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
gpu->shared.total = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
2026-03-29 09:28:49 +08:00
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) {
gpu->shared.total *= 1024 * 1024;
ffStrbufAppendS(pciDir, "/zx_info/free_fb_mem");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(pciDir->chars, buffer)) {
gpu->shared.used = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(pciDir, pciDirLen);
2026-03-29 09:28:49 +08:00
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) {
gpu->shared.used *= 1024 * 1024;
gpu->shared.used = gpu->shared.total - gpu->shared.used;
}
}
}
return NULL;
}
2026-06-14 17:36:54 +08:00
static inline uint16_t pcieLinkSpeedToGen(const FFstrbuf* buffer) {
int64_t speed = ffStrbufToSInt(buffer, FF_GPU_PCIE_SPEED_UNSET);
if (speed >= 64) {
return 6;
} else if (speed >= 32) {
return 5;
} else if (speed >= 16) {
return 4;
} else if (speed >= 8) {
return 3;
} else if (speed >= 5) {
return 2;
} else if (speed >= 2) { // 2.5
return 1;
}
return FF_GPU_PCIE_SPEED_UNSET;
}
static inline uint16_t pcieWidthToLanes(const FFstrbuf* buffer) {
int64_t width = ffStrbufToSInt(buffer, FF_GPU_PCIE_SPEED_UNSET);
if (width > 0 && width < 255) { // kernel returns 255 if the value is unknown
return (uint16_t) width;
}
return FF_GPU_PCIE_SPEED_UNSET;
}
2026-03-29 09:28:49 +08:00
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey) {
2024-05-19 19:34:48 +08:00
const uint32_t drmDirPathLength = deviceDir->length;
uint32_t vendorId, deviceId, subVendorId, subDeviceId;
uint8_t classId, subclassId;
2026-03-29 09:28:49 +08:00
if (sscanf(buffer->chars + strlen("pci:"), "v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) {
return "Failed to parse pci modalias";
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) {
2024-04-18 21:00:47 +08:00
return "Not a GPU device";
2026-03-29 09:28:49 +08:00
}
char pciPath[PATH_MAX];
2024-05-19 19:34:48 +08:00
const char* pPciPath = NULL;
2026-03-29 09:28:49 +08:00
if (drmKey) {
2024-10-18 16:15:48 +08:00
ssize_t pathLength = readlink(deviceDir->chars, pciPath, ARRAY_SIZE(pciPath) - 1);
2026-03-29 09:28:49 +08:00
if (pathLength <= 0) {
2024-05-19 19:34:48 +08:00
return "Unable to get PCI device path";
2026-03-29 09:28:49 +08:00
}
2024-05-19 19:34:48 +08:00
pciPath[pathLength] = '\0';
pPciPath = strrchr(pciPath, '/');
2026-03-29 09:28:49 +08:00
if (__builtin_expect(pPciPath != NULL, true)) {
2024-05-19 19:34:48 +08:00
pPciPath++;
2026-03-29 09:28:49 +08:00
} else {
2024-05-19 19:34:48 +08:00
pPciPath = pciPath;
2026-03-29 09:28:49 +08:00
}
} else {
2025-03-25 11:03:47 +08:00
pPciPath = memrchr(deviceDir->chars, '/', deviceDir->length);
assert(pPciPath);
pPciPath++;
2024-05-19 19:34:48 +08:00
}
uint32_t pciDomain, pciBus, pciDevice, pciFunc;
2026-03-29 09:28:49 +08:00
if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) {
return "Invalid PCI device path";
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (pciFunc > 0 && subclassId == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/) {
2025-10-30 14:46:30 +08:00
return "Likely an auxiliary display controller"; // #2034
2026-03-29 09:28:49 +08:00
}
2025-10-30 14:46:30 +08:00
2026-04-14 10:41:18 +08:00
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString((uint16_t) vendorId));
ffStrbufInit(&gpu->name);
ffStrbufInit(&gpu->driver);
ffStrbufInit(&gpu->platformApi);
ffStrbufInit(&gpu->memoryType);
2024-09-19 15:22:09 +08:00
gpu->index = FF_GPU_INDEX_UNSET;
gpu->temperature = FF_GPU_TEMP_UNSET;
2024-08-10 21:08:35 +08:00
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = ffGPUPciAddr2Id(pciDomain, pciBus, pciDevice, pciFunc);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
2026-06-14 17:36:54 +08:00
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
char drmKeyBuffer[8];
2026-03-29 09:28:49 +08:00
if (!drmKey) {
ffStrbufAppendS(deviceDir, "/drm");
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(deviceDir->chars);
2026-03-29 09:28:49 +08:00
if (dirp) {
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (ffStrStartsWith(entry->d_name, "card")) {
ffStrCopy(drmKeyBuffer, entry->d_name, ARRAY_SIZE(drmKeyBuffer));
drmKey = drmKeyBuffer;
break;
}
}
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
}
2026-03-29 09:28:49 +08:00
if (drmKey) {
ffStrbufSetF(&gpu->platformApi, "DRM (%s)", drmKey);
}
2024-05-19 20:01:32 +08:00
pciDetectDriver(&gpu->driver, deviceDir, buffer, drmKey);
2024-05-19 19:34:48 +08:00
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
2026-06-13 15:20:40 +08:00
ffStrbufAppendS(deviceDir, "/max_link_speed");
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
2026-06-14 17:36:54 +08:00
gpu->psMax.gen = pcieLinkSpeedToGen(buffer);
2026-06-13 15:20:40 +08:00
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
2026-06-14 17:36:54 +08:00
if (gpu->psMax.gen != FF_GPU_PCIE_SPEED_UNSET) {
2026-06-13 15:20:40 +08:00
ffStrbufAppendS(deviceDir, "/max_link_width");
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
2026-06-14 17:36:54 +08:00
gpu->psMax.lanes = pcieWidthToLanes(buffer);
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
}
ffStrbufAppendS(deviceDir, "/current_link_speed");
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
gpu->psCurr.gen = pcieLinkSpeedToGen(buffer);
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
if (gpu->psCurr.gen != FF_GPU_PCIE_SPEED_UNSET) {
ffStrbufAppendS(deviceDir, "/current_link_width");
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
gpu->psCurr.lanes = pcieWidthToLanes(buffer);
2026-06-13 15:20:40 +08:00
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
}
2026-03-29 09:28:49 +08:00
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) {
bool ok = false;
2026-03-29 09:28:49 +08:00
if (drmKey && options->driverSpecific) {
ok = drmDetectAmdSpecific(options, gpu, drmKey, buffer) == NULL;
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (!ok) {
pciDetectAmdSpecific(options, gpu, deviceDir, buffer);
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
ffStrbufAppendS(deviceDir, "/revision");
2026-03-29 09:28:49 +08:00
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
char* pend;
uint64_t revision = strtoul(buffer->chars, &pend, 16);
2026-03-29 09:28:49 +08:00
if (pend != buffer->chars) {
ffGPUQueryAmdGpuName((uint16_t) deviceId, (uint8_t) revision, gpu);
2026-03-29 09:28:49 +08:00
}
}
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
}
2026-03-29 09:28:49 +08:00
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) {
pciDetectIntelSpecific(options, gpu, deviceDir, buffer, drmKey);
2024-05-19 19:34:48 +08:00
ffStrbufSubstrBefore(deviceDir, drmDirPathLength);
2026-03-29 09:28:49 +08:00
if (options->driverSpecific && drmKey) {
drmDetectIntelSpecific(gpu, drmKey, buffer);
2026-03-29 09:28:49 +08:00
}
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && ffStrbufEqualS(&gpu->driver, "nouveau")) {
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
2026-03-29 09:28:49 +08:00
if (options->driverSpecific && drmKey) {
drmDetectNouveauSpecific(gpu, drmKey, buffer);
2026-03-29 09:28:49 +08:00
}
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_ZHAOXIN && ffStrbufStartsWithS(&gpu->driver, "zx")) {
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
pciDetectZxSpecific(options, gpu, deviceDir, buffer);
2026-03-29 09:28:49 +08:00
} else {
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
2026-03-29 09:28:49 +08:00
.domain = pciDomain,
.bus = pciBus,
.device = pciDevice,
.func = pciFunc,
});
}
2022-09-05 17:42:42 +02:00
2026-03-29 09:28:49 +08:00
if (gpu->name.length == 0) {
ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu);
2026-03-29 09:28:49 +08:00
}
ffGPUFillVendorByDeviceName(gpu);
2024-11-28 21:28:35 +08:00
return NULL;
}
2024-08-02 14:13:33 +08:00
#if __aarch64__
#include "detection/cpu/cpu.h"
2024-08-02 14:13:33 +08:00
2026-04-09 16:20:59 +08:00
FF_A_UNUSED static const char* drmDetectAsahiSpecific(FFGPUResult* gpu, const char* name, FF_A_UNUSED FFstrbuf* buffer, FF_A_UNUSED const char* drmKey) {
2026-03-29 09:28:49 +08:00
if (sscanf(name, "agx-t%lu", &gpu->deviceId) == 1) {
ffStrbufSetStatic(&gpu->name, ffCPUAppleCodeToName((uint32_t) gpu->deviceId));
2026-03-29 09:28:49 +08:00
}
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE);
ffStrbufSetS(buffer, "/dev/dri/");
ffStrbufAppendS(buffer, drmKey);
2025-06-19 18:11:54 +08:00
FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (fd >= 0) {
return ffDrmDetectAsahi(gpu, fd);
2026-03-29 09:28:49 +08:00
}
return NULL;
}
#endif
2026-03-29 09:28:49 +08:00
static const char* detectOf(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir, const char* drmKey) {
char compatible[256]; // vendor,model-name
2026-03-29 09:28:49 +08:00
if (sscanf(buffer->chars + strlen("of:"), "NgpuT%*[^C]C%255[^C]", compatible) != 1) {
return "Failed to parse of modalias or not a GPU device";
2026-03-29 09:28:49 +08:00
}
char* name = strchr(compatible, ',');
2026-03-29 09:28:49 +08:00
if (name) {
*name = '\0';
++name;
}
2026-04-14 10:41:18 +08:00
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
gpu->index = FF_GPU_INDEX_UNSET;
gpu->deviceId = 0;
ffStrbufInit(&gpu->name);
ffStrbufInit(&gpu->vendor);
ffStrbufInit(&gpu->driver);
ffStrbufInit(&gpu->memoryType);
ffStrbufInitF(&gpu->platformApi, "DRM (%s)", drmKey);
gpu->temperature = FF_GPU_TEMP_UNSET;
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_INTEGRATED;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
2026-06-15 09:24:14 +08:00
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
pciDetectDriver(&gpu->driver, drmDir, buffer, drmKey);
2026-03-29 09:28:49 +08:00
#ifdef __aarch64__
if (ffStrbufEqualS(&gpu->driver, "asahi")) {
drmDetectAsahiSpecific(gpu, name, buffer, drmKey);
2026-03-29 09:28:49 +08:00
}
#endif
2026-03-29 09:28:49 +08:00
if (!gpu->name.length) {
ffStrbufSetS(&gpu->name, name ?: compatible);
2024-09-29 23:08:41 +08:00
ffStrbufTrimRightSpace(&gpu->name);
}
2026-03-29 09:28:49 +08:00
if (!gpu->vendor.length && name) {
if (ffStrEquals(compatible, "brcm")) {
2024-09-30 15:02:49 +08:00
ffStrbufSetStatic(&gpu->vendor, "Broadcom"); // Raspberry Pi
2026-03-29 09:28:49 +08:00
} else {
2024-09-30 15:02:49 +08:00
ffStrbufSetS(&gpu->vendor, compatible);
gpu->vendor.chars[0] = (char) toupper(compatible[0]);
}
}
return NULL;
}
2026-03-29 09:28:49 +08:00
static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) {
FF_STRBUF_AUTO_DESTROY drmDir = ffStrbufCreateA(64);
ffStrbufAppendS(&drmDir, "/sys/class/drm/");
const uint32_t drmDirLength = drmDir.length;
FF_AUTO_CLOSE_DIR DIR* dir = opendir(drmDir.chars);
2026-03-29 09:28:49 +08:00
if (dir == NULL) {
2024-04-18 21:00:47 +08:00
return "Failed to open `/sys/class/drm/`";
2026-03-29 09:28:49 +08:00
}
2024-02-12 00:43:06 +08:00
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
2022-09-05 17:42:42 +02:00
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dir)) != NULL) {
if (!ffStrStartsWith(entry->d_name, "card") ||
2026-03-29 09:28:49 +08:00
strchr(entry->d_name + 4, '-') != NULL) {
continue;
2026-03-29 09:28:49 +08:00
}
ffStrbufAppendS(&drmDir, entry->d_name);
ffStrbufAppendS(&drmDir, "/device/modalias");
2026-03-29 09:28:49 +08:00
if (!ffReadFileBuffer(drmDir.chars, &buffer)) {
continue;
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(&drmDir, drmDir.length - (uint32_t) strlen("/modalias"));
2022-09-05 17:42:42 +02:00
2026-03-29 09:28:49 +08:00
if (ffStrbufStartsWithS(&buffer, "pci:")) {
2024-05-19 19:34:48 +08:00
detectPci(options, gpus, &buffer, &drmDir, entry->d_name);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(&buffer, "of:")) { // Open Firmware
detectOf(gpus, &buffer, &drmDir, entry->d_name);
2026-03-29 09:28:49 +08:00
}
ffStrbufSubstrBefore(&drmDir, drmDirLength);
2022-09-05 17:42:42 +02:00
}
2022-09-16 18:11:39 +08:00
return NULL;
2022-09-05 17:42:42 +02:00
}
2026-03-29 09:28:49 +08:00
static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) {
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci
2024-04-18 21:00:47 +08:00
const char* pciDirPath = "/sys/bus/pci/devices/";
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDirPath);
2026-03-29 09:28:49 +08:00
if (dirp == NULL) {
2024-04-18 21:00:47 +08:00
return "Failed to open `/sys/bus/pci/devices/`";
2026-03-29 09:28:49 +08:00
}
2024-04-18 21:00:47 +08:00
FF_STRBUF_AUTO_DESTROY pciDir = ffStrbufCreateA(64);
ffStrbufAppendS(&pciDir, pciDirPath);
const uint32_t pciBaseDirLength = pciDir.length;
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
2024-04-18 21:00:47 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2024-04-18 21:00:47 +08:00
ffStrbufSubstrBefore(&pciDir, pciBaseDirLength);
ffStrbufAppendS(&pciDir, entry->d_name);
const uint32_t pciDevDirLength = pciDir.length;
ffStrbufAppendS(&pciDir, "/modalias");
2026-03-29 09:28:49 +08:00
if (!ffReadFileBuffer(pciDir.chars, &buffer)) {
2024-04-18 21:00:47 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2024-04-18 21:00:47 +08:00
ffStrbufSubstrBefore(&pciDir, pciDevDirLength);
assert(ffStrbufStartsWithS(&buffer, "pci:"));
2024-05-19 19:34:48 +08:00
detectPci(options, gpus, &buffer, &pciDir, NULL);
2024-04-18 21:00:47 +08:00
ffStrbufSubstrBefore(&pciDir, pciBaseDirLength);
}
return NULL;
}
2026-03-29 09:28:49 +08:00
const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) {
#if __x86_64__ || __aarch64__
2026-03-20 18:35:07 +08:00
const char* ffGPUDetectWsl2(const FFGPUOptions* options, FFlist* gpus);
2026-03-29 09:28:49 +08:00
if (ffGPUDetectWsl2(options, gpus) == NULL) {
2026-03-20 18:35:07 +08:00
return NULL;
2026-03-29 09:28:49 +08:00
}
#endif
2024-08-08 18:53:56 +08:00
2026-03-29 09:28:49 +08:00
if (options->detectionMethod == FF_GPU_DETECTION_METHOD_AUTO) {
if (drmDetectGPUs(options, gpus) == NULL && gpus->length > 0) {
return NULL;
2026-03-29 09:28:49 +08:00
}
}
2024-04-18 21:00:47 +08:00
return pciDetectGPUs(options, gpus);
2022-09-05 17:42:42 +02:00
}