ohmu
·
2026-10-15
1#include "gpu.h"
2#include "common/io.h"
3
4#include <hurd.h>
5#include <hurd/pci.h>
6#include <hurd/paths.h>
7
8enum {
9 PCI_VENDOR_ID = 0x00,
10 PCI_DEVICE_ID = 0x02,
11 PCI_REVISION_ID = 0x08,
12 PCI_CLASS_PROG = 0x09,
13 PCI_SUBCLASS = 0x0a,
14 PCI_CLASS_DEVICE = 0x0b,
15 PCI_CONF_SIZE = 0x40,
16};
17
18const char* ffDetectGPUImpl([[maybe_unused]] const FFGPUOptions* options, FFlist* gpus) {
19 int dDomainFd = open(_SERVERS_BUS "/pci/0000", O_RDONLY | O_CLOEXEC);
20 if (dDomainFd < 0) {
21 return "open(_SERVERS_BUS \"/pci/0000\") failed";
22 }
23
24 FF_AUTO_CLOSE_DIR DIR* dirDomain = fdopendir(dDomainFd);
25 if (dirDomain == nullptr) {
26 return "fdopendir(domain) failed";
27 }
28
29 struct dirent* busEntry;
30 while ((busEntry = readdir(dirDomain)) != nullptr) {
31 if (busEntry->d_type != DT_DIR || busEntry->d_name[0] == '.') {
32 continue;
33 }
34
35 char* endptr;
36 uint16_t pciBus = (uint16_t) strtoul(busEntry->d_name, &endptr, 16);
37 if (*endptr != '\0') {
38 continue;
39 }
40
41 int dBusFd = openat(dDomainFd, busEntry->d_name, O_RDONLY | O_CLOEXEC);
42 if (dBusFd < 0) {
43 continue;
44 }
45
46 FF_AUTO_CLOSE_DIR DIR* dirBus = fdopendir(dBusFd);
47 if (dirBus == nullptr) {
48 continue;
49 }
50
51 struct dirent* devEntry;
52 while ((devEntry = readdir(dirBus)) != nullptr) {
53 if (devEntry->d_type != DT_DIR || devEntry->d_name[0] == '.') {
54 continue;
55 }
56
57 uint8_t pciDev = (uint8_t) strtoul(devEntry->d_name, &endptr, 16);
58 if (*endptr != '\0') {
59 continue;
60 }
61
62 int dDevFd = openat(dBusFd, devEntry->d_name, O_RDONLY | O_CLOEXEC);
63 if (dDevFd < 0) {
64 continue;
65 }
66
67 FF_AUTO_CLOSE_DIR DIR* dirDev = fdopendir(dDevFd);
68 if (dirDev == nullptr) {
69 continue;
70 }
71
72 struct dirent* funcEntry;
73 while ((funcEntry = readdir(dirDev)) != nullptr) {
74 if (funcEntry->d_type != DT_DIR || funcEntry->d_name[0] == '.') {
75 continue;
76 }
77
78 uint8_t pciFunc = (uint8_t) strtoul(funcEntry->d_name, &endptr, 16);
79 if (*endptr != '\0') {
80 continue;
81 }
82
83 char subpath[PATH_MAX];
84 snprintf(subpath, ARRAY_SIZE(subpath), "%s/%s/%s/%s/config", _SERVERS_BUS "/pci/0000", busEntry->d_name, devEntry->d_name, funcEntry->d_name);
85
86 mach_port_t devicePort = file_name_lookup(subpath, 0, 0);
87 if (devicePort == MACH_PORT_NULL) {
88 continue;
89 }
90
91 mach_msg_type_number_t nread = 0;
92
93 uint8_t data[PCI_CONF_SIZE];
94 data_t pData = (data_t) data;
95 kern_return_t kr = pci_conf_read(devicePort, 0, &pData, &nread, PCI_CONF_SIZE);
96 mach_port_deallocate(mach_task_self(), devicePort);
97 if (kr != KERN_SUCCESS || nread < PCI_CONF_SIZE) {
98 continue;
99 }
100
101 if (pData != (data_t) data) {
102 memcpy(data, pData, PCI_CONF_SIZE);
103 vm_deallocate(mach_task_self(), (vm_address_t) pData, nread);
104 }
105
106 uint8_t classBase = data[PCI_CLASS_DEVICE];
107 if (classBase != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) {
108 continue;
109 }
110
111 uint8_t classSub = data[PCI_SUBCLASS];
112 if (pciFunc > 0 && classSub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/) { // Likely an auxiliary display controller (#2034)
113 continue;
114 }
115
116 uint8_t revision = data[PCI_REVISION_ID];
117 uint16_t vendorId = data[PCI_VENDOR_ID] | (data[PCI_VENDOR_ID + 1] << 8);
118 uint16_t deviceId = data[PCI_DEVICE_ID] | (data[PCI_DEVICE_ID + 1] << 8);
119
120 FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
121 ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
122 ffStrbufInit(&gpu->name);
123 ffStrbufInit(&gpu->driver);
124 ffStrbufInitStatic(&gpu->platformApi, "/servers/bus/pci");
125 ffStrbufInit(&gpu->memoryType);
126 gpu->temperature = FF_GPU_TEMP_UNSET;
127 gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
128 gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
129 gpu->type = FF_GPU_TYPE_UNKNOWN;
130 gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
131 gpu->deviceId = ffGPUPciAddr2Id(0, pciBus, pciDev, pciFunc);
132 gpu->frequency = FF_GPU_FREQUENCY_UNSET;
133 gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
134
135 if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) {
136 ffGPUQueryAmdGpuName(deviceId, revision, gpu);
137 }
138
139 if (gpu->name.length == 0) {
140 ffGPUFillVendorAndName(classSub, vendorId, deviceId, gpu);
141 }
142 }
143 }
144 }
145
146 return nullptr;
147}