• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
loader_init_dispatch_dev_ext_entry(struct loader_instance * inst,struct loader_device * dev,uint32_t idx,const char * funcName)44 void loader_init_dispatch_dev_ext_entry(struct loader_instance *inst, struct loader_device *dev, uint32_t idx, const char *funcName)
45 
46 {
47     void *gdpa_value;
48     if (dev != NULL) {
49         gdpa_value = dev->loader_dispatch.core_dispatch.GetDeviceProcAddr(dev->chain_device, funcName);
50         if (gdpa_value != NULL) dev->loader_dispatch.ext_dispatch[idx] = (PFN_vkDevExt)gdpa_value;
51     } else {
52         for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) {
53             struct loader_device *ldev = icd_term->logical_device_list;
54             while (ldev) {
55                 gdpa_value = ldev->loader_dispatch.core_dispatch.GetDeviceProcAddr(ldev->chain_device, funcName);
56                 if (gdpa_value != NULL) ldev->loader_dispatch.ext_dispatch[idx] = (PFN_vkDevExt)gdpa_value;
57                 ldev = ldev->next;
58             }
59         }
60     }
61 }
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.
loader_init_dispatch_dev_ext(struct loader_instance * inst,struct loader_device * dev)65 void loader_init_dispatch_dev_ext(struct loader_instance *inst, struct loader_device *dev) {
66     for (uint32_t i = 0; i < MAX_NUM_UNKNOWN_EXTS; i++) {
67         if (inst->dev_ext_disp_functions[i] != NULL)
68             loader_init_dispatch_dev_ext_entry(inst, dev, i, inst->dev_ext_disp_functions[i]);
69     }
70 }
71 
loader_check_icds_for_dev_ext_address(struct loader_instance * inst,const char * funcName)72 bool loader_check_icds_for_dev_ext_address(struct loader_instance *inst, const char *funcName) {
73     struct loader_icd_term *icd_term;
74     icd_term = inst->icd_terms;
75     while (NULL != icd_term) {
76         if (icd_term->scanned_icd->GetInstanceProcAddr(icd_term->instance, funcName))
77             // this icd supports funcName
78             return true;
79         icd_term = icd_term->next;
80     }
81 
82     return false;
83 }
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
loader_check_layer_list_for_dev_ext_address(struct loader_instance * inst,const char * funcName)87 bool loader_check_layer_list_for_dev_ext_address(struct loader_instance *inst, const char *funcName) {
88     struct loader_layer_properties *layer_prop_list = inst->expanded_activated_layer_list.list;
89 
90     // Iterate over the layers.
91     for (uint32_t layer = 0; layer < inst->expanded_activated_layer_list.count; ++layer) {
92         // Iterate over the extensions.
93         const struct loader_device_extension_list *const extensions = &(layer_prop_list[layer].device_extension_list);
94         for (uint32_t extension = 0; extension < extensions->count; ++extension) {
95             // Iterate over the entry points.
96             const struct loader_dev_ext_props *const property = &(extensions->list[extension]);
97             for (uint32_t entry = 0; entry < property->entrypoint_count; ++entry) {
98                 if (strcmp(property->entrypoints[entry], funcName) == 0) {
99                     return true;
100                 }
101             }
102         }
103     }
104     // If the function pointer doesn't appear in the layer manifest for intercepted device functions, look down the
105     // vkGetInstanceProcAddr chain
106     if (inst->expanded_activated_layer_list.count > 0) {
107         const struct loader_layer_functions *const functions = &(layer_prop_list[0].functions);
108         if (NULL != functions->get_instance_proc_addr) {
109             return NULL != functions->get_instance_proc_addr((VkInstance)inst->instance, funcName);
110         }
111     }
112 
113     return false;
114 }
115 
loader_free_dev_ext_table(struct loader_instance * inst)116 void loader_free_dev_ext_table(struct loader_instance *inst) {
117     for (uint32_t i = 0; i < inst->dev_ext_disp_function_count; i++) {
118         loader_instance_heap_free(inst, inst->dev_ext_disp_functions[i]);
119     }
120     memset(inst->dev_ext_disp_functions, 0, sizeof(inst->dev_ext_disp_functions));
121 }
122 
123 /*
124  * This function returns generic trampoline code address for unknown entry points.
125  * Presumably, these unknown entry points (as given by funcName) are device extension
126  * entrypoints.
127  * A function name array is used to keep a list of unknown entry points and their
128  * mapping to the device extension dispatch table.
129  * \returns
130  * For a given entry point string (funcName), if an existing mapping is found the
131  * trampoline address for that mapping is returned.
132  * Otherwise, this unknown entry point has not been seen yet.
133  * Next check if an ICD supports it, and if is_tramp is true, check if any layer
134  * supports it by calling down the chain.
135  * If so then a new entry in the function name array is added and that trampoline
136  * address for the new entry is returned.
137  * NULL is returned if the function name array is full or if no discovered layer or
138  * ICD returns a non-NULL GetProcAddr for it.
139  */
loader_dev_ext_gpa_impl(struct loader_instance * inst,const char * funcName,bool is_tramp)140 void *loader_dev_ext_gpa_impl(struct loader_instance *inst, const char *funcName, bool is_tramp) {
141     // Linearly look through already added functions to make sure we haven't seen it before
142     // if we have, return the function at the index found
143     for (uint32_t i = 0; i < inst->dev_ext_disp_function_count; i++) {
144         if (inst->dev_ext_disp_functions[i] && !strcmp(inst->dev_ext_disp_functions[i], funcName))
145             return loader_get_dev_ext_trampoline(i);
146     }
147 
148     // Check if funcName is supported in either ICDs or a layer library
149     if (!loader_check_icds_for_dev_ext_address(inst, funcName)) {
150         if (!is_tramp || !loader_check_layer_list_for_dev_ext_address(inst, funcName)) {
151             // if support found in layers continue on
152             return NULL;
153         }
154     }
155     if (inst->dev_ext_disp_function_count >= MAX_NUM_UNKNOWN_EXTS) {
156         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_dev_ext_gpa: Exhausted the unknown device function array!");
157         return NULL;
158     }
159 
160     // add found function to dev_ext_disp_functions;
161     size_t funcName_len = strlen(funcName) + 1;
162     inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count] =
163         (char *)loader_instance_heap_alloc(inst, funcName_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
164     if (NULL == inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count]) {
165         // failed to allocate memory, return NULL
166         return NULL;
167     }
168     strncpy(inst->dev_ext_disp_functions[inst->dev_ext_disp_function_count], funcName, funcName_len);
169     // init any dev dispatch table entries as needed
170     loader_init_dispatch_dev_ext_entry(inst, NULL, inst->dev_ext_disp_function_count, funcName);
171     void *out_function = loader_get_dev_ext_trampoline(inst->dev_ext_disp_function_count);
172     inst->dev_ext_disp_function_count++;
173     return out_function;
174 }
175 
loader_dev_ext_gpa_tramp(struct loader_instance * inst,const char * funcName)176 void *loader_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) {
177     return loader_dev_ext_gpa_impl(inst, funcName, true);
178 }
179 
loader_dev_ext_gpa_term(struct loader_instance * inst,const char * funcName)180 void *loader_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) {
181     return loader_dev_ext_gpa_impl(inst, funcName, false);
182 }
183 
184 // Physical Device function handling
185 
loader_check_icds_for_phys_dev_ext_address(struct loader_instance * inst,const char * funcName)186 bool loader_check_icds_for_phys_dev_ext_address(struct loader_instance *inst, const char *funcName) {
187     struct loader_icd_term *icd_term;
188     icd_term = inst->icd_terms;
189     while (NULL != icd_term) {
190         if (icd_term->scanned_icd->interface_version >= MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION &&
191             icd_term->scanned_icd->GetPhysicalDeviceProcAddr(icd_term->instance, funcName))
192             // this icd supports funcName
193             return true;
194         icd_term = icd_term->next;
195     }
196 
197     return false;
198 }
199 
loader_check_layer_list_for_phys_dev_ext_address(struct loader_instance * inst,const char * funcName)200 bool loader_check_layer_list_for_phys_dev_ext_address(struct loader_instance *inst, const char *funcName) {
201     struct loader_layer_properties *layer_prop_list = inst->expanded_activated_layer_list.list;
202     for (uint32_t layer = 0; layer < inst->expanded_activated_layer_list.count; 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         if (layer_prop_list[layer].interface_version > 1) {
207             const struct loader_layer_functions *const functions = &(layer_prop_list[layer].functions);
208             if (NULL != functions->get_physical_device_proc_addr) {
209                 return NULL != functions->get_physical_device_proc_addr((VkInstance)inst->instance, funcName);
210             }
211         }
212     }
213     return false;
214 }
215 
loader_free_phys_dev_ext_table(struct loader_instance * inst)216 void loader_free_phys_dev_ext_table(struct loader_instance *inst) {
217     for (uint32_t i = 0; i < MAX_NUM_UNKNOWN_EXTS; i++) {
218         loader_instance_heap_free(inst, inst->phys_dev_ext_disp_functions[i]);
219     }
220     memset(inst->phys_dev_ext_disp_functions, 0, sizeof(inst->phys_dev_ext_disp_functions));
221 }
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.
loader_phys_dev_ext_gpa_impl(struct loader_instance * inst,const char * funcName,bool is_tramp)235 void *loader_phys_dev_ext_gpa_impl(struct loader_instance *inst, const char *funcName, bool is_tramp) {
236     assert(NULL != inst);
237 
238     // We should always check to see if any ICD supports it.
239     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         if (!is_tramp || !loader_check_layer_list_for_phys_dev_ext_address(inst, funcName)) {
243             return NULL;
244         }
245     }
246 
247     bool has_found = false;
248     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     for (uint32_t i = 0; i < inst->phys_dev_ext_disp_function_count; i++) {
252         if (inst->phys_dev_ext_disp_functions[i] && !strcmp(inst->phys_dev_ext_disp_functions[i], funcName)) {
253             has_found = true;
254             new_function_index = i;
255             break;
256         }
257     }
258 
259     // A never before seen function name, store it in the array
260     if (!has_found) {
261         if (inst->phys_dev_ext_disp_function_count >= MAX_NUM_UNKNOWN_EXTS) {
262             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
263                        "loader_dev_ext_gpa: Exhausted the unknown physical device function array!");
264             return NULL;
265         }
266 
267         loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0,
268                    "loader_phys_dev_ext_gpa: Adding unknown physical function %s to internal store at index %u", funcName,
269                    inst->phys_dev_ext_disp_function_count);
270 
271         // add found function to phys_dev_ext_disp_functions;
272         size_t funcName_len = strlen(funcName) + 1;
273         inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count] =
274             (char *)loader_instance_heap_alloc(inst, funcName_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
275         if (NULL == inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count]) {
276             // failed to allocate memory, return NULL
277             return NULL;
278         }
279         strncpy(inst->phys_dev_ext_disp_functions[inst->phys_dev_ext_disp_function_count], funcName, funcName_len);
280 
281         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         inst->phys_dev_ext_disp_function_count++;
284         has_found = true;
285     }
286 
287     // Setup the ICD function pointers
288     struct loader_icd_term *icd_term = inst->icd_terms;
289     while (NULL != icd_term) {
290         if (MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION <= icd_term->scanned_icd->interface_version &&
291             NULL != icd_term->scanned_icd->GetPhysicalDeviceProcAddr) {
292             icd_term->phys_dev_ext[new_function_index] =
293                 (PFN_PhysDevExt)icd_term->scanned_icd->GetPhysicalDeviceProcAddr(icd_term->instance, funcName);
294             if (NULL != icd_term->phys_dev_ext[new_function_index]) {
295                 // Make sure we set the instance dispatch to point to the loader's terminator now since we can at least handle
296                 // it in one ICD.
297                 inst->disp->phys_dev_ext[new_function_index] = loader_get_phys_dev_ext_termin(new_function_index);
298 
299                 loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "loader_phys_dev_ext_gpa: Driver %s returned ptr %p for %s",
300                            icd_term->scanned_icd->lib_name, inst->disp->phys_dev_ext[new_function_index], funcName);
301             }
302         } else {
303             icd_term->phys_dev_ext[new_function_index] = NULL;
304         }
305 
306         icd_term = icd_term->next;
307     }
308 
309     // Now if this is being run in the trampoline, search for the first layer attached and query using it to get the first entry
310     // point. Only set the instance dispatch table to it if it isn't NULL.
311     if (is_tramp) {
312         for (uint32_t i = 0; i < inst->expanded_activated_layer_list.count; i++) {
313             struct loader_layer_properties *layer_prop = &inst->expanded_activated_layer_list.list[i];
314             if (layer_prop->interface_version > 1 && NULL != layer_prop->functions.get_physical_device_proc_addr) {
315                 void *layer_ret_function =
316                     (PFN_PhysDevExt)layer_prop->functions.get_physical_device_proc_addr(inst->instance, funcName);
317                 if (NULL != layer_ret_function) {
318                     inst->disp->phys_dev_ext[new_function_index] = layer_ret_function;
319                     loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "loader_phys_dev_ext_gpa: Layer %s returned ptr %p for %s",
320                                layer_prop->info.layerName, inst->disp->phys_dev_ext[new_function_index], funcName);
321                     break;
322                 }
323             }
324         }
325     }
326 
327     if (is_tramp) {
328         return loader_get_phys_dev_ext_tramp(new_function_index);
329     } else {
330         return loader_get_phys_dev_ext_termin(new_function_index);
331     }
332 }
333 // Main interface functions, makes it clear whether it is getting a terminator or trampoline
loader_phys_dev_ext_gpa_tramp(struct loader_instance * inst,const char * funcName)334 void *loader_phys_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) {
335     return loader_phys_dev_ext_gpa_impl(inst, funcName, true);
336 }
loader_phys_dev_ext_gpa_term(struct loader_instance * inst,const char * funcName)337 void *loader_phys_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) {
338     return loader_phys_dev_ext_gpa_impl(inst, funcName, false);
339 }
340