1/*
2 * Copyright (c) 2021-2022 The Khronos Group Inc.
3 * Copyright (c) 2021-2022 Valve Corporation
4 * Copyright (c) 2021-2022 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and/or associated documentation files (the "Materials"), to
8 * deal in the Materials without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Materials, and to permit persons to whom the Materials are
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice(s) and this permission notice shall be included in
14 * all copies or substantial portions of the Materials.
15 *
16 * THE MATERIALS ARE 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.
19 *
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23 * USE OR OTHER DEALINGS IN THE MATERIALS.
24 *
25 * Author: Charles Giessen <charles@lunarg.com>
26 */
27
28#pragma once
29
30#include "test_util.h"
31
32#include <unordered_set>
33#include <stdlib.h>
34
35#if defined(WIN32)
36#include <strsafe.h>
37#include <cfgmgr32.h>
38#include <initguid.h>
39#include <devpkey.h>
40#include <winternl.h>
41#include <appmodel.h>
42
43#define CINTERFACE
44#include <dxgi1_6.h>
45#include <adapters.h>
46#endif
47
48enum class ManifestCategory { implicit_layer, explicit_layer, icd, settings };
49enum class GpuType { unspecified, integrated, discrete, external };
50
51#if defined(WIN32)
52#define VK_VARIANT_REG_STR ""
53#define VK_VARIANT_REG_STR_W L""
54
55#define VK_DRIVERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\Drivers"
56#define VK_ELAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ExplicitLayers"
57#define VK_ILAYERS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\ImplicitLayers"
58#define VK_SETTINGS_INFO_REGISTRY_LOC "SOFTWARE\\Khronos\\Vulkan" VK_VARIANT_REG_STR "\\LoaderSettings"
59
60struct RegistryEntry {
61    RegistryEntry() = default;
62    RegistryEntry(std::string const& name) noexcept : name(name) {}
63    RegistryEntry(std::string const& name, DWORD value) noexcept : name(name), value(value) {}
64    std::string name;
65    DWORD value{};
66};
67
68struct HKeyHandle {
69    explicit HKeyHandle(const size_t value, const std::string& key_path) noexcept : key(HKEY{}), path(key_path) {
70        key = reinterpret_cast<HKEY>(value);
71    }
72
73    HKEY get() const noexcept { return key; }
74
75    HKEY key{};
76    std::string path;
77};
78
79static const char* pnp_registry_path = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}";
80
81// Needed for DXGI mocking
82struct KnownDriverData {
83    const char* filename = nullptr;
84    int vendor_id = 0;
85};
86static std::array<KnownDriverData, 4> known_driver_list = {
87#if defined(_WIN64)
88    KnownDriverData{"igvk64.json", 0x8086}, KnownDriverData{"nv-vk64.json", 0x10de}, KnownDriverData{"amd-vulkan64.json", 0x1002},
89    KnownDriverData{"amdvlk64.json", 0x1002}
90#else
91    KnownDriverData{"igvk32.json", 0x8086}, KnownDriverData{"nv-vk32.json", 0x10de}, KnownDriverData{"amd-vulkan32.json", 0x1002},
92    KnownDriverData{"amdvlk32.json", 0x1002}
93#endif
94};
95
96struct DXGIAdapter {
97    GpuType gpu_preference = GpuType::unspecified;
98    DXGI_ADAPTER_DESC1 desc1{};
99    uint32_t adapter_index = 0;
100    IDXGIAdapter1 adapter_instance{};
101    IDXGIAdapter1Vtbl adapter_vtbl_instance{};
102};
103
104struct D3DKMT_Adapter {
105    D3DKMT_Adapter& add_driver_manifest_path(fs::path const& src);
106    D3DKMT_Adapter& add_implicit_layer_manifest_path(fs::path const& src);
107    D3DKMT_Adapter& add_explicit_layer_manifest_path(fs::path const& src);
108
109    UINT hAdapter;
110    LUID adapter_luid;
111    std::vector<std::wstring> driver_paths;
112    std::vector<std::wstring> implicit_layer_paths;
113    std::vector<std::wstring> explicit_layer_paths;
114
115   private:
116    D3DKMT_Adapter& add_path(fs::path src, std::vector<std::wstring>& dest);
117};
118
119#elif COMMON_UNIX_PLATFORMS
120
121struct DirEntry {
122    DIR* directory = nullptr;
123    std::string folder_path;
124    std::vector<struct dirent*> contents;
125    // the current item being read by an app (incremented by readdir, reset to zero by opendir & closedir)
126    size_t current_index = 0;
127    bool is_fake_path = false;  // true when this entry is for folder redirection
128};
129
130#endif
131
132struct FrameworkEnvironment;  // forward declaration
133
134// Necessary to have inline definitions as shim is a dll and thus functions
135// defined in the .cpp wont be found by the rest of the application
136struct PlatformShim {
137    PlatformShim() = default;
138    PlatformShim(std::vector<fs::FolderManager>* folders) : folders(folders) {}
139
140    // Used to get info about which drivers & layers have been added to folders
141    std::vector<fs::FolderManager>* folders;
142
143    // Test Framework interface
144    void reset();
145
146    void redirect_all_paths(fs::path const& path);
147    void redirect_category(fs::path const& new_path, ManifestCategory category);
148
149    // fake paths are paths that the loader normally looks in but actually point to locations inside the test framework
150    void set_fake_path(ManifestCategory category, fs::path const& path);
151
152    // known paths are real paths but since the test framework guarantee's the order files are found in, files in these paths
153    // need to be ordered correctly
154    void add_known_path(fs::path const& path);
155
156    void add_manifest(ManifestCategory category, fs::path const& path);
157    void add_unsecured_manifest(ManifestCategory category, fs::path const& path);
158
159// platform specific shim interface
160#if defined(WIN32)
161    // Control Platform Elevation Level
162    void set_elevated_privilege(bool elev) { elevation_level = (elev) ? SECURITY_MANDATORY_HIGH_RID : SECURITY_MANDATORY_LOW_RID; }
163    unsigned long elevation_level = SECURITY_MANDATORY_LOW_RID;
164
165    void add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 desc1);
166    void add_d3dkmt_adapter(D3DKMT_Adapter const& adapter);
167    void set_app_package_path(fs::path const& path);
168
169    std::unordered_map<uint32_t, DXGIAdapter> dxgi_adapters;
170
171    std::vector<D3DKMT_Adapter> d3dkmt_adapters;
172
173    // TODO:
174    void add_CM_Device_ID(std::wstring const& id, fs::path const& icd_path, fs::path const& layer_path);
175    std::wstring CM_device_ID_list = {L'\0'};
176    std::vector<RegistryEntry> CM_device_ID_registry_keys;
177
178    uint32_t random_base_path = 0;
179
180    std::vector<fs::path> icd_paths;
181
182    std::vector<RegistryEntry> hkey_current_user_explicit_layers;
183    std::vector<RegistryEntry> hkey_current_user_implicit_layers;
184    std::vector<RegistryEntry> hkey_local_machine_explicit_layers;
185    std::vector<RegistryEntry> hkey_local_machine_implicit_layers;
186    std::vector<RegistryEntry> hkey_local_machine_drivers;
187    std::vector<RegistryEntry> hkey_local_machine_settings;
188    std::vector<RegistryEntry> hkey_current_user_settings;
189
190    std::wstring app_package_path;
191
192    // When a key is created, return the index of the
193    size_t created_key_count = 0;
194    std::vector<HKeyHandle> created_keys;
195
196#elif COMMON_UNIX_PLATFORMS
197    bool is_fake_path(fs::path const& path);
198    fs::path const& get_real_path_from_fake_path(fs::path const& path);
199
200    void redirect_path(fs::path const& path, fs::path const& new_path);
201    void remove_redirect(fs::path const& path);
202
203    bool is_known_path(fs::path const& path);
204    void remove_known_path(fs::path const& path);
205
206    void redirect_dlopen_name(fs::path const& filename, fs::path const& actual_path);
207    bool is_dlopen_redirect_name(fs::path const& filename);
208
209    fs::path query_default_redirect_path(ManifestCategory category);
210
211    std::unordered_map<std::string, fs::path> redirection_map;
212    std::unordered_map<std::string, fs::path> dlopen_redirection_map;
213    std::unordered_set<std::string> known_path_set;
214
215    void set_elevated_privilege(bool elev) { use_fake_elevation = elev; }
216    bool use_fake_elevation = false;
217
218    std::vector<DirEntry> dir_entries;
219
220#if defined(__APPLE__)
221    std::string bundle_contents;
222#endif
223#endif
224    bool is_during_destruction = false;
225};
226
227std::vector<std::string> parse_env_var_list(std::string const& var);
228std::string category_path_name(ManifestCategory category);
229
230std::vector<std::string> get_folder_contents(std::vector<fs::FolderManager>* folders, std::string folder_name) noexcept;
231
232extern "C" {
233// dynamically link on windows and macos
234#if defined(WIN32) || defined(__APPLE__)
235using PFN_get_platform_shim = PlatformShim* (*)(std::vector<fs::FolderManager>* folders);
236#define GET_PLATFORM_SHIM_STR "get_platform_shim"
237
238#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__)
239// statically link on linux
240PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders);
241#endif
242}
243