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