Added OpenCL Universal Driver Support for Win10 RS3 (#21)
[platform/upstream/OpenCL-ICD-Loader.git] / icd_windows_hkr.c
1 /*
2  * Copyright (c) 2017 The Khronos Group Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software source and associated documentation files (the "Materials"),
6  * to deal in the Materials without restriction, including without limitation
7  * the rights to use, copy, modify, compile, merge, publish, distribute,
8  * sublicense, and/or sell copies of the Materials, and to permit persons to
9  * whom the Materials are furnished to do so, subject the following terms and
10  * conditions:
11  *
12  * All modifications to the Materials used to create a binary that is
13  * distributed to third parties shall be provided to Khronos with an
14  * unrestricted license to use for the purposes of implementing bug fixes and
15  * enhancements to the Materials;
16  *
17  * If the binary is used as part of an OpenCL(TM) implementation, whether binary
18  * is distributed together with or separately to that implementation, then
19  * recipient must become an OpenCL Adopter and follow the published OpenCL
20  * conformance process for that implementation, details at:
21  * http://www.khronos.org/conformance/;
22  *
23  * The above copyright notice, the OpenCL trademark license, and this permission
24  * notice shall be included in all copies or substantial portions of the
25  * Materials.
26  *
27  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32  * OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN
33  * THE MATERIALS.
34  *
35  * OpenCL is a trademark of Apple Inc. used under license by Khronos.
36  */
37
38 #include "icd.h"
39 #include "icd_windows_hkr.h"
40 #include <windows.h>
41 #include <cfgmgr32.h>
42 #include <assert.h>
43 #include <stdbool.h>
44 #include <initguid.h>
45 #include <Devpkey.h>
46 #include <devguid.h>
47
48  // This GUID was only added to devguid.h on Windows SDK v10.0.16232 which
49  // corresponds to Windows 10 Redstone 3 (Windows 10 Fall Creators Update).
50 DEFINE_GUID(OCL_GUID_DEVCLASS_SOFTWARECOMPONENT, 0x5c4c3332, 0x344d, 0x483c, 0x87, 0x39, 0x25, 0x9e, 0x93, 0x4c, 0x9c, 0xc8);
51
52 typedef enum
53 {
54     ProbeFailure,
55     PendingReboot,
56     Valid
57 } DeviceProbeResult;
58
59 #define KHR_SAFE_RELEASE(mem)       \
60     do                              \
61     {                               \
62         free(mem);                  \
63         mem = NULL;                 \
64     } while (0)
65
66 #ifdef _WIN64
67 static const char OPENCL_REG_SUB_KEY[] = "OpenCLDriverName";
68 #else
69 static const char OPENCL_REG_SUB_KEY[] = "OpenCLDriverNameWow";
70 #endif
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             OPENCL_REG_SUB_KEY,
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_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 }