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