summary refs log tree commit diff
path: root/devices/src/virtio/gpu/virtio_3d_backend.rs
diff options
context:
space:
mode:
Diffstat (limited to 'devices/src/virtio/gpu/virtio_3d_backend.rs')
-rw-r--r--devices/src/virtio/gpu/virtio_3d_backend.rs905
1 files changed, 905 insertions, 0 deletions
diff --git a/devices/src/virtio/gpu/virtio_3d_backend.rs b/devices/src/virtio/gpu/virtio_3d_backend.rs
new file mode 100644
index 0000000..21a5ac1
--- /dev/null
+++ b/devices/src/virtio/gpu/virtio_3d_backend.rs
@@ -0,0 +1,905 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Implementation of a virtio-gpu protocol command processor which supports display and accelerated
+//! rendering.
+
+use std::cell::RefCell;
+use std::collections::btree_map::Entry;
+use std::collections::BTreeMap as Map;
+use std::fs::File;
+use std::os::unix::io::AsRawFd;
+use std::rc::Rc;
+use std::usize;
+
+use libc::EINVAL;
+
+use data_model::*;
+use msg_socket::{MsgReceiver, MsgSender};
+use resources::Alloc;
+use sys_util::{error, warn, Error, GuestAddress, GuestMemory};
+
+use gpu_display::*;
+use gpu_renderer::{
+    Box3, Context as RendererContext, Error as GpuRendererError, Renderer, RendererFlags,
+    Resource as GpuRendererResource, ResourceCreateArgs,
+};
+
+use super::protocol::{
+    AllocationMetadataResponse, GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET3,
+    VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2, VIRTIO_GPU_MEMORY_HOST_COHERENT,
+};
+pub use crate::virtio::gpu::virtio_backend::{VirtioBackend, VirtioResource};
+use crate::virtio::gpu::{
+    Backend, DisplayBackend, VIRTIO_F_VERSION_1, VIRTIO_GPU_F_HOST_COHERENT, VIRTIO_GPU_F_MEMORY,
+    VIRTIO_GPU_F_VIRGL,
+};
+
+use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse};
+
+struct Virtio3DResource {
+    width: u32,
+    height: u32,
+    gpu_resource: GpuRendererResource,
+    display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
+    kvm_slot: Option<u32>,
+}
+
+impl Virtio3DResource {
+    pub fn new(width: u32, height: u32, gpu_resource: GpuRendererResource) -> Virtio3DResource {
+        Virtio3DResource {
+            width,
+            height,
+            gpu_resource,
+            display_import: None,
+            kvm_slot: None,
+        }
+    }
+
+    pub fn v2_new(
+        width: u32,
+        height: u32,
+        kvm_slot: u32,
+        gpu_resource: GpuRendererResource,
+    ) -> Virtio3DResource {
+        Virtio3DResource {
+            width,
+            height,
+            gpu_resource,
+            display_import: None,
+            kvm_slot: Some(kvm_slot),
+        }
+    }
+
+    fn as_mut(&mut self) -> &mut dyn VirtioResource {
+        self
+    }
+}
+
+impl VirtioResource for Virtio3DResource {
+    fn width(&self) -> u32 {
+        self.width
+    }
+
+    fn height(&self) -> u32 {
+        self.height
+    }
+
+    fn import_to_display(&mut self, display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
+        if let Some((self_display, import)) = &self.display_import {
+            if Rc::ptr_eq(self_display, display) {
+                return Some(*import);
+            }
+        }
+
+        let (query, dmabuf) = match self.gpu_resource.export() {
+            Ok(export) => (export.0, export.1),
+            Err(GpuRendererError::Virglrenderer(e)) if e == -EINVAL => return None,
+            Err(e) => {
+                error!("failed to query resource: {}", e);
+                return None;
+            }
+        };
+
+        match display.borrow_mut().import_dmabuf(
+            dmabuf.as_raw_fd(),
+            query.out_offsets[0],
+            query.out_strides[0],
+            query.out_modifier,
+            self.width,
+            self.height,
+            query.out_fourcc,
+        ) {
+            Ok(import_id) => {
+                self.display_import = Some((display.clone(), import_id));
+                Some(import_id)
+            }
+            Err(e) => {
+                error!("failed to import dmabuf for display: {}", e);
+                None
+            }
+        }
+    }
+
+    fn write_from_guest_memory(
+        &mut self,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+        src_offset: u64,
+        _mem: &GuestMemory,
+    ) {
+        let res = self.gpu_resource.transfer_write(
+            None,
+            0,
+            0,
+            0,
+            Box3::new_2d(x, y, width, height),
+            src_offset,
+        );
+        if let Err(e) = res {
+            error!(
+                "failed to write to resource (x={} y={} w={} h={}, src_offset={}): {}",
+                x, y, width, height, src_offset, e
+            );
+        }
+    }
+
+    fn read_to_volatile(
+        &mut self,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+        dst: VolatileSlice,
+        dst_stride: u32,
+    ) {
+        let res = self.gpu_resource.read_to_volatile(
+            None,
+            0,
+            dst_stride,
+            0, /* layer_stride */
+            Box3::new_2d(x, y, width, height),
+            0, /* offset */
+            dst,
+        );
+        if let Err(e) = res {
+            error!("failed to read from resource: {}", e);
+        }
+    }
+}
+
+/// The virtio-gpu backend state tracker which supports accelerated rendering.
+pub struct Virtio3DBackend {
+    base: VirtioBackend,
+    renderer: Renderer,
+    resources: Map<u32, Virtio3DResource>,
+    contexts: Map<u32, RendererContext>,
+    gpu_device_socket: VmMemoryControlRequestSocket,
+    pci_bar: Alloc,
+}
+
+impl Virtio3DBackend {
+    /// Creates a new backend for virtio-gpu that realizes all commands using the given `display`
+    /// for showing the results, and `renderer` for submitting rendering commands.
+    ///
+    /// All buffer allocations will be done internally by the renderer or the display and buffer
+    /// data is copied as needed.
+    pub fn new(
+        display: GpuDisplay,
+        display_width: u32,
+        display_height: u32,
+        renderer: Renderer,
+        gpu_device_socket: VmMemoryControlRequestSocket,
+        pci_bar: Alloc,
+    ) -> Virtio3DBackend {
+        Virtio3DBackend {
+            base: VirtioBackend {
+                display: Rc::new(RefCell::new(display)),
+                display_width,
+                display_height,
+                event_devices: Default::default(),
+                scanout_resource_id: None,
+                scanout_surface_id: None,
+                cursor_resource_id: None,
+                cursor_surface_id: None,
+            },
+            renderer,
+            resources: Default::default(),
+            contexts: Default::default(),
+            gpu_device_socket,
+            pci_bar,
+        }
+    }
+}
+
+impl Backend for Virtio3DBackend {
+    /// Returns the number of capsets provided by the Backend.
+    fn capsets() -> u32 {
+        3
+    }
+
+    /// Returns the bitset of virtio features provided by the Backend.
+    fn features() -> u64 {
+        1 << VIRTIO_GPU_F_VIRGL
+            | 1 << VIRTIO_F_VERSION_1
+            | 1 << VIRTIO_GPU_F_MEMORY
+            | 1 << VIRTIO_GPU_F_HOST_COHERENT
+    }
+
+    /// Returns the underlying Backend.
+    fn build(
+        possible_displays: &[DisplayBackend],
+        display_width: u32,
+        display_height: u32,
+        renderer_flags: RendererFlags,
+        event_devices: Vec<EventDevice>,
+        gpu_device_socket: VmMemoryControlRequestSocket,
+        pci_bar: Alloc,
+    ) -> Option<Box<dyn Backend>> {
+        let mut renderer_flags = renderer_flags;
+        let mut display_opt = None;
+        for display in possible_displays {
+            match display.build() {
+                Ok(c) => {
+                    // If X11 is being used, that's an indication that the renderer should also be
+                    // using glx. Otherwise, we are likely in an enviroment in which GBM will work
+                    // for doing allocations of buffers we wish to display. TODO(zachr): this is a
+                    // heuristic (or terrible hack depending on your POV). We should do something
+                    // either smarter or more configurable.
+                    if display.is_x() {
+                        renderer_flags = RendererFlags::new().use_glx(true);
+                    }
+                    display_opt = Some(c);
+                    break;
+                }
+                Err(e) => error!("failed to open display: {}", e),
+            };
+        }
+        let display = match display_opt {
+            Some(d) => d,
+            None => {
+                error!("failed to open any displays");
+                return None;
+            }
+        };
+
+        if cfg!(debug_assertions) {
+            let ret = unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
+            if ret == -1 {
+                warn!("unable to dup2 stdout to stderr: {}", Error::last());
+            }
+        }
+
+        let renderer = match Renderer::init(renderer_flags) {
+            Ok(r) => r,
+            Err(e) => {
+                error!("failed to initialize gpu renderer: {}", e);
+                return None;
+            }
+        };
+
+        let mut backend_3d = Virtio3DBackend::new(
+            display,
+            display_width,
+            display_height,
+            renderer,
+            gpu_device_socket,
+            pci_bar,
+        );
+
+        for event_device in event_devices {
+            backend_3d.import_event_device(event_device, 0);
+        }
+
+        Some(Box::new(backend_3d))
+    }
+
+    /// Gets a reference to the display passed into `new`.
+    fn display(&self) -> &Rc<RefCell<GpuDisplay>> {
+        &self.base.display
+    }
+
+    /// Processes the internal `display` events and returns `true` if the main display was closed.
+    fn process_display(&mut self) -> bool {
+        self.base.process_display()
+    }
+
+    /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples.
+    fn display_info(&self) -> [(u32, u32); 1] {
+        self.base.display_info()
+    }
+
+    /// Attaches the given input device to the given surface of the display (to allow for input
+    /// from an X11 window for example).
+    fn import_event_device(&mut self, event_device: EventDevice, scanout: u32) {
+        self.base.import_event_device(event_device, scanout);
+    }
+
+    /// If supported, export the resource with the given id to a file.
+    fn export_resource(&mut self, id: u32) -> Option<File> {
+        let test: Option<File> = self
+            .resources
+            .get(&id) // Option<resource>
+            .and_then(|resource| resource.gpu_resource.export().ok()) // Option<(Query, File)>
+            .and_then(|t| Some(t.1)); // Option<File>
+        return test;
+    }
+
+    /// Creates a fence with the given id that can be used to determine when the previous command
+    /// completed.
+    fn create_fence(&mut self, ctx_id: u32, fence_id: u32) -> GpuResponse {
+        // There is a mismatch of ordering that is intentional.
+        // This create_fence matches the other functions in Backend, yet
+        // the renderer matches the virgl interface.
+        match self.renderer.create_fence(fence_id, ctx_id) {
+            Ok(_) => GpuResponse::OkNoData,
+            Err(e) => {
+                error!("failed to create fence: {}", e);
+                GpuResponse::ErrUnspec
+            }
+        }
+    }
+
+    /// Returns the id of the latest fence to complete.
+    fn fence_poll(&mut self) -> u32 {
+        self.renderer.poll()
+    }
+
+    fn force_ctx_0(&mut self) {
+        self.renderer.force_ctx_0();
+    }
+
+    /// Creates a 2D resource with the given properties and associated it with the given id.
+    fn create_resource_2d(&mut self, id: u32, width: u32, height: u32, format: u32) -> GpuResponse {
+        if id == 0 {
+            return GpuResponse::ErrInvalidResourceId;
+        }
+        match self.resources.entry(id) {
+            Entry::Vacant(slot) => {
+                let gpu_resource = self.renderer.create_resource_2d(id, width, height, format);
+                match gpu_resource {
+                    Ok(gpu_resource) => {
+                        let virtio_gpu_resource =
+                            Virtio3DResource::new(width, height, gpu_resource);
+                        slot.insert(virtio_gpu_resource);
+                        GpuResponse::OkNoData
+                    }
+                    Err(e) => {
+                        error!("failed to create renderer resource: {}", e);
+                        GpuResponse::ErrUnspec
+                    }
+                }
+            }
+            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Removes the guest's reference count for the given resource id.
+    fn unref_resource(&mut self, id: u32) -> GpuResponse {
+        match self.resources.remove(&id) {
+            Some(_) => GpuResponse::OkNoData,
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Sets the given resource id as the source of scanout to the display.
+    fn set_scanout(&mut self, _scanout_id: u32, resource_id: u32) -> GpuResponse {
+        if resource_id == 0 || self.resources.get_mut(&resource_id).is_some() {
+            self.base.set_scanout(resource_id)
+        } else {
+            GpuResponse::ErrInvalidResourceId
+        }
+    }
+
+    /// Flushes the given rectangle of pixels of the given resource to the display.
+    fn flush_resource(
+        &mut self,
+        id: u32,
+        _x: u32,
+        _y: u32,
+        _width: u32,
+        _height: u32,
+    ) -> GpuResponse {
+        if id == 0 {
+            return GpuResponse::OkNoData;
+        }
+
+        let resource = match self.resources.get_mut(&id) {
+            Some(r) => r,
+            None => return GpuResponse::ErrInvalidResourceId,
+        };
+
+        self.base.flush_resource(resource, id)
+    }
+
+    /// Copes the given rectangle of pixels of the given resource's backing memory to the host side
+    /// resource.
+    fn transfer_to_resource_2d(
+        &mut self,
+        id: u32,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+        src_offset: u64,
+        mem: &GuestMemory,
+    ) -> GpuResponse {
+        match self.resources.get_mut(&id) {
+            Some(res) => {
+                res.write_from_guest_memory(x, y, width, height, src_offset, mem);
+                GpuResponse::OkNoData
+            }
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
+    /// tuples in the guest's physical address space.
+    fn attach_backing(
+        &mut self,
+        id: u32,
+        mem: &GuestMemory,
+        vecs: Vec<(GuestAddress, usize)>,
+    ) -> GpuResponse {
+        match self.resources.get_mut(&id) {
+            Some(resource) => match resource.gpu_resource.attach_backing(&vecs[..], mem) {
+                Ok(_) => GpuResponse::OkNoData,
+                Err(_) => GpuResponse::ErrUnspec,
+            },
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Detaches any backing memory from the given resource, if there is any.
+    fn detach_backing(&mut self, id: u32) -> GpuResponse {
+        match self.resources.get_mut(&id) {
+            Some(resource) => {
+                resource.gpu_resource.detach_backing();
+                GpuResponse::OkNoData
+            }
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Updates the cursor's memory to the given id, and sets its position to the given coordinates.
+    fn update_cursor(&mut self, id: u32, x: u32, y: u32) -> GpuResponse {
+        let resource = self.resources.get_mut(&id).map(|r| r.as_mut());
+
+        self.base.update_cursor(id, x, y, resource)
+    }
+
+    /// Moves the cursor's position to the given coordinates.
+    fn move_cursor(&mut self, x: u32, y: u32) -> GpuResponse {
+        self.base.move_cursor(x, y)
+    }
+
+    /// Gets the renderer's capset information associated with `index`.
+    fn get_capset_info(&self, index: u32) -> GpuResponse {
+        let id = match index {
+            0 => VIRTIO_GPU_CAPSET_VIRGL,
+            1 => VIRTIO_GPU_CAPSET_VIRGL2,
+            2 => VIRTIO_GPU_CAPSET3,
+            _ => return GpuResponse::ErrInvalidParameter,
+        };
+
+        let (version, size) = self.renderer.get_cap_set_info(id);
+        GpuResponse::OkCapsetInfo { id, version, size }
+    }
+
+    /// Gets the capset of `version` associated with `id`.
+    fn get_capset(&self, id: u32, version: u32) -> GpuResponse {
+        GpuResponse::OkCapset(self.renderer.get_cap_set(id, version))
+    }
+
+    /// Creates a fresh renderer context with the given `id`.
+    fn create_renderer_context(&mut self, id: u32) -> GpuResponse {
+        if id == 0 {
+            return GpuResponse::ErrInvalidContextId;
+        }
+        match self.contexts.entry(id) {
+            Entry::Occupied(_) => GpuResponse::ErrInvalidContextId,
+            Entry::Vacant(slot) => match self.renderer.create_context(id) {
+                Ok(ctx) => {
+                    slot.insert(ctx);
+                    GpuResponse::OkNoData
+                }
+                Err(e) => {
+                    error!("failed to create renderer ctx: {}", e);
+                    GpuResponse::ErrUnspec
+                }
+            },
+        }
+    }
+
+    /// Destorys the renderer context associated with `id`.
+    fn destroy_renderer_context(&mut self, id: u32) -> GpuResponse {
+        match self.contexts.remove(&id) {
+            Some(_) => GpuResponse::OkNoData,
+            None => GpuResponse::ErrInvalidContextId,
+        }
+    }
+
+    /// Attaches the indicated resource to the given context.
+    fn context_attach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
+        match (
+            self.contexts.get_mut(&ctx_id),
+            self.resources.get_mut(&res_id),
+        ) {
+            (Some(ctx), Some(res)) => {
+                ctx.attach(&res.gpu_resource);
+                GpuResponse::OkNoData
+            }
+            (None, _) => GpuResponse::ErrInvalidContextId,
+            (_, None) => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// detaches the indicated resource to the given context.
+    fn context_detach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
+        match (
+            self.contexts.get_mut(&ctx_id),
+            self.resources.get_mut(&res_id),
+        ) {
+            (Some(ctx), Some(res)) => {
+                ctx.detach(&res.gpu_resource);
+                GpuResponse::OkNoData
+            }
+            (None, _) => GpuResponse::ErrInvalidContextId,
+            (_, None) => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Creates a 3D resource with the given properties and associated it with the given id.
+    fn resource_create_3d(
+        &mut self,
+        id: u32,
+        target: u32,
+        format: u32,
+        bind: u32,
+        width: u32,
+        height: u32,
+        depth: u32,
+        array_size: u32,
+        last_level: u32,
+        nr_samples: u32,
+        flags: u32,
+    ) -> GpuResponse {
+        if id == 0 {
+            return GpuResponse::ErrInvalidResourceId;
+        }
+
+        let create_args = ResourceCreateArgs {
+            handle: id,
+            target,
+            format,
+            bind,
+            width,
+            height,
+            depth,
+            array_size,
+            last_level,
+            nr_samples,
+            flags,
+        };
+
+        match self.resources.entry(id) {
+            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+            Entry::Vacant(slot) => {
+                let gpu_resource = self.renderer.create_resource(create_args);
+                match gpu_resource {
+                    Ok(gpu_resource) => {
+                        let query = match gpu_resource.query() {
+                            Ok(query) => query,
+                            Err(_) => return GpuResponse::ErrUnspec,
+                        };
+
+                        let response = match query.out_num_fds {
+                            0 => GpuResponse::OkNoData,
+                            1 => {
+                                let mut plane_info = Vec::with_capacity(4);
+                                for plane_index in 0..4 {
+                                    plane_info.push(GpuResponsePlaneInfo {
+                                        stride: query.out_strides[plane_index],
+                                        offset: query.out_offsets[plane_index],
+                                    });
+                                }
+
+                                let format_modifier = query.out_modifier;
+                                GpuResponse::OkResourcePlaneInfo {
+                                    format_modifier,
+                                    plane_info,
+                                }
+                            }
+                            _ => return GpuResponse::ErrUnspec,
+                        };
+
+                        let virtio_gpu_resource =
+                            Virtio3DResource::new(width, height, gpu_resource);
+                        slot.insert(virtio_gpu_resource);
+                        response
+                    }
+                    Err(e) => {
+                        error!("failed to create renderer resource: {}", e);
+                        GpuResponse::ErrUnspec
+                    }
+                }
+            }
+        }
+    }
+    /// Copes the given 3D rectangle of pixels of the given resource's backing memory to the host
+    /// side resource.
+    fn transfer_to_resource_3d(
+        &mut self,
+        ctx_id: u32,
+        res_id: u32,
+        x: u32,
+        y: u32,
+        z: u32,
+        width: u32,
+        height: u32,
+        depth: u32,
+        level: u32,
+        stride: u32,
+        layer_stride: u32,
+        offset: u64,
+    ) -> GpuResponse {
+        let ctx = match ctx_id {
+            0 => None,
+            id => match self.contexts.get(&id) {
+                None => return GpuResponse::ErrInvalidContextId,
+                ctx => ctx,
+            },
+        };
+        match self.resources.get_mut(&res_id) {
+            Some(res) => {
+                let transfer_box = Box3 {
+                    x,
+                    y,
+                    z,
+                    w: width,
+                    h: height,
+                    d: depth,
+                };
+                let res = res.gpu_resource.transfer_write(
+                    ctx,
+                    level,
+                    stride,
+                    layer_stride,
+                    transfer_box,
+                    offset,
+                );
+                match res {
+                    Ok(_) => GpuResponse::OkNoData,
+                    Err(e) => {
+                        error!("failed to transfer to host: {}", e);
+                        GpuResponse::ErrUnspec
+                    }
+                }
+            }
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Copes the given rectangle of pixels from the resource to the given resource's backing
+    /// memory.
+    fn transfer_from_resource_3d(
+        &mut self,
+        ctx_id: u32,
+        res_id: u32,
+        x: u32,
+        y: u32,
+        z: u32,
+        width: u32,
+        height: u32,
+        depth: u32,
+        level: u32,
+        stride: u32,
+        layer_stride: u32,
+        offset: u64,
+    ) -> GpuResponse {
+        let ctx = match ctx_id {
+            0 => None,
+            id => match self.contexts.get(&id) {
+                None => return GpuResponse::ErrInvalidContextId,
+                ctx => ctx,
+            },
+        };
+        match self.resources.get_mut(&res_id) {
+            Some(res) => {
+                let transfer_box = Box3 {
+                    x,
+                    y,
+                    z,
+                    w: width,
+                    h: height,
+                    d: depth,
+                };
+                let res = res.gpu_resource.transfer_read(
+                    ctx,
+                    level,
+                    stride,
+                    layer_stride,
+                    transfer_box,
+                    offset,
+                );
+                match res {
+                    Ok(_) => GpuResponse::OkNoData,
+                    Err(e) => {
+                        error!("failed to transfer from host: {}", e);
+                        GpuResponse::ErrUnspec
+                    }
+                }
+            }
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    /// Submits a command buffer to the given rendering context.
+    fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> GpuResponse {
+        match self.contexts.get_mut(&ctx_id) {
+            Some(ctx) => match ctx.submit(&mut commands[..]) {
+                Ok(_) => GpuResponse::OkNoData,
+                Err(e) => {
+                    error!("failed to submit command buffer: {}", e);
+                    GpuResponse::ErrUnspec
+                }
+            },
+            None => GpuResponse::ErrInvalidContextId,
+        }
+    }
+
+    fn allocation_metadata(
+        &mut self,
+        request_id: u32,
+        request: Vec<u8>,
+        mut response: Vec<u8>,
+    ) -> GpuResponse {
+        let res = self.renderer.allocation_metadata(&request, &mut response);
+
+        match res {
+            Ok(_) => {
+                let res_info = AllocationMetadataResponse {
+                    request_id,
+                    response,
+                };
+
+                GpuResponse::OkAllocationMetadata { res_info }
+            }
+            Err(_) => {
+                error!("failed to get metadata");
+                GpuResponse::ErrUnspec
+            }
+        }
+    }
+
+    fn resource_create_v2(
+        &mut self,
+        resource_id: u32,
+        guest_memory_type: u32,
+        guest_caching_type: u32,
+        size: u64,
+        pci_addr: u64,
+        mem: &GuestMemory,
+        vecs: Vec<(GuestAddress, usize)>,
+        args: Vec<u8>,
+    ) -> GpuResponse {
+        match self.resources.entry(resource_id) {
+            Entry::Vacant(entry) => {
+                let resource = match self.renderer.resource_create_v2(
+                    resource_id,
+                    guest_memory_type,
+                    guest_caching_type,
+                    size,
+                    mem,
+                    &vecs,
+                    &args,
+                ) {
+                    Ok(resource) => resource,
+                    Err(e) => {
+                        error!("failed to create resource: {}", e);
+                        return GpuResponse::ErrUnspec;
+                    }
+                };
+
+                match guest_memory_type {
+                    VIRTIO_GPU_MEMORY_HOST_COHERENT => {
+                        let dma_buf_fd = match resource.export() {
+                            Ok(export) => export.1,
+                            Err(e) => {
+                                error!("failed to export plane fd: {}", e);
+                                return GpuResponse::ErrUnspec;
+                            }
+                        };
+
+                        let request = VmMemoryRequest::RegisterMemoryAtAddress(
+                            self.pci_bar,
+                            MaybeOwnedFd::Borrowed(dma_buf_fd.as_raw_fd()),
+                            size as usize,
+                            pci_addr,
+                        );
+
+                        match self.gpu_device_socket.send(&request) {
+                            Ok(_resq) => match self.gpu_device_socket.recv() {
+                                Ok(response) => match response {
+                                    VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
+                                        entry.insert(Virtio3DResource::v2_new(
+                                            self.base.display_width,
+                                            self.base.display_height,
+                                            slot,
+                                            resource,
+                                        ));
+                                        GpuResponse::OkNoData
+                                    }
+                                    VmMemoryResponse::Err(e) => {
+                                        error!("received an error: {}", e);
+                                        GpuResponse::ErrUnspec
+                                    }
+                                    _ => {
+                                        error!("recieved an unexpected response");
+                                        GpuResponse::ErrUnspec
+                                    }
+                                },
+                                Err(e) => {
+                                    error!("failed to receive data: {}", e);
+                                    GpuResponse::ErrUnspec
+                                }
+                            },
+                            Err(e) => {
+                                error!("failed to send request: {}", e);
+                                GpuResponse::ErrUnspec
+                            }
+                        }
+                    }
+                    _ => {
+                        entry.insert(Virtio3DResource::new(
+                            self.base.display_width,
+                            self.base.display_height,
+                            resource,
+                        ));
+
+                        GpuResponse::OkNoData
+                    }
+                }
+            }
+            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    fn resource_v2_unref(&mut self, resource_id: u32) -> GpuResponse {
+        match self.resources.remove(&resource_id) {
+            Some(entry) => match entry.kvm_slot {
+                Some(kvm_slot) => {
+                    let request = VmMemoryRequest::UnregisterMemory(kvm_slot);
+                    match self.gpu_device_socket.send(&request) {
+                        Ok(_resq) => match self.gpu_device_socket.recv() {
+                            Ok(response) => match response {
+                                VmMemoryResponse::Ok => GpuResponse::OkNoData,
+                                VmMemoryResponse::Err(e) => {
+                                    error!("received an error: {}", e);
+                                    GpuResponse::ErrUnspec
+                                }
+                                _ => {
+                                    error!("recieved an unexpected response");
+                                    GpuResponse::ErrUnspec
+                                }
+                            },
+                            Err(e) => {
+                                error!("failed to receive data: {}", e);
+                                GpuResponse::ErrUnspec
+                            }
+                        },
+                        Err(e) => {
+                            error!("failed to send request: {}", e);
+                            GpuResponse::ErrUnspec
+                        }
+                    }
+                }
+                None => GpuResponse::OkNoData,
+            },
+            None => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+}