xref: /aosp_15_r20/external/OpenCL-ICD-Loader/loader/windows/icd_windows_hkr.c (revision 1cddb830dba8aa7c1cc1039338e56b3b9fa24952)
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