From 719f2831ed394bf4f3fd777938261bda493b05fc Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Tue, 30 Apr 2019 10:22:15 -0700 Subject: [PATCH] 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 Tested-by: kokoro Tested-by: Gurchetan Singh Commit-Queue: Gurchetan Singh --- devices/src/virtio/gpu/backend.rs | 144 ++++++++++++++++++++++++++++- devices/src/virtio/gpu/mod.rs | 50 ++++++++++ devices/src/virtio/gpu/protocol.rs | 36 ++++++++ gpu_renderer/src/lib.rs | 56 +++++++++++ 4 files changed, 282 insertions(+), 4 deletions(-) diff --git a/devices/src/virtio/gpu/backend.rs b/devices/src/virtio/gpu/backend.rs index 4cfac7a0d3..75dbdafbdc 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>, u32)>, + kvm_slot: Option, } 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, contexts: Map, - #[allow(dead_code)] gpu_device_socket: VmMemoryControlRequestSocket, scanout_surface: Option, cursor_surface: Option, 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, + ) -> 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 2ad1f2e94d..29142eaa04 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::() { + 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 9c3b11219a..c6773ddcc0 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 42df26a74c..f04ee66b5e 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 { + #[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.