1 //
2 // Copyright (C) 2024 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #define VK_ENABLE_BETA_EXTENSIONS 1
18 #include <vulkan/vk_layer_interface.h>
19 #include <vulkan/vulkan.h>
20
21 #include <map>
22 #include <mutex>
23 #include <tuple>
24 #include <utility>
25
26 #include "berberis/base/logging.h"
27 #include "berberis/base/strings.h"
28 #include "berberis/guest_abi/function_wrappers.h"
29 #include "berberis/guest_abi/guest_arguments.h"
30 #include "berberis/guest_abi/guest_params.h"
31 #include "berberis/guest_loader/guest_loader.h"
32 #include "berberis/proxy_loader/proxy_library_builder.h"
33 #include "berberis/runtime_primitives/known_guest_function_wrapper.h"
34 #include "berberis/runtime_primitives/runtime_library.h"
35
36 #include "binary_search.h"
37 #include "vulkan_xml.h"
38
39 namespace berberis {
40
41 namespace {
42
43 std::mutex g_primary_command_buffer_mutex;
44 // Map from VkCommandBuffer opaque handle to bool value which is true when command buffer is primary.
45 // Note: we have to handle primary and secondary command buffers differently in vkBeginCommandBuffer,
46 // but that function, itself, doesn't have any means to know that.
47 std::map<VkCommandBuffer, bool> g_primary_command_buffer;
48
DoCustomTrampolineWithThunk_vkAllocateCommandBuffers(HostCode callee,ProcessState * state)49 void DoCustomTrampolineWithThunk_vkAllocateCommandBuffers(HostCode callee, ProcessState* state) {
50 PFN_vkAllocateCommandBuffers callee_function = AsFuncPtr(callee);
51 auto [device_guest, pAllocateInfo_guest, pCommandBuffers_guest] =
52 GuestParamsValues<PFN_vkAllocateCommandBuffers>(state);
53 [[maybe_unused]] bool out_of_memory;
54 VkDevice device_host = device_guest;
55 GuestType<const struct VkCommandBufferAllocateInfo*>::HostHolder pAllocateInfo_holder;
56 const struct VkCommandBufferAllocateInfo* pAllocateInfo_host =
57 ToHostType(pAllocateInfo_guest, pAllocateInfo_holder, out_of_memory);
58 VkCommandBuffer* pCommandBuffers_host = pCommandBuffers_guest;
59 auto&& [ret] = GuestReturnReference<PFN_vkAllocateCommandBuffers>(state);
60 ret = callee_function(device_host, pAllocateInfo_host, pCommandBuffers_host);
61 if (ret >= VkResult::BERBERIS_VK_SUCCESS) {
62 std::lock_guard lock(g_primary_command_buffer_mutex);
63 for (uint32_t idx = 0; idx < pAllocateInfo_host->commandBufferCount; ++idx) {
64 VkCommandBuffer command_buffer = pCommandBuffers_host[idx];
65 // We may be called with the same set of command buffers because of layers.
66 if (g_primary_command_buffer.find(command_buffer) != g_primary_command_buffer.end()) {
67 continue;
68 }
69 g_primary_command_buffer.insert(
70 std::pair{command_buffer,
71 pAllocateInfo_host->level ==
72 VkCommandBufferLevel::BERBERIS_VK_COMMAND_BUFFER_LEVEL_PRIMARY});
73 }
74 }
75 }
76
DoCustomTrampolineWithThunk_vkBeginCommandBuffer(HostCode callee,ProcessState * state)77 void DoCustomTrampolineWithThunk_vkBeginCommandBuffer(HostCode callee, ProcessState* state) {
78 PFN_vkBeginCommandBuffer callee_function = AsFuncPtr(callee);
79 auto [commandBuffer_guest, pBeginInfo_guest] = GuestParamsValues<PFN_vkBeginCommandBuffer>(state);
80 bool out_of_memory;
81 VkCommandBuffer commandBuffer_host = commandBuffer_guest;
82 bool convert_inheritance_info = false;
83 {
84 std::lock_guard lock(g_primary_command_buffer_mutex);
85 if (auto it = g_primary_command_buffer.find(commandBuffer_guest);
86 it != g_primary_command_buffer.end()) {
87 convert_inheritance_info = !it->second;
88 }
89 }
90 GuestType<const struct VkCommandBufferBeginInfo*>::HostHolder pBeginInfo_holder;
91 const struct VkCommandBufferBeginInfo* pBeginInfo_host =
92 ToHostType(pBeginInfo_guest, pBeginInfo_holder, convert_inheritance_info, out_of_memory);
93 auto&& [ret] = GuestReturnReference<PFN_vkBeginCommandBuffer>(state);
94 ret = callee_function(commandBuffer_host, pBeginInfo_host);
95 }
96
DoCustomTrampolineWithThunk_vkFreeCommandBuffers(HostCode callee,ProcessState * state)97 void DoCustomTrampolineWithThunk_vkFreeCommandBuffers(HostCode callee, ProcessState* state) {
98 PFN_vkFreeCommandBuffers callee_function = AsFuncPtr(callee);
99 auto [device_guest, commandPool_guest, commandBufferCount_guest, pCommandBuffers_guest] =
100 GuestParamsValues<PFN_vkFreeCommandBuffers>(state);
101 VkDevice device_host = device_guest;
102 VkCommandPool commandPool_host = commandPool_guest;
103 std::uint32_t commandBufferCount_host = commandBufferCount_guest;
104 const VkCommandBuffer* pCommandBuffers_host = pCommandBuffers_guest;
105 {
106 std::lock_guard lock(g_primary_command_buffer_mutex);
107 for (uint32_t idx = 0; idx < commandBufferCount_host; ++idx) {
108 VkCommandBuffer command_buffer = pCommandBuffers_host[idx];
109 // We may be called with the same set of command buffers because of layers.
110 if (auto it = g_primary_command_buffer.find(command_buffer);
111 it != g_primary_command_buffer.end()) {
112 g_primary_command_buffer.erase(it);
113 }
114 }
115 }
116 callee_function(device_host, commandPool_host, commandBufferCount_host, pCommandBuffers_host);
117 }
118
119 template <typename VkResultType>
FilterOutExtensionProperties(VkResultType & result,uint32_t * properties_out_buf_size,VkExtensionProperties * properties_out_buf,uint32_t properties_in_buf_size,VkExtensionProperties * properties_in_buf)120 void FilterOutExtensionProperties(VkResultType& result,
121 uint32_t* properties_out_buf_size,
122 VkExtensionProperties* properties_out_buf,
123 uint32_t properties_in_buf_size,
124 VkExtensionProperties* properties_in_buf) {
125 const auto& extensions_map = GetExtensionsMap();
126 uint32_t property_count = 0;
127 for (uint32_t i = 0; i < properties_in_buf_size; ++i) {
128 if (auto conversion = FindElementByName(extensions_map, properties_in_buf[i].extensionName)) {
129 if (!properties_out_buf) {
130 property_count++;
131 continue;
132 }
133 if (property_count == *properties_out_buf_size) {
134 result = VkResult::BERBERIS_VK_INCOMPLETE;
135 return;
136 }
137 properties_out_buf[property_count++] = properties_in_buf[i];
138 // Some extensions get new revisions over time and since we don't know if they may introduce
139 // new functions we reduce version the latest known to us.
140 if (properties_out_buf[property_count - 1].specVersion > conversion->maxsupported_spec) {
141 properties_out_buf[property_count - 1].specVersion = conversion->maxsupported_spec;
142 }
143 }
144 }
145 *properties_out_buf_size = property_count;
146 }
147
DoCustomTrampolineWithThunk_vkEnumerateDeviceExtensionProperties(HostCode callee,ProcessState * state)148 void DoCustomTrampolineWithThunk_vkEnumerateDeviceExtensionProperties(HostCode callee,
149 ProcessState* state) {
150 PFN_vkEnumerateDeviceExtensionProperties callee_function = AsFuncPtr(callee);
151 auto [physicalDevice_guest, pLayerName_guest, pPropertyCount_guest, pProperties_guest] =
152 GuestParamsValues<PFN_vkEnumerateDeviceExtensionProperties>(state);
153 VkPhysicalDevice physicalDevice_host = physicalDevice_guest;
154 const char* pLayerName_host = pLayerName_guest;
155 std::uint32_t* pPropertyCount_host = pPropertyCount_guest;
156 struct VkExtensionProperties* pProperties_host = pProperties_guest;
157 // This function is called twice with nullptr to get the buffer size and with the buffer itself to
158 // get the extensions. Technically the number of extensions may change between these two calls, so
159 // it should be valid to return unfiltered buffer size on the first call, and then apply the
160 // filter only on the second call and return a smaller buffer size in addition to the buffer
161 // itself. But CTS verifies that the size doesn't change between the calls. Thus we need to do the
162 // filtering even on the first call to figure out the size after the filtering.
163 //
164 // Technically consistent results are not guaranteed, but since official Vulkan dEQP tests rely on
165 // that particularity it should be possible to achieve it in practice.
166 auto&& [ret] = GuestReturnReference<PFN_vkEnumerateDeviceExtensionProperties>(state);
167 for (;;) {
168 uint32_t properties_in_buf_size;
169 ret = callee_function(physicalDevice_host, pLayerName_host, &properties_in_buf_size, nullptr);
170 if (ret < VkResult::BERBERIS_VK_SUCCESS) {
171 return;
172 }
173
174 uint32_t properties_in_buf_size2 = properties_in_buf_size;
175 VkExtensionProperties properties_in_buf[properties_in_buf_size];
176 ret = callee_function(
177 physicalDevice_host, pLayerName_host, &properties_in_buf_size2, properties_in_buf);
178 if (ret < VkResult::BERBERIS_VK_SUCCESS) {
179 return;
180 }
181 if (properties_in_buf_size != properties_in_buf_size2 ||
182 ret == VkResult::BERBERIS_VK_INCOMPLETE) {
183 continue;
184 }
185
186 FilterOutExtensionProperties(
187 ret, pPropertyCount_host, pProperties_host, properties_in_buf_size, properties_in_buf);
188 return;
189 }
190 }
191
DoCustomTrampolineWithThunk_vkEnumerateInstanceExtensionProperties(HostCode callee,ProcessState * state)192 void DoCustomTrampolineWithThunk_vkEnumerateInstanceExtensionProperties(HostCode callee,
193 ProcessState* state) {
194 PFN_vkEnumerateInstanceExtensionProperties callee_function = AsFuncPtr(callee);
195 auto [pLayerName_guest, pPropertyCount_guest, pProperties_guest] =
196 GuestParamsValues<PFN_vkEnumerateInstanceExtensionProperties>(state);
197 const char* pLayerName_host = pLayerName_guest;
198 std::uint32_t* pPropertyCount_host = pPropertyCount_guest;
199 struct VkExtensionProperties* pProperties_host = pProperties_guest;
200 auto&& [ret] = GuestReturnReference<PFN_vkEnumerateInstanceExtensionProperties>(state);
201 for (;;) {
202 uint32_t properties_in_buf_size;
203 ret = callee_function(pLayerName_host, &properties_in_buf_size, nullptr);
204 if (ret < VkResult::BERBERIS_VK_SUCCESS) {
205 return;
206 }
207
208 uint32_t properties_in_buf_size2 = properties_in_buf_size;
209 VkExtensionProperties properties_in_buf[properties_in_buf_size];
210 ret = callee_function(pLayerName_host, &properties_in_buf_size2, properties_in_buf);
211 if (ret < VkResult::BERBERIS_VK_SUCCESS) {
212 return;
213 }
214 if (properties_in_buf_size != properties_in_buf_size2 ||
215 ret == VkResult::BERBERIS_VK_INCOMPLETE) {
216 continue;
217 }
218
219 FilterOutExtensionProperties(
220 ret, pPropertyCount_host, pProperties_host, properties_in_buf_size, properties_in_buf);
221 return;
222 }
223 }
224
DoCustomTrampolineWithThunk_vkGetDeviceProcAddr(HostCode callee,ProcessState * state)225 void DoCustomTrampolineWithThunk_vkGetDeviceProcAddr(HostCode callee, ProcessState* state) {
226 PFN_vkGetDeviceProcAddr callee_function = AsFuncPtr(callee);
227 auto [device, function_name] = GuestParamsValues<PFN_vkGetDeviceProcAddr>(state);
228 const auto& function_map = GetMapForvkGetProcAddr();
229 if (auto conversion = FindElementByName(function_map, function_name)) {
230 auto func = callee_function(device, function_name);
231 WrapHostFunctionImpl(reinterpret_cast<void*>(func), conversion->trampoline, function_name);
232 auto&& [ret] = GuestReturnReference<PFN_vkGetDeviceProcAddr>(state);
233 ret = PFN_vkVoidFunction(func);
234 return;
235 }
236 ALOGE("Unknown function is used with vkGetDeviceProcAddr: %s",
237 static_cast<const char*>(function_name));
238 auto&& [ret] = GuestReturnReference<PFN_vkGetDeviceProcAddr>(state);
239 ret = PFN_vkVoidFunction(0);
240 }
241
DoCustomTrampolineWithThunk_vkGetInstanceProcAddr(HostCode callee,ProcessState * state)242 void DoCustomTrampolineWithThunk_vkGetInstanceProcAddr(HostCode callee, ProcessState* state) {
243 PFN_vkGetInstanceProcAddr callee_function = AsFuncPtr(callee);
244 auto [instance, function_name] = GuestParamsValues<PFN_vkGetInstanceProcAddr>(state);
245 const auto& function_map = GetMapForvkGetProcAddr();
246 if (auto conversion = FindElementByName(function_map, function_name)) {
247 auto func = callee_function(instance, function_name);
248 WrapHostFunctionImpl(reinterpret_cast<void*>(func), conversion->trampoline, function_name);
249 auto&& [ret] = GuestReturnReference<PFN_vkGetInstanceProcAddr>(state);
250 ret = PFN_vkVoidFunction(func);
251 return;
252 }
253 ALOGE("Unknown function is used with vkGetInstanceProcAddr: %s",
254 static_cast<const char*>(function_name));
255 auto&& [ret] = GuestReturnReference<PFN_vkGetInstanceProcAddr>(state);
256 ret = PFN_vkVoidFunction(0);
257 }
258
RunGuest_vkEnumerateDeviceExtensionProperties(GuestAddr pc,GuestArgumentBuffer * buf)259 void RunGuest_vkEnumerateDeviceExtensionProperties(GuestAddr pc, GuestArgumentBuffer* buf) {
260 auto [physicalDevice_host, pLayerName_host, pPropertyCount_host, pProperties_host] =
261 HostArgumentsValues<PFN_vkEnumerateDeviceExtensionProperties>(buf);
262 for (;;) {
263 uint32_t properties_in_buf_size;
264 auto [physicalDevice_guest, pLayerName_guest, pPropertyCount_guest, pProperties_guest] =
265 GuestArgumentsReferences<PFN_vkEnumerateDeviceExtensionProperties>(buf);
266 physicalDevice_guest = physicalDevice_host;
267 pLayerName_guest = pLayerName_host;
268 pPropertyCount_guest = &properties_in_buf_size;
269 pProperties_guest = nullptr;
270 RunGuestCall(pc, buf);
271 auto&& [result] = HostResultReference<PFN_vkEnumerateDeviceExtensionProperties>(buf);
272 if (result < VkResult::BERBERIS_VK_SUCCESS) {
273 return;
274 }
275
276 uint32_t properties_in_buf_size2 = properties_in_buf_size;
277 VkExtensionProperties properties_in_buf[properties_in_buf_size];
278 physicalDevice_guest = physicalDevice_host;
279 pLayerName_guest = pLayerName_host;
280 pPropertyCount_guest = &properties_in_buf_size;
281 pProperties_guest = properties_in_buf;
282 RunGuestCall(pc, buf);
283 if (result < VkResult::BERBERIS_VK_SUCCESS) {
284 return;
285 }
286 if (properties_in_buf_size != properties_in_buf_size2 ||
287 result == VkResult::BERBERIS_VK_INCOMPLETE) {
288 continue;
289 }
290
291 FilterOutExtensionProperties(
292 result, pPropertyCount_host, pProperties_host, properties_in_buf_size, properties_in_buf);
293 return;
294 }
295 }
296
RunGuest_vkEnumerateInstanceExtensionProperties(GuestAddr pc,GuestArgumentBuffer * buf)297 void RunGuest_vkEnumerateInstanceExtensionProperties(GuestAddr pc, GuestArgumentBuffer* buf) {
298 auto [pLayerName_host, pPropertyCount_host, pProperties_host] =
299 HostArgumentsValues<PFN_vkEnumerateInstanceExtensionProperties>(buf);
300 for (;;) {
301 uint32_t properties_in_buf_size;
302 auto [pLayerName_guest, pPropertyCount_guest, pProperties_guest] =
303 GuestArgumentsReferences<PFN_vkEnumerateInstanceExtensionProperties>(buf);
304 pLayerName_guest = pLayerName_host;
305 pPropertyCount_guest = &properties_in_buf_size;
306 pProperties_guest = nullptr;
307 RunGuestCall(pc, buf);
308 auto&& [result] = HostResultReference<PFN_vkEnumerateInstanceExtensionProperties>(buf);
309 if (result < VkResult::BERBERIS_VK_SUCCESS) {
310 return;
311 }
312
313 uint32_t properties_in_buf_size2 = properties_in_buf_size;
314 VkExtensionProperties properties_in_buf[properties_in_buf_size];
315 pLayerName_guest = pLayerName_host;
316 pPropertyCount_guest = &properties_in_buf_size;
317 pProperties_guest = properties_in_buf;
318 RunGuestCall(pc, buf);
319 if (result < VkResult::BERBERIS_VK_SUCCESS) {
320 return;
321 }
322 if (properties_in_buf_size != properties_in_buf_size2 ||
323 result == VkResult::BERBERIS_VK_INCOMPLETE) {
324 continue;
325 }
326
327 FilterOutExtensionProperties(
328 result, pPropertyCount_host, pProperties_host, properties_in_buf_size, properties_in_buf);
329 return;
330 }
331 }
332
RunGuest_vkCreateInstance(GuestAddr pc,GuestArgumentBuffer * buf)333 void RunGuest_vkCreateInstance(GuestAddr pc, GuestArgumentBuffer* buf) {
334 auto [pCreateInfo_host, pAllocator_host, pInstance_host] =
335 HostArgumentsValues<PFN_vkCreateInstance>(buf);
336 {
337 [[maybe_unused]] bool out_of_memory;
338 auto [pCreateInfo_guest, pAllocator_guest, pInstance_guest] =
339 GuestArgumentsReferences<PFN_vkCreateInstance>(buf);
340
341 const VkLayerInstanceCreateInfo* layer_create_info =
342 bit_cast<const VkLayerInstanceCreateInfo*>(pCreateInfo_host);
343
344 // Step through the pNext chain until we get to the link function
345 while (layer_create_info &&
346 (layer_create_info->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO ||
347 layer_create_info->function != VK_LAYER_FUNCTION_LINK)) {
348 layer_create_info = static_cast<const VkLayerInstanceCreateInfo*>(layer_create_info->pNext);
349 }
350 if (layer_create_info) {
351 void* func = bit_cast<void*>(layer_create_info->u.pLayerInfo->pfnNextGetInstanceProcAddr);
352 WrapHostFunctionImpl(
353 func, DoCustomTrampolineWithThunk_vkGetInstanceProcAddr, "NextGetInstanceProcAddr");
354 }
355
356 GuestType<const struct VkAllocationCallbacks*>::GuestHolder pAllocator_holder;
357 pAllocator_guest =
358 GuestType<const struct VkAllocationCallbacks*>(pAllocator_host, pAllocator_holder, out_of_memory);
359 RunGuestCall(pc, buf);
360 }
361 }
362
RunGuest_vkGetDeviceProcAddr(GuestAddr pc,GuestArgumentBuffer * buf)363 void RunGuest_vkGetDeviceProcAddr(GuestAddr pc, GuestArgumentBuffer* buf) {
364 const auto& function_map = GetMapForRunGuestvkGetInstanceProcAddr();
365
366 auto [device, function_name] = HostArgumentsValues<PFN_vkGetDeviceProcAddr>(buf);
367
368 if (auto conversion = FindElementByName(function_map, function_name)) {
369 RunGuestCall(pc, buf);
370 auto&& [host_result] = HostResultReference<PFN_vkGetDeviceProcAddr>(buf);
371 auto [guest_result] = GuestResultValue<PFN_vkGetDeviceProcAddr>(buf);
372 host_result = bit_cast<PFN_vkVoidFunction>(conversion->wrapper(ToGuestAddr(guest_result)));
373 return;
374 }
375 ALOGE("Unknown function is used with vkGetDeviceProcAddr: %s", function_name);
376 auto&& [result] = HostResultReference<PFN_vkGetDeviceProcAddr>(buf);
377 result = bit_cast<PFN_vkVoidFunction>(nullptr);
378 }
379
RunGuest_vkGetInstanceProcAddr(GuestAddr pc,GuestArgumentBuffer * buf)380 void RunGuest_vkGetInstanceProcAddr(GuestAddr pc, GuestArgumentBuffer* buf) {
381 const auto& function_map = GetMapForRunGuestvkGetInstanceProcAddr();
382
383 auto [instance, function_name] = HostArgumentsValues<PFN_vkGetInstanceProcAddr>(buf);
384
385 if (auto conversion = FindElementByName(function_map, function_name)) {
386 RunGuestCall(pc, buf);
387 auto&& [host_result] = HostResultReference<PFN_vkGetDeviceProcAddr>(buf);
388 auto [guest_result] = GuestResultValue<PFN_vkGetDeviceProcAddr>(buf);
389 host_result = bit_cast<PFN_vkVoidFunction>(conversion->wrapper(ToGuestAddr(guest_result)));
390 return;
391 }
392 ALOGE("Unknown function is used with vkGetInstanceProcAddr: %s", function_name);
393 auto&& [result] = HostResultReference<PFN_vkGetDeviceProcAddr>(buf);
394 result = bit_cast<PFN_vkVoidFunction>(nullptr);
395 }
396
397 } // namespace
398
399 #if defined(NATIVE_BRIDGE_GUEST_ARCH_ARM) && defined(__i386__)
400
401 #include "trampolines_arm_to_x86-inl.h" // generated file NOLINT [build/include]
402
403 #elif defined(NATIVE_BRIDGE_GUEST_ARCH_ARM64) && defined(__x86_64__)
404
405 #include "trampolines_arm64_to_x86_64-inl.h" // generated file NOLINT [build/include]
406
407 #elif defined(NATIVE_BRIDGE_GUEST_ARCH_RISCV64) && defined(__x86_64__)
408
409 #include "trampolines_riscv64_to_x86_64-inl.h" // generated file NOLINT [build/include]
410
411 #else
412
413 #error "Unknown guest/host arch combination"
414
415 #endif
416
InitProxyLibrary(ProxyLibraryBuilder * builder)417 extern "C" void InitProxyLibrary(ProxyLibraryBuilder* builder) {
418 builder->Build("libvulkan.so",
419 sizeof(kKnownTrampolines) / sizeof(kKnownTrampolines[0]),
420 kKnownTrampolines,
421 sizeof(kKnownVariables) / sizeof(kKnownVariables[0]),
422 kKnownVariables);
423 for (const auto& named_wrapper : GetMapForRunGuestvkGetInstanceProcAddr()) {
424 RegisterKnownGuestFunctionWrapper(named_wrapper.name, named_wrapper.wrapper);
425 }
426 }
427
428 } // namespace berberis
429