diff --git a/src/devices/src/virtio/gpu/virtio_gpu.rs b/src/devices/src/virtio/gpu/virtio_gpu.rs index b760786b..d457e17f 100644 --- a/src/devices/src/virtio/gpu/virtio_gpu.rs +++ b/src/devices/src/virtio/gpu/virtio_gpu.rs @@ -24,8 +24,8 @@ use rutabaga_gfx::{ }; #[cfg(target_os = "linux")] use rutabaga_gfx::{ - RUTABAGA_CHANNEL_TYPE_X11, RUTABAGA_MAP_ACCESS_MASK, RUTABAGA_MAP_ACCESS_READ, - RUTABAGA_MAP_ACCESS_RW, RUTABAGA_MAP_ACCESS_WRITE, + RUTABAGA_CHANNEL_TYPE_PW, RUTABAGA_CHANNEL_TYPE_X11, RUTABAGA_MAP_ACCESS_MASK, + RUTABAGA_MAP_ACCESS_READ, RUTABAGA_MAP_ACCESS_RW, RUTABAGA_MAP_ACCESS_WRITE, }; use utils::eventfd::EventFd; use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, VolatileSlice}; @@ -210,6 +210,19 @@ impl VirtioGpu { }); } } + #[cfg(target_os = "linux")] + if let Ok(pw_sock_dir) = env::var("PIPEWIRE_RUNTIME_DIR") + .or_else(|_| env::var("XDG_RUNTIME_DIR")) + .or_else(|_| env::var("USERPROFILE")) + { + let name = env::var("PIPEWIRE_REMOTE").unwrap_or_else(|_| "pipewire-0".to_string()); + let mut pw_path = PathBuf::from(pw_sock_dir); + pw_path.push(name); + rutabaga_channels.push(RutabagaChannel { + base_channel: pw_path, + channel_type: RUTABAGA_CHANNEL_TYPE_PW, + }); + } let rutabaga_channels_opt = Some(rutabaga_channels); let builder = RutabagaBuilder::new( diff --git a/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h b/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h index caaa59c4..8ab5ad70 100644 --- a/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h +++ b/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h @@ -87,6 +87,7 @@ extern "C" { * Rutabaga channel types */ #define RUTABAGA_CHANNEL_TYPE_WAYLAND 1 +#define RUTABAGA_CHANNEL_TYPE_PW 0x10 #define RUTABAGA_CHANNEL_TYPE_X11 0x11 /** diff --git a/src/rutabaga_gfx/ffi/src/tests/virtgpu_cross_domain_protocol.h b/src/rutabaga_gfx/ffi/src/tests/virtgpu_cross_domain_protocol.h index e1bf410a..71e95b58 100644 --- a/src/rutabaga_gfx/ffi/src/tests/virtgpu_cross_domain_protocol.h +++ b/src/rutabaga_gfx/ffi/src/tests/virtgpu_cross_domain_protocol.h @@ -19,6 +19,7 @@ // Channel types (must match rutabaga channel types) #define CROSS_DOMAIN_CHANNEL_TYPE_WAYLAND 0x0001 #define CROSS_DOMAIN_CHANNEL_TYPE_CAMERA 0x0002 +#define CROSS_DOMAIN_CHANNEL_TYPE_PW 0x0010 #define CROSS_DOMAIN_CHANNEL_TYPE_X11 0x0011 // The maximum number of identifiers (value based on wp_linux_dmabuf) diff --git a/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs b/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs index f96c9222..2e1222d7 100644 --- a/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs +++ b/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs @@ -21,14 +21,19 @@ pub const CROSS_DOMAIN_CMD_WRITE: u8 = 7; pub const CROSS_DOMAIN_CMD_FUTEX_NEW: u8 = 8; pub const CROSS_DOMAIN_CMD_FUTEX_SIGNAL: u8 = 9; pub const CROSS_DOMAIN_CMD_FUTEX_DESTROY: u8 = 10; +pub const CROSS_DOMAIN_CMD_READ_EVENTFD_NEW: u8 = 11; +pub const CROSS_DOMAIN_CMD_READ_EVENTFD_DESTROY: u8 = 12; /// Channel types (must match rutabaga channel types) pub const CROSS_DOMAIN_CHANNEL_TYPE_WAYLAND: u32 = 0x0001; pub const CROSS_DOMAIN_CHANNEL_TYPE_CAMERA: u32 = 0x0002; +pub const CROSS_DOMAIN_CHANNEL_TYPE_PW: u32 = 0x0010; pub const CROSS_DOMAIN_CHANNEL_TYPE_X11: u32 = 0x0011; /// The maximum number of identifiers (value inspired by wp_linux_dmabuf) pub const CROSS_DOMAIN_MAX_IDENTIFIERS: usize = 4; +/// As above, but inspired by sommelier +pub const CROSS_DOMAIN_MAX_IDENTIFIERS_V2: usize = 28; /// virtgpu memory resource ID. Also works with non-blob memory resources, despite the name. pub const CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB: u32 = 1; @@ -42,6 +47,7 @@ pub const CROSS_DOMAIN_ID_TYPE_READ_PIPE: u32 = 3; pub const CROSS_DOMAIN_ID_TYPE_WRITE_PIPE: u32 = 4; pub const CROSS_DOMAIN_ID_TYPE_SHM: u32 = 5; +pub const CROSS_DOMAIN_ID_TYPE_EVENTFD: u32 = 6; /// No ring pub const CROSS_DOMAIN_RING_NONE: u32 = 0xffffffff; @@ -86,11 +92,33 @@ pub struct CrossDomainHeader { #[repr(C)] #[derive(Copy, Clone, Default, AsBytes, FromBytes)] -pub struct CrossDomainInit { +pub struct CrossDomainInitV1 { pub hdr: CrossDomainHeader, pub query_ring_id: u32, pub channel_ring_id: u32, pub channel_type: u32, + pub protocol_version: u32, +} + +#[repr(C)] +#[derive(Copy, Clone, Default, AsBytes, FromBytes)] +pub struct CrossDomainInitV0 { + pub hdr: CrossDomainHeader, + pub query_ring_id: u32, + pub channel_ring_id: u32, + pub channel_type: u32, +} + +impl CrossDomainInitV0 { + pub(crate) fn upgrade(&self) -> CrossDomainInitV1 { + CrossDomainInitV1 { + hdr: self.hdr, + query_ring_id: self.query_ring_id, + channel_ring_id: self.channel_ring_id, + channel_type: self.channel_type, + protocol_version: 0, + } + } } #[repr(C)] @@ -103,6 +131,14 @@ pub struct CrossDomainGetImageRequirements { pub flags: u32, } +pub trait CrossDomainSendReceiveBase: Copy + Clone + Default + AsBytes + FromBytes { + const MAX_IDENTIFIERS: usize; + fn hdr_mut(&mut self) -> &mut CrossDomainHeader; + fn num_identifiers_mut(&mut self) -> &mut u32; + fn opaque_data_size_mut(&mut self) -> &mut u32; + fn iter_over_identifiers(&mut self) -> impl Iterator<Item = (&mut u32, &mut u32, &mut u32)>; +} + #[repr(C)] #[derive(Copy, Clone, Default, AsBytes, FromBytes)] pub struct CrossDomainSendReceive { @@ -114,6 +150,57 @@ pub struct CrossDomainSendReceive { pub identifier_sizes: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], // Data of size "opaque data size follows" } +impl CrossDomainSendReceiveBase for CrossDomainSendReceive { + const MAX_IDENTIFIERS: usize = CROSS_DOMAIN_MAX_IDENTIFIERS; + fn hdr_mut(&mut self) -> &mut CrossDomainHeader { + &mut self.hdr + } + fn num_identifiers_mut(&mut self) -> &mut u32 { + &mut self.num_identifiers + } + fn opaque_data_size_mut(&mut self) -> &mut u32 { + &mut self.opaque_data_size + } + fn iter_over_identifiers(&mut self) -> impl Iterator<Item = (&mut u32, &mut u32, &mut u32)> { + self.identifiers + .iter_mut() + .zip(self.identifier_types.iter_mut()) + .zip(self.identifier_sizes.iter_mut()) + .map(|((i, it), is)| (i, it, is)) + } +} + +#[repr(C)] +#[derive(Copy, Clone, Default, AsBytes, FromBytes)] +pub struct CrossDomainSendReceiveV2 { + pub hdr: CrossDomainHeader, + pub num_identifiers: u32, + pub opaque_data_size: u32, + pub identifiers: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS_V2], + pub identifier_types: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS_V2], + pub identifier_sizes: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS_V2], + // Data of size "opaque data size follows" +} + +impl CrossDomainSendReceiveBase for CrossDomainSendReceiveV2 { + const MAX_IDENTIFIERS: usize = CROSS_DOMAIN_MAX_IDENTIFIERS_V2; + fn hdr_mut(&mut self) -> &mut CrossDomainHeader { + &mut self.hdr + } + fn num_identifiers_mut(&mut self) -> &mut u32 { + &mut self.num_identifiers + } + fn opaque_data_size_mut(&mut self) -> &mut u32 { + &mut self.opaque_data_size + } + fn iter_over_identifiers(&mut self) -> impl Iterator<Item = (&mut u32, &mut u32, &mut u32)> { + self.identifiers + .iter_mut() + .zip(self.identifier_types.iter_mut()) + .zip(self.identifier_sizes.iter_mut()) + .map(|((i, it), is)| (i, it, is)) + } +} #[repr(C)] #[derive(Copy, Clone, Default, AsBytes, FromBytes)] @@ -151,3 +238,11 @@ pub struct CrossDomainFutexDestroy { pub id: u32, pub pad: u32, } + +#[repr(C)] +#[derive(Copy, Clone, Default, AsBytes, FromBytes)] +pub struct CrossDomainReadEventfdNew { + pub hdr: CrossDomainHeader, + pub id: u32, + pub pad: u32, +} diff --git a/src/rutabaga_gfx/src/cross_domain/mod.rs b/src/rutabaga_gfx/src/cross_domain/mod.rs index 4811a333..ca34a5c0 100644 --- a/src/rutabaga_gfx/src/cross_domain/mod.rs +++ b/src/rutabaga_gfx/src/cross_domain/mod.rs @@ -7,7 +7,9 @@ #[cfg(feature = "x")] use libc::{c_int, FUTEX_WAKE_BITSET}; +use libc::{O_ACCMODE, O_WRONLY}; use log::{error, info}; +use nix::fcntl::{fcntl, FcntlArg}; #[cfg(feature = "x")] use nix::sys::mman::{mmap, munmap, MapFlags, ProtFlags}; use std::cmp::max; @@ -16,6 +18,7 @@ use std::collections::VecDeque; use std::convert::TryInto; use std::ffi::c_void; use std::fs::{read_link, File}; +use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::os::fd::AsRawFd; #[cfg(feature = "x")] @@ -34,7 +37,6 @@ use crate::cross_domain::cross_domain_protocol::*; use crate::cross_domain::sys::channel; use crate::cross_domain::sys::channel_signal; use crate::cross_domain::sys::channel_wait; -use crate::cross_domain::sys::descriptor_analysis; use crate::cross_domain::sys::read_volatile; use crate::cross_domain::sys::write_volatile; use crate::cross_domain::sys::Receiver; @@ -47,11 +49,11 @@ use crate::rutabaga_core::RutabagaContext; use crate::rutabaga_core::RutabagaResource; use crate::rutabaga_os::SafeDescriptor; use crate::rutabaga_utils::*; -use crate::DrmFormat; use crate::ImageAllocationInfo; use crate::ImageMemoryRequirements; use crate::RutabagaGralloc; use crate::RutabagaGrallocFlags; +use crate::{AsRawDescriptor, DrmFormat}; mod cross_domain_protocol; mod sys; @@ -73,11 +75,14 @@ pub enum CrossDomainToken { Kill, #[cfg(feature = "x")] Futex(u32), + ReadEventfd(u32), } const CROSS_DOMAIN_DEFAULT_BUFFER_SIZE: usize = 4096; const CROSS_DOMAIN_MAX_SEND_RECV_SIZE: usize = CROSS_DOMAIN_DEFAULT_BUFFER_SIZE - size_of::<CrossDomainSendReceive>(); +const CROSS_DOMAIN_MAX_SEND_RECV_SIZE_V2: usize = + CROSS_DOMAIN_DEFAULT_BUFFER_SIZE - size_of::<CrossDomainSendReceiveV2>(); pub(crate) enum CrossDomainItem { ImageRequirements(ImageMemoryRequirements), @@ -86,6 +91,7 @@ pub(crate) enum CrossDomainItem { #[allow(dead_code)] // `WaylandReadPipe` is never constructed on Windows. WaylandReadPipe(File), WaylandWritePipe(File), + Eventfd(File), } pub(crate) enum CrossDomainJob { @@ -95,6 +101,7 @@ pub(crate) enum CrossDomainJob { Finish, #[cfg(feature = "x")] AddFutex(u32, Arc<Receiver>), + AddReadEventfd(u32), } enum RingWrite<'a, T> { @@ -136,6 +143,7 @@ struct CrossDomainWorker { state: Arc<CrossDomainState>, pub(crate) item_state: CrossDomainItemState, fence_handler: RutabagaFenceHandler, + protocol_version: u32, } #[allow(dead_code)] // Never used on macOS @@ -251,6 +259,7 @@ pub(crate) struct CrossDomainContext { worker_thread: Option<thread::JoinHandle<RutabagaResult<()>>>, pub(crate) resample_evt: Option<Sender>, kill_evt: Option<Sender>, + protocol_version: u32, } /// The CrossDomain component contains a list of channels that the guest may connect to and the @@ -396,12 +405,14 @@ impl CrossDomainWorker { state: Arc<CrossDomainState>, item_state: CrossDomainItemState, fence_handler: RutabagaFenceHandler, + protocol_version: u32, ) -> CrossDomainWorker { CrossDomainWorker { wait_ctx, state, item_state, fence_handler, + protocol_version, } } @@ -433,61 +444,11 @@ impl CrossDomainWorker { if let Some(event) = events.first() { match event.token { CrossDomainToken::ContextChannel => { - let (len, files) = self.state.receive_msg(receive_buf)?; - let mut cmd_receive: CrossDomainSendReceive = Default::default(); - - let num_files = files.len(); - cmd_receive.hdr.cmd = CROSS_DOMAIN_CMD_RECEIVE; - cmd_receive.num_identifiers = files.len().try_into()?; - cmd_receive.opaque_data_size = len.try_into()?; - - let iter = cmd_receive - .identifiers - .iter_mut() - .zip(cmd_receive.identifier_types.iter_mut()) - .zip(cmd_receive.identifier_sizes.iter_mut()) - .zip(files) - .take(num_files); - - for (((identifier, identifier_type), identifier_size), mut file) in iter { - // Safe since the descriptors from receive_msg(..) are owned by us and valid. - descriptor_analysis(&mut file, identifier_type, identifier_size)?; - - *identifier = match *identifier_type { - CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB => { - let fd_path = - read_link(format!("/proc/self/fd/{}", file.as_raw_fd())) - .unwrap(); - if fd_path.starts_with("/dmabuf:") { - add_item(&self.item_state, CrossDomainItem::DmaBuf(file.into())) - } else if fd_path.starts_with("/memfd:") { - add_item( - &self.item_state, - CrossDomainItem::ShmBlob(file.into()), - ) - } else { - info!( - "Unknown fd item path {:?}, treating as a shmem blob", - fd_path - ); - add_item( - &self.item_state, - CrossDomainItem::ShmBlob(file.into()), - ) - } - } - CROSS_DOMAIN_ID_TYPE_WRITE_PIPE => { - add_item(&self.item_state, CrossDomainItem::WaylandWritePipe(file)) - } - _ => return Err(RutabagaError::InvalidCrossDomainItemType), - }; + if self.protocol_version == 0 { + self.process_receive::<CrossDomainSendReceive, {CrossDomainSendReceive::MAX_IDENTIFIERS}>(fence, receive_buf)?; + } else { + self.process_receive::<CrossDomainSendReceiveV2, {CrossDomainSendReceiveV2::MAX_IDENTIFIERS}>(fence, receive_buf)?; } - - self.state.write_to_ring( - RingWrite::Write(cmd_receive, Some(&receive_buf[0..len])), - self.state.channel_ring_id, - )?; - self.fence_handler.call(fence); } CrossDomainToken::Resample => { // The resample event is triggered when the job queue is in the following state: @@ -540,6 +501,32 @@ impl CrossDomainWorker { self.fence_handler.call(fence); } + CrossDomainToken::ReadEventfd(efd_id) => { + let mut items = self.item_state.lock().unwrap(); + let mut cmd_read: CrossDomainReadWrite = Default::default(); + + cmd_read.hdr.cmd = CROSS_DOMAIN_CMD_READ; + cmd_read.identifier = efd_id; + + let item = items + .table + .get_mut(&efd_id) + .ok_or(RutabagaError::InvalidCrossDomainItemId)?; + + match item { + CrossDomainItem::Eventfd(ref mut file) => { + let ring_write = + RingWrite::WriteFromFile(cmd_read, file, event.readable); + self.state.write_to_ring::<CrossDomainReadWrite>( + ring_write, + self.state.channel_ring_id, + )?; + } + _ => return Err(RutabagaError::InvalidCrossDomainItemType), + } + + self.fence_handler.call(fence); + } #[cfg(feature = "x")] CrossDomainToken::Futex(id) => { let mut futexes = self.state.futexes.lock().unwrap(); @@ -582,6 +569,66 @@ impl CrossDomainWorker { Ok(()) } + fn process_receive<T: CrossDomainSendReceiveBase, const MAX_IDENTIFIERS: usize>( + &mut self, + fence: RutabagaFence, + receive_buf: &mut [u8], + ) -> RutabagaResult<()> { + let (len, files) = self.state.receive_msg::<MAX_IDENTIFIERS>(receive_buf)?; + let mut cmd_receive: T = Default::default(); + + let num_files = files.len(); + cmd_receive.hdr_mut().cmd = CROSS_DOMAIN_CMD_RECEIVE; + *cmd_receive.num_identifiers_mut() = files.len().try_into()?; + *cmd_receive.opaque_data_size_mut() = len.try_into()?; + + let iter = cmd_receive + .iter_over_identifiers() + .zip(files) + .take(num_files); + + for ((identifier, identifier_type, identifier_size), mut file) in iter { + // Safe since the descriptors from receive_msg(..) are owned by us and valid. + if let Ok(seek_size) = file.seek(SeekFrom::End(0)) { + *identifier_type = CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB; + *identifier_size = seek_size.try_into()?; + let fd_path = read_link(format!("/proc/self/fd/{}", file.as_raw_fd())) + .unwrap() + .as_os_str() + .to_string_lossy() + .into_owned(); + *identifier = if fd_path.starts_with("/dmabuf:") { + add_item(&self.item_state, CrossDomainItem::DmaBuf(file.into())) + } else if fd_path.starts_with("/memfd:") { + add_item(&self.item_state, CrossDomainItem::ShmBlob(file.into())) + } else if fd_path.starts_with("anon_inode:[eventfd]") { + *identifier_type = CROSS_DOMAIN_ID_TYPE_EVENTFD; + add_item(&self.item_state, CrossDomainItem::Eventfd(file)) + } else { + info!( + "Unknown fd item path {:?}, treating as a shmem blob", + fd_path + ); + add_item(&self.item_state, CrossDomainItem::ShmBlob(file.into())) + }; + } else { + let flags = fcntl(file.as_raw_descriptor(), FcntlArg::F_GETFL)?; + *identifier_type = match flags & O_ACCMODE { + O_WRONLY => CROSS_DOMAIN_ID_TYPE_WRITE_PIPE, + _ => return Err(RutabagaError::InvalidCrossDomainItemType), + }; + *identifier = add_item(&self.item_state, CrossDomainItem::WaylandWritePipe(file)); + } + } + + self.state.write_to_ring( + RingWrite::Write(cmd_receive, Some(&receive_buf[0..len])), + self.state.channel_ring_id, + )?; + self.fence_handler.call(fence); + Ok(()) + } + fn run( &mut self, thread_kill_evt: Receiver, @@ -591,7 +638,12 @@ impl CrossDomainWorker { .add(CrossDomainToken::Resample, &thread_resample_evt)?; self.wait_ctx .add(CrossDomainToken::Kill, &thread_kill_evt)?; - let mut receive_buf: Vec<u8> = vec![0; CROSS_DOMAIN_MAX_SEND_RECV_SIZE]; + let buf_size = if self.protocol_version == 0 { + CROSS_DOMAIN_MAX_SEND_RECV_SIZE + } else { + CROSS_DOMAIN_MAX_SEND_RECV_SIZE_V2 + }; + let mut receive_buf: Vec<u8> = vec![0; buf_size]; while let Some(job) = self.state.wait_for_job() { match job { @@ -618,6 +670,20 @@ impl CrossDomainWorker { _ => return Err(RutabagaError::InvalidCrossDomainItemType), } } + CrossDomainJob::AddReadEventfd(efd_id) => { + let items = self.item_state.lock().unwrap(); + let item = items + .table + .get(&efd_id) + .ok_or(RutabagaError::InvalidCrossDomainItemId)?; + + match item { + CrossDomainItem::Eventfd(file) => self + .wait_ctx + .add(CrossDomainToken::ReadEventfd(efd_id), file)?, + _ => return Err(RutabagaError::InvalidCrossDomainItemType), + } + } #[cfg(feature = "x")] CrossDomainJob::AddFutex(id, recv) => { self.wait_ctx.add(CrossDomainToken::Futex(id), recv)?; @@ -649,7 +715,7 @@ impl CrossDomain { } impl CrossDomainContext { - fn initialize(&mut self, cmd_init: &CrossDomainInit) -> RutabagaResult<()> { + fn initialize(&mut self, cmd_init: &CrossDomainInitV1) -> RutabagaResult<()> { if !self .context_resources .lock() @@ -663,6 +729,7 @@ impl CrossDomainContext { let channel_ring_id = cmd_init.channel_ring_id; let context_resources = self.context_resources.clone(); let futexes = self.futexes.clone(); + let protocol_version = cmd_init.protocol_version; // Zero means no requested channel. if cmd_init.channel_type != 0 { @@ -708,6 +775,7 @@ impl CrossDomainContext { thread_state, thread_items, thread_fence_handler, + protocol_version, ) .run(thread_kill_evt, thread_resample_evt) }); @@ -726,6 +794,8 @@ impl CrossDomainContext { ))); } + self.protocol_version = protocol_version; + Ok(()) } @@ -872,6 +942,28 @@ impl CrossDomainContext { Ok(()) } + fn read_eventfd_new( + &mut self, + cmd_eventfd_new: &CrossDomainReadEventfdNew, + ) -> RutabagaResult<()> { + let items = self.item_state.lock().unwrap(); + + if let Some(item) = items.table.get(&cmd_eventfd_new.id) { + if let CrossDomainItem::Eventfd(_) = item { + self.state + .as_ref() + .unwrap() + .add_job(CrossDomainJob::AddReadEventfd(cmd_eventfd_new.id)); + channel_signal(self.resample_evt.as_ref().unwrap())?; + Ok(()) + } else { + Err(RutabagaError::InvalidCrossDomainItemType) + } + } else { + Err(RutabagaError::InvalidCrossDomainItemId) + } + } + fn write(&self, cmd_write: &CrossDomainReadWrite, opaque_data: &[u8]) -> RutabagaResult<()> { let mut items = self.item_state.lock().unwrap(); @@ -902,6 +994,26 @@ impl CrossDomainContext { _ => Err(RutabagaError::InvalidCrossDomainItemType), } } + + fn process_cmd_send<T: CrossDomainSendReceiveBase, const MAX_IDENTIFIERS: usize>( + &mut self, + commands: &mut [u8], + ) -> RutabagaResult<()> { + let opaque_data_offset = size_of::<T>(); + let mut cmd_send = + T::read_from_prefix(commands.as_bytes()).ok_or(RutabagaError::InvalidCommandBuffer)?; + + let opaque_data = commands + .get_mut( + opaque_data_offset..opaque_data_offset + *cmd_send.opaque_data_size_mut() as usize, + ) + .ok_or(RutabagaError::InvalidCommandSize( + *cmd_send.opaque_data_size_mut() as usize, + ))?; + + self.send::<T, MAX_IDENTIFIERS>(&mut cmd_send, opaque_data)?; + Ok(()) + } } impl Drop for CrossDomainContext { @@ -928,12 +1040,24 @@ impl Drop for CrossDomainContext { #[repr(C)] #[derive(Copy, Clone, Default, AsBytes, FromBytes)] -struct CrossDomainInitLegacy { +struct CrossDomainInitVMinus1 { hdr: CrossDomainHeader, query_ring_id: u32, channel_type: u32, } +impl CrossDomainInitVMinus1 { + pub(crate) fn upgrade(&self) -> CrossDomainInitV1 { + CrossDomainInitV1 { + hdr: self.hdr, + query_ring_id: self.query_ring_id, + channel_ring_id: self.query_ring_id, + channel_type: self.channel_type, + protocol_version: 0, + } + } +} + impl RutabagaContext for CrossDomainContext { fn context_create_blob( &mut self, @@ -1012,7 +1136,7 @@ impl RutabagaContext for CrossDomainContext { blob: true, blob_mem: resource_create_blob.blob_mem, blob_flags: resource_create_blob.blob_flags, - map_info: Some(RUTABAGA_MAP_CACHE_CACHED | RUTABAGA_MAP_ACCESS_READ), + map_info: Some(RUTABAGA_MAP_CACHE_CACHED | RUTABAGA_MAP_ACCESS_RW), #[cfg(target_os = "macos")] map_ptr: None, info_2d: None, @@ -1025,6 +1149,11 @@ impl RutabagaContext for CrossDomainContext { }) } CrossDomainItem::DmaBuf(descriptor) => { + let mut access = RUTABAGA_MAP_ACCESS_READ; + if fcntl(descriptor.as_raw_fd(), FcntlArg::F_GETFL)? & libc::O_WRONLY != 0 { + access |= RUTABAGA_MAP_ACCESS_WRITE; + } + let hnd = RutabagaHandle { os_handle: descriptor, handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, @@ -1036,7 +1165,7 @@ impl RutabagaContext for CrossDomainContext { blob: true, blob_mem: resource_create_blob.blob_mem, blob_flags: resource_create_blob.blob_flags, - map_info: Some(RUTABAGA_MAP_CACHE_CACHED | RUTABAGA_MAP_ACCESS_RW), + map_info: Some(RUTABAGA_MAP_CACHE_CACHED | access), #[cfg(target_os = "macos")] map_ptr: None, info_2d: None, @@ -1063,24 +1192,16 @@ impl RutabagaContext for CrossDomainContext { match hdr.cmd { CROSS_DOMAIN_CMD_INIT => { - let cmd_init = match CrossDomainInit::read_from_prefix(commands.as_bytes()) { - Some(cmd_init) => cmd_init, - None => { - if let Some(cmd_init) = - CrossDomainInitLegacy::read_from_prefix(commands.as_bytes()) - { - CrossDomainInit { - hdr: cmd_init.hdr, - query_ring_id: cmd_init.query_ring_id, - channel_ring_id: cmd_init.query_ring_id, - channel_type: cmd_init.channel_type, - } - } else { - return Err(RutabagaError::InvalidCommandBuffer); - } - } - }; - + let cmd_init = CrossDomainInitV1::read_from_prefix(commands.as_bytes()) + .or_else(|| { + CrossDomainInitV0::read_from_prefix(commands.as_bytes()) + .map(|x| x.upgrade()) + }) + .or_else(|| { + CrossDomainInitVMinus1::read_from_prefix(commands.as_bytes()) + .map(|x| x.upgrade()) + }) + .ok_or(RutabagaError::InvalidCommandBuffer)?; self.initialize(&cmd_init)?; } CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS => { @@ -1091,20 +1212,11 @@ impl RutabagaContext for CrossDomainContext { self.get_image_requirements(&cmd_get_reqs)?; } CROSS_DOMAIN_CMD_SEND => { - let opaque_data_offset = size_of::<CrossDomainSendReceive>(); - let cmd_send = CrossDomainSendReceive::read_from_prefix(commands.as_bytes()) - .ok_or(RutabagaError::InvalidCommandBuffer)?; - - let opaque_data = commands - .get_mut( - opaque_data_offset - ..opaque_data_offset + cmd_send.opaque_data_size as usize, - ) - .ok_or(RutabagaError::InvalidCommandSize( - cmd_send.opaque_data_size as usize, - ))?; - - self.send(&cmd_send, opaque_data)?; + if self.protocol_version == 0 { + self.process_cmd_send::<CrossDomainSendReceive, {CrossDomainSendReceive::MAX_IDENTIFIERS}>(commands)?; + } else { + self.process_cmd_send::<CrossDomainSendReceiveV2, {CrossDomainSendReceiveV2::MAX_IDENTIFIERS}>(commands)?; + } } CROSS_DOMAIN_CMD_POLL => { // Actual polling is done in the subsequent when creating a fence. @@ -1145,6 +1257,12 @@ impl RutabagaContext for CrossDomainContext { .ok_or(RutabagaError::InvalidCommandBuffer)?; self.futex_destroy(&cmd_futex_destroy)?; } + CROSS_DOMAIN_CMD_READ_EVENTFD_NEW => { + let cmd_new_efd = + CrossDomainReadEventfdNew::read_from_prefix(commands.as_bytes()) + .ok_or(RutabagaError::InvalidCommandBuffer)?; + self.read_eventfd_new(&cmd_new_efd)?; + } _ => return Err(RutabagaError::SpecViolation("invalid cross domain command")), } @@ -1282,6 +1400,7 @@ impl RutabagaComponent for CrossDomain { kill_evt: None, export_table: self.export_table.clone(), futexes: Arc::new(Mutex::new(Default::default())), + protocol_version: 0, })) } diff --git a/src/rutabaga_gfx/src/cross_domain/sys/mod.rs b/src/rutabaga_gfx/src/cross_domain/sys/mod.rs index 4f84669e..f42316e2 100644 --- a/src/rutabaga_gfx/src/cross_domain/sys/mod.rs +++ b/src/rutabaga_gfx/src/cross_domain/sys/mod.rs @@ -18,7 +18,6 @@ cfg_if::cfg_if! { pub use platform::channel; pub use platform::channel_signal; pub use platform::channel_wait; -pub use platform::descriptor_analysis; pub use platform::read_volatile; pub use platform::write_volatile; pub use platform::Receiver; diff --git a/src/rutabaga_gfx/src/cross_domain/sys/unix.rs b/src/rutabaga_gfx/src/cross_domain/sys/unix.rs index b44ae6da..47366f4c 100644 --- a/src/rutabaga_gfx/src/cross_domain/sys/unix.rs +++ b/src/rutabaga_gfx/src/cross_domain/sys/unix.rs @@ -5,17 +5,11 @@ use std::fs::File; use std::io::IoSlice; use std::io::IoSliceMut; -use std::io::Seek; -use std::io::SeekFrom; use std::os::unix::io::AsRawFd; use std::os::unix::io::FromRawFd; use std::os::unix::prelude::AsFd; -use libc::O_ACCMODE; -use libc::O_WRONLY; use nix::cmsg_space; -use nix::fcntl::fcntl; -use nix::fcntl::FcntlArg; use nix::sys::epoll::EpollCreateFlags; use nix::sys::epoll::EpollFlags; use nix::sys::eventfd::eventfd; @@ -36,18 +30,17 @@ use nix::unistd::read; use nix::unistd::write; use super::super::add_item; -use super::super::cross_domain_protocol::CrossDomainSendReceive; use super::super::cross_domain_protocol::CROSS_DOMAIN_ID_TYPE_READ_PIPE; use super::super::cross_domain_protocol::CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB; -use super::super::cross_domain_protocol::CROSS_DOMAIN_ID_TYPE_WRITE_PIPE; -use super::super::cross_domain_protocol::CROSS_DOMAIN_MAX_IDENTIFIERS; use super::super::CrossDomainContext; use super::super::CrossDomainItem; use super::super::CrossDomainJob; use super::super::CrossDomainState; use super::epoll_internal::Epoll; use super::epoll_internal::EpollEvent; -use crate::cross_domain::cross_domain_protocol::{CrossDomainInit, CROSS_DOMAIN_ID_TYPE_SHM}; +use crate::cross_domain::cross_domain_protocol::{ + CrossDomainInitV1, CrossDomainSendReceiveBase, CROSS_DOMAIN_ID_TYPE_SHM, +}; use crate::cross_domain::CrossDomainEvent; use crate::cross_domain::CrossDomainToken; use crate::cross_domain::WAIT_CONTEXT_MAX; @@ -59,31 +52,6 @@ use crate::RutabagaResult; pub type SystemStream = File; -// Determine type of OS-specific descriptor. See `from_file` in wl.rs for explantation on the -// current, Linux-based method. -pub fn descriptor_analysis( - descriptor: &mut File, - descriptor_type: &mut u32, - size: &mut u32, -) -> RutabagaResult<()> { - match descriptor.seek(SeekFrom::End(0)) { - Ok(seek_size) => { - *descriptor_type = CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB; - *size = seek_size.try_into()?; - Ok(()) - } - _ => { - let flags = fcntl(descriptor.as_raw_descriptor(), FcntlArg::F_GETFL)?; - *descriptor_type = match flags & O_ACCMODE { - O_WRONLY => CROSS_DOMAIN_ID_TYPE_WRITE_PIPE, - _ => return Err(RutabagaError::InvalidCrossDomainItemType), - }; - - Ok(()) - } - } -} - impl CrossDomainState { fn send_msg(&self, opaque_data: &[u8], descriptors: &[RawDescriptor]) -> RutabagaResult<usize> { let cmsg = ControlMessage::ScmRights(descriptors); @@ -102,10 +70,13 @@ impl CrossDomainState { Err(RutabagaError::InvalidCrossDomainChannel) } - pub(crate) fn receive_msg(&self, opaque_data: &mut [u8]) -> RutabagaResult<(usize, Vec<File>)> { + pub(crate) fn receive_msg<const MAX_IDENTIFIERS: usize>( + &self, + opaque_data: &mut [u8], + ) -> RutabagaResult<(usize, Vec<File>)> { // If any errors happen, the socket will get dropped, preventing more reading. let mut iovecs = [IoSliceMut::new(opaque_data)]; - let mut cmsgspace = cmsg_space!([RawDescriptor; CROSS_DOMAIN_MAX_IDENTIFIERS]); + let mut cmsgspace = cmsg_space!([RawDescriptor; MAX_IDENTIFIERS]); let flags = MsgFlags::empty(); if let Some(connection) = &self.connection { @@ -140,7 +111,7 @@ impl CrossDomainState { impl CrossDomainContext { pub(crate) fn get_connection( &mut self, - cmd_init: &CrossDomainInit, + cmd_init: &CrossDomainInitV1, ) -> RutabagaResult<Option<SystemStream>> { let channels = self .channels @@ -165,32 +136,30 @@ impl CrossDomainContext { Ok(Some(stream)) } - pub(crate) fn send( + pub(crate) fn send<T: CrossDomainSendReceiveBase, const MAX_IDENTIFIERS: usize>( &self, - cmd_send: &CrossDomainSendReceive, + cmd_send: &mut T, opaque_data: &[u8], ) -> RutabagaResult<()> { - let mut descriptors = [0; CROSS_DOMAIN_MAX_IDENTIFIERS]; + let mut descriptors = [0; MAX_IDENTIFIERS]; let mut write_pipe_opt: Option<File> = None; let mut read_pipe_id_opt: Option<u32> = None; - let num_identifiers = cmd_send.num_identifiers.try_into()?; + let num_identifiers = (*cmd_send.num_identifiers_mut()).try_into()?; - if num_identifiers > CROSS_DOMAIN_MAX_IDENTIFIERS { + if num_identifiers > MAX_IDENTIFIERS { return Err(RutabagaError::SpecViolation( "max cross domain identifiers exceeded", )); } let iter = cmd_send - .identifiers - .iter() - .zip(cmd_send.identifier_types.iter()) + .iter_over_identifiers() .zip(descriptors.iter_mut()) .take(num_identifiers); - for ((identifier, identifier_type), descriptor) in iter { + for ((identifier, identifier_type, _), descriptor) in iter { if *identifier_type == CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB { let context_resources = self.context_resources.lock().unwrap(); diff --git a/src/rutabaga_gfx/src/rutabaga_utils.rs b/src/rutabaga_gfx/src/rutabaga_utils.rs index 9ab9621d..1377ddcc 100644 --- a/src/rutabaga_gfx/src/rutabaga_utils.rs +++ b/src/rutabaga_gfx/src/rutabaga_utils.rs @@ -599,6 +599,7 @@ impl Transfer3D { /// Rutabaga channel types pub const RUTABAGA_CHANNEL_TYPE_WAYLAND: u32 = 0x0001; pub const RUTABAGA_CHANNEL_TYPE_CAMERA: u32 = 0x0002; +pub const RUTABAGA_CHANNEL_TYPE_PW: u32 = 0x0010; pub const RUTABAGA_CHANNEL_TYPE_X11: u32 = 0x0011; /// Information needed to open an OS-specific RutabagaConnection (TBD). Only Linux hosts are