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

327 lines
10 KiB
C
Raw Normal View History

2023-02-14 17:50:53 +08:00
#include "detection/gpu/gpu.h"
#include "detection/vulkan/vulkan.h"
2022-09-05 17:42:42 +02:00
#ifdef FF_HAVE_LIBPCI
#include "common/library.h"
#include "common/properties.h"
#include "common/parsing.h"
2022-09-25 15:23:00 +08:00
#include "detection/temps/temps_linux.h"
2023-02-01 15:56:25 +01:00
#include "util/stringUtils.h"
2022-09-05 17:42:42 +02:00
#include <string.h>
#include <unistd.h>
#include <pci/pci.h>
#include <setjmp.h>
2022-09-05 17:42:42 +02:00
// Fix building on Ubuntu 20.04
#ifndef PCI_IORESOURCE_MEM
#define PCI_IORESOURCE_MEM 0x00000200
#endif
#ifndef PCI_IORESOURCE_PREFETCH
#define PCI_IORESOURCE_PREFETCH 0x00002000
#endif
2022-09-05 17:42:42 +02:00
typedef struct PCIData
{
struct pci_access* access;
2022-09-23 11:51:16 +02:00
FF_LIBRARY_SYMBOL(pci_fill_info)
FF_LIBRARY_SYMBOL(pci_read_byte)
FF_LIBRARY_SYMBOL(pci_lookup_name)
FF_LIBRARY_SYMBOL(pci_get_param)
2022-09-21 12:02:14 +02:00
#if PCI_LIB_VERSION >= 0x030800
2022-09-23 11:51:16 +02:00
FF_LIBRARY_SYMBOL(pci_get_string_property)
2022-09-21 12:02:14 +02:00
#endif
2022-09-05 17:42:42 +02:00
} PCIData;
static void pciDetectVendorName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device)
{
2023-02-06 11:07:49 +08:00
ffStrbufAppendS(&gpu->vendor, ffGetGPUVendorString(device->vendor_id));
2022-09-05 17:42:42 +02:00
if(gpu->vendor.length > 0)
return;
2022-09-09 17:40:08 +02:00
ffStrbufEnsureFree(&gpu->vendor, 255);
pci->ffpci_lookup_name(pci->access, gpu->vendor.chars, (int) gpu->vendor.allocated, PCI_LOOKUP_VENDOR, device->vendor_id);
2022-09-05 17:42:42 +02:00
ffStrbufRecalculateLength(&gpu->vendor);
2023-03-25 13:04:44 +08:00
if(ffStrbufContainS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI"))
2022-09-05 17:42:42 +02:00
ffStrbufSetS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD);
2023-03-25 13:04:44 +08:00
else if(ffStrbufContainS(&gpu->vendor, "Intel"))
2022-09-05 17:42:42 +02:00
ffStrbufSetS(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL);
2023-03-25 13:04:44 +08:00
else if(ffStrbufContainS(&gpu->vendor, "NVIDIA"))
2022-09-05 17:42:42 +02:00
ffStrbufSetS(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA);
}
static void drmDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device)
2022-09-05 17:42:42 +02:00
{
2023-06-13 11:12:31 +08:00
u8 revId = 0;
2023-01-12 11:41:07 +01:00
bool revIdSet = false;
2022-09-19 15:54:27 +02:00
2022-09-19 16:43:43 +02:00
#if PCI_LIB_VERSION >= 0x030800
revIdSet = pci->ffpci_fill_info(device, PCI_FILL_CLASS_EXT) & PCI_FILL_CLASS_EXT;
if(revIdSet)
revId = device->rev_id;
2022-09-19 15:54:27 +02:00
#endif
2022-09-19 16:43:43 +02:00
if(!revIdSet)
{
#ifdef __FreeBSD__
return;
#else
revId = pci->ffpci_read_byte(device, PCI_REVISION_ID);
#endif
}
2022-09-19 15:54:27 +02:00
FF_STRBUF_AUTO_DESTROY query = ffStrbufCreateF("%X, %X,", device->device_id, revId);
ffParsePropFileData("libdrm/amdgpu.ids", query.chars, &gpu->name);
2022-09-05 17:42:42 +02:00
const char* removeStrings[] = {
"AMD ", "ATI ",
" (TM)", "(TM)",
" Graphics Adapter", " Graphics", " Series", " Edition"
};
ffStrbufRemoveStrings(&gpu->name, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings);
2022-09-05 17:42:42 +02:00
}
static void pciDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device)
2022-09-05 17:42:42 +02:00
{
if(ffStrbufEqualS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD))
2022-09-05 17:42:42 +02:00
{
drmDetectDeviceName(gpu, pci, device);
2022-09-05 17:42:42 +02:00
if(gpu->name.length > 0)
return;
}
2022-09-09 17:40:08 +02:00
ffStrbufEnsureFree(&gpu->name, 255);
pci->ffpci_lookup_name(pci->access, gpu->name.chars, (int) gpu->name.allocated, PCI_LOOKUP_DEVICE, device->vendor_id, device->device_id);
2022-09-05 17:42:42 +02:00
ffStrbufRecalculateLength(&gpu->name);
2022-09-09 17:47:16 +02:00
uint32_t openingBracket = ffStrbufFirstIndexC(&gpu->name, '[');
2022-09-09 17:53:19 +02:00
uint32_t closingBracket = ffStrbufNextIndexC(&gpu->name, openingBracket, ']');
if(closingBracket < gpu->name.length)
2022-09-09 17:47:16 +02:00
{
ffStrbufSubstrBefore(&gpu->name, closingBracket);
ffStrbufSubstrAfter(&gpu->name, openingBracket);
}
2022-09-05 17:42:42 +02:00
}
static void pciDetectDriverName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device)
{
2022-09-19 15:54:27 +02:00
#if PCI_LIB_VERSION >= 0x030800
pci->ffpci_fill_info(device, PCI_FILL_DRIVER);
ffStrbufAppendS(&gpu->driver, pci->ffpci_get_string_property(device, PCI_FILL_DRIVER));
if(gpu->driver.length > 0)
return;
#endif
2022-09-05 17:42:42 +02:00
const char* base = pci->ffpci_get_param(pci->access, "sysfs.path");
if(!ffStrSet(base))
return;
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateF("%s/devices/%04x:%02x:%02x.%d/driver", base, device->domain, device->bus, device->dev, device->func);
2022-09-05 17:42:42 +02:00
ffStrbufEnsureFree(&gpu->driver, 1023);
ssize_t resultLength = readlink(path.chars, gpu->driver.chars, gpu->driver.allocated - 1); //-1 for null terminator
if(resultLength > 0)
{
gpu->driver.length = (uint32_t) resultLength;
gpu->driver.chars[resultLength] = '\0';
ffStrbufSubstrAfterLastC(&gpu->driver, '/');
}
}
FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, struct pci_dev* device)
2022-09-05 17:42:42 +02:00
{
2023-01-21 06:18:19 +08:00
const FFTempsResult* tempsResult = ffDetectTemps();
2022-09-05 17:42:42 +02:00
for(uint32_t i = 0; i < tempsResult->values.length; i++)
{
FFTempValue* tempValue = ffListGet(&tempsResult->values, i);
//The kernel exposes the device class multiplied by 256 for some reason
2023-01-21 06:18:19 +08:00
if(tempValue->deviceClass == device->device_class * 256)
2022-09-05 17:42:42 +02:00
{
gpu->temperature = tempValue->value;
return;
}
}
}
FF_MAYBE_UNUSED static bool pciDetectMemory(FFGPUResult* gpu, const PCIData* pci, struct pci_dev* device)
2023-01-12 11:41:07 +01:00
{
gpu->dedicated.used = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
2023-01-12 11:41:07 +01:00
uint32_t flags = (uint32_t) pci->ffpci_fill_info(device, PCI_FILL_IO_FLAGS | PCI_FILL_SIZES);
if (!(flags & PCI_FILL_IO_FLAGS) || !(flags & PCI_FILL_SIZES))
2023-01-12 11:41:07 +01:00
{
gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET;
return false;
2023-01-12 11:41:07 +01:00
}
gpu->dedicated.total = gpu->shared.total = 0;
for (uint32_t i = 0; i < sizeof(device->size) / sizeof(device->size[0]); i++)
2023-01-12 12:54:04 +01:00
{
if (!(device->flags[i] & PCI_IORESOURCE_MEM)) continue;
// Assume dedicated memories are prefetchable
// At least it's true for my laptop
if (device->flags[i] & PCI_IORESOURCE_PREFETCH)
gpu->dedicated.total += device->size[i];
else
gpu->shared.total += device->size[i];
2023-01-12 12:54:04 +01:00
}
if (gpu->dedicated.total == 0 && gpu->shared.total == 0)
2023-01-12 12:54:04 +01:00
{
gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET;
return false;
2023-01-12 12:54:04 +01:00
}
return true;
}
FF_MAYBE_UNUSED static void pciDetectType(FFGPUResult* gpu)
{
//There is no straightforward way to detect the type of a GPU.
//The approach taken here is to look at the memory sizes of the device.
//Since integrated GPUs usually use the system ram, they don't have expansive ROMs
//and their memory sizes are usually smaller than 1GB.
if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET)
{
gpu->type = gpu->dedicated.total > 1024 * 1024 * 1024 // 1GB
? FF_GPU_TYPE_DISCRETE
: FF_GPU_TYPE_INTEGRATED;
}
else
gpu->type = FF_GPU_TYPE_UNKNOWN;
2023-01-12 11:41:07 +01:00
}
static void pciHandleDevice(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* results, PCIData* pci, struct pci_dev* device)
2022-09-05 17:42:42 +02:00
{
2022-09-19 15:54:27 +02:00
pci->ffpci_fill_info(device, PCI_FILL_CLASS);
2022-09-05 17:42:42 +02:00
char class[1024];
pci->ffpci_lookup_name(pci->access, class, sizeof(class) - 1, PCI_LOOKUP_CLASS, device->device_class);
if(
2023-01-12 11:41:07 +01:00
//https://pci-ids.ucw.cz/read/PD/03
2022-09-05 17:42:42 +02:00
strcasecmp("VGA compatible controller", class) != 0 &&
2023-01-12 11:41:07 +01:00
strcasecmp("XGA compatible controller", class) != 0 &&
2022-09-05 17:42:42 +02:00
strcasecmp("3D controller", class) != 0 &&
strcasecmp("Display controller", class) != 0
) return;
2022-09-19 15:54:27 +02:00
pci->ffpci_fill_info(device, PCI_FILL_IDENT);
2022-09-05 17:42:42 +02:00
FFGPUResult* gpu = ffListAdd(results);
ffStrbufInit(&gpu->vendor);
pciDetectVendorName(gpu, pci, device);
ffStrbufInit(&gpu->name);
pciDetectDeviceName(gpu, pci, device);
2022-09-05 17:42:42 +02:00
ffStrbufInit(&gpu->driver);
pciDetectDriverName(gpu, pci, device);
#if FF_USE_PCI_MEMORY
// Libpci reports at least 2 false results (#495, #497)
pciDetectMemory(gpu, pci, device);
pciDetectType(gpu);
#else
gpu->dedicated.used = gpu->shared.used = gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
#endif
2023-01-12 11:41:07 +01:00
2022-09-16 18:11:39 +08:00
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
2022-09-05 17:42:42 +02:00
gpu->temperature = FF_GPU_TEMP_UNSET;
#ifdef __linux__
2023-06-12 11:30:18 +08:00
if(options->temp)
pciDetectTemp(gpu, device);
#endif
2022-09-05 17:42:42 +02:00
}
jmp_buf pciInitJmpBuf;
static void __attribute__((__noreturn__))
handlePciInitError(FF_MAYBE_UNUSED char *msg, ...)
{
longjmp(pciInitJmpBuf, 1);
}
// https://github.com/pciutils/pciutils/blob/bca0412843fa650c749128ade03f35ab3e8fe2b9/lib/init.c#L186
static void __attribute__((__noreturn__))
handlePciGenericError(char *msg, ...)
{
va_list args;
va_start(args, msg);
fputs("pcilib: ", stderr);
vfprintf(stderr, msg, args);
va_end(args);
fputc('\n', stderr);
exit(1);
}
static void handlePciWarning(FF_MAYBE_UNUSED char *msg, ...)
{
// noop
}
static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus)
2022-09-05 17:42:42 +02:00
{
PCIData pci;
2023-10-25 16:24:24 +08:00
FF_LIBRARY_LOAD(libpci, &instance.config.library.libPCI, "dlopen libpci.so failed", "libpci" FF_LIBRARY_EXTENSION, 4);
2022-09-21 12:02:14 +02:00
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_alloc);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_init);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_scan_bus);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libpci, pci_cleanup);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_fill_info);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_lookup_name);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_read_byte);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_get_param);
#if PCI_LIB_VERSION >= 0x030800
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_get_string_property);
#endif
2022-09-05 17:42:42 +02:00
pci.access = ffpci_alloc();
pci.access->warning = handlePciWarning;
pci.access->error = handlePciInitError;
if(setjmp(pciInitJmpBuf) == 0) // https://github.com/pciutils/pciutils/issues/136
ffpci_init(pci.access);
else
return "pcilib: Cannot find any working access method.";
pci.access->error = handlePciGenericError; // set back to generic error so we don't mess up error handling in other places
2022-09-05 17:42:42 +02:00
ffpci_scan_bus(pci.access);
struct pci_dev* device = pci.access->devices;
while(device != NULL)
{
pciHandleDevice(options, gpus, &pci, device);
2022-09-05 17:42:42 +02:00
device = device->next;
}
ffpci_cleanup(pci.access);
2022-09-16 18:11:39 +08:00
return NULL;
2022-09-05 17:42:42 +02:00
}
#endif
const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus)
2022-09-05 17:42:42 +02:00
{
#ifdef FF_HAVE_DIRECTX_HEADERS
const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus);
if (!ffGPUDetectByDirectX(options, gpus))
return NULL;
#endif
2022-09-05 17:42:42 +02:00
#ifdef FF_HAVE_LIBPCI
return pciDetectGPUs(options, gpus);
2022-09-19 23:16:43 +02:00
#else
FF_UNUSED(options, gpus);
2023-02-06 11:07:49 +08:00
return "fastfetch is built without libpci support";
2022-09-05 17:42:42 +02:00
#endif
}