master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_nbsd.c
ohmu  ·  2026-10-15
  1#include "gpu.h"
  2#include "common/debug.h"
  3#include "common/io.h"
  4
  5#include <sys/param.h>
  6#include <sys/ioctl.h>
  7#include <fcntl.h>
  8#include <dev/pci/pcireg.h>
  9#include <dev/pci/pcidevs.h>
 10#include <dev/pci/pciio.h>
 11
 12static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result) {
 13    struct pciio_bdf_cfgreg bdfr = {
 14        .bus = bus,
 15        .device = device,
 16        .function = func,
 17        .cfgreg = {
 18            .reg = reg,
 19        },
 20    };
 21
 22    if (ioctl(fd, PCI_IOC_BDF_CFGREAD, &bdfr) == -1) {
 23        return -1;
 24    }
 25
 26    *result = bdfr.cfgreg.val;
 27    return 0;
 28}
 29
 30const char* ffDetectGPUImpl([[maybe_unused]] const FFGPUOptions* options, FFlist* gpus) {
 31    char pciDevPath[] = "/dev/pciXXX";
 32
 33    for (uint32_t idev = 0; idev <= 255; idev++) {
 34        snprintf(pciDevPath + strlen("/dev/pci"), 4, "%u", idev);
 35
 36        FF_AUTO_CLOSE_FD int pcifd = open(pciDevPath, O_RDONLY | O_CLOEXEC);
 37        if (pcifd < 0) {
 38            if (errno == ENOENT) {
 39                break; // No more /dev/pciN devices
 40            }
 41            FF_DEBUG("open(\"%s\", O_RDONLY | O_CLOEXEC) failed: %s", pciDevPath, strerror(errno));
 42            return "open(\"/dev/pciN\", O_RDONLY | O_CLOEXEC) failed";
 43        }
 44
 45        struct pciio_businfo businfo;
 46        if (ioctl(pcifd, PCI_IOC_BUSINFO, &businfo) != 0) {
 47            continue;
 48        }
 49
 50        uint32_t bus = businfo.busno;
 51        for (uint32_t dev = 0; dev < businfo.maxdevs; dev++) {
 52            uint32_t maxfuncs = 0;
 53            for (uint32_t func = 0; func <= maxfuncs; func++) {
 54                uint32_t pciid, pciclass;
 55                if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0) {
 56                    continue;
 57                }
 58
 59                if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0) {
 60                    continue;
 61                }
 62
 63                if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0) {
 64                    continue;
 65                }
 66
 67                if (func == 0) {
 68                    // For some reason, pciReadConf returns success even for non-existing devices.
 69                    // So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
 70                    uint32_t bhlcr;
 71                    if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0) {
 72                        continue;
 73                    }
 74
 75                    if (PCI_HDRTYPE_MULTIFN(bhlcr)) {
 76                        maxfuncs = 7;
 77                    }
 78                }
 79
 80                if (PCI_CLASS(pciclass) != PCI_CLASS_DISPLAY) {
 81                    continue;
 82                }
 83
 84                if (func > 0 && PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_DISPLAY_MISC) {
 85                    continue; // Likely an auxiliary display controller (#2034)
 86                }
 87
 88                FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
 89                ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(PCI_VENDOR(pciid)));
 90                ffStrbufInit(&gpu->name);
 91                ffStrbufInit(&gpu->driver);
 92                ffStrbufInitS(&gpu->platformApi, pciDevPath);
 93                ffStrbufInit(&gpu->memoryType);
 94                gpu->index = FF_GPU_INDEX_UNSET;
 95                gpu->temperature = FF_GPU_TEMP_UNSET;
 96                gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
 97                gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
 98                gpu->type = FF_GPU_TYPE_UNKNOWN;
 99                gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
100                gpu->deviceId = ffGPUPciAddr2Id(0, bus, dev, func);
101                gpu->frequency = FF_GPU_FREQUENCY_UNSET;
102                gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
103
104                if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) {
105                    ffGPUQueryAmdGpuName(PCI_PRODUCT(pciid), PCI_REVISION(pciid), gpu);
106                }
107                if (gpu->name.length == 0) {
108                    ffGPUFillVendorAndName(PCI_SUBCLASS(pciclass), PCI_VENDOR(pciid), PCI_PRODUCT(pciid), gpu);
109                }
110
111                struct pciio_drvname drvname = {
112                    .device = dev,
113                    .function = func,
114                };
115                if (ioctl(pcifd, PCI_IOC_DRVNAME, &drvname) == 0) {
116                    ffStrbufInitS(&gpu->driver, drvname.name);
117                }
118            }
119        }
120    }
121
122    return nullptr;
123}