master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_gnu.c
ohmu  ·  2026-10-15
  1#include "gpu.h"
  2#include "common/io.h"
  3
  4#include <hurd.h>
  5#include <hurd/pci.h>
  6#include <hurd/paths.h>
  7
  8enum {
  9    PCI_VENDOR_ID = 0x00,
 10    PCI_DEVICE_ID = 0x02,
 11    PCI_REVISION_ID = 0x08,
 12    PCI_CLASS_PROG = 0x09,
 13    PCI_SUBCLASS = 0x0a,
 14    PCI_CLASS_DEVICE = 0x0b,
 15    PCI_CONF_SIZE = 0x40,
 16};
 17
 18const char* ffDetectGPUImpl([[maybe_unused]] const FFGPUOptions* options, FFlist* gpus) {
 19    int dDomainFd = open(_SERVERS_BUS "/pci/0000", O_RDONLY | O_CLOEXEC);
 20    if (dDomainFd < 0) {
 21        return "open(_SERVERS_BUS \"/pci/0000\") failed";
 22    }
 23
 24    FF_AUTO_CLOSE_DIR DIR* dirDomain = fdopendir(dDomainFd);
 25    if (dirDomain == nullptr) {
 26        return "fdopendir(domain) failed";
 27    }
 28
 29    struct dirent* busEntry;
 30    while ((busEntry = readdir(dirDomain)) != nullptr) {
 31        if (busEntry->d_type != DT_DIR || busEntry->d_name[0] == '.') {
 32            continue;
 33        }
 34
 35        char* endptr;
 36        uint16_t pciBus = (uint16_t) strtoul(busEntry->d_name, &endptr, 16);
 37        if (*endptr != '\0') {
 38            continue;
 39        }
 40
 41        int dBusFd = openat(dDomainFd, busEntry->d_name, O_RDONLY | O_CLOEXEC);
 42        if (dBusFd < 0) {
 43            continue;
 44        }
 45
 46        FF_AUTO_CLOSE_DIR DIR* dirBus = fdopendir(dBusFd);
 47        if (dirBus == nullptr) {
 48            continue;
 49        }
 50
 51        struct dirent* devEntry;
 52        while ((devEntry = readdir(dirBus)) != nullptr) {
 53            if (devEntry->d_type != DT_DIR || devEntry->d_name[0] == '.') {
 54                continue;
 55            }
 56
 57            uint8_t pciDev = (uint8_t) strtoul(devEntry->d_name, &endptr, 16);
 58            if (*endptr != '\0') {
 59                continue;
 60            }
 61
 62            int dDevFd = openat(dBusFd, devEntry->d_name, O_RDONLY | O_CLOEXEC);
 63            if (dDevFd < 0) {
 64                continue;
 65            }
 66
 67            FF_AUTO_CLOSE_DIR DIR* dirDev = fdopendir(dDevFd);
 68            if (dirDev == nullptr) {
 69                continue;
 70            }
 71
 72            struct dirent* funcEntry;
 73            while ((funcEntry = readdir(dirDev)) != nullptr) {
 74                if (funcEntry->d_type != DT_DIR || funcEntry->d_name[0] == '.') {
 75                    continue;
 76                }
 77
 78                uint8_t pciFunc = (uint8_t) strtoul(funcEntry->d_name, &endptr, 16);
 79                if (*endptr != '\0') {
 80                    continue;
 81                }
 82
 83                char subpath[PATH_MAX];
 84                snprintf(subpath, ARRAY_SIZE(subpath), "%s/%s/%s/%s/config", _SERVERS_BUS "/pci/0000", busEntry->d_name, devEntry->d_name, funcEntry->d_name);
 85
 86                mach_port_t devicePort = file_name_lookup(subpath, 0, 0);
 87                if (devicePort == MACH_PORT_NULL) {
 88                    continue;
 89                }
 90
 91                mach_msg_type_number_t nread = 0;
 92
 93                uint8_t data[PCI_CONF_SIZE];
 94                data_t pData = (data_t) data;
 95                kern_return_t kr = pci_conf_read(devicePort, 0, &pData, &nread, PCI_CONF_SIZE);
 96                mach_port_deallocate(mach_task_self(), devicePort);
 97                if (kr != KERN_SUCCESS || nread < PCI_CONF_SIZE) {
 98                    continue;
 99                }
100
101                if (pData != (data_t) data) {
102                    memcpy(data, pData, PCI_CONF_SIZE);
103                    vm_deallocate(mach_task_self(), (vm_address_t) pData, nread);
104                }
105
106                uint8_t classBase = data[PCI_CLASS_DEVICE];
107                if (classBase != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) {
108                    continue;
109                }
110
111                uint8_t classSub = data[PCI_SUBCLASS];
112                if (pciFunc > 0 && classSub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/) { // Likely an auxiliary display controller (#2034)
113                    continue;
114                }
115
116                uint8_t revision = data[PCI_REVISION_ID];
117                uint16_t vendorId = data[PCI_VENDOR_ID] | (data[PCI_VENDOR_ID + 1] << 8);
118                uint16_t deviceId = data[PCI_DEVICE_ID] | (data[PCI_DEVICE_ID + 1] << 8);
119
120                FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
121                ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
122                ffStrbufInit(&gpu->name);
123                ffStrbufInit(&gpu->driver);
124                ffStrbufInitStatic(&gpu->platformApi, "/servers/bus/pci");
125                ffStrbufInit(&gpu->memoryType);
126                gpu->temperature = FF_GPU_TEMP_UNSET;
127                gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
128                gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
129                gpu->type = FF_GPU_TYPE_UNKNOWN;
130                gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
131                gpu->deviceId = ffGPUPciAddr2Id(0, pciBus, pciDev, pciFunc);
132                gpu->frequency = FF_GPU_FREQUENCY_UNSET;
133                gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
134
135                if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) {
136                    ffGPUQueryAmdGpuName(deviceId, revision, gpu);
137                }
138
139                if (gpu->name.length == 0) {
140                    ffGPUFillVendorAndName(classSub, vendorId, deviceId, gpu);
141                }
142            }
143        }
144    }
145
146    return nullptr;
147}