rutabaga_gfx: cross-process Vulkan mappings

In multi-process mode, we currently rely on dma-buf mmap() to
map GPU buffers into the guest.  We usually have to fix the
Mesa driver, and maybe even the kernel to get to work.  That's
"kind of" fine for ChromeOS, which owns the entire stack.

For their Virtual Graphics Interface (VGI) initiative, Android
upstream has requested multi-process mode to work in a
cross-platform, generic way.  Using Vulkan is the only option
that meets the rigorous, uncompromising, strict, meticulous and
bone-crushing requirements of Android upstream.

This has possible two benefits:
  1) We can enable multi-process mode on Nvidia or other
     closed-source drivers, which is nice for Cuttlefish.

  2) On open-source drivers, dma-buf memory is pinned to the
     GTT (amdgpu), even when ideally it can be moved into faster
     vram regions.  This atleast gives the implementation the
     chance to do the smarter and faster option.

We shouldn't run into any SELinux issues since the main crosvm
process is not sandboxed.

Incidentals:
   * Changes vulkano_gralloc to consider integrated GPUs and dGPUs.
     Metadata query is preferred done on the integrated GPU.
   * Update vulkano_gralloc to match top of tree vulkano.

BUG=b:173630595
TEST=used Vulkano allocator and mapped memory into the guest

Change-Id: I78b069c7478d11b3201397894dcccd13bdc61f2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2792042
Tested-by: Gurchetan Singh <gurchetansingh@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
diff --git a/devices/src/virtio/gpu/virtio_gpu.rs b/devices/src/virtio/gpu/virtio_gpu.rs
index d2ba1b6..5608650 100644
--- a/devices/src/virtio/gpu/virtio_gpu.rs
+++ b/devices/src/virtio/gpu/virtio_gpu.rs
@@ -638,15 +638,28 @@
             .ok_or(ErrInvalidResourceId)?;
 
         let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?;
+        let vulkan_info_opt = self.rutabaga.vulkan_info(resource_id).ok();
+
         let export = self.rutabaga.export_blob(resource_id);
 
         let request = match export {
-            Ok(export) => VmMemoryRequest::RegisterFdAtPciBarOffset(
-                self.pci_bar,
-                export.os_handle,
-                resource.size as usize,
-                offset,
-            ),
+            Ok(export) => match vulkan_info_opt {
+                Some(vulkan_info) => VmMemoryRequest::RegisterVulkanMemoryAtPciBarOffset {
+                    alloc: self.pci_bar,
+                    descriptor: export.os_handle,
+                    handle_type: export.handle_type,
+                    memory_idx: vulkan_info.memory_idx,
+                    physical_device_idx: vulkan_info.physical_device_idx,
+                    offset,
+                    size: resource.size,
+                },
+                None => VmMemoryRequest::RegisterFdAtPciBarOffset(
+                    self.pci_bar,
+                    export.os_handle,
+                    resource.size as usize,
+                    offset,
+                ),
+            },
             Err(_) => {
                 if self.external_blob {
                     return Err(ErrUnspec);
diff --git a/rutabaga_gfx/src/rutabaga_core.rs b/rutabaga_gfx/src/rutabaga_core.rs
index e0b336e..fb75594 100644
--- a/rutabaga_gfx/src/rutabaga_core.rs
+++ b/rutabaga_gfx/src/rutabaga_core.rs
@@ -505,8 +505,18 @@
             .get(&resource_id)
             .ok_or(RutabagaError::InvalidResourceId)?;
 
-        let map_info = resource.map_info.ok_or(RutabagaError::SpecViolation)?;
-        Ok(map_info)
+        resource.map_info.ok_or(RutabagaError::SpecViolation)
+    }
+
+    /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
+    /// index and memory index associated with the resource.
+    pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
+        let resource = self
+            .resources
+            .get(&resource_id)
+            .ok_or(RutabagaError::InvalidResourceId)?;
+
+        resource.vulkan_info.ok_or(RutabagaError::Unsupported)
     }
 
     /// Returns the 3D info associated with the resource, if any.
@@ -516,8 +526,7 @@
             .get(&resource_id)
             .ok_or(RutabagaError::InvalidResourceId)?;
 
-        let info_3d = resource.info_3d.ok_or(RutabagaError::Unsupported)?;
-        Ok(info_3d)
+        resource.info_3d.ok_or(RutabagaError::Unsupported)
     }
 
     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs b/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
index 4222ba9..9b3f25f 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
@@ -7,7 +7,7 @@
 
 use std::collections::BTreeMap as Map;
 
-use base::round_up_to_page_size;
+use base::{round_up_to_page_size, MappedRegion};
 
 use crate::rutabaga_gralloc::formats::*;
 use crate::rutabaga_gralloc::system_gralloc::SystemGralloc;
@@ -89,6 +89,26 @@
         }
     }
 
+    /// Sets the SW write flag's presence.
+    #[inline(always)]
+    pub fn use_sw_write(self, e: bool) -> RutabagaGrallocFlags {
+        if e {
+            RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
+        } else {
+            RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
+        }
+    }
+
+    /// Sets the SW read flag's presence.
+    #[inline(always)]
+    pub fn use_sw_read(self, e: bool) -> RutabagaGrallocFlags {
+        if e {
+            RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
+        } else {
+            RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
+        }
+    }
+
     /// Returns true if the texturing flag is set.
     #[inline(always)]
     pub fn uses_texturing(self) -> bool {
@@ -167,6 +187,17 @@
     /// Implementations must allocate memory given the requirements and return a RutabagaHandle
     /// upon success.
     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>;
+
+    /// Implementations must import the given `handle` and return a mapping, suitable for use with
+    /// KVM and other hypervisors.  This is optional and only works with the Vulkano backend.
+    fn import_and_map(
+        &mut self,
+        _handle: RutabagaHandle,
+        _vulkan_info: VulkanInfo,
+        _size: u64,
+    ) -> RutabagaResult<Box<dyn MappedRegion>> {
+        Err(RutabagaError::Unsupported)
+    }
 }
 
 /// Enumeration of possible allocation backends.
@@ -293,6 +324,22 @@
 
         gralloc.allocate_memory(reqs)
     }
+
+    /// Imports the `handle` using the given `vulkan_info`.  Returns a mapping using Vulkano upon
+    /// success.  Should not be used with minigbm or system gralloc backends.
+    pub fn import_and_map(
+        &mut self,
+        handle: RutabagaHandle,
+        vulkan_info: VulkanInfo,
+        size: u64,
+    ) -> RutabagaResult<Box<dyn MappedRegion>> {
+        let gralloc = self
+            .grallocs
+            .get_mut(&GrallocBackend::Vulkano)
+            .ok_or(RutabagaError::Unsupported)?;
+
+        gralloc.import_and_map(handle, vulkan_info, size)
+    }
 }
 
 #[cfg(test)]
@@ -363,4 +410,44 @@
         // Reallocate with same requirements
         let _handle2 = gralloc.allocate_memory(reqs).unwrap();
     }
+
+    #[test]
+    fn export_and_map() {
+        let gralloc_result = RutabagaGralloc::new();
+        if gralloc_result.is_err() {
+            return;
+        }
+
+        let mut gralloc = gralloc_result.unwrap();
+
+        let info = ImageAllocationInfo {
+            width: 512,
+            height: 1024,
+            drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
+            flags: RutabagaGrallocFlags::empty()
+                .use_linear(true)
+                .use_sw_write(true)
+                .use_sw_read(true),
+        };
+
+        let mut reqs = gralloc.get_image_memory_requirements(info).unwrap();
+
+        // Anything else can use the mmap(..) system call.
+        if reqs.vulkan_info.is_none() {
+            return;
+        }
+
+        let handle = gralloc.allocate_memory(reqs).unwrap();
+        let vulkan_info = reqs.vulkan_info.take().unwrap();
+
+        let mapping = gralloc
+            .import_and_map(handle, vulkan_info, reqs.size)
+            .unwrap();
+
+        let addr = mapping.as_ptr();
+        let size = mapping.size();
+
+        assert_eq!(size as u64, reqs.size);
+        assert_ne!(addr as *const u8, std::ptr::null());
+    }
 }
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs b/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
index c122224..7c66d21 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
@@ -9,22 +9,27 @@
 
 #![cfg(feature = "vulkano")]
 
+use std::collections::BTreeMap as Map;
+use std::convert::TryInto;
 use std::iter::Empty;
 use std::sync::Arc;
 
+use base::MappedRegion;
+
 use crate::rutabaga_gralloc::gralloc::{Gralloc, ImageAllocationInfo, ImageMemoryRequirements};
 use crate::rutabaga_utils::*;
 
 use vulkano::device::{Device, DeviceCreationError, DeviceExtensions};
-use vulkano::image::{sys, ImageCreationError, ImageDimensions, ImageUsage};
+use vulkano::image::{sys, ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage};
 
 use vulkano::instance::{
     Instance, InstanceCreationError, InstanceExtensions, MemoryType, PhysicalDevice,
+    PhysicalDeviceType,
 };
 
 use vulkano::memory::{
-    DedicatedAlloc, DeviceMemoryAllocError, DeviceMemoryBuilder, ExternalMemoryHandleType,
-    MemoryRequirements,
+    DedicatedAlloc, DeviceMemoryAllocError, DeviceMemoryBuilder, DeviceMemoryMapping,
+    ExternalMemoryHandleType, MemoryRequirements,
 };
 
 use vulkano::memory::pool::AllocFromRequirementsFilter;
@@ -32,7 +37,32 @@
 
 /// A gralloc implementation capable of allocation `VkDeviceMemory`.
 pub struct VulkanoGralloc {
-    device: Arc<Device>,
+    devices: Map<PhysicalDeviceType, Arc<Device>>,
+    has_integrated_gpu: bool,
+}
+
+struct VulkanoMapping {
+    mapping: DeviceMemoryMapping,
+    size: usize,
+}
+
+impl VulkanoMapping {
+    pub fn new(mapping: DeviceMemoryMapping, size: usize) -> VulkanoMapping {
+        VulkanoMapping { mapping, size }
+    }
+}
+
+unsafe impl MappedRegion for VulkanoMapping {
+    /// Used for passing this region for hypervisor memory mappings.  We trust crosvm to use this
+    /// safely.
+    fn as_ptr(&self) -> *mut u8 {
+        unsafe { self.mapping.as_ptr() }
+    }
+
+    /// Returns the size of the memory region in bytes.
+    fn size(&self) -> usize {
+        self.size
+    }
 }
 
 impl VulkanoGralloc {
@@ -42,39 +72,52 @@
         // explanation of VK initialization.
         let instance = Instance::new(None, &InstanceExtensions::none(), None)?;
 
-        // We should really check for integrated GPU versus dGPU.
-        let physical = PhysicalDevice::enumerate(&instance)
-            .next()
-            .ok_or(RutabagaError::Unsupported)?;
+        let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default();
+        let mut has_integrated_gpu = false;
 
-        let queue_family = physical
-            .queue_families()
-            .find(|&q| {
-                // We take the first queue family that supports graphics.
-                q.supports_graphics()
-            })
-            .ok_or(RutabagaError::Unsupported)?;
+        for physical in PhysicalDevice::enumerate(&instance) {
+            let queue_family = physical
+                .queue_families()
+                .find(|&q| {
+                    // We take the first queue family that supports graphics.
+                    q.supports_graphics()
+                })
+                .ok_or(RutabagaError::Unsupported)?;
 
-        let supported_extensions = DeviceExtensions::supported_by_device(physical);
-        let desired_extensions = DeviceExtensions {
-            khr_dedicated_allocation: true,
-            khr_get_memory_requirements2: true,
-            khr_external_memory: true,
-            khr_external_memory_fd: true,
-            ext_external_memory_dmabuf: true,
-            ..DeviceExtensions::none()
-        };
+            let supported_extensions = DeviceExtensions::supported_by_device(physical);
 
-        let intersection = supported_extensions.intersection(&desired_extensions);
+            let desired_extensions = DeviceExtensions {
+                khr_dedicated_allocation: true,
+                khr_get_memory_requirements2: true,
+                khr_external_memory: true,
+                khr_external_memory_fd: true,
+                ext_external_memory_dmabuf: true,
+                ..DeviceExtensions::none()
+            };
 
-        let (device, mut _queues) = Device::new(
-            physical,
-            physical.supported_features(),
-            &intersection,
-            [(queue_family, 0.5)].iter().cloned(),
-        )?;
+            let intersection = supported_extensions.intersection(&desired_extensions);
 
-        Ok(Box::new(VulkanoGralloc { device }))
+            let (device, mut _queues) = Device::new(
+                physical,
+                physical.supported_features(),
+                &intersection,
+                [(queue_family, 0.5)].iter().cloned(),
+            )?;
+
+            if device.physical_device().ty() == PhysicalDeviceType::IntegratedGpu {
+                has_integrated_gpu = true
+            }
+
+            // If we have two devices of the same type (two integrated GPUs), the old value is
+            // dropped.  Vulkano is verbose enough such that a keener selection algorithm may
+            // be used, but the need for such complexity does not seem to exist now.
+            devices.insert(device.physical_device().ty(), device);
+        }
+
+        Ok(Box::new(VulkanoGralloc {
+            devices,
+            has_integrated_gpu,
+        }))
     }
 
     // This function is used safely in this module because gralloc does not:
@@ -88,6 +131,16 @@
         &mut self,
         info: ImageAllocationInfo,
     ) -> RutabagaResult<(sys::UnsafeImage, MemoryRequirements)> {
+        let device = if self.has_integrated_gpu {
+            self.devices
+                .get(&PhysicalDeviceType::IntegratedGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        } else {
+            self.devices
+                .get(&PhysicalDeviceType::DiscreteGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        };
+
         let usage = match info.flags.uses_rendering() {
             true => ImageUsage {
                 color_attachment: true,
@@ -111,14 +164,14 @@
 
         let vulkan_format = info.drm_format.vulkan_format()?;
         let (unsafe_image, memory_requirements) = sys::UnsafeImage::new(
-            self.device.clone(),
+            device.clone(),
             usage,
             vulkan_format,
+            ImageCreateFlags::none(),
             ImageDimensions::Dim2d {
                 width: info.width,
                 height: info.height,
                 array_layers: 1,
-                cubemap_compatible: false,
             },
             1, /* number of samples */
             1, /* mipmap count */
@@ -133,11 +186,23 @@
 
 impl Gralloc for VulkanoGralloc {
     fn supports_external_gpu_memory(&self) -> bool {
-        self.device.loaded_extensions().khr_external_memory
+        for device in self.devices.values() {
+            if !device.loaded_extensions().khr_external_memory {
+                return false;
+            }
+        }
+
+        true
     }
 
     fn supports_dmabuf(&self) -> bool {
-        self.device.loaded_extensions().ext_external_memory_dmabuf
+        for device in self.devices.values() {
+            if !device.loaded_extensions().ext_external_memory_dmabuf {
+                return false;
+            }
+        }
+
+        true
     }
 
     fn get_image_memory_requirements(
@@ -148,6 +213,16 @@
 
         let (unsafe_image, memory_requirements) = unsafe { self.create_image(info)? };
 
+        let device = if self.has_integrated_gpu {
+            self.devices
+                .get(&PhysicalDeviceType::IntegratedGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        } else {
+            self.devices
+                .get(&PhysicalDeviceType::DiscreteGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        };
+
         let planar_layout = info.drm_format.planar_layout()?;
 
         // Safe because we created the image with the linear bit set and verified the format is
@@ -188,13 +263,11 @@
                 AllocFromRequirementsFilter::Allowed
             };
 
-            let first_loop = self
-                .device
+            let first_loop = device
                 .physical_device()
                 .memory_types()
                 .map(|t| (t, AllocFromRequirementsFilter::Preferred));
-            let second_loop = self
-                .device
+            let second_loop = device
                 .physical_device()
                 .memory_types()
                 .map(|t| (t, AllocFromRequirementsFilter::Allowed));
@@ -219,7 +292,7 @@
 
         reqs.vulkan_info = Some(VulkanInfo {
             memory_idx: memory_type.id() as u32,
-            physical_device_idx: self.device.physical_device().index() as u32,
+            physical_device_idx: device.physical_device().index() as u32,
         });
 
         Ok(reqs)
@@ -227,15 +300,26 @@
 
     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
         let (unsafe_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };
+
         let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::SpecViolation)?;
-        let memory_type = self
-            .device
+
+        let device = if self.has_integrated_gpu {
+            self.devices
+                .get(&PhysicalDeviceType::IntegratedGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        } else {
+            self.devices
+                .get(&PhysicalDeviceType::DiscreteGpu)
+                .ok_or(RutabagaError::Unsupported)?
+        };
+
+        let memory_type = device
             .physical_device()
             .memory_type_by_id(vulkan_info.memory_idx)
             .ok_or(RutabagaError::SpecViolation)?;
 
         let (handle_type, rutabaga_type) =
-            match self.device.loaded_extensions().ext_external_memory_dmabuf {
+            match device.loaded_extensions().ext_external_memory_dmabuf {
                 true => (
                     ExternalMemoryHandleType {
                         dma_buf: true,
@@ -252,7 +336,7 @@
                 ),
             };
 
-        let dedicated = match self.device.loaded_extensions().khr_dedicated_allocation {
+        let dedicated = match device.loaded_extensions().khr_dedicated_allocation {
             true => {
                 if memory_requirements.prefer_dedicated {
                     DedicatedAlloc::Image(&unsafe_image)
@@ -264,7 +348,7 @@
         };
 
         let device_memory =
-            DeviceMemoryBuilder::new(self.device.clone(), memory_type, reqs.size as usize)
+            DeviceMemoryBuilder::new(device.clone(), memory_type.id(), reqs.size as usize)
                 .dedicated_info(dedicated)
                 .export_info(handle_type)
                 .build()?;
@@ -276,6 +360,43 @@
             handle_type: rutabaga_type,
         })
     }
+
+    /// Implementations must map the memory associated with the `resource_id` upon success.
+    fn import_and_map(
+        &mut self,
+        handle: RutabagaHandle,
+        vulkan_info: VulkanInfo,
+        size: u64,
+    ) -> RutabagaResult<Box<dyn MappedRegion>> {
+        let device = self
+            .devices
+            .values()
+            .find(|device| {
+                device.physical_device().index() as u32 == vulkan_info.physical_device_idx
+            })
+            .ok_or(RutabagaError::Unsupported)?;
+
+        let handle_type = match handle.handle_type {
+            RUTABAGA_MEM_HANDLE_TYPE_DMABUF => ExternalMemoryHandleType {
+                dma_buf: true,
+                ..ExternalMemoryHandleType::none()
+            },
+            RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD => ExternalMemoryHandleType {
+                opaque_fd: true,
+                ..ExternalMemoryHandleType::none()
+            },
+            _ => return Err(RutabagaError::Unsupported),
+        };
+
+        let valid_size: usize = size.try_into()?;
+        let device_memory =
+            DeviceMemoryBuilder::new(device.clone(), vulkan_info.memory_idx, valid_size)
+                .import_info(handle.os_handle.into(), handle_type)
+                .build()?;
+        let mapping = DeviceMemoryMapping::new(device.clone(), device_memory.clone(), 0, size, 0)?;
+
+        Ok(Box::new(VulkanoMapping::new(mapping, valid_size)))
+    }
 }
 
 // Vulkano should really define an universal type that wraps all these errors, say
diff --git a/rutabaga_gfx/src/rutabaga_utils.rs b/rutabaga_gfx/src/rutabaga_utils.rs
index b81989d..a1db693 100644
--- a/rutabaga_gfx/src/rutabaga_utils.rs
+++ b/rutabaga_gfx/src/rutabaga_utils.rs
@@ -6,6 +6,7 @@
 
 use std::fmt::{self, Display};
 use std::io::Error as IoError;
+use std::num::TryFromIntError;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use std::str::Utf8Error;
@@ -154,6 +155,8 @@
     SpecViolation,
     /// System error returned as a result of rutabaga library operation.
     SysError(SysError),
+    /// An attempted integer conversion failed.
+    TryFromIntError(TryFromIntError),
     /// The command is unsupported.
     Unsupported,
     /// Utf8 error.
@@ -209,6 +212,7 @@
             ComponentError(ret) => write!(f, "rutabaga component failed with error {}", ret),
             SpecViolation => write!(f, "violation of the rutabaga spec"),
             SysError(e) => write!(f, "rutabaga received a system error: {}", e),
+            TryFromIntError(e) => write!(f, "int conversion failed: {}", e),
             Unsupported => write!(f, "feature or function unsupported"),
             Utf8Error(e) => write!(f, "an utf8 error occured: {}", e),
             VolatileMemoryError(e) => write!(f, "noticed a volatile memory error {}", e),
@@ -238,6 +242,12 @@
     }
 }
 
+impl From<TryFromIntError> for RutabagaError {
+    fn from(e: TryFromIntError) -> RutabagaError {
+        RutabagaError::TryFromIntError(e)
+    }
+}
+
 impl From<Utf8Error> for RutabagaError {
     fn from(e: Utf8Error) -> RutabagaError {
         RutabagaError::Utf8Error(e)
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 2026315..41847cb 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -32,7 +32,10 @@
 };
 use hypervisor::{IrqRoute, IrqSource, Vm};
 use resources::{Alloc, MmioType, SystemAllocator};
-use rutabaga_gfx::{DrmFormat, ImageAllocationInfo, RutabagaGralloc, RutabagaGrallocFlags};
+use rutabaga_gfx::{
+    DrmFormat, ImageAllocationInfo, RutabagaGralloc, RutabagaGrallocFlags, RutabagaHandle,
+    VulkanInfo,
+};
 use sync::Mutex;
 use vm_memory::GuestAddress;
 
@@ -263,6 +266,17 @@
     RegisterFdAtPciBarOffset(Alloc, SafeDescriptor, usize, u64),
     /// Similar to RegisterFdAtPciBarOffset, but is for buffers in the current address space.
     RegisterHostPointerAtPciBarOffset(Alloc, u64),
+    /// Similiar to `RegisterFdAtPciBarOffset`, but uses Vulkano to map the resource instead of
+    /// the mmap system call.
+    RegisterVulkanMemoryAtPciBarOffset {
+        alloc: Alloc,
+        descriptor: SafeDescriptor,
+        handle_type: u32,
+        memory_idx: u32,
+        physical_device_idx: u32,
+        offset: u64,
+        size: u64,
+    },
     /// Unregister the given memory slot that was previously registered with `RegisterMemory*`.
     UnregisterMemory(MemSlot),
     /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
@@ -292,14 +306,14 @@
     /// `VmMemoryResponse` with the intended purpose of sending the response back over the socket
     /// that received this `VmMemoryResponse`.
     pub fn execute(
-        &self,
+        self,
         vm: &mut impl Vm,
         sys_allocator: &mut SystemAllocator,
         map_request: Arc<Mutex<Option<ExternalMapping>>>,
         gralloc: &mut RutabagaGralloc,
     ) -> VmMemoryResponse {
         use self::VmMemoryRequest::*;
-        match *self {
+        match self {
             RegisterMemory(ref shm) => {
                 match register_memory(vm, sys_allocator, shm, shm.size() as usize, None) {
                     Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
@@ -323,7 +337,39 @@
                     .ok_or_else(|| VmMemoryResponse::Err(SysError::new(EINVAL)))
                     .unwrap();
 
-                match register_memory_hva(vm, sys_allocator, Box::new(mem), (alloc, offset)) {
+                match register_host_pointer(vm, sys_allocator, Box::new(mem), (alloc, offset)) {
+                    Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
+                    Err(e) => VmMemoryResponse::Err(e),
+                }
+            }
+            RegisterVulkanMemoryAtPciBarOffset {
+                alloc,
+                descriptor,
+                handle_type,
+                memory_idx,
+                physical_device_idx,
+                offset,
+                size,
+            } => {
+                let mapped_region = match gralloc.import_and_map(
+                    RutabagaHandle {
+                        os_handle: descriptor,
+                        handle_type,
+                    },
+                    VulkanInfo {
+                        memory_idx,
+                        physical_device_idx,
+                    },
+                    size,
+                ) {
+                    Ok(mapped_region) => mapped_region,
+                    Err(e) => {
+                        error!("gralloc failed to import and map: {}", e);
+                        return VmMemoryResponse::Err(SysError::new(EINVAL));
+                    }
+                };
+
+                match register_host_pointer(vm, sys_allocator, mapped_region, (alloc, offset)) {
                     Ok((pfn, slot)) => VmMemoryResponse::RegisterMemory { pfn, slot },
                     Err(e) => VmMemoryResponse::Err(e),
                 }
@@ -908,7 +954,7 @@
     Ok((addr >> 12, slot))
 }
 
-fn register_memory_hva(
+fn register_host_pointer(
     vm: &mut impl Vm,
     allocator: &mut SystemAllocator,
     mem: Box<dyn MappedRegion>,