master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_bsddrm.c
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