From 4675008b57c41bd13c101cbc0b54c8064b16823b Mon Sep 17 00:00:00 2001 From: Lenny Komow Date: Mon, 9 Sep 2019 03:33:41 -0600 Subject: [PATCH] loader: Match ICDs against DXGI adapters Previously, ICDs located through the HKLM registry would be loaded all the time. This changes the loader to iterate DXGI adapters and only loader the driver is there is an adapter that it corresponds to. Change-Id: I204f37916b9e8ca668921507e68ef6e8e18a440a --- loader/CMakeLists.txt | 4 +- loader/loader.c | 102 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 3799e73592..5013439461 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -213,10 +213,10 @@ if(WIN32) endif() if(ENABLE_WIN10_ONECORE) - target_link_libraries(vulkan OneCoreUAP.lib LIBCMT.LIB LIBCMTD.LIB LIBVCRUNTIME.LIB LIBUCRT.LIB) + target_link_libraries(vulkan OneCoreUAP.lib LIBCMT.LIB LIBCMTD.LIB LIBVCRUNTIME.LIB LIBUCRT.LIB Dxgi) set_target_properties(vulkan PROPERTIES LINK_FLAGS "/NODEFAULTLIB") else() - target_link_libraries(vulkan Cfgmgr32) + target_link_libraries(vulkan Cfgmgr32 Dxgi) endif() add_dependencies(vulkan loader_asm_gen_files) diff --git a/loader/loader.c b/loader/loader.c index ef0d392894..2ad7775960 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -71,6 +71,7 @@ #include #include #include +#include typedef _Check_return_ NTSTATUS (APIENTRY *PFN_D3DKMTEnumAdapters2)(const D3DKMT_ENUMADAPTERS2*); typedef _Check_return_ NTSTATUS (APIENTRY *PFN_D3DKMTQueryAdapterInfo)(const D3DKMT_QUERYADAPTERINFO*); @@ -779,6 +780,41 @@ static char *loader_get_next_path(char *path); // When done using the returned string list, the caller should free the pointer. VkResult loaderGetRegistryFiles(const struct loader_instance *inst, char *location, bool use_secondary_hive, char **reg_data, PDWORD reg_data_size) { + // This list contains all of the allowed ICDs. This allows us to verify that a device is actually present from the vendor + // specified. This does disallow other vendors, but any new driver should use the device-specific registries anyway. + static const struct { + const char *filename; + int vendor_id; + } known_drivers[] = { +#if defined(_WIN64) + { + .filename = "igvk64.json", .vendor_id = 0x8086, + }, + { + .filename = "nvogl64.dll", .vendor_id = 0x10de, + }, + { + .filename = "nv-vk64.json", .vendor_id = 0x10de, + }, + { + .filename = "amdvlk64.json", .vendor_id = 0x1002, + }, +#else + { + .filename = "igvk32.json", .vendor_id = 0x8086, + }, + { + .filename = "nvogl64.json", .vendor_id = 0x10de, + }, + { + .filename = "nv-vk32.json", .vendor_id = 0x10de, + }, + { + .filename = "amdvlk32.json", .vendor_id = 0x1002, + }, +#endif + }; + LONG rtn_value; HKEY hive = DEFAULT_VK_REGISTRY_HIVE, key; DWORD access_flags; @@ -791,12 +827,25 @@ VkResult loaderGetRegistryFiles(const struct loader_instance *inst, char *locati DWORD value_size = sizeof(value); VkResult result = VK_SUCCESS; bool found = false; + IDXGIFactory1 *dxgi_factory = NULL; + bool is_driver = !strcmp(location, VK_DRIVERS_INFO_REGISTRY_LOC); if (NULL == reg_data) { result = VK_ERROR_INITIALIZATION_FAILED; goto out; } + if (is_driver) { + HRESULT hres = CreateDXGIFactory1(&IID_IDXGIFactory1, &dxgi_factory); + if (hres != S_OK) { + loader_log( + inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetRegistryFiles: Failed to create dxgi factory for ICD registry verification. No ICDs will be added from " + "legacy registry locations"); + goto out; + } + } + while (*loc) { next = loader_get_next_path(loc); access_flags = KEY_QUERY_VALUE; @@ -831,9 +880,59 @@ VkResult loaderGetRegistryFiles(const struct loader_instance *inst, char *locati *reg_data = new_ptr; *reg_data_size *= 2; } + + // We've now found a json file. If this is an ICD, we still need to check if there is actually a device + // that matches this ICD loader_log( inst, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0, "Located json file \"%s\" from registry \"%s\\%s\"", name, hive == DEFAULT_VK_REGISTRY_HIVE ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR, location); + if (is_driver) { + int i; + for (i = 0; i < sizeof(known_drivers) / sizeof(known_drivers[0]); ++i) { + if (!strcmp(name + strlen(name) - strlen(known_drivers[i].filename), known_drivers[i].filename)) { + break; + } + } + if (i == sizeof(known_drivers) / sizeof(known_drivers[0])) { + loader_log(inst, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0, + "Dropping driver %s as it was not recognized as a known driver", name); + continue; + } + + bool found_gpu = false; + for (int j = 0;; ++j) { + IDXGIAdapter1 *adapter; + HRESULT hres = dxgi_factory->lpVtbl->EnumAdapters1(dxgi_factory, j, &adapter); + if (hres == DXGI_ERROR_NOT_FOUND) { + break; + } else if (hres != S_OK) { + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "Failed to enumerate DXGI adapters at index %d. As a result, drivers may be skipped", j); + continue; + } + + DXGI_ADAPTER_DESC1 description; + hres = adapter->lpVtbl->GetDesc1(adapter, &description); + if (hres != S_OK) { + loader_log( + inst, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0, + "Failed to get DXGI adapter information at index %d. As a result, drivers may be skipped", j); + continue; + } + + if (description.VendorId == known_drivers[i].vendor_id) { + found_gpu = true; + break; + } + } + + if (!found_gpu) { + loader_log(inst, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0, + "Dropping driver %s as no corresponduing DXGI adapter was found", name); + continue; + } + } + if (strlen(*reg_data) == 0) { // The list is emtpy. Add the first entry. (void)snprintf(*reg_data, name_size + 1, "%s", name); @@ -886,6 +985,9 @@ VkResult loaderGetRegistryFiles(const struct loader_instance *inst, char *locati } out: + if (is_driver && dxgi_factory != NULL) { + dxgi_factory->lpVtbl->Release(dxgi_factory); + } return result; }