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