1 /*
2 * Copyright (c) 2017-2022 The Khronos Group Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * OpenCL is a trademark of Apple Inc. used under license by Khronos.
17 */
18
19 #include "icd.h"
20 #include "icd_windows_hkr.h"
21 #include <windows.h>
22 #include "icd_windows_dxgk.h"
23 #include <cfgmgr32.h>
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <initguid.h>
27 #include <devpkey.h>
28 #include <devguid.h>
29
30 // This GUID was only added to devguid.h on Windows SDK v10.0.16232 which
31 // corresponds to Windows 10 Redstone 3 (Windows 10 Fall Creators Update).
32 DEFINE_GUID(OCL_GUID_DEVCLASS_SOFTWARECOMPONENT, 0x5c4c3332, 0x344d, 0x483c, 0x87, 0x39, 0x25, 0x9e, 0x93, 0x4c, 0x9c, 0xc8);
33
34 typedef enum
35 {
36 ProbeFailure,
37 PendingReboot,
38 Valid
39 } DeviceProbeResult;
40
41 #define KHR_SAFE_RELEASE(mem) \
42 do \
43 { \
44 free(mem); \
45 mem = NULL; \
46 } while (0)
47
48 static const char OPENCL_REG_SUB_KEY[] = "OpenCLDriverName";
49
50 #ifndef _WIN64
51 static const char OPENCL_REG_SUB_KEY_WOW[] = "OpenCLDriverNameWow";
52 #endif
53
54 // Do not free the memory returned by this function.
getOpenCLRegKeyName(void)55 const char* getOpenCLRegKeyName(void)
56 {
57 #ifdef _WIN64
58 return OPENCL_REG_SUB_KEY;
59 #else
60 // The suffix/substring "WoW" is meaningful only when a 32-bit
61 // application is running on a 64-bit Windows OS. A 32-bit application
62 // running on a 32-bit OS uses non-WoW names.
63 BOOL is_wow64;
64 if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)
65 {
66 return OPENCL_REG_SUB_KEY_WOW;
67 }
68
69 return OPENCL_REG_SUB_KEY;
70 #endif
71 }
72
ReadOpenCLKey(DEVINST dnDevNode)73 static bool ReadOpenCLKey(DEVINST dnDevNode)
74 {
75 HKEY hkey = 0;
76 CONFIGRET ret;
77 bool bRet = false;
78 DWORD dwLibraryNameType = 0;
79 char *cszOclPath = NULL;
80 DWORD dwOclPathSize = 0;
81 LSTATUS result;
82
83 ret = CM_Open_DevNode_Key(
84 dnDevNode,
85 KEY_QUERY_VALUE,
86 0,
87 RegDisposition_OpenExisting,
88 &hkey,
89 CM_REGISTRY_SOFTWARE);
90
91 if (CR_SUCCESS != ret)
92 {
93 KHR_ICD_TRACE("Failed with ret 0x%"PRIxDW"\n", ret);
94 goto out;
95 }
96 else
97 {
98 result = RegQueryValueExA(
99 hkey,
100 getOpenCLRegKeyName(),
101 NULL,
102 &dwLibraryNameType,
103 NULL,
104 &dwOclPathSize);
105
106 if (ERROR_SUCCESS != result)
107 {
108 KHR_ICD_TRACE("Failed to open sub key 0x%"PRIxDW"\n", result);
109 goto out;
110 }
111
112 cszOclPath = malloc(dwOclPathSize);
113 if (NULL == cszOclPath)
114 {
115 KHR_ICD_TRACE("Failed to allocate %"PRIuDW" bytes for registry value\n", dwOclPathSize);
116 goto out;
117 }
118
119 result = RegQueryValueExA(
120 hkey,
121 getOpenCLRegKeyName(),
122 NULL,
123 &dwLibraryNameType,
124 (LPBYTE)cszOclPath,
125 &dwOclPathSize);
126 if (ERROR_SUCCESS != result)
127 {
128 KHR_ICD_TRACE("Failed to open sub key 0x%"PRIxDW"\n", result);
129 goto out;
130 }
131
132 if (REG_SZ != dwLibraryNameType)
133 {
134 if (REG_MULTI_SZ == dwLibraryNameType)
135 {
136 KHR_ICD_TRACE("Accepting multi-string registry key type\n");
137 }
138 else
139 {
140 KHR_ICD_TRACE("Unexpected registry entry 0x%"PRIxDW"! continuing\n", dwLibraryNameType);
141 goto out;
142 }
143 }
144
145 KHR_ICD_TRACE(" Path: %s\n", cszOclPath);
146
147 bRet |= adapterAdd(cszOclPath, ZeroLuid);
148 }
149
150 out:
151 free(cszOclPath);
152
153 if (hkey)
154 {
155 result = RegCloseKey(hkey);
156 if (ERROR_SUCCESS != result)
157 {
158 KHR_ICD_TRACE("WARNING: failed to close hkey 0x%"PRIxDW"\n", result);
159 }
160 }
161
162 return bRet;
163 }
164
ProbeDevice(DEVINST devnode)165 static DeviceProbeResult ProbeDevice(DEVINST devnode)
166 {
167 CONFIGRET ret;
168 ULONG ulStatus;
169 ULONG ulProblem;
170
171 ret = CM_Get_DevNode_Status(
172 &ulStatus,
173 &ulProblem,
174 devnode,
175 0);
176
177 if (CR_SUCCESS != ret)
178 {
179 KHR_ICD_TRACE(" WARNING: failed to probe the status of the device 0x%"PRIxDW"\n", ret);
180 return ProbeFailure;
181 }
182
183 //
184 // Careful here, we need to check 2 scenarios:
185 // 1. DN_NEED_RESTART
186 // status flag indicates that a reboot is needed when an _already started_
187 // device cannot be stopped. This covers devices that are still started with their
188 // old KMD (because they couldn't be stopped/restarted) while the UMD is updated
189 // and possibly out of sync.
190 //
191 // 2. Status & DN_HAS_PROBLEM && Problem == CM_PROB_NEED_RESTART
192 // indicates that a reboot is needed when a _stopped device_ cannot be (re)started.
193 //
194 if (((ulStatus & DN_HAS_PROBLEM) && ulProblem == CM_PROB_NEED_RESTART) ||
195 ulStatus & DN_NEED_RESTART)
196 {
197 KHR_ICD_TRACE(" WARNING: device is pending reboot (0x%" PRIxUL "), skipping...\n", ulStatus);
198 return PendingReboot;
199 }
200
201 return Valid;
202 }
203
204 // Tries to look for the OpenCL key under the display devices and
205 // if not found, falls back to software component devices.
khrIcdOsVendorsEnumerateHKR(void)206 bool khrIcdOsVendorsEnumerateHKR(void)
207 {
208 CONFIGRET ret;
209 int iret;
210 bool foundOpenCLKey = false;
211 DEVINST devinst = 0;
212 DEVINST devchild = 0;
213 wchar_t *deviceIdList = NULL;
214 ULONG szBuffer = 0;
215
216 OLECHAR display_adapter_guid_str[MAX_GUID_STRING_LEN];
217 #if defined(CM_GETIDLIST_FILTER_CLASS) && defined(CM_GETIDLIST_FILTER_PRESENT)
218 ULONG ulFlags = CM_GETIDLIST_FILTER_CLASS |
219 CM_GETIDLIST_FILTER_PRESENT;
220 #else
221 ULONG ulFlags = 0x300;
222 #endif
223
224 iret = StringFromGUID2(
225 &GUID_DEVCLASS_DISPLAY,
226 display_adapter_guid_str,
227 MAX_GUID_STRING_LEN);
228
229 if (MAX_GUID_STRING_LEN != iret)
230 {
231 KHR_ICD_TRACE("StringFromGUID2 failed with %d\n", iret);
232 goto out;
233 }
234
235 // Paranoia: we might have a new device added to the list between the call
236 // to CM_Get_Device_ID_List_Size() and the call to CM_Get_Device_ID_List().
237 do
238 {
239 ret = CM_Get_Device_ID_List_SizeW(
240 &szBuffer,
241 display_adapter_guid_str,
242 ulFlags);
243
244 if (CR_SUCCESS != ret)
245 {
246 KHR_ICD_TRACE("CM_Get_Device_ID_List_size failed with 0x%"PRIxDW"\n", ret);
247 break;
248 }
249
250 // "pulLen [out] Receives a value representing the required buffer
251 // size, in characters."
252 // So we need to allocate the right size in bytes but we still need
253 // to keep szBuffer as it was returned from CM_Get_Device_ID_List_Size so
254 // the call to CM_Get_Device_ID_List will receive the correct size.
255 deviceIdList = malloc(szBuffer * sizeof(wchar_t));
256 if (NULL == deviceIdList)
257 {
258 KHR_ICD_TRACE("Failed to allocate %" PRIuUL " bytes for device ID strings\n", szBuffer);
259 break;
260 }
261
262 ret = CM_Get_Device_ID_ListW(
263 display_adapter_guid_str,
264 deviceIdList,
265 szBuffer,
266 ulFlags);
267
268 if (CR_SUCCESS != ret)
269 {
270 KHR_ICD_TRACE("CM_Get_Device_ID_List failed with 0x%"PRIxDW"\n", ret);
271 KHR_SAFE_RELEASE(deviceIdList);
272 }
273 } while (CR_BUFFER_SMALL == ret);
274
275 if (NULL == deviceIdList)
276 {
277 goto out;
278 }
279
280 for (PWSTR deviceId = deviceIdList; *deviceId; deviceId += wcslen(deviceId) + 1)
281 {
282 KHR_ICD_WIDE_TRACE(L"Device ID: %ls\n", deviceId);
283
284 ret = CM_Locate_DevNodeW(&devinst, deviceId, 0);
285 if (CR_SUCCESS == ret)
286 {
287 KHR_ICD_TRACE(" devinst: %lu\n", devinst);
288 }
289 else
290 {
291 KHR_ICD_TRACE("CM_Locate_DevNode failed with 0x%"PRIxDW"\n", ret);
292 continue;
293 }
294
295 if (ProbeDevice(devinst) != Valid)
296 {
297 continue;
298 }
299
300 KHR_ICD_TRACE(" Trying to look for the key in the display adapter HKR...\n");
301 if (ReadOpenCLKey(devinst))
302 {
303 foundOpenCLKey = true;
304 continue;
305 }
306
307 KHR_ICD_TRACE(" Could not find the key, proceeding to children software components...\n");
308
309 ret = CM_Get_Child(
310 &devchild,
311 devinst,
312 0);
313
314 if (CR_SUCCESS != ret)
315 {
316 KHR_ICD_TRACE(" CM_Get_Child returned 0x%"PRIxDW", skipping children...\n", ret);
317 }
318 else
319 {
320 do
321 {
322 wchar_t deviceInstanceID[MAX_DEVICE_ID_LEN] = { 0 };
323 GUID guid;
324 ULONG szGuid = sizeof(guid);
325
326 KHR_ICD_TRACE(" devchild: %lu\n", devchild);
327 ret = CM_Get_Device_IDW(
328 devchild,
329 deviceInstanceID,
330 sizeof(deviceInstanceID),
331 0);
332
333 if (CR_SUCCESS != ret)
334 {
335 KHR_ICD_TRACE(" CM_Get_Device_ID returned 0x%"PRIxDW", skipping device...\n", ret);
336 continue;
337 }
338 else
339 {
340 KHR_ICD_WIDE_TRACE(L" deviceInstanceID: %ls\n", deviceInstanceID);
341 }
342
343 ret = CM_Get_DevNode_Registry_PropertyW(
344 devchild,
345 CM_DRP_CLASSGUID,
346 NULL,
347 (PBYTE)&guid,
348 &szGuid,
349 0);
350
351 if (CR_SUCCESS != ret)
352 {
353 KHR_ICD_TRACE(" CM_Get_DevNode_Registry_PropertyW returned 0x%"PRIxDW", skipping device...\n", ret);
354 continue;
355 }
356 else if (!IsEqualGUID(&OCL_GUID_DEVCLASS_SOFTWARECOMPONENT, &guid))
357 {
358 KHR_ICD_TRACE(" GUID does not match, skipping device...\n");
359 continue;
360 }
361
362 if (ProbeDevice(devchild) != Valid)
363 {
364 continue;
365 }
366
367 if (ReadOpenCLKey(devchild))
368 {
369 foundOpenCLKey = true;
370 break;
371 }
372 } while (CM_Get_Sibling(&devchild, devchild, 0) == CR_SUCCESS);
373 }
374 }
375
376 out:
377 free(deviceIdList);
378 return foundOpenCLKey;
379 }
380