/src/vulkan-loader/loader/unknown_function_handling.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022 The Khronos Group Inc. |
3 | | * Copyright (c) 2022 Valve Corporation |
4 | | * Copyright (c) 2022 LunarG, Inc. |
5 | | * |
6 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | * you may not use this file except in compliance with the License. |
8 | | * You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | * See the License for the specific language governing permissions and |
16 | | * limitations under the License. |
17 | | * |
18 | | * Author: Jon Ashburn <jon@lunarg.com> |
19 | | * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> |
20 | | * Author: Mark Young <marky@lunarg.com> |
21 | | * Author: Lenny Komow <lenny@lunarg.com> |
22 | | * Author: Charles Giessen <charles@lunarg.com> |
23 | | */ |
24 | | |
25 | | #include "unknown_function_handling.h" |
26 | | |
27 | | #include "allocation.h" |
28 | | #include "log.h" |
29 | | |
30 | | // Forward declarations |
31 | | void *loader_get_dev_ext_trampoline(uint32_t index); |
32 | | void *loader_get_phys_dev_ext_tramp(uint32_t index); |
33 | | void *loader_get_phys_dev_ext_termin(uint32_t index); |
34 | | |
35 | | // Device function handling |
36 | | |
37 | | // Initialize device_ext dispatch table entry as follows: |
38 | | // If dev == NULL find all logical devices created within this instance and |
39 | | // init the entry (given by idx) in the ext dispatch table. |
40 | | // If dev != NULL only initialize the entry in the given dev's dispatch table. |
41 | | // The initialization value is gotten by calling down the device chain with |
42 | | // GDPA. |
43 | | // If GDPA returns NULL then don't initialize the dispatch table entry. |
44 | | void loader_init_dispatch_dev_ext_entry(struct loader_instance *inst, struct loader_device *dev, uint32_t idx, const char *funcName) |
45 | | |
46 | 0 | { |
47 | 0 | void *gdpa_value; |
48 | 0 | if (dev != NULL) { |
49 | 0 | gdpa_value = dev->loader_dispatch.core_dispatch.GetDeviceProcAddr(dev->chain_device, funcName); |
50 | 0 | if (gdpa_value != NULL) dev->loader_dispatch.ext_dispatch[idx] = (PFN_vkDevExt)gdpa_value; |
51 | 0 | } else { |
52 | 0 | for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { |
53 | 0 | struct loader_device *ldev = icd_term->logical_device_list; |
54 | 0 | while (ldev) { |
55 | 0 | gdpa_value = ldev->loader_dispatch.core_dispatch.GetDeviceProcAddr(ldev->chain_device, funcName); |
56 | 0 | if (gdpa_value != NULL) ldev->loader_dispatch.ext_dispatch[idx] = (PFN_vkDevExt)gdpa_value; |
57 | 0 | ldev = ldev->next; |
58 | 0 | } |
59 | 0 | } |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | // Find all dev extension in the function names array and initialize the dispatch table |
64 | | // for dev for each of those extension entrypoints found in function names array. |
65 | 0 | void loader_init_dispatch_dev_ext(struct loader_instance *inst, struct loader_device *dev) { |
66 | 0 | for (uint32_t i = 0; i < MAX_NUM_UNKNOWN_EXTS; i++) { |
67 | 0 | if (inst->dev_ext_disp_functions[i] != NULL) |
68 | 0 | loader_init_dispatch_dev_ext_entry(inst, dev, i, inst->dev_ext_disp_functions[i]); |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | 0 | bool loader_check_icds_for_dev_ext_address(struct loader_instance *inst, const char *funcName) { |
73 | 0 | struct loader_icd_term *icd_term; |
74 | 0 | icd_term = inst->icd_terms; |
75 | 0 | while (NULL != icd_term) { |
76 | 0 | if (icd_term->scanned_icd->GetInstanceProcAddr(icd_term->instance, funcName)) |
77 | | // this icd supports funcName |
78 | 0 | return true; |
79 | 0 | icd_term = icd_term->next; |
80 | 0 | } |
81 | | |
82 | 0 | return false; |
83 | 0 | } |
84 | | |
85 | | // Look in the layers list of device extensions, which contain names of entry points. If funcName is present, return true |
86 | | // If not, call down the first layer's vkGetInstanceProcAddr to determine if any layers support the function |
87 | 0 | bool loader_check_layer_list_for_dev_ext_address(struct loader_instance *inst, const char *funcName) { |
88 | | // Iterate over the layers. |
89 | 0 | for (uint32_t layer = 0; layer < inst->expanded_activated_layer_list.count; ++layer) { |
90 | | // Iterate over the extensions. |
91 | 0 | const struct loader_device_extension_list *const extensions = |
92 | 0 | &(inst->expanded_activated_layer_list.list[layer]->device_extension_list); |
93 | 0 | for (uint32_t extension = 0; extension < extensions->count; ++extension) { |
94 | | // Iterate over the entry points. |
95 | 0 | const struct loader_dev_ext_props *const property = &(extensions->list[extension]); |
96 | 0 | for (uint32_t entry = 0; entry < property->entrypoints.count; ++entry) { |
97 | 0 | if (strcmp(property->entrypoints.list[entry], funcName) == 0) { |
98 | 0 | return true; |
99 | 0 | } |
100 | 0 | } |
101 | 0 | } |
102 | 0 | } |
103 | | // If the function pointer doesn't appear in the layer manifest for intercepted device functions, look down the |
104 | | // vkGetInstanceProcAddr chain |
105 | 0 | if (inst->expanded_activated_layer_list.count > 0) { |
106 | 0 | const struct loader_layer_functions *const functions = &(inst->expanded_activated_layer_list.list[0]->functions); |
107 | 0 | if (NULL != functions->get_instance_proc_addr) { |
108 | 0 | return NULL != functions->get_instance_proc_addr((VkInstance)inst->instance, funcName); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | 0 | return false; |
113 | 0 | } |
114 | | |
115 | 0 | void loader_free_dev_ext_table(struct loader_instance *inst) { |
116 | 0 | for (uint32_t i = 0; i < inst->dev_ext_disp_function_count; i++) { |
117 | 0 | loader_instance_heap_free(inst, inst->dev_ext_disp_functions[i]); |
118 | 0 | } |
119 | 0 | memset(inst->dev_ext_disp_functions, 0, sizeof(inst->dev_ext_disp_functions)); |
120 | 0 | } |
121 | | |
122 | | /* |
123 | | * This function returns generic trampoline code address for unknown entry points. |
124 | | * Presumably, these unknown entry points (as given by funcName) are device extension |
125 | | * entrypoints. |
126 | | * A function name array is used to keep a list of unknown entry points and their |
127 | | * mapping to the device extension dispatch table. |
128 | | * \returns |
129 | | * For a given entry point string (funcName), if an existing mapping is found the |
130 | | * trampoline address for that mapping is returned. |
131 | | * Otherwise, this unknown entry point has not been seen yet. |
132 | | * Next check if an ICD supports it, and if is_tramp is true, check if any layer |
133 | | * supports it by calling down the chain. |
134 | | * If so then a new entry in the function name array is added and that trampoline |
135 | | * address for the new entry is returned. |
136 | | * NULL is returned if the function name array is full or if no discovered layer or |
137 | | * ICD returns a non-NULL GetProcAddr for it. |
138 | | */ |
139 | 0 | void *loader_dev_ext_gpa_impl(struct loader_instance *inst, const char *funcName, bool is_tramp) { |
140 | | // Linearly look through already added functions to make sure we haven't seen it before |
141 | | // if we have, return the function at the index found |
142 | 0 | for (uint32_t i = 0; i < inst->dev_ext_disp_function_count; i++) { |
143 | 0 | if (inst->dev_ext_disp_functions[i] && !strcmp(inst->dev_ext_disp_functions[i], funcName)) |
144 | 0 | return loader_get_dev_ext_trampoline(i); |
145 | 0 | } |
146 | | |
147 | | // Check if funcName is supported in either ICDs or a layer library |
148 | 0 | if (!loader_check_icds_for_dev_ext_address(inst, funcName)) { |
149 | 0 | if (!is_tramp || !loader_check_layer_list_for_dev_ext_address(inst, funcName)) { |
150 | | // if support found in layers continue on |
151 | 0 | return NULL; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | if (inst->dev_ext_disp_function_count >= MAX_NUM_UNKNOWN_EXTS) { |
155 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_dev_ext_gpa: Exhausted the unknown device function array!"); |
156 | 0 | return NULL; |
157 | 0 | } |
158 | | |
159 | | // add found function to dev_ext_disp_functions; |
160 | 0 | size_t funcName_len = strlen(funcName) + 1; |
161 | 0 | inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count] = |
162 | 0 | (char *)loader_instance_heap_alloc(inst, funcName_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
163 | 0 | if (NULL == inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count]) { |
164 | | // failed to allocate memory, return NULL |
165 | 0 | return NULL; |
166 | 0 | } |
167 | 0 | strncpy(inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count], funcName, funcName_len); |
168 | | // init any dev dispatch table entries as needed |
169 | 0 | loader_init_dispatch_dev_ext_entry(inst, NULL, inst->dev_ext_disp_function_count, funcName); |
170 | 0 | void *out_function = loader_get_dev_ext_trampoline(inst->dev_ext_disp_function_count); |
171 | 0 | inst->dev_ext_disp_function_count++; |
172 | 0 | return out_function; |
173 | 0 | } |
174 | | |
175 | 0 | void *loader_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) { |
176 | 0 | return loader_dev_ext_gpa_impl(inst, funcName, true); |
177 | 0 | } |
178 | | |
179 | 0 | void *loader_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) { |
180 | 0 | return loader_dev_ext_gpa_impl(inst, funcName, false); |
181 | 0 | } |
182 | | |
183 | | // Physical Device function handling |
184 | | |
185 | 0 | bool loader_check_icds_for_phys_dev_ext_address(struct loader_instance *inst, const char *funcName) { |
186 | 0 | struct loader_icd_term *icd_term; |
187 | 0 | icd_term = inst->icd_terms; |
188 | 0 | while (NULL != icd_term) { |
189 | 0 | if (icd_term->scanned_icd->interface_version >= MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION && |
190 | 0 | icd_term->scanned_icd->GetPhysicalDeviceProcAddr && |
191 | 0 | icd_term->scanned_icd->GetPhysicalDeviceProcAddr(icd_term->instance, funcName)) |
192 | | // this icd supports funcName |
193 | 0 | return true; |
194 | 0 | icd_term = icd_term->next; |
195 | 0 | } |
196 | | |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 0 | bool loader_check_layer_list_for_phys_dev_ext_address(struct loader_instance *inst, const char *funcName) { |
201 | 0 | for (uint32_t layer = 0; layer < inst->expanded_activated_layer_list.count; layer++) { |
202 | 0 | struct loader_layer_properties *layer_prop_list = inst->expanded_activated_layer_list.list[layer]; |
203 | | // Find the first layer in the call chain which supports vk_layerGetPhysicalDeviceProcAddr |
204 | | // and call that, returning whether it found a valid pointer for this function name. |
205 | | // We return if the topmost layer supports GPDPA since the layer should call down the chain for us. |
206 | 0 | if (layer_prop_list[layer].interface_version > 1) { |
207 | 0 | const struct loader_layer_functions *const functions = &(layer_prop_list[layer].functions); |
208 | 0 | if (NULL != functions->get_physical_device_proc_addr) { |
209 | 0 | return NULL != functions->get_physical_device_proc_addr((VkInstance)inst->instance, funcName); |
210 | 0 | } |
211 | 0 | } |
212 | 0 | } |
213 | 0 | return false; |
214 | 0 | } |
215 | | |
216 | 0 | void loader_free_phys_dev_ext_table(struct loader_instance *inst) { |
217 | 0 | for (uint32_t i = 0; i < MAX_NUM_UNKNOWN_EXTS; i++) { |
218 | 0 | loader_instance_heap_free(inst, inst->phys_dev_ext_disp_functions[i]); |
219 | 0 | } |
220 | 0 | memset(inst->phys_dev_ext_disp_functions, 0, sizeof(inst->phys_dev_ext_disp_functions)); |
221 | 0 | } |
222 | | |
223 | | // This function returns a generic trampoline or terminator function |
224 | | // address for any unknown physical device extension commands. An array |
225 | | // is used to keep a list of unknown entry points and their |
226 | | // mapping to the physical device extension dispatch table (struct |
227 | | // loader_phys_dev_ext_dispatch_table). |
228 | | // For a given entry point string (funcName), if an existing mapping is |
229 | | // found, then the address for that mapping is returned. The is_tramp |
230 | | // parameter is used to decide whether to return a trampoline or terminator |
231 | | // If it has not been seen before check if a layer or and ICD supports it. |
232 | | // If so then a new entry in the function name array is added. |
233 | | // Null is returned if discovered layer or ICD returns a non-NULL GetProcAddr for it |
234 | | // or if the function name table is full. |
235 | 0 | void *loader_phys_dev_ext_gpa_impl(struct loader_instance *inst, const char *funcName, bool is_tramp) { |
236 | 0 | assert(NULL != inst); |
237 | | |
238 | | // We should always check to see if any ICD supports it. |
239 | 0 | if (!loader_check_icds_for_phys_dev_ext_address(inst, funcName)) { |
240 | | // If we're not checking layers, or we are and it's not in a layer, just |
241 | | // return |
242 | 0 | if (!is_tramp || !loader_check_layer_list_for_phys_dev_ext_address(inst, funcName)) { |
243 | 0 | return NULL; |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | 0 | bool has_found = false; |
248 | 0 | uint32_t new_function_index = 0; |
249 | | // Linearly look through already added functions to make sure we haven't seen it before |
250 | | // if we have, return the function at the index found |
251 | 0 | for (uint32_t i = 0; i < inst->phys_dev_ext_disp_function_count; i++) { |
252 | 0 | if (inst->phys_dev_ext_disp_functions[i] && !strcmp(inst->phys_dev_ext_disp_functions[i], funcName)) { |
253 | 0 | has_found = true; |
254 | 0 | new_function_index = i; |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | // A never before seen function name, store it in the array |
260 | 0 | if (!has_found) { |
261 | 0 | if (inst->phys_dev_ext_disp_function_count >= MAX_NUM_UNKNOWN_EXTS) { |
262 | 0 | loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
263 | 0 | "loader_dev_ext_gpa: Exhausted the unknown physical device function array!"); |
264 | 0 | return NULL; |
265 | 0 | } |
266 | | |
267 | 0 | loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, |
268 | 0 | "loader_phys_dev_ext_gpa: Adding unknown physical function %s to internal store at index %u", funcName, |
269 | 0 | inst->phys_dev_ext_disp_function_count); |
270 | | |
271 | | // add found function to phys_dev_ext_disp_functions; |
272 | 0 | size_t funcName_len = strlen(funcName) + 1; |
273 | 0 | inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count] = |
274 | 0 | (char *)loader_instance_heap_alloc(inst, funcName_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
275 | 0 | if (NULL == inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count]) { |
276 | | // failed to allocate memory, return NULL |
277 | 0 | return NULL; |
278 | 0 | } |
279 | 0 | strncpy(inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count], funcName, funcName_len); |
280 | |
|
281 | 0 | new_function_index = inst->phys_dev_ext_disp_function_count; |
282 | | // increment the count so that the subsequent logic includes the newly added entry point when searching for functions |
283 | 0 | inst->phys_dev_ext_disp_function_count++; |
284 | 0 | } |
285 | | |
286 | | // Setup the ICD function pointers |
287 | 0 | struct loader_icd_term *icd_term = inst->icd_terms; |
288 | 0 | while (NULL != icd_term) { |
289 | 0 | if (MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION <= icd_term->scanned_icd->interface_version && |
290 | 0 | NULL != icd_term->scanned_icd->GetPhysicalDeviceProcAddr) { |
291 | 0 | icd_term->phys_dev_ext[new_function_index] = |
292 | 0 | (PFN_PhysDevExt)icd_term->scanned_icd->GetPhysicalDeviceProcAddr(icd_term->instance, funcName); |
293 | 0 | if (NULL != icd_term->phys_dev_ext[new_function_index]) { |
294 | | // Make sure we set the instance dispatch to point to the loader's terminator now since we can at least handle |
295 | | // it in one ICD. |
296 | 0 | inst->disp->phys_dev_ext[new_function_index] = loader_get_phys_dev_ext_termin(new_function_index); |
297 | |
|
298 | 0 | loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "loader_phys_dev_ext_gpa: Driver %s returned ptr %p for %s", |
299 | 0 | icd_term->scanned_icd->lib_name, inst->disp->phys_dev_ext[new_function_index], funcName); |
300 | 0 | } |
301 | 0 | } else { |
302 | 0 | icd_term->phys_dev_ext[new_function_index] = NULL; |
303 | 0 | } |
304 | |
|
305 | 0 | icd_term = icd_term->next; |
306 | 0 | } |
307 | | |
308 | | // Now if this is being run in the trampoline, search for the first layer attached and query using it to get the first entry |
309 | | // point. Only set the instance dispatch table to it if it isn't NULL. |
310 | 0 | if (is_tramp) { |
311 | 0 | for (uint32_t i = 0; i < inst->expanded_activated_layer_list.count; i++) { |
312 | 0 | struct loader_layer_properties *layer_prop = inst->expanded_activated_layer_list.list[i]; |
313 | 0 | if (layer_prop->interface_version > 1 && NULL != layer_prop->functions.get_physical_device_proc_addr) { |
314 | 0 | void *layer_ret_function = |
315 | 0 | (PFN_PhysDevExt)layer_prop->functions.get_physical_device_proc_addr(inst->instance, funcName); |
316 | 0 | if (NULL != layer_ret_function) { |
317 | 0 | inst->disp->phys_dev_ext[new_function_index] = layer_ret_function; |
318 | 0 | loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "loader_phys_dev_ext_gpa: Layer %s returned ptr %p for %s", |
319 | 0 | layer_prop->info.layerName, inst->disp->phys_dev_ext[new_function_index], funcName); |
320 | 0 | break; |
321 | 0 | } |
322 | 0 | } |
323 | 0 | } |
324 | 0 | } |
325 | |
|
326 | 0 | if (is_tramp) { |
327 | 0 | return loader_get_phys_dev_ext_tramp(new_function_index); |
328 | 0 | } else { |
329 | 0 | return loader_get_phys_dev_ext_termin(new_function_index); |
330 | 0 | } |
331 | 0 | } |
332 | | // Main interface functions, makes it clear whether it is getting a terminator or trampoline |
333 | 0 | void *loader_phys_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) { |
334 | 0 | return loader_phys_dev_ext_gpa_impl(inst, funcName, true); |
335 | 0 | } |
336 | 0 | void *loader_phys_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) { |
337 | 0 | return loader_phys_dev_ext_gpa_impl(inst, funcName, false); |
338 | 0 | } |