diff --git a/vhost-device-gpu/src/backend/common.rs b/vhost-device-gpu/src/backend/common.rs index 147ad96..249d860 100644 --- a/vhost-device-gpu/src/backend/common.rs +++ b/vhost-device-gpu/src/backend/common.rs @@ -2,12 +2,16 @@ // // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use std::sync::{Arc, Mutex}; +use std::{ + os::fd::{AsFd, AsRawFd}, + sync::{Arc, Mutex}, +}; -use log::{debug, error}; +use log::{debug, error, trace}; use vhost::vhost_user::{ gpu_message::{VhostUserGpuCursorPos, VhostUserGpuCursorUpdate, VhostUserGpuEdidRequest}, - GpuBackend, + message::{VhostUserMMap, VhostUserMMapFlags}, + Backend, GpuBackend, VhostUserFrontendReqHandler, }; use vm_memory::VolatileSlice; @@ -21,6 +25,59 @@ use crate::{ renderer::Renderer, }; +/// Common helper for mapping blob resources. +/// Maps a blob resource using the vhost-user backend's shmem_map. +pub fn common_map_blob( + backend: &Backend, + flags: VhostUserMMapFlags, + handle_fd: &(impl AsFd + AsRawFd), + blob_size: u64, + offset: u64, + resource_id: u32, +) -> Result<(), GpuResponse> { + trace!("Mapping blob resource_id={resource_id} offset={offset} size={blob_size}"); + + let map_request = VhostUserMMap { + shmid: 0, + padding: Default::default(), + fd_offset: 0, + shm_offset: offset, + len: blob_size, + flags: flags.bits(), + }; + + backend.shmem_map(&map_request, handle_fd).map_err(|e| { + error!("Failed to mmap by frontend: {e:?}"); + ErrUnspec + })?; + + Ok(()) +} + +/// Common helper for unmapping blob resources. +/// Unmaps a blob resource using the vhost-user backend's shmem_unmap. +pub fn common_unmap_blob( + backend: &Backend, + blob_size: u64, + offset: u64, +) -> Result<(), GpuResponse> { + let unmap_request = VhostUserMMap { + shmid: 0, + padding: Default::default(), + fd_offset: 0, + shm_offset: offset, + len: blob_size, + flags: 0, + }; + + backend.shmem_unmap(&unmap_request).map_err(|e| { + error!("Failed to unmap by frontend: {e:?}"); + ErrUnspec + })?; + + Ok(()) +} + #[derive(Debug, Clone)] pub struct VirtioGpuScanout { pub resource_id: u32, diff --git a/vhost-device-gpu/src/backend/gfxstream.rs b/vhost-device-gpu/src/backend/gfxstream.rs index c4b18a7..26ffdcf 100644 --- a/vhost-device-gpu/src/backend/gfxstream.rs +++ b/vhost-device-gpu/src/backend/gfxstream.rs @@ -8,7 +8,10 @@ use std::{ cell::RefCell, collections::BTreeMap, io::IoSliceMut, - os::{fd::FromRawFd, raw::c_void}, + os::{ + fd::{AsFd, FromRawFd}, + raw::c_void, + }, sync::{Arc, Mutex}, }; @@ -16,32 +19,39 @@ use log::{debug, error, warn}; use rutabaga_gfx::{ ResourceCreate3D, Rutabaga, RutabagaBuilder, RutabagaComponentType, RutabagaFence, RutabagaFenceHandler, RutabagaHandle, RutabagaIntoRawDescriptor, RutabagaIovec, Transfer3D, + RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD, RUTABAGA_MAP_ACCESS_MASK, RUTABAGA_MAP_ACCESS_READ, + RUTABAGA_MAP_ACCESS_RW, RUTABAGA_MAP_ACCESS_WRITE, RUTABAGA_MAP_CACHE_MASK, }; use vhost::vhost_user::{ gpu_message::{ VhostUserGpuCursorPos, VhostUserGpuEdidRequest, VhostUserGpuScanout, VhostUserGpuUpdate, }, + message::VhostUserMMapFlags, Backend, GpuBackend, }; use vhost_user_backend::{VringRwLock, VringT}; +use virtio_bindings::virtio_gpu::VIRTIO_GPU_BLOB_MEM_HOST3D; use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, VolatileSlice}; use vmm_sys_util::eventfd::EventFd; use crate::{ backend::{ common, - common::{common_set_scanout_disable, AssociatedScanouts, CursorConfig, VirtioGpuScanout}, + common::{ + common_map_blob, common_set_scanout_disable, common_unmap_blob, AssociatedScanouts, + CursorConfig, VirtioGpuScanout, + }, }, device::Error, - gpu_types::{FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{ virtio_gpu_rect, GpuResponse, GpuResponse::{ - ErrInvalidParameter, ErrInvalidResourceId, ErrUnspec, OkCapset, OkCapsetInfo, OkNoData, - OkResourcePlaneInfo, + ErrInvalidParameter, ErrInvalidResourceId, ErrUnspec, OkCapset, OkCapsetInfo, + OkMapInfo, OkNoData, OkResourcePlaneInfo, }, - GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_FLAG_INFO_RING_IDX, - VIRTIO_GPU_MAX_SCANOUTS, + GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE, + VIRTIO_GPU_FLAG_INFO_RING_IDX, VIRTIO_GPU_MAX_SCANOUTS, }, renderer::Renderer, GpuConfig, @@ -59,6 +69,8 @@ pub struct GfxstreamResource { scanouts: common::AssociatedScanouts, pub info_3d: Option, pub handle: Option>, + pub blob_size: u64, + pub blob_shmem_offset: Option, } impl GfxstreamResource { @@ -85,6 +97,8 @@ impl GfxstreamResource { scanouts: AssociatedScanouts::default(), info_3d: None, handle: None, + blob_size: 0, + blob_shmem_offset: None, } } } @@ -289,6 +303,8 @@ impl Renderer for GfxstreamAdapter { scanouts: AssociatedScanouts::default(), info_3d: None, handle: None, + blob_size: 0, + blob_shmem_offset: None, }; debug_assert!( !self.resources.contains_key(&resource_id), @@ -661,25 +677,119 @@ impl Renderer for GfxstreamAdapter { fn resource_create_blob( &mut self, - _ctx_id: u32, - _resource_id: u32, - _blob_id: u64, - _size: u64, - _blob_mem: u32, - _blob_flags: u32, + ctx_id: u32, + resource_create_blob: ResourceCreateBlob, + vecs: Vec<(vm_memory::GuestAddress, usize)>, + mem: &vm_memory::GuestMemoryMmap, ) -> VirtioGpuResult { - error!("Not implemented: resource_create_blob"); - Err(ErrUnspec) + let mut rutabaga_iovecs = None; + + if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 { + panic!("GUEST_HANDLE unimplemented"); + } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D { + rutabaga_iovecs = + Some(Self::sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?); + } + + Self::with_rutabaga(|rutabaga| { + rutabaga.resource_create_blob( + ctx_id, + resource_create_blob.resource_id, + resource_create_blob.into(), + rutabaga_iovecs, + None, + ) + })?; + + let resource = GfxstreamResource { + id: resource_create_blob.resource_id, + width: 0, + height: 0, + scanouts: AssociatedScanouts::default(), + info_3d: None, + handle: None, + blob_size: resource_create_blob.size, + blob_shmem_offset: None, + }; + + debug_assert!( + !self + .resources + .contains_key(&resource_create_blob.resource_id), + "Resource ID {} already exists in the resources map.", + resource_create_blob.resource_id + ); + + // Rely on rutabaga to check for duplicate resource ids. + self.resources + .insert(resource_create_blob.resource_id, resource); + Ok(Self::result_from_query(resource_create_blob.resource_id)) } - fn resource_map_blob(&mut self, _resource_id: u32, _offset: u64) -> VirtioGpuResult { - error!("Not implemented: resource_map_blob"); - Err(ErrUnspec) + fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult { + let resource = self + .resources + .get_mut(&resource_id) + .ok_or(ErrInvalidResourceId)?; + + let map_info = Self::with_rutabaga(|rutabaga| rutabaga.map_info(resource_id)) + .map_err(|_| ErrUnspec)?; + + let export = Self::with_rutabaga(|rutabaga| rutabaga.export_blob(resource_id)) + .map_err(|_| ErrUnspec)?; + + // Check handle type - we don't support OPAQUE_FD mapping + if export.handle_type == RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD { + return Err(ErrUnspec); + } + + // Convert map_info access flags to VhostUserMMapFlags + let flags = match map_info & RUTABAGA_MAP_ACCESS_MASK { + RUTABAGA_MAP_ACCESS_READ => VhostUserMMapFlags::default(), + RUTABAGA_MAP_ACCESS_WRITE => VhostUserMMapFlags::WRITABLE, + RUTABAGA_MAP_ACCESS_RW => VhostUserMMapFlags::WRITABLE, + _ => { + error!("Invalid access mask for blob resource, map_info: {map_info}"); + return Err(ErrUnspec); + } + }; + + common_map_blob( + &self.backend, + flags, + &export.os_handle.as_fd(), + resource.blob_size, + offset, + resource_id, + )?; + + resource.blob_shmem_offset = Some(offset); + + // Return cache flags only (access flags not part of virtio-gpu spec) + Ok(OkMapInfo { + map_info: map_info & RUTABAGA_MAP_CACHE_MASK, + }) } - fn resource_unmap_blob(&mut self, _resource_id: u32) -> VirtioGpuResult { - error!("Not implemented: resource_unmap_blob"); - Err(ErrUnspec) + fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult { + let resource = self + .resources + .get_mut(&resource_id) + .ok_or(ErrInvalidResourceId)?; + + let Some(offset) = resource.blob_shmem_offset else { + log::warn!( + "Guest tried to unmap blob resource with resource_id={resource_id}, but it is not \ + mapped!" + ); + return Err(ErrInvalidParameter); + }; + + common_unmap_blob(&self.backend, resource.blob_size, offset)?; + + resource.blob_shmem_offset = None; + + Ok(OkNoData) } } diff --git a/vhost-device-gpu/src/backend/null.rs b/vhost-device-gpu/src/backend/null.rs index 6be1b3e..92cb659 100644 --- a/vhost-device-gpu/src/backend/null.rs +++ b/vhost-device-gpu/src/backend/null.rs @@ -13,7 +13,7 @@ use vm_memory::{GuestAddress, GuestMemoryMmap, VolatileSlice}; use vmm_sys_util::eventfd::EventFd; use crate::{ - gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{virtio_gpu_rect, GpuResponse, VirtioGpuResult}, renderer::Renderer, GpuConfig, @@ -27,6 +27,7 @@ impl NullAdapter { pub fn new( _queue_ctl: &vhost_user_backend::VringRwLock, _config: &GpuConfig, + _backend: vhost::vhost_user::Backend, gpu_backend: GpuBackend, ) -> Self { trace!("NullAdapter created"); @@ -224,11 +225,9 @@ impl Renderer for NullAdapter { fn resource_create_blob( &mut self, _ctx_id: u32, - _resource_id: u32, - _blob_id: u64, - _size: u64, - _blob_mem: u32, - _blob_flags: u32, + _resource_create_blob: ResourceCreateBlob, + _vecs: Vec<(vm_memory::GuestAddress, usize)>, + _mem: &vm_memory::GuestMemoryMmap, ) -> VirtioGpuResult { trace!("NullAdapter::resource_create_blob - no-op"); Ok(GpuResponse::OkNoData) @@ -256,15 +255,16 @@ mod tests { use crate::{GpuFlags, GpuMode}; fn create_null_adapter() -> NullAdapter { - let (_, backend) = UnixStream::pair().unwrap(); - let gpu_backend = GpuBackend::from_stream(backend); + let (stream1, stream2) = UnixStream::pair().unwrap(); + let backend = vhost::vhost_user::Backend::from_stream(stream1); + let gpu_backend = GpuBackend::from_stream(stream2); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), ); let vring = VringRwLock::new(mem, 0x100).unwrap(); let config = GpuConfig::new(GpuMode::Null, None, GpuFlags::default()).unwrap(); - NullAdapter::new(&vring, &config, gpu_backend) + NullAdapter::new(&vring, &config, backend, gpu_backend) } #[test] @@ -430,9 +430,21 @@ mod tests { #[test] fn test_null_adapter_blob_operations() { let mut adapter = create_null_adapter(); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); // Verify blob resource creation succeeds - let result = adapter.resource_create_blob(0, 1, 1, 4096, 0, 0); + let result = adapter.resource_create_blob( + 0, + ResourceCreateBlob { + resource_id: 1, + blob_id: 1, + blob_mem: 0, + blob_flags: 0, + size: 4096, + }, + vec![], + &mem, + ); assert!(matches!(result, Ok(GpuResponse::OkNoData))); // Verify mapping blob resource succeeds diff --git a/vhost-device-gpu/src/backend/virgl.rs b/vhost-device-gpu/src/backend/virgl.rs index 1b1db4a..ec3ff4d 100644 --- a/vhost-device-gpu/src/backend/virgl.rs +++ b/vhost-device-gpu/src/backend/virgl.rs @@ -19,27 +19,32 @@ use vhost::vhost_user::{ VhostUserGpuCursorPos, VhostUserGpuDMABUFScanout, VhostUserGpuDMABUFScanout2, VhostUserGpuEdidRequest, VhostUserGpuUpdate, }, + message::VhostUserMMapFlags, Backend, GpuBackend, }; use vhost_user_backend::{VringRwLock, VringT}; use virglrenderer::{ FenceHandler, Iovec, VirglRenderer, VirglRendererFlags, VirglResource, - VIRGL_HANDLE_TYPE_MEM_DMABUF, + VIRGL_HANDLE_TYPE_MEM_DMABUF, VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD, VIRGL_MAP_CACHE_MASK, }; +use virtio_bindings::virtio_gpu::VIRTIO_GPU_BLOB_MEM_HOST3D; use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, VolatileSlice}; use vmm_sys_util::eventfd::EventFd; use crate::{ backend::{ common, - common::{common_set_scanout_disable, AssociatedScanouts, CursorConfig, VirtioGpuScanout}, + common::{ + common_map_blob, common_set_scanout_disable, common_unmap_blob, AssociatedScanouts, + CursorConfig, VirtioGpuScanout, + }, }, - gpu_types::{FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{ virtio_gpu_rect, GpuResponse, GpuResponse::{ ErrInvalidContextId, ErrInvalidParameter, ErrInvalidResourceId, ErrInvalidScanoutId, - ErrUnspec, OkCapset, OkCapsetInfo, OkNoData, + ErrUnspec, OkCapset, OkCapsetInfo, OkMapInfo, OkNoData, }, VirtioGpuResult, VIRTIO_GPU_MAX_SCANOUTS, }, @@ -54,6 +59,8 @@ pub struct GpuResource { // resource. Resource could be used for multiple scanouts. pub scanouts: AssociatedScanouts, pub backing_iovecs: Arc>>>, + pub blob_size: u64, + pub blob_shmem_offset: Option, } fn sglist_to_iovecs( @@ -190,6 +197,8 @@ impl Renderer for VirglRendererAdapter { virgl_resource, scanouts: AssociatedScanouts::default(), backing_iovecs: Arc::new(Mutex::new(None)), + blob_size: 0, + blob_shmem_offset: None, }; self.resources.insert(resource_id, local_resource); Ok(OkNoData) @@ -611,25 +620,112 @@ impl Renderer for VirglRendererAdapter { fn resource_create_blob( &mut self, - _ctx_id: u32, - _resource_id: u32, - _blob_id: u64, - _size: u64, - _blob_mem: u32, - _blob_flags: u32, + ctx_id: u32, + resource_create_blob: ResourceCreateBlob, + vecs: Vec<(GuestAddress, usize)>, + mem: &GuestMemoryMmap, ) -> VirtioGpuResult { - error!("Not implemented: resource_create_blob"); - Err(ErrUnspec) + let mut virgl_iovecs = None; + + if resource_create_blob.blob_flags + & crate::protocol::VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE + != 0 + { + error!("GUEST_HANDLE unimplemented for virgl backend"); + return Err(ErrUnspec); + } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D { + virgl_iovecs = Some(sglist_to_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?); + } + + let virgl_resource = self + .renderer + .create_blob( + ctx_id, + 0, // width + 0, // height + resource_create_blob.resource_id, + resource_create_blob.into(), + virgl_iovecs.as_deref(), + ) + .map_err(|_| ErrUnspec)?; + + let resource = GpuResource { + virgl_resource, + scanouts: AssociatedScanouts::default(), + backing_iovecs: Arc::new(Mutex::new(virgl_iovecs)), + blob_size: resource_create_blob.size, + blob_shmem_offset: None, + }; + + debug_assert!( + !self + .resources + .contains_key(&resource_create_blob.resource_id), + "Resource ID {} already exists in the resources map.", + resource_create_blob.resource_id + ); + + self.resources + .insert(resource_create_blob.resource_id, resource); + Ok(OkNoData) } - fn resource_map_blob(&mut self, _resource_id: u32, _offset: u64) -> VirtioGpuResult { - error!("Not implemented: resource_map_blob"); - Err(ErrUnspec) + fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult { + let resource = self + .resources + .get_mut(&resource_id) + .ok_or(ErrInvalidResourceId)?; + + let map_info = resource.virgl_resource.map_info.ok_or(ErrUnspec)?; + + let handle = resource.virgl_resource.handle.as_ref().ok_or(ErrUnspec)?; + + // Check handle type - we don't support OPAQUE_FD mapping + if handle.handle_type == VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD { + error!("VIRGL_HANDLE_TYPE_MEM_OPAQUE_FD not supported for mapping"); + return Err(ErrUnspec); + } + + // virgl doesn't provide detailed permissions for mapping, map everything as + // writable + let flags = VhostUserMMapFlags::WRITABLE; + + common_map_blob( + &self.backend, + flags, + &handle.os_handle.as_fd(), + resource.blob_size, + offset, + resource_id, + )?; + + resource.blob_shmem_offset = Some(offset); + + // Return cache flags only (access flags not part of virtio-gpu spec) + Ok(OkMapInfo { + map_info: map_info & VIRGL_MAP_CACHE_MASK, + }) } - fn resource_unmap_blob(&mut self, _resource_id: u32) -> VirtioGpuResult { - error!("Not implemented: resource_unmap_blob"); - Err(ErrUnspec) + fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult { + let resource = self + .resources + .get_mut(&resource_id) + .ok_or(ErrInvalidResourceId)?; + + let Some(offset) = resource.blob_shmem_offset else { + warn!( + "Guest tried to unmap blob resource with resource_id={resource_id}, but it is not \ + mapped!" + ); + return Err(ErrInvalidParameter); + }; + + common_unmap_blob(&self.backend, resource.blob_size, offset)?; + + resource.blob_shmem_offset = None; + + Ok(OkNoData) } } @@ -647,7 +743,10 @@ mod virgl_cov_tests { use super::*; use crate::{ - gpu_types::{FenceDescriptor, FenceState, ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{ + FenceDescriptor, FenceState, ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, + VirtioGpuRing, + }, protocol::{virtio_gpu_rect, GpuResponse, VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM}, renderer::Renderer, testutils::{ @@ -871,18 +970,30 @@ mod virgl_cov_tests { test_capset_operations(&gpu, index); } - // Test blob resource functions (all should return ErrUnspec - not implemented) + // Test blob resource functions assert_matches!( - gpu.resource_create_blob(1, 100, 0, 4096, 0, 0), + gpu.resource_create_blob( + 1, + ResourceCreateBlob { + resource_id: 100, + blob_id: 0, + blob_mem: 0, + blob_flags: 0, + size: 4096, + }, + vec![], + &gm_back + ), Err(GpuResponse::ErrUnspec) ); + // resource 100 was never created, so these return ErrInvalidResourceId assert_matches!( gpu.resource_map_blob(100, 0), - Err(GpuResponse::ErrUnspec) + Err(GpuResponse::ErrInvalidResourceId) ); assert_matches!( gpu.resource_unmap_blob(100), - Err(GpuResponse::ErrUnspec) + Err(GpuResponse::ErrInvalidResourceId) ); // Test resource_assign_uuid (not implemented) diff --git a/vhost-device-gpu/src/device.rs b/vhost-device-gpu/src/device.rs index 5432f72..4a91d6c 100644 --- a/vhost-device-gpu/src/device.rs +++ b/vhost-device-gpu/src/device.rs @@ -80,7 +80,7 @@ use crate::backend::gfxstream::GfxstreamAdapter; use crate::backend::virgl::VirglRendererAdapter; use crate::{ backend::null::NullAdapter, - gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{ virtio_gpu_ctrl_hdr, virtio_gpu_ctx_create, virtio_gpu_get_edid, virtio_gpu_resource_create_2d, virtio_gpu_resource_create_3d, virtio_gpu_transfer_host_3d, @@ -260,18 +260,27 @@ impl VhostUserGpuBackendInner { fence_ids, mut cmd_data, } => renderer.submit_command(hdr.ctx_id.into(), &mut cmd_data, &fence_ids), - GpuCommand::ResourceCreateBlob(_) => { - panic!("virtio_gpu: GpuCommand::ResourceCreateBlob unimplemented") - } + GpuCommand::ResourceCreateBlob(info, vecs) => renderer.resource_create_blob( + hdr.ctx_id.into(), + ResourceCreateBlob { + resource_id: info.resource_id.into(), + blob_id: info.blob_id.into(), + blob_mem: info.blob_mem.into(), + blob_flags: info.blob_flags.into(), + size: info.size.into(), + }, + vecs, + mem, + ), GpuCommand::SetScanoutBlob(_) => { panic!("virtio_gpu: GpuCommand::SetScanoutBlob unimplemented") } - GpuCommand::ResourceMapBlob(_) => { - panic!("virtio_gpu: GpuCommand::ResourceMapBlob unimplemented") + GpuCommand::ResourceMapBlob(info) => { + renderer.resource_map_blob(info.resource_id.into(), info.offset.into()) } - GpuCommand::ResourceUnmapBlob(_) => { - panic!("virtio_gpu: GpuCommand::ResourceUnmapBlob unimplemented") + GpuCommand::ResourceUnmapBlob(info) => { + renderer.resource_unmap_blob(info.resource_id.into()) } } } @@ -648,8 +657,8 @@ impl VhostUserGpuBackendInner { GpuMode::Null => handle_adapter!( NullAdapter, TLS_NULL, - |control_vring, _backend, gpu_backend| { - NullAdapter::new(control_vring, &self.gpu_config, gpu_backend) + |control_vring, backend, gpu_backend| { + NullAdapter::new(control_vring, &self.gpu_config, backend, gpu_backend) }, self, device_event, @@ -706,6 +715,7 @@ impl VhostUserBackend for VhostUserGpuBackend { | VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::BACKEND_REQ | VhostUserProtocolFeatures::BACKEND_SEND_FD + | VhostUserProtocolFeatures::SHMEM } fn set_event_idx(&self, enabled: bool) { @@ -802,7 +812,7 @@ mod tests { use super::*; use crate::{ - gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{ virtio_gpu_ctrl_hdr, virtio_gpu_ctx_create, virtio_gpu_ctx_destroy, virtio_gpu_ctx_resource, virtio_gpu_get_capset, virtio_gpu_get_capset_info, @@ -836,11 +846,9 @@ mod tests { fn resource_create_blob( &mut self, ctx_id: u32, - resource_id: u32, - blob_id: u64, - size: u64, - blob_mem: u32, - blob_flags: u32, + resource_create_blob: ResourceCreateBlob, + vecs: Vec<(GuestAddress, usize)>, + mem: &GuestMemoryMmap, ) -> VirtioGpuResult; fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult; fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult; @@ -1422,6 +1430,7 @@ mod tests { | VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::BACKEND_REQ | VhostUserProtocolFeatures::BACKEND_SEND_FD + | VhostUserProtocolFeatures::SHMEM ); assert_eq!(backend.queues_per_thread(), vec![0xffff_ffff]); assert_eq!(backend.get_config(0, 0), Vec::::new()); diff --git a/vhost-device-gpu/src/gpu_types.rs b/vhost-device-gpu/src/gpu_types.rs index 6c9df9e..8380106 100644 --- a/vhost-device-gpu/src/gpu_types.rs +++ b/vhost-device-gpu/src/gpu_types.rs @@ -48,6 +48,22 @@ macro_rules! impl_from_resource_create3d { }; } +#[cfg(any(feature = "backend-virgl", feature = "backend-gfxstream"))] +macro_rules! impl_from_resource_create_blob { + ($target:ty) => { + impl From for $target { + fn from(r: ResourceCreateBlob) -> Self { + Self { + blob_id: r.blob_id, + blob_mem: r.blob_mem, + blob_flags: r.blob_flags, + size: r.size, + } + } + } + }; +} + use std::{collections::BTreeMap, os::raw::c_void}; #[cfg(feature = "backend-gfxstream")] @@ -147,6 +163,21 @@ impl_from_resource_create3d!(rutabaga_gfx::ResourceCreate3D); #[cfg(feature = "backend-virgl")] impl_from_resource_create3d!(virglrenderer::ResourceCreate3D); +#[cfg(feature = "backend-gfxstream")] +impl_from_resource_create_blob!(rutabaga_gfx::ResourceCreateBlob); +#[cfg(feature = "backend-virgl")] +impl_from_resource_create_blob!(virglrenderer::ResourceCreateBlob); + +/// Parameters for creating a blob resource. +#[derive(Debug, Clone, Copy)] +pub struct ResourceCreateBlob { + pub resource_id: u32, + pub blob_id: u64, + pub blob_mem: u32, + pub blob_flags: u32, + pub size: u64, +} + #[derive(Debug, Clone, Copy)] pub struct ResourceCreate2d { pub resource_id: u32, diff --git a/vhost-device-gpu/src/protocol.rs b/vhost-device-gpu/src/protocol.rs index ffde38c..292b708 100644 --- a/vhost-device-gpu/src/protocol.rs +++ b/vhost-device-gpu/src/protocol.rs @@ -707,7 +707,7 @@ pub enum GpuCommand { cmd_data: Vec, fence_ids: Vec, }, - ResourceCreateBlob(virtio_gpu_resource_create_blob), + ResourceCreateBlob(virtio_gpu_resource_create_blob, Vec<(GuestAddress, usize)>), ResourceMapBlob(virtio_gpu_resource_map_blob), ResourceUnmapBlob(virtio_gpu_resource_unmap_blob), UpdateCursor(virtio_gpu_update_cursor), @@ -753,6 +753,23 @@ impl fmt::Debug for GpuCommand { } } +fn read_mem_entries( + reader: &mut Reader, + num_entries: u32, +) -> Result, GpuCommandDecodeError> { + let mut entries = Vec::with_capacity(num_entries as usize); + + for _ in 0..num_entries { + let entry: virtio_gpu_mem_entry = + reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?; + entries.push(( + GuestAddress(entry.addr.into()), + entry.length.to_native() as usize, + )) + } + Ok(entries) +} + impl GpuCommand { pub const fn command_name(&self) -> &'static str { use GpuCommand::*; @@ -777,7 +794,7 @@ impl GpuCommand { TransferToHost3d(_info) => "TransferToHost3d", TransferFromHost3d(_info) => "TransferFromHost3d", CmdSubmit3d { .. } => "CmdSubmit3d", - ResourceCreateBlob(_info) => "ResourceCreateBlob", + ResourceCreateBlob(_info, _) => "ResourceCreateBlob", ResourceMapBlob(_info) => "ResourceMapBlob", ResourceUnmapBlob(_info) => "ResourceUnmapBlob", UpdateCursor(_info) => "UpdateCursor", @@ -824,16 +841,7 @@ impl GpuCommand { VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => { let info: virtio_gpu_resource_attach_backing = reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?; - let mut entries = - Vec::with_capacity(>::into(info.nr_entries) as usize); - for _ in 0..info.nr_entries.into() { - let entry: virtio_gpu_mem_entry = - reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?; - entries.push(( - GuestAddress(entry.addr.into()), - >::into(entry.length) as usize, - )); - } + let entries = read_mem_entries(reader, info.nr_entries.into())?; ResourceAttachBacking(info, entries) } VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => { @@ -891,7 +899,11 @@ impl GpuCommand { } } VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB => { - ResourceCreateBlob(reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?) + let info: virtio_gpu_resource_create_blob = + reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?; + + let entries = read_mem_entries(reader, info.nr_entries.into())?; + ResourceCreateBlob(info, entries) } VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB => { ResourceMapBlob(reader.read_obj().map_err(|_| Error::DescriptorReadFailed)?) @@ -1311,7 +1323,7 @@ mod tests { "CmdSubmit3d", ), ( - ResourceCreateBlob(virtio_gpu_resource_create_blob::default()), + ResourceCreateBlob(virtio_gpu_resource_create_blob::default(), Vec::new()), "ResourceCreateBlob", ), ( diff --git a/vhost-device-gpu/src/renderer.rs b/vhost-device-gpu/src/renderer.rs index 49d3557..3a39bb7 100644 --- a/vhost-device-gpu/src/renderer.rs +++ b/vhost-device-gpu/src/renderer.rs @@ -8,7 +8,7 @@ use vm_memory::{GuestAddress, GuestMemoryMmap, VolatileSlice}; use vmm_sys_util::eventfd::EventFd; use crate::{ - gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing}, + gpu_types::{ResourceCreate3d, ResourceCreateBlob, Transfer3DDesc, VirtioGpuRing}, protocol::{virtio_gpu_rect, VirtioGpuResult}, }; @@ -91,11 +91,9 @@ pub trait Renderer: Send + Sync { fn resource_create_blob( &mut self, ctx_id: u32, - resource_id: u32, - blob_id: u64, - size: u64, - blob_mem: u32, - blob_flags: u32, + resource_create_blob: ResourceCreateBlob, + vecs: Vec<(vm_memory::GuestAddress, usize)>, + mem: &vm_memory::GuestMemoryMmap, ) -> VirtioGpuResult; fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult; fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult;