Coverage Report

Created: 2023-06-07 06:30

/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
}