radv: Fix various non-critical integer overflows
[mesa.git] / src / vulkan / device-select-layer / device_select_layer.c
1 /*
2 * Copyright © 2017 Google
3 * Copyright © 2019 Red Hat
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 /* Rules for device selection.
26 * Is there an X or wayland connection open (or DISPLAY set).
27 * If no - try and find which device was the boot_vga device.
28 * If yes - try and work out which device is the connection primary,
29 * DRI_PRIME tagged overrides only work if bus info, =1 will just pick an alternate.
30 */
31
32 #include <vulkan/vk_layer.h>
33
34 #include <assert.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39
40 #include "device_select.h"
41 #include "c99_compat.h"
42 #include "hash_table.h"
43 #include "vk_util.h"
44 #include "c11/threads.h"
45
46 struct instance_info {
47 PFN_vkDestroyInstance DestroyInstance;
48 PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
49 PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
50 PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
51 PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr;
52 PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
53 PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
54 PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
55 bool has_props2, has_pci_bus;
56 bool has_wayland, has_xcb;
57 };
58
59 static struct hash_table *device_select_instance_ht = NULL;
60 static mtx_t device_select_mutex;
61
62 static once_flag device_select_is_init = ONCE_FLAG_INIT;
63
64 static void device_select_once_init(void) {
65 mtx_init(&device_select_mutex, mtx_plain);
66 }
67
68 static void
69 device_select_init_instances(void)
70 {
71 call_once(&device_select_is_init, device_select_once_init);
72
73 mtx_lock(&device_select_mutex);
74 if (!device_select_instance_ht)
75 device_select_instance_ht = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
76 _mesa_key_pointer_equal);
77 mtx_unlock(&device_select_mutex);
78 }
79
80 static void
81 device_select_try_free_ht(void)
82 {
83 mtx_lock(&device_select_mutex);
84 if (device_select_instance_ht) {
85 if (_mesa_hash_table_num_entries(device_select_instance_ht) == 0) {
86 _mesa_hash_table_destroy(device_select_instance_ht, NULL);
87 device_select_instance_ht = NULL;
88 }
89 }
90 mtx_unlock(&device_select_mutex);
91 }
92
93 static void
94 device_select_layer_add_instance(VkInstance instance, struct instance_info *info)
95 {
96 device_select_init_instances();
97 mtx_lock(&device_select_mutex);
98 _mesa_hash_table_insert(device_select_instance_ht, instance, info);
99 mtx_unlock(&device_select_mutex);
100 }
101
102 static struct instance_info *
103 device_select_layer_get_instance(VkInstance instance)
104 {
105 struct hash_entry *entry;
106 struct instance_info *info = NULL;
107 mtx_lock(&device_select_mutex);
108 entry = _mesa_hash_table_search(device_select_instance_ht, (void *)instance);
109 if (entry)
110 info = (struct instance_info *)entry->data;
111 mtx_unlock(&device_select_mutex);
112 return info;
113 }
114
115 static void
116 device_select_layer_remove_instance(VkInstance instance)
117 {
118 mtx_lock(&device_select_mutex);
119 _mesa_hash_table_remove_key(device_select_instance_ht, instance);
120 mtx_unlock(&device_select_mutex);
121 device_select_try_free_ht();
122 }
123
124 static VkResult device_select_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
125 const VkAllocationCallbacks *pAllocator,
126 VkInstance *pInstance)
127 {
128 VkLayerInstanceCreateInfo *chain_info;
129 for(chain_info = (VkLayerInstanceCreateInfo*)pCreateInfo->pNext; chain_info; chain_info = (VkLayerInstanceCreateInfo*)chain_info->pNext)
130 if(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && chain_info->function == VK_LAYER_LINK_INFO)
131 break;
132
133 assert(chain_info->u.pLayerInfo);
134 struct instance_info *info = (struct instance_info *)calloc(1, sizeof(struct instance_info));
135
136 info->GetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
137 PFN_vkCreateInstance fpCreateInstance =
138 (PFN_vkCreateInstance)info->GetInstanceProcAddr(NULL, "vkCreateInstance");
139 if (fpCreateInstance == NULL) {
140 free(info);
141 return VK_ERROR_INITIALIZATION_FAILED;
142 }
143
144 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
145
146 VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
147 if (result != VK_SUCCESS) {
148 free(info);
149 return result;
150 }
151
152 for (unsigned i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
153 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
154 info->has_props2 = true;
155 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
156 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME))
157 info->has_wayland = true;
158 #endif
159 #ifdef VK_USE_PLATFORM_XCB_KHR
160 if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME))
161 info->has_xcb = true;
162 #endif
163 }
164
165 info->GetPhysicalDeviceProcAddr = (PFN_GetPhysicalDeviceProcAddr)info->GetInstanceProcAddr(*pInstance, "vk_layerGetPhysicalDeviceProcAddr");
166 #define DEVSEL_GET_CB(func) info->func = (PFN_vk##func)info->GetInstanceProcAddr(*pInstance, "vk" #func)
167 DEVSEL_GET_CB(DestroyInstance);
168 DEVSEL_GET_CB(EnumeratePhysicalDevices);
169 DEVSEL_GET_CB(EnumeratePhysicalDeviceGroups);
170 DEVSEL_GET_CB(GetPhysicalDeviceProperties);
171 DEVSEL_GET_CB(EnumerateDeviceExtensionProperties);
172 if (info->has_props2)
173 DEVSEL_GET_CB(GetPhysicalDeviceProperties2KHR);
174 #undef DEVSEL_GET_CB
175
176 device_select_layer_add_instance(*pInstance, info);
177
178 return VK_SUCCESS;
179 }
180
181 static void device_select_DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator)
182 {
183 struct instance_info *info = device_select_layer_get_instance(instance);
184
185 device_select_layer_remove_instance(instance);
186 info->DestroyInstance(instance, pAllocator);
187 free(info);
188 }
189
190
191 static void print_gpu(const struct instance_info *info, unsigned index, VkPhysicalDevice device)
192 {
193 const char *type = "";
194 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
195 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
196 };
197 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
198 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
199 };
200 if (info->has_props2 && info->has_pci_bus)
201 properties.pNext = &ext_pci_properties;
202 if (info->GetPhysicalDeviceProperties2KHR)
203 info->GetPhysicalDeviceProperties2KHR(device, &properties);
204 else
205 info->GetPhysicalDeviceProperties(device, &properties.properties);
206
207 switch(properties.properties.deviceType) {
208 case VK_PHYSICAL_DEVICE_TYPE_OTHER:
209 default:
210 type = "other";
211 break;
212 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
213 type = "integrated GPU";
214 break;
215 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
216 type = "discrete GPU";
217 break;
218 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
219 type = "virtual GPU";
220 break;
221 case VK_PHYSICAL_DEVICE_TYPE_CPU:
222 type = "CPU";
223 break;
224 }
225 fprintf(stderr, " GPU %d: %x:%x \"%s\" %s", index, properties.properties.vendorID,
226 properties.properties.deviceID, properties.properties.deviceName, type);
227 if (info->has_pci_bus)
228 fprintf(stderr, " %04x:%02x:%02x.%x", ext_pci_properties.pciDomain,
229 ext_pci_properties.pciBus, ext_pci_properties.pciDevice,
230 ext_pci_properties.pciFunction);
231 fprintf(stderr, "\n");
232 }
233
234 static void fill_drm_device_info(const struct instance_info *info,
235 struct device_pci_info *drm_device,
236 VkPhysicalDevice device)
237 {
238 VkPhysicalDevicePCIBusInfoPropertiesEXT ext_pci_properties = (VkPhysicalDevicePCIBusInfoPropertiesEXT) {
239 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
240 };
241
242 VkPhysicalDeviceProperties2KHR properties = (VkPhysicalDeviceProperties2KHR){
243 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR
244 };
245
246 if (info->has_props2 && info->has_pci_bus)
247 properties.pNext = &ext_pci_properties;
248 if (info->GetPhysicalDeviceProperties2KHR)
249 info->GetPhysicalDeviceProperties2KHR(device, &properties);
250 else
251 info->GetPhysicalDeviceProperties(device, &properties.properties);
252
253 drm_device->dev_info.vendor_id = properties.properties.vendorID;
254 drm_device->dev_info.device_id = properties.properties.deviceID;
255 if (info->has_pci_bus) {
256 drm_device->has_bus_info = true;
257 drm_device->bus_info.domain = ext_pci_properties.pciDomain;
258 drm_device->bus_info.bus = ext_pci_properties.pciBus;
259 drm_device->bus_info.dev = ext_pci_properties.pciDevice;
260 drm_device->bus_info.func = ext_pci_properties.pciFunction;
261 }
262 }
263
264 static int device_select_find_explicit_default(struct device_pci_info *pci_infos,
265 uint32_t device_count,
266 const char *selection)
267 {
268 int default_idx = -1;
269 unsigned vendor_id, device_id;
270 int matched = sscanf(selection, "%x:%x", &vendor_id, &device_id);
271 if (matched != 2)
272 return default_idx;
273
274 for (unsigned i = 0; i < device_count; ++i) {
275 if (pci_infos[i].dev_info.vendor_id == vendor_id &&
276 pci_infos[i].dev_info.device_id == device_id)
277 default_idx = i;
278 }
279 return default_idx;
280 }
281
282 static int device_select_find_dri_prime_tag_default(struct device_pci_info *pci_infos,
283 uint32_t device_count,
284 const char *dri_prime)
285 {
286 int default_idx = -1;
287 for (unsigned i = 0; i < device_count; ++i) {
288 char *tag = NULL;
289 if (asprintf(&tag, "pci-%04x_%02x_%02x_%1u",
290 pci_infos[i].bus_info.domain,
291 pci_infos[i].bus_info.bus,
292 pci_infos[i].bus_info.dev,
293 pci_infos[i].bus_info.func) >= 0) {
294 if (strcmp(dri_prime, tag))
295 default_idx = i;
296 }
297 free(tag);
298 }
299 return default_idx;
300 }
301
302 static int device_select_find_boot_vga_default(struct device_pci_info *pci_infos,
303 uint32_t device_count)
304 {
305 char boot_vga_path[1024];
306 int default_idx = -1;
307 for (unsigned i = 0; i < device_count; ++i) {
308 /* fallback to probing the pci bus boot_vga device. */
309 snprintf(boot_vga_path, 1023, "/sys/bus/pci/devices/%04x:%02x:%02x.%x/boot_vga", pci_infos[i].bus_info.domain,
310 pci_infos[i].bus_info.bus, pci_infos[i].bus_info.dev, pci_infos[i].bus_info.func);
311 int fd = open(boot_vga_path, O_RDONLY);
312 if (fd != -1) {
313 uint8_t val;
314 if (read(fd, &val, 1) == 1) {
315 if (val == '1')
316 default_idx = i;
317 }
318 close(fd);
319 }
320 if (default_idx != -1)
321 break;
322 }
323 return default_idx;
324 }
325
326 static uint32_t get_default_device(const struct instance_info *info,
327 const char *selection,
328 uint32_t physical_device_count,
329 VkPhysicalDevice *pPhysicalDevices)
330 {
331 int default_idx = -1;
332 const char *dri_prime = getenv("DRI_PRIME");
333 bool dri_prime_is_one = false;
334
335 if (dri_prime && !strcmp(dri_prime, "1"))
336 dri_prime_is_one = true;
337
338 if (dri_prime && !dri_prime_is_one && !info->has_pci_bus) {
339 fprintf(stderr, "device-select: cannot correctly use DRI_PRIME tag\n");
340 }
341
342 struct device_pci_info *pci_infos = (struct device_pci_info *)calloc(physical_device_count, sizeof(struct device_pci_info));
343 if (!pci_infos)
344 return 0;
345
346 for (unsigned i = 0; i < physical_device_count; ++i) {
347 fill_drm_device_info(info, &pci_infos[i], pPhysicalDevices[i]);
348 }
349
350 if (selection)
351 default_idx = device_select_find_explicit_default(pci_infos, physical_device_count, selection);
352 if (default_idx == -1 && info->has_pci_bus && dri_prime && !dri_prime_is_one)
353 default_idx = device_select_find_dri_prime_tag_default(pci_infos, physical_device_count, dri_prime);
354 if (default_idx == -1 && info->has_wayland)
355 default_idx = device_select_find_wayland_pci_default(pci_infos, physical_device_count);
356 if (default_idx == -1 && info->has_xcb)
357 default_idx = device_select_find_xcb_pci_default(pci_infos, physical_device_count);
358 if (info->has_pci_bus && default_idx == -1) {
359 default_idx = device_select_find_boot_vga_default(pci_infos, physical_device_count);
360 }
361
362 /* DRI_PRIME=1 handling - pick any other device than default. */
363 if (default_idx != -1 && dri_prime_is_one && physical_device_count > 1) {
364 if (default_idx == 0)
365 default_idx = 1;
366 else if (default_idx == 1)
367 default_idx = 0;
368 }
369 free(pci_infos);
370 return default_idx == -1 ? 0 : default_idx;
371 }
372
373 static VkResult device_select_EnumeratePhysicalDevices(VkInstance instance,
374 uint32_t* pPhysicalDeviceCount,
375 VkPhysicalDevice *pPhysicalDevices)
376 {
377 struct instance_info *info = device_select_layer_get_instance(instance);
378 uint32_t physical_device_count = 0;
379 uint32_t selected_physical_device_count = 0;
380 const char* selection = getenv("MESA_VK_DEVICE_SELECT");
381 VkResult result = info->EnumeratePhysicalDevices(instance, &physical_device_count, NULL);
382 VK_OUTARRAY_MAKE(out, pPhysicalDevices, pPhysicalDeviceCount);
383 if (result != VK_SUCCESS)
384 return result;
385
386 VkPhysicalDevice *physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice), physical_device_count);
387 VkPhysicalDevice *selected_physical_devices = (VkPhysicalDevice*)calloc(sizeof(VkPhysicalDevice),
388 physical_device_count);
389
390 if (!physical_devices || !selected_physical_devices) {
391 result = VK_ERROR_OUT_OF_HOST_MEMORY;
392 goto out;
393 }
394
395 result = info->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices);
396 if (result != VK_SUCCESS)
397 goto out;
398
399 for (unsigned i = 0; i < physical_device_count; i++) {
400 uint32_t count;
401 info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, NULL);
402 if (count > 0) {
403 VkExtensionProperties *extensions = calloc(count, sizeof(VkExtensionProperties));
404 if (info->EnumerateDeviceExtensionProperties(physical_devices[i], NULL, &count, extensions) == VK_SUCCESS) {
405 for (unsigned j = 0; j < count; j++) {
406 if (!strcmp(extensions[j].extensionName, VK_EXT_PCI_BUS_INFO_EXTENSION_NAME))
407 info->has_pci_bus = true;
408 }
409 }
410 free(extensions);
411 }
412 }
413 if (selection && strcmp(selection, "list") == 0) {
414 fprintf(stderr, "selectable devices:\n");
415 for (unsigned i = 0; i < physical_device_count; ++i)
416 print_gpu(info, i, physical_devices[i]);
417 exit(0);
418 } else {
419 unsigned selected_index = get_default_device(info, selection, physical_device_count, physical_devices);
420 selected_physical_device_count = physical_device_count;
421 selected_physical_devices[0] = physical_devices[selected_index];
422 for (unsigned i = 0; i < physical_device_count - 1; ++i) {
423 unsigned this_idx = i < selected_index ? i : i + 1;
424 selected_physical_devices[i + 1] = physical_devices[this_idx];
425 }
426 }
427
428 if (selected_physical_device_count == 0) {
429 fprintf(stderr, "WARNING: selected no devices with MESA_VK_DEVICE_SELECT\n");
430 }
431
432 assert(result == VK_SUCCESS);
433
434 for (unsigned i = 0; i < selected_physical_device_count; i++) {
435 vk_outarray_append(&out, ent) {
436 *ent = selected_physical_devices[i];
437 }
438 }
439 result = vk_outarray_status(&out);
440 out:
441 free(physical_devices);
442 free(selected_physical_devices);
443 return result;
444 }
445
446 static VkResult device_select_EnumeratePhysicalDeviceGroups(VkInstance instance,
447 uint32_t* pPhysicalDeviceGroupCount,
448 VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroups)
449 {
450 struct instance_info *info = device_select_layer_get_instance(instance);
451 VkResult result = info->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroups);
452 return result;
453 }
454
455 static void (*get_pdevice_proc_addr(VkInstance instance, const char* name))()
456 {
457 struct instance_info *info = device_select_layer_get_instance(instance);
458 return info->GetPhysicalDeviceProcAddr(instance, name);
459 }
460
461 static void (*get_instance_proc_addr(VkInstance instance, const char* name))()
462 {
463 if (strcmp(name, "vkCreateInstance") == 0)
464 return (void(*)())device_select_CreateInstance;
465 if (strcmp(name, "vkDestroyInstance") == 0)
466 return (void(*)())device_select_DestroyInstance;
467 if (strcmp(name, "vkEnumeratePhysicalDevices") == 0)
468 return (void(*)())device_select_EnumeratePhysicalDevices;
469 if (strcmp(name, "vkEnumeratePhysicalDeviceGroups") == 0)
470 return (void(*)())device_select_EnumeratePhysicalDeviceGroups;
471
472 struct instance_info *info = device_select_layer_get_instance(instance);
473 return info->GetInstanceProcAddr(instance, name);
474 }
475
476 VK_LAYER_EXPORT VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
477 {
478 if (pVersionStruct->loaderLayerInterfaceVersion < 2)
479 return VK_ERROR_INITIALIZATION_FAILED;
480 pVersionStruct->loaderLayerInterfaceVersion = 2;
481
482 pVersionStruct->pfnGetInstanceProcAddr = get_instance_proc_addr;
483 pVersionStruct->pfnGetPhysicalDeviceProcAddr = get_pdevice_proc_addr;
484
485 return VK_SUCCESS;
486 }