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}