diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index 3f65e179..310f526f 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -118,3 +118,73 @@ const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result) return "GPU detection failed"; } + +void ffGPUParsePciIds(FFstrbuf* content, uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu) +{ + if (content->length) + { + char buffer[32]; + uint32_t len = (uint32_t) snprintf(buffer, sizeof(buffer), "\n%04x ", vendor); + char* start = (char*) memmem(content->chars, content->length, buffer, len); + char* end = content->chars + content->length; + if (start) + { + start += len; + end = memchr(start, '\n', (uint32_t) (end - start)); + if (!end) + end = content->chars + content->length; + if (!gpu->vendor.length) + ffStrbufSetNS(&gpu->vendor, (uint32_t) (end - start), start); + + start = end; // point to '\n' of vendor + end = start + 1; // point to start of devices + // find the start of next vendor + while (end[0] == '\t' || end[0] == '#') + { + end = strchr(end, '\n'); + if (!end) + { + end = content->chars + content->length; + break; + } + else + end++; + } + + len = (uint32_t) snprintf(buffer, sizeof(buffer), "\n\t%04x ", device); + start = memmem(start, (size_t) (end - start), buffer, len); + if (start) + { + start += len; + end = memchr(start, '\n', (uint32_t) (end - start)); + if (!end) + end = content->chars + content->length; + + char* openingBracket = memchr(start, '[', (uint32_t) (end - start)); + if (openingBracket) + { + openingBracket++; + char* closingBracket = memchr(openingBracket, ']', (uint32_t) (end - openingBracket)); + if (closingBracket) + ffStrbufSetNS(&gpu->name, (uint32_t) (closingBracket - openingBracket), openingBracket); + } + if (!gpu->name.length) + ffStrbufSetNS(&gpu->name, (uint32_t) (end - start), start); + } + } + } + + if (!gpu->name.length) + { + const char* subclassStr; + switch (subclass) + { + case 0 /*PCI_CLASS_DISPLAY_VGA*/: subclassStr = " (VGA compatible)"; break; + case 1 /*PCI_CLASS_DISPLAY_XGA*/: subclassStr = " (XGA compatible)"; break; + case 2 /*PCI_CLASS_DISPLAY_3D*/: subclassStr = " (3D)"; break; + default: subclassStr = ""; break; + } + + ffStrbufSetF(&gpu->name, "%s Device %04X%s", gpu->vendor.length ? gpu->vendor.chars : "Unknown", device, subclassStr); + } +} diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index 440d175d..69f0f6eb 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -42,3 +42,4 @@ const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result); const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus); const char* ffGetGPUVendorString(unsigned vendorId); +void ffGPUParsePciIds(FFstrbuf* content, uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu); diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 1775295f..ec17cc27 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -1,154 +1,24 @@ #include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" +#include "detection/temps/temps_linux.h" +#include "common/io/io.h" +#include "util/stringUtils.h" #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API #include "detection/gpu/gpu_driver_specific.h" #endif -#ifdef FF_HAVE_LIBPCI -#include "common/io/io.h" -#include "common/library.h" -#include "common/properties.h" -#include "common/parsing.h" -#include "detection/temps/temps_linux.h" -#include "util/stringUtils.h" -#include -#include -#include -#include +#include -// 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 - -typedef struct PCIData -{ - struct pci_access* access; - FF_LIBRARY_SYMBOL(pci_fill_info) - FF_LIBRARY_SYMBOL(pci_read_byte) - FF_LIBRARY_SYMBOL(pci_lookup_name) - FF_LIBRARY_SYMBOL(pci_get_param) - - #if PCI_LIB_VERSION >= 0x030800 - FF_LIBRARY_SYMBOL(pci_get_string_property) - #endif -} PCIData; - -static void pciDetectVendorName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) -{ - ffStrbufSetStatic(&gpu->vendor, ffGetGPUVendorString(device->vendor_id)); - - if(gpu->vendor.length > 0) - return; - - ffStrbufEnsureFree(&gpu->vendor, 255); - pci->ffpci_lookup_name(pci->access, gpu->vendor.chars, (int) gpu->vendor.allocated, PCI_LOOKUP_VENDOR, device->vendor_id); - ffStrbufRecalculateLength(&gpu->vendor); - - if(ffStrbufContainIgnCaseS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); - else if(ffStrbufContainIgnCaseS(&gpu->vendor, "Intel")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); - else if(ffStrbufContainIgnCaseS(&gpu->vendor, "NVIDIA")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); - else if(ffStrbufContainIgnCaseS(&gpu->vendor, "Apple")) - ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); -} - -static void drmDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) -{ - u8 revId = 0; - bool revIdSet = false; - - #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; - #endif - - if(!revIdSet) - { - #ifdef __FreeBSD__ - return; - #else - revId = pci->ffpci_read_byte(device, PCI_REVISION_ID); - #endif - } - - FF_STRBUF_AUTO_DESTROY query = ffStrbufCreateF("%X, %X,", device->device_id, revId); - ffParsePropFileData("libdrm/amdgpu.ids", query.chars, &gpu->name); - - const char* removeStrings[] = { - "AMD ", "ATI ", - " (TM)", "(TM)", - " Graphics Adapter", " Graphics", " Series", " Edition" - }; - ffStrbufRemoveStrings(&gpu->name, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings); -} - -static void pciDetectDeviceName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) -{ - if(ffStrbufEqualS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD)) - { - drmDetectDeviceName(gpu, pci, device); - if(gpu->name.length > 0) - return; - } - - 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); - ffStrbufRecalculateLength(&gpu->name); - - uint32_t openingBracket = ffStrbufFirstIndexC(&gpu->name, '['); - uint32_t closingBracket = ffStrbufNextIndexC(&gpu->name, openingBracket, ']'); - if(closingBracket < gpu->name.length) - { - ffStrbufSubstrBefore(&gpu->name, closingBracket); - ffStrbufSubstrAfter(&gpu->name, openingBracket); - } -} - -static void pciDetectDriverName(FFGPUResult* gpu, PCIData* pci, struct pci_dev* device) -{ - #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)); - #endif - - 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); - if (gpu->driver.length == 0) - { - 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, '/'); - } - } - - ffStrbufAppendC(&gpu->driver, ' '); - ffStrbufAppendS(&path, "/module/version"); - ffAppendFileBuffer(path.chars, &gpu->driver); - ffStrbufTrimRightSpace(&gpu->driver); -} - -FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, struct pci_dev* device) +FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, uint32_t deviceClass) { const FFlist* tempsResult = ffDetectTemps(); FF_LIST_FOR_EACH(FFTempValue, tempValue, *tempsResult) { + // FIXME: this code doesn't take multiGPUs into count //The kernel exposes the device class multiplied by 256 for some reason - if(tempValue->deviceClass == device->device_class * 256) + if(tempValue->deviceClass == deviceClass * 256) { gpu->temperature = tempValue->value; return; @@ -156,187 +26,142 @@ FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, struct pci_dev* devi } } -FF_MAYBE_UNUSED static bool pciDetectMemory(FFGPUResult* gpu, const PCIData* pci, struct pci_dev* device) +static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { - gpu->dedicated.used = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; - - 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)) + ffStrbufAppendS(pciDir, "/driver"); + char pathBuf[PATH_MAX]; + ssize_t resultLength = readlink(pciDir->chars, pathBuf, sizeof(pathBuf)); + if(resultLength > 0) { - gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET; - return false; + const char* slash = memrchr(pathBuf, '/', (size_t) resultLength); + if (slash) + { + slash++; + ffStrbufSetNS(&gpu->driver, (uint32_t) (resultLength - (slash - pathBuf)), slash); + } + + ffStrbufAppendS(pciDir, "/module/version"); + if (ffReadFileBuffer(pciDir->chars, buffer)) + { + ffStrbufTrimRightSpace(buffer); + ffStrbufAppendC(&gpu->driver, ' '); + ffStrbufAppend(&gpu->driver, buffer); + } } - - gpu->dedicated.total = gpu->shared.total = 0; - for (uint32_t i = 0; i < sizeof(device->size) / sizeof(device->size[0]); i++) - { - 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]; - } - - if (gpu->dedicated.total == 0 && gpu->shared.total == 0) - { - gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET; - return false; - } - - return true; } -FF_MAYBE_UNUSED static void pciDetectType(FFGPUResult* gpu) +static bool loadPciIds(FFstrbuf* pciids) { - //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 > (uint64_t)1024 * 1024 * 1024 // 1GB - ? FF_GPU_TYPE_DISCRETE - : FF_GPU_TYPE_INTEGRATED; - } - else - gpu->type = FF_GPU_TYPE_UNKNOWN; -} + ffReadFileBuffer("/usr/share/hwdata/pci.ids", pciids); + if (pciids->length > 0) return true; -static void pciHandleDevice(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* results, PCIData* pci, struct pci_dev* device) -{ - pci->ffpci_fill_info(device, PCI_FILL_CLASS); + ffReadFileBuffer("/usr/share/misc/pci.ids", pciids); // debian? + if (pciids->length > 0) return true; - if (device->device_class >> 8 != PCI_BASE_CLASS_DISPLAY) - return; + ffReadFileBuffer("/usr/local/share/hwdata/pci.ids", pciids); + if (pciids->length > 0) return true; - pci->ffpci_fill_info(device, PCI_FILL_IDENT); - - FFGPUResult* gpu = ffListAdd(results); - - ffStrbufInit(&gpu->platformApi); - - ffStrbufInit(&gpu->vendor); - pciDetectVendorName(gpu, pci, device); - - ffStrbufInit(&gpu->name); - pciDetectDeviceName(gpu, pci, device); - - 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 - - gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; - gpu->temperature = FF_GPU_TEMP_UNSET; - gpu->frequency = FF_GPU_FREQUENCY_UNSET; - - #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) - { - ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, - .pciBusId = { - .domain = (uint32_t) device->domain, - .bus = device->bus, - .device = device->dev, - .func = device->func, - }, - }, (FFGpuDriverResult) { - .temp = options->temp ? &gpu->temperature : NULL, - .memory = options->driverSpecific ? &gpu->dedicated : NULL, - .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, - .type = &gpu->type, - .frequency = &gpu->frequency, - }, "libnvidia-ml.so"); - - if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) - gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - } - #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API - - #ifdef __linux__ - if(options->temp && gpu->temperature != gpu->temperature) - pciDetectTemp(gpu, device); - #endif -} - -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 + return false; } static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) { - PCIData pci; + //https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci + const char* pciDirPath = "/sys/bus/pci/devices/"; - FF_LIBRARY_LOAD(libpci, &instance.config.library.libPCI, "dlopen libpci.so failed", "libpci" FF_LIBRARY_EXTENSION, 4); - 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); + DIR* dirp = opendir(pciDirPath); + if(dirp == NULL) + return "Failed to open `/sys/bus/pci/devices/`"; - 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); + FF_STRBUF_AUTO_DESTROY pciDir = ffStrbufCreateA(64); + ffStrbufAppendS(&pciDir, pciDirPath); - #if PCI_LIB_VERSION >= 0x030800 - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libpci, pci, pci_get_string_property); - #endif + const uint32_t pciBaseDirLength = pciDir.length; - 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 + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY pciids = ffStrbufCreate(); + loadPciIds(&pciids); - ffpci_scan_bus(pci.access); - - struct pci_dev* device = pci.access->devices; - while(device != NULL) + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) { - pciHandleDevice(options, gpus, &pci, device); - device = device->next; + if(entry->d_name[0] == '.') + continue; + + ffStrbufAppendS(&pciDir, entry->d_name); + + const uint32_t pciDevDirLength = pciDir.length; + + ffStrbufAppendS(&pciDir, "/modalias"); + if (!ffReadFileBuffer(pciDir.chars, &buffer)) + { + ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); + continue; + } + ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + + uint32_t vendorId, deviceId; + uint8_t classId, subclassId; + if (sscanf(buffer.chars, "pci:v%8" SCNx32 "d%8" SCNx32 "sv%*8ssd%*8sbc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &classId, &subclassId) != 4) + continue; + + if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) + continue; + + uint32_t pciDomain, pciBus, pciDevice, pciFunc; + if (sscanf(entry->d_name, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) + continue; + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); + ffStrbufInit(&gpu->name); + ffStrbufInit(&gpu->driver); + ffStrbufInit(&gpu->platformApi); + gpu->temperature = FF_GPU_TEMP_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 = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; + gpu->frequency = FF_GPU_FREQUENCY_UNSET; + + ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + + pciDetectDriver(gpu, &pciDir, &buffer); + ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) + { + ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { + .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, + .pciBusId = { + .domain = pciDomain, + .bus = pciBus, + .device = pciDevice, + .func = pciFunc, + }, + }, (FFGpuDriverResult) { + .temp = options->temp ? &gpu->temperature : NULL, + .memory = options->driverSpecific ? &gpu->dedicated : NULL, + .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .type = &gpu->type, + .frequency = &gpu->frequency, + }, "libnvidia-ml.so"); + + if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } + #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API + + #ifdef __linux__ + if(options->temp && gpu->temperature != gpu->temperature) + pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); + #endif } - ffpci_cleanup(pci.access); return NULL; } -#endif - const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { #ifdef FF_HAVE_DIRECTX_HEADERS @@ -345,10 +170,5 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) return NULL; #endif - #ifdef FF_HAVE_LIBPCI - return pciDetectGPUs(options, gpus); - #else - FF_UNUSED(options, gpus); - return "fastfetch is built without libpci support"; - #endif + return pciDetectGPUs(options, gpus); }