ohmu
·
2026-10-15
1#include "gpu_driver_specific.h"
2
3#include "common/library.h"
4#include "common/mallocHelper.h"
5#include "igcl.h"
6
7struct FFIgclData {
8 FF_LIBRARY_SYMBOL(ctlClose)
9
10 FF_LIBRARY_SYMBOL(ctlEnumerateDevices)
11 FF_LIBRARY_SYMBOL(ctlGetDeviceProperties)
12 FF_LIBRARY_SYMBOL(ctlEnumTemperatureSensors)
13 FF_LIBRARY_SYMBOL(ctlTemperatureGetProperties)
14 FF_LIBRARY_SYMBOL(ctlEnumMemoryModules)
15 FF_LIBRARY_SYMBOL(ctlMemoryGetProperties)
16 FF_LIBRARY_SYMBOL(ctlMemoryGetState)
17 FF_LIBRARY_SYMBOL(ctlEnumFrequencyDomains)
18 FF_LIBRARY_SYMBOL(ctlFrequencyGetProperties)
19 FF_LIBRARY_SYMBOL(ctlPciGetProperties)
20 FF_LIBRARY_SYMBOL(ctlPciGetState)
21
22 bool inited;
23 ctl_api_handle_t apiHandle;
24} igclData;
25
26static void shutdownIgcl() {
27 if (igclData.apiHandle) {
28 igclData.ffctlClose(igclData.apiHandle);
29 igclData.apiHandle = nullptr;
30 }
31}
32
33const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName) {
34 if (!igclData.inited) {
35 igclData.inited = true;
36 FF_LIBRARY_LOAD(libigcl, "dlopen igcl (ControlLib) failed", soName, 1);
37 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libigcl, ctlInit)
38 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlClose)
39 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumerateDevices)
40 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlGetDeviceProperties)
41 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumTemperatureSensors)
42 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlTemperatureGetProperties)
43 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumMemoryModules)
44 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetProperties)
45 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetState)
46 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumFrequencyDomains)
47 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlFrequencyGetProperties)
48 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlPciGetProperties)
49 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlPciGetState)
50
51 if (ffctlInit(&(ctl_init_args_t) {
52 .AppVersion = CTL_IMPL_VERSION,
53 .flags = CTL_INIT_FLAG_USE_LEVEL_ZERO,
54 .Size = sizeof(ctl_init_args_t),
55 .Version = 0,
56 },
57 &igclData.apiHandle) != CTL_RESULT_SUCCESS) {
58 return "loading igcl library failed";
59 }
60 atexit(shutdownIgcl);
61 libigcl = nullptr; // don't close igcl
62 }
63
64 if (!igclData.apiHandle) {
65 return "loading igcl library failed";
66 }
67
68 uint32_t deviceCount = 0;
69 // ctlEnumerateDevices MUST be called with deviceCount = 0 and devices = nullptr first,
70 // otherwise it will be silently ignored and return CTL_RESULT_SUCCESS with deviceCount and devices unmodified.
71 // VERY STRANGE BEHAVIOR
72 if (igclData.ffctlEnumerateDevices(igclData.apiHandle, &deviceCount, nullptr) != CTL_RESULT_SUCCESS) {
73 return "ctlEnumerateDevices(nullptr) failed";
74 }
75 if (deviceCount == 0) {
76 return "No Intel graphics adapter found";
77 }
78
79 FF_AUTO_FREE ctl_device_adapter_handle_t* devices = malloc(deviceCount * sizeof(*devices));
80 if (igclData.ffctlEnumerateDevices(igclData.apiHandle, &deviceCount, devices) != CTL_RESULT_SUCCESS) {
81 return "ctlEnumerateDevices(devices) failed";
82 }
83
84 ctl_device_adapter_handle_t device = nullptr;
85
86 uint64_t /* LUID */ deviceId = 0;
87 ctl_device_adapter_properties_t properties = {
88 .Size = sizeof(properties),
89 .pDeviceID = &deviceId,
90 .device_id_size = sizeof(deviceId),
91 .Version = 2,
92 };
93 for (uint32_t iDev = 0; iDev < deviceCount; iDev++) {
94 if (igclData.ffctlGetDeviceProperties(devices[iDev], &properties) != CTL_RESULT_SUCCESS) {
95 continue;
96 }
97
98 if (properties.device_type != CTL_DEVICE_TYPE_GRAPHICS) {
99 continue;
100 }
101
102 if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) {
103 if (cond->pciBusId.bus == properties.adapter_bdf.bus &&
104 cond->pciBusId.device == properties.adapter_bdf.device &&
105 cond->pciBusId.func == properties.adapter_bdf.function) {
106 device = devices[iDev];
107 break;
108 }
109 } else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID) {
110 if (cond->luid == deviceId) {
111 device = devices[iDev];
112 break;
113 }
114 } else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID) {
115 if (
116 cond->pciDeviceId.deviceId == properties.pci_device_id &&
117 cond->pciDeviceId.vendorId == properties.pci_vendor_id &&
118 cond->pciDeviceId.subSystemId == (uint32_t) ((properties.pci_subsys_id << 16u) | properties.pci_subsys_vendor_id) &&
119 cond->pciDeviceId.revId == properties.rev_id) {
120 device = devices[iDev];
121 break;
122 }
123 }
124 }
125
126 if (!device) {
127 return "Device not found";
128 }
129
130 if (result.coreCount) {
131 *result.coreCount = properties.num_slices * properties.num_sub_slices_per_slice * properties.num_eus_per_sub_slice;
132 }
133
134 if (result.memory) {
135 ctl_mem_handle_t memoryModules[16];
136 uint32_t memoryCount = ARRAY_SIZE(memoryModules);
137 if (igclData.ffctlEnumMemoryModules(device, &memoryCount, memoryModules) == CTL_RESULT_SUCCESS && memoryCount > 0) {
138 result.memory->used = 0;
139 result.memory->total = 0;
140 for (uint32_t iMem = 0; iMem < memoryCount; iMem++) {
141 ctl_mem_properties_t memoryProperties = {
142 .Size = sizeof(memoryProperties),
143 .Version = 0,
144 };
145 if (igclData.ffctlMemoryGetProperties(memoryModules[iMem], &memoryProperties) == CTL_RESULT_SUCCESS) {
146 if (memoryProperties.location == CTL_MEM_LOC_DEVICE && result.memoryType) {
147 switch (memoryProperties.type) {
148#define FF_ICTL_MEM_TYPE_CASE(type) \
149 case CTL_MEM_TYPE_##type: \
150 ffStrbufSetStatic(result.memoryType, #type); \
151 break
152 FF_ICTL_MEM_TYPE_CASE(HBM);
153 FF_ICTL_MEM_TYPE_CASE(DDR);
154 FF_ICTL_MEM_TYPE_CASE(DDR3);
155 FF_ICTL_MEM_TYPE_CASE(DDR4);
156 FF_ICTL_MEM_TYPE_CASE(DDR5);
157 FF_ICTL_MEM_TYPE_CASE(LPDDR);
158 FF_ICTL_MEM_TYPE_CASE(LPDDR3);
159 FF_ICTL_MEM_TYPE_CASE(LPDDR4);
160 FF_ICTL_MEM_TYPE_CASE(LPDDR5);
161 FF_ICTL_MEM_TYPE_CASE(GDDR4);
162 FF_ICTL_MEM_TYPE_CASE(GDDR5);
163 FF_ICTL_MEM_TYPE_CASE(GDDR5X);
164 FF_ICTL_MEM_TYPE_CASE(GDDR6);
165 FF_ICTL_MEM_TYPE_CASE(GDDR6X);
166 FF_ICTL_MEM_TYPE_CASE(GDDR7);
167#undef FF_ICTL_MEM_TYPE_CASE
168 default:
169 ffStrbufSetF(result.memoryType, "Unknown (%u)", memoryProperties.type);
170 break;
171 }
172 }
173
174 ctl_mem_state_t memoryState = {
175 .Size = sizeof(ctl_mem_state_t),
176 .Version = 0,
177 };
178 if (igclData.ffctlMemoryGetState(memoryModules[iMem], &memoryState) == CTL_RESULT_SUCCESS) {
179 if (memoryProperties.location == CTL_MEM_LOC_DEVICE) {
180 result.memory->total += memoryState.size;
181 result.memory->used += memoryState.size - memoryState.free;
182 } else if (result.sharedMemory && memoryProperties.location == CTL_MEM_LOC_SYSTEM) {
183 result.sharedMemory->total += memoryState.size;
184 result.sharedMemory->used += memoryState.size - memoryState.free;
185 }
186 }
187 }
188 }
189 }
190 }
191
192 if (result.type) {
193 *result.type = properties.graphics_adapter_properties & CTL_ADAPTER_PROPERTIES_FLAG_INTEGRATED
194 ? FF_GPU_TYPE_INTEGRATED
195 : FF_GPU_TYPE_DISCRETE;
196 }
197
198 if (result.temp) {
199 ctl_temp_handle_t sensors[16];
200 uint32_t sensorCount = ARRAY_SIZE(sensors);
201 if (igclData.ffctlEnumTemperatureSensors(device, &sensorCount, sensors) == CTL_RESULT_SUCCESS && sensorCount > 0) {
202 for (uint32_t iSensor = 0; iSensor < sensorCount; iSensor++) {
203 ctl_temp_properties_t props = { .Size = sizeof(props) };
204 // The official sample code does not set Version
205 // https://github.com/intel/drivers.gpu.control-library/blob/1bbacbf3814f2fd0d2b930cdf42fad83f3628db9/Samples/Telemetry_Samples/Sample_TelemetryAPP.cpp#L256
206 if (igclData.ffctlTemperatureGetProperties(sensors[iSensor], &props) == CTL_RESULT_SUCCESS) {
207 if (props.type == CTL_TEMP_SENSORS_GPU) {
208 *result.temp = props.maxTemperature;
209 break;
210 }
211 }
212 }
213 }
214 }
215
216 if (result.frequency) {
217 ctl_freq_handle_t domains[16];
218 uint32_t domainCount = ARRAY_SIZE(domains);
219 if (igclData.ffctlEnumFrequencyDomains(device, &domainCount, domains) == CTL_RESULT_SUCCESS && domainCount > 0) {
220 double maxValue = 0;
221 ctl_freq_properties_t props = { .Size = sizeof(props), .Version = 0 };
222 for (uint32_t iDomain = 0; iDomain < domainCount; iDomain++) {
223 if (igclData.ffctlFrequencyGetProperties(domains[iDomain], &props) == CTL_RESULT_SUCCESS) {
224 if (props.type == CTL_FREQ_DOMAIN_GPU && props.max > maxValue) {
225 maxValue = props.max;
226 }
227 }
228 }
229 *result.frequency = (uint32_t) (maxValue + 0.5);
230 }
231 }
232
233 if (result.name) {
234 ffStrbufSetS(result.name, properties.name);
235 }
236
237 if (result.psMax) {
238 ctl_pci_properties_t pciProps = { .Size = sizeof(pciProps), .Version = 0 };
239 if (igclData.ffctlPciGetProperties(device, &pciProps) == CTL_RESULT_SUCCESS) {
240 if (pciProps.maxSpeed.gen > 0) {
241 result.psMax->gen = (uint16_t) pciProps.maxSpeed.gen;
242 }
243 if (pciProps.maxSpeed.width > 0) {
244 result.psMax->lanes = (uint16_t) pciProps.maxSpeed.width;
245 }
246 }
247 }
248 if (result.psCurr) {
249 ctl_pci_state_t pciState = { .Size = sizeof(pciState), .Version = 0 };
250 if (igclData.ffctlPciGetState(device, &pciState) == CTL_RESULT_SUCCESS) {
251 if (pciState.speed.gen > 0) {
252 result.psCurr->gen = (uint16_t) pciState.speed.gen;
253 }
254 if (pciState.speed.width > 0) {
255 result.psCurr->lanes = (uint16_t) pciState.speed.width;
256 }
257 }
258 }
259
260 return nullptr;
261}