summary refs log tree commit diff
diff options
context:
space:
mode:
authorGurchetan Singh <gurchetansingh@chromium.org>2019-04-30 10:22:15 -0700
committerCommit Bot <commit-bot@chromium.org>2019-10-01 07:53:34 +0000
commit719f2831ed394bf4f3fd777938261bda493b05fc (patch)
tree9397041cb0b3db2df13fee96dec621db7a691d14
parent5b636babc1e83b7187ef72bd85b3a3bdc2e5d03f (diff)
downloadcrosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar.gz
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar.bz2
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar.lz
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar.xz
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.tar.zst
crosvm-719f2831ed394bf4f3fd777938261bda493b05fc.zip
virtio-gpu: handle VIRTIO_GPU_CMD_RESOURCE_CREATE_V2
BUG=chromium:924405
TEST=compile

Change-Id: I57379452f6805aaf429c268b95ddd3aecd07e90e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1591463
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Gurchetan Singh <gurchetansingh@chromium.org>
Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org>
-rw-r--r--devices/src/virtio/gpu/backend.rs144
-rw-r--r--devices/src/virtio/gpu/mod.rs50
-rw-r--r--devices/src/virtio/gpu/protocol.rs36
-rw-r--r--gpu_renderer/src/lib.rs56
4 files changed, 282 insertions, 4 deletions
diff --git a/devices/src/virtio/gpu/backend.rs b/devices/src/virtio/gpu/backend.rs
index 4cfac7a..75dbdaf 100644
--- a/devices/src/virtio/gpu/backend.rs
+++ b/devices/src/virtio/gpu/backend.rs
@@ -24,10 +24,11 @@ use gpu_renderer::{
 
 use super::protocol::{
     AllocationMetadataResponse, GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET3,
-    VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2,
+    VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2, VIRTIO_GPU_MEMORY_HOST_COHERENT,
 };
 use crate::virtio::resource_bridge::*;
-use vm_control::VmMemoryControlRequestSocket;
+
+use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse};
 
 const DEFAULT_WIDTH: u32 = 1280;
 const DEFAULT_HEIGHT: u32 = 1024;
@@ -37,6 +38,7 @@ struct VirtioGpuResource {
     height: u32,
     gpu_resource: GpuRendererResource,
     display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
+    kvm_slot: Option<u32>,
 }
 
 impl VirtioGpuResource {
@@ -46,6 +48,19 @@ impl VirtioGpuResource {
             height,
             gpu_resource,
             display_import: None,
+            kvm_slot: None,
+        }
+    }
+
+    pub fn v2_new(kvm_slot: u32, gpu_resource: GpuRendererResource) -> VirtioGpuResource {
+        // Choose DEFAULT_WIDTH and DEFAULT_HEIGHT, since that matches the default modes
+        // for virtgpu-kms.
+        VirtioGpuResource {
+            width: DEFAULT_WIDTH,
+            height: DEFAULT_HEIGHT,
+            gpu_resource,
+            display_import: None,
+            kvm_slot: Some(kvm_slot),
         }
     }
 
@@ -143,13 +158,11 @@ pub struct Backend {
     renderer: Renderer,
     resources: Map<u32, VirtioGpuResource>,
     contexts: Map<u32, RendererContext>,
-    #[allow(dead_code)]
     gpu_device_socket: VmMemoryControlRequestSocket,
     scanout_surface: Option<u32>,
     cursor_surface: Option<u32>,
     scanout_resource: u32,
     cursor_resource: u32,
-    #[allow(dead_code)]
     pci_bar: Alloc,
 }
 
@@ -802,4 +815,127 @@ impl Backend {
             }
         }
     }
+
+    pub 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(VirtioGpuResource::v2_new(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(VirtioGpuResource::new(
+                            DEFAULT_WIDTH,
+                            DEFAULT_HEIGHT,
+                            resource,
+                        ));
+
+                        GpuResponse::OkNoData
+                    }
+                }
+            }
+            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
+        }
+    }
+
+    pub 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,
+        }
+    }
 }
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 2ad1f2e..29142ea 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -285,6 +285,56 @@ impl Frontend {
                     GpuResponse::ErrUnspec
                 }
             }
+            GpuCommand::ResourceCreateV2(info) => {
+                if reader.available_bytes() != 0 {
+                    let resource_id = info.resource_id.to_native();
+                    let guest_memory_type = info.guest_memory_type.to_native();
+                    let size = info.size.to_native();
+                    let guest_caching_type = info.guest_caching_type.to_native();
+                    let pci_addr = info.pci_addr.to_native();
+                    let entry_count = info.nr_entries.to_native();
+                    let args_size = info.args_size.to_native();
+                    if args_size > VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE
+                        || entry_count > VIRTIO_GPU_MAX_IOVEC_ENTRIES
+                    {
+                        return GpuResponse::ErrUnspec;
+                    }
+
+                    let mut iovecs = Vec::with_capacity(entry_count as usize);
+                    let mut args = vec![0; args_size as usize];
+
+                    for _ in 0..entry_count {
+                        match reader.read_obj::<virtio_gpu_mem_entry>() {
+                            Ok(entry) => {
+                                let addr = GuestAddress(entry.addr.to_native());
+                                let len = entry.length.to_native() as usize;
+                                iovecs.push((addr, len))
+                            }
+                            Err(_) => return GpuResponse::ErrUnspec,
+                        }
+                    }
+
+                    match reader.read(&mut args[..]) {
+                        Ok(_) => self.backend.resource_create_v2(
+                            resource_id,
+                            guest_memory_type,
+                            guest_caching_type,
+                            size,
+                            pci_addr,
+                            mem,
+                            iovecs,
+                            args,
+                        ),
+                        Err(_) => GpuResponse::ErrUnspec,
+                    }
+                } else {
+                    GpuResponse::ErrUnspec
+                }
+            }
+            GpuCommand::ResourceV2Unref(info) => {
+                let resource_id = info.resource_id.to_native();
+                self.backend.resource_v2_unref(resource_id)
+            }
         }
     }
 
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
index 9c3b112..c6773dd 100644
--- a/devices/src/virtio/gpu/protocol.rs
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -86,6 +86,8 @@ pub const VIRTIO_GPU_UNCACHED: u32 = 3;
 
 /* Limits on virtio-gpu stream (not upstreamed) */
 pub const VIRTIO_GPU_MAX_BLOB_ARGUMENT_SIZE: u32 = 4096;
+/* This matches the limit in udmabuf.c */
+pub const VIRTIO_GPU_MAX_IOVEC_ENTRIES: u32 = 1024;
 
 pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
     match cmd {
@@ -515,6 +517,32 @@ pub struct virtio_gpu_resp_allocation_metadata {
 
 unsafe impl DataInit for virtio_gpu_resp_allocation_metadata {}
 
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_resource_create_v2 {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub guest_memory_type: Le32,
+    pub guest_caching_type: Le32,
+    pub padding: Le32,
+    pub size: Le64,
+    pub pci_addr: Le64,
+    pub args_size: Le32,
+    pub nr_entries: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_create_v2 {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_resource_v2_unref {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_v2_unref {}
+
 /* simple formats for fbcon/X use */
 pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
 pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
@@ -546,6 +574,8 @@ pub enum GpuCommand {
     TransferToHost3d(virtio_gpu_transfer_host_3d),
     TransferFromHost3d(virtio_gpu_transfer_host_3d),
     CmdSubmit3d(virtio_gpu_cmd_submit),
+    ResourceCreateV2(virtio_gpu_resource_create_v2),
+    ResourceV2Unref(virtio_gpu_resource_v2_unref),
     AllocationMetadata(virtio_gpu_allocation_metadata),
     UpdateCursor(virtio_gpu_update_cursor),
     MoveCursor(virtio_gpu_update_cursor),
@@ -604,6 +634,8 @@ impl fmt::Debug for GpuCommand {
             TransferToHost3d(_info) => f.debug_struct("TransferToHost3d").finish(),
             TransferFromHost3d(_info) => f.debug_struct("TransferFromHost3d").finish(),
             CmdSubmit3d(_info) => f.debug_struct("CmdSubmit3d").finish(),
+            ResourceCreateV2(_info) => f.debug_struct("ResourceCreateV2").finish(),
+            ResourceV2Unref(_info) => f.debug_struct("ResourceV2Unref").finish(),
             AllocationMetadata(_info) => f.debug_struct("AllocationMetadata").finish(),
             UpdateCursor(_info) => f.debug_struct("UpdateCursor").finish(),
             MoveCursor(_info) => f.debug_struct("MoveCursor").finish(),
@@ -635,6 +667,8 @@ impl GpuCommand {
             VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => TransferToHost3d(cmd.read_obj()?),
             VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => TransferFromHost3d(cmd.read_obj()?),
             VIRTIO_GPU_CMD_SUBMIT_3D => CmdSubmit3d(cmd.read_obj()?),
+            VIRTIO_GPU_CMD_RESOURCE_CREATE_V2 => ResourceCreateV2(cmd.read_obj()?),
+            VIRTIO_GPU_CMD_RESOURCE_CREATE_V2_UNREF => ResourceV2Unref(cmd.read_obj()?),
             VIRTIO_GPU_CMD_ALLOCATION_METADATA => AllocationMetadata(cmd.read_obj()?),
             VIRTIO_GPU_CMD_UPDATE_CURSOR => UpdateCursor(cmd.read_obj()?),
             VIRTIO_GPU_CMD_MOVE_CURSOR => MoveCursor(cmd.read_obj()?),
@@ -664,6 +698,8 @@ impl GpuCommand {
             TransferToHost3d(info) => &info.hdr,
             TransferFromHost3d(info) => &info.hdr,
             CmdSubmit3d(info) => &info.hdr,
+            ResourceCreateV2(info) => &info.hdr,
+            ResourceV2Unref(info) => &info.hdr,
             AllocationMetadata(info) => &info.hdr,
             UpdateCursor(info) => &info.hdr,
             MoveCursor(info) => &info.hdr,
diff --git a/gpu_renderer/src/lib.rs b/gpu_renderer/src/lib.rs
index 42df26a..f04ee66 100644
--- a/gpu_renderer/src/lib.rs
+++ b/gpu_renderer/src/lib.rs
@@ -410,6 +410,62 @@ impl Renderer {
         #[cfg(not(feature = "virtio-gpu-next"))]
         Err(Error::Unsupported)
     }
+
+    #[allow(unused_variables)]
+    pub fn resource_create_v2(
+        &self,
+        resource_id: u32,
+        guest_memory_type: u32,
+        guest_caching_type: u32,
+        size: u64,
+        mem: &GuestMemory,
+        iovecs: &[(GuestAddress, usize)],
+        args: &[u8],
+    ) -> Result<Resource> {
+        #[cfg(feature = "virtio-gpu-next")]
+        {
+            if iovecs
+                .iter()
+                .any(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).is_err())
+            {
+                return Err(Error::InvalidIovec);
+            }
+
+            let mut vecs = Vec::new();
+            for &(addr, len) in iovecs {
+                // Unwrap will not panic because we already checked the slices.
+                let slice = mem.get_slice(addr.offset(), len as u64).unwrap();
+                vecs.push(VirglVec {
+                    base: slice.as_ptr() as *mut c_void,
+                    len,
+                });
+            }
+
+            let ret = unsafe {
+                virgl_renderer_resource_create_v2(
+                    resource_id,
+                    guest_memory_type,
+                    guest_caching_type,
+                    size,
+                    vecs.as_ptr() as *const iovec,
+                    vecs.len() as u32,
+                    args.as_ptr() as *const c_void,
+                    args.len() as u32,
+                )
+            };
+
+            ret_to_res(ret)?;
+
+            Ok(Resource {
+                id: resource_id,
+                backing_iovecs: vecs,
+                backing_mem: None,
+                no_sync_send: PhantomData,
+            })
+        }
+        #[cfg(not(feature = "virtio-gpu-next"))]
+        Err(Error::Unsupported)
+    }
 }
 
 /// A context in which resources can be attached/detached and commands can be submitted.