ohmu
·
2026-10-15
1#include "gpu.h"
2
3#if FF_HAVE_DRM
4
5 #include "common/strutil.h"
6 #include "common/io.h"
7
8 #include <drm.h>
9 #include <sys/ioctl.h>
10 #if __FreeBSD__
11 #include <sys/sysctl.h>
12 #include <inttypes.h>
13 #include <sys/pciio.h>
14 #include <fcntl.h>
15 #if __has_include(<dev/pci/pcireg.h>)
16 #include <dev/pci/pcireg.h> // FreeBSD
17 #else
18 #include <bus/pci/pcireg.h> // DragonFly
19 #endif
20 #endif
21
22// https://github.com/freebsd/drm-kmod/blob/8fea1f06b3ac3fa24217e24ba7b2133abad705a9/include/uapi/drm/drm.h#L1095
23struct drm_pciinfo {
24 uint16_t domain;
25 uint8_t bus;
26 uint8_t dev;
27 uint8_t func;
28 uint16_t vendor_id;
29 uint16_t device_id;
30 uint16_t subvendor_id;
31 uint16_t subdevice_id;
32 uint8_t revision_id;
33};
34
35 #define DRM_IOCTL_GET_PCIINFO DRM_IOR(0x15, struct drm_pciinfo)
36
37static const char* drmGetPciinfo(int drmfd, const char* drmId, struct drm_pciinfo* result) {
38 #if !__FreeBSD__
39 FF_UNUSED(drmId);
40 if (ioctl(drmfd, DRM_IOCTL_GET_PCIINFO, result) < 0) {
41 return "ioctl(DRM_IOCTL_GET_PCIINFO) failed";
42 }
43 #else
44 FF_UNUSED(drmfd);
45 char requestBuf[64];
46 snprintf(requestBuf, ARRAY_SIZE(requestBuf), "hw.dri.%s.busid", drmId);
47 char pciidBuf[64];
48 size_t pciidLen = ARRAY_SIZE(pciidBuf);
49 if (sysctlbyname(requestBuf, pciidBuf, &pciidLen, nullptr, 0) < 0) {
50 return "sysctlbyname(hw.dri.%s.busid) failed";
51 }
52 // hw.dri.0.busid: pci:0000:01:00.0
53 struct pci_match_conf match = {
54 .pc_class = PCIC_DISPLAY,
55 .flags = PCI_GETCONF_MATCH_CLASS | PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC,
56 };
57 if (sscanf(pciidBuf, "pci:%" SCNx32 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8, &match.pc_sel.pc_domain, &match.pc_sel.pc_bus, &match.pc_sel.pc_dev, &match.pc_sel.pc_func) != 4) {
58 return "Invalid PCI busid found";
59 }
60
61 FF_AUTO_CLOSE_FD int pcifd = open("/dev/pci", O_RDONLY | O_CLOEXEC);
62 if (pcifd < 0) {
63 return "open(\"/dev/pci\", O_RDONLY | O_CLOEXEC, 0) failed";
64 }
65
66 struct pci_conf conf;
67 struct pci_conf_io pcio = {
68 .pat_buf_len = sizeof(match),
69 .num_patterns = 1,
70 .patterns = &match,
71 .match_buf_len = sizeof(conf),
72 .matches = &conf,
73 };
74
75 if (ioctl(pcifd, PCIOCGETCONF, &pcio) < 0) {
76 return "ioctl(pcifd, PCIOCGETCONF, &pc) failed";
77 }
78
79 if (pcio.status == PCI_GETCONF_ERROR) {
80 return "ioctl(pcifd, PCIOCGETCONF, &pc) returned error";
81 }
82
83 if (pcio.num_matches != 1) {
84 return "ioctl(pcifd, PCIOCGETCONF, &pc) returned no results";
85 }
86
87 *result = (struct drm_pciinfo){
88 .domain = (uint16_t) conf.pc_sel.pc_domain,
89 .bus = conf.pc_sel.pc_bus,
90 .dev = conf.pc_sel.pc_dev,
91 .func = conf.pc_sel.pc_func,
92 .vendor_id = conf.pc_vendor,
93 .device_id = conf.pc_device,
94 .subvendor_id = conf.pc_subvendor,
95 .subdevice_id = conf.pc_subdevice,
96 .revision_id = conf.pc_revid,
97 };
98
99 // This sysctl can also be useful
100 // dev.drm.0.PCI_ID: 10de:2782
101 #endif
102
103 return nullptr;
104}
105
106const char* ffGPUDetectByDrmBSD(const FFGPUOptions* options, FFlist* gpus) {
107 FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/dev/dri/");
108 if (dirp == nullptr) {
109 return "opendir(/dev/dri/) failed";
110 }
111 int drifd = dirfd(dirp);
112
113 struct dirent* entry;
114 while ((entry = readdir(dirp)) != nullptr) {
115 if (entry->d_name[0] == '.' || !ffStrStartsWith(entry->d_name, "card")) {
116 continue;
117 }
118
119 FF_AUTO_CLOSE_FD int fd = openat(drifd, entry->d_name, O_RDWR | O_CLOEXEC);
120 if (fd < 0) {
121 continue;
122 }
123
124 // Currently, DRM_BUS_PCI is the only bus type supported by drm-kmod on BSD (hard-coded in libdrm source tree)
125 struct drm_pciinfo pciInfo;
126 if (drmGetPciinfo(fd, entry->d_name + strlen("card"), &pciInfo) != nullptr) {
127 continue;
128 }
129
130 FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
131 ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(pciInfo.vendor_id));
132 ffStrbufInit(&gpu->name);
133 ffStrbufInit(&gpu->driver);
134 ffStrbufInitF(&gpu->platformApi, "/dev/dri/%s", entry->d_name);
135 ffStrbufInit(&gpu->memoryType);
136 gpu->index = FF_GPU_INDEX_UNSET;
137 gpu->temperature = FF_GPU_TEMP_UNSET;
138 gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
139 gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
140 gpu->type = FF_GPU_TYPE_UNKNOWN;
141 gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
142 gpu->deviceId = ffGPUPciAddr2Id(pciInfo.domain, pciInfo.bus, pciInfo.dev, pciInfo.func);
143 gpu->frequency = FF_GPU_FREQUENCY_UNSET;
144 gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
145
146 char driverName[64];
147 driverName[0] = '\0';
148 struct drm_version ver = {
149 .name = driverName,
150 .name_len = ARRAY_SIZE(driverName) - 1,
151 };
152 if (ioctl(fd, DRM_IOCTL_VERSION, &ver) == 0) {
153 driverName[ver.name_len] = '\0';
154 ffStrbufSetF(&gpu->driver, "%s %d.%d.%d", ver.name, ver.version_major, ver.version_minor, ver.version_patchlevel);
155 }
156
157 if (ffStrStartsWith(driverName, "i915")) {
158 ffDrmDetectI915(gpu, fd);
159 } else if (ffStrStartsWith(driverName, "amdgpu")) {
160 uint32_t primaryNodeId = (uint32_t) strtoul(entry->d_name + strlen("card"), nullptr, 10);
161 FF_STRBUF_AUTO_DESTROY renderNode = ffStrbufCreateF("/dev/dri/renderD%d", primaryNodeId + 128);
162 ffDrmDetectAmdgpu(options, gpu, renderNode.chars);
163 } else if (ffStrStartsWith(driverName, "radeon")) {
164 uint32_t primaryNodeId = (uint32_t) strtoul(entry->d_name + strlen("card"), nullptr, 10);
165 FF_STRBUF_AUTO_DESTROY renderNode = ffStrbufCreateF("/dev/dri/renderD%d", primaryNodeId + 128);
166 ffDrmDetectRadeon(options, gpu, renderNode.chars);
167 } else if (ffStrStartsWith(driverName, "xe")) {
168 ffDrmDetectXe(gpu, fd);
169 } else if (ffStrStartsWith(driverName, "asahi")) {
170 ffDrmDetectAsahi(gpu, fd);
171 } else if (ffStrStartsWith(driverName, "nouveau")) {
172 ffDrmDetectNouveau(gpu, fd);
173 } else {
174 ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId){
175 .domain = pciInfo.domain,
176 .bus = pciInfo.bus,
177 .device = pciInfo.dev,
178 .func = pciInfo.func,
179 });
180 }
181
182 if (gpu->name.length == 0) {
183 if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) {
184 ffGPUQueryAmdGpuName(pciInfo.device_id, pciInfo.revision_id, gpu);
185 }
186 if (gpu->name.length == 0) {
187 ffGPUFillVendorAndName(0, pciInfo.vendor_id, pciInfo.device_id, gpu);
188 }
189 }
190
191 ffGPUDetectTypeByVendorAndName(gpu);
192 }
193
194 return nullptr;
195}
196
197#else
198
199const char* ffGPUDetectByDrmBSD(const FFGPUOptions* options, FFlist* gpus) {
200 FF_UNUSED(options, gpus);
201 return "Fastfetch was built without libdrm support";
202}
203
204#endif // FF_HAVE_DRM