diff --git a/Cargo.lock b/Cargo.lock index e606319fd..088408edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1010,6 +1010,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mesa3d_util" +version = "0.1.76-libkrun.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a95e11093683dd73b2e70dbd3f848cddd54f05a971fdcb5e3dc655773062fd79" +dependencies = [ + "cfg-if", + "libc", + "log", + "remain", + "rustix", + "thiserror 1.0.69", + "zerocopy", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1370,18 +1385,19 @@ checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rutabaga_gfx" -version = "0.1.2" +version = "0.1.76-libkrun.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc09e93072d8a9282568ebf9958e77f5f15a04eadbe41074efdfe57d6507db2" dependencies = [ - "anyhow", "cfg-if", "libc", "log", - "nix 0.30.1", + "mesa3d_util", "pkg-config", "remain", + "serde", + "serde_json", "thiserror 1.0.69", - "vmm-sys-util 0.14.0", - "winapi", "zerocopy", ] diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index c933b2bca..cfc682e9b 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -38,7 +38,7 @@ krun_input = { path = "../krun_input", features = ["bindgen_clang_runtime"], opt arch = { path = "../arch" } utils = { path = "../utils" } polly = { path = "../polly" } -rutabaga_gfx = { path = "../rutabaga_gfx", features = ["virgl_renderer", "virgl_renderer_next"], optional = true } +rutabaga_gfx = { version = "0.1.76-libkrun.1", optional = true } imago = { version = "0.2.1", features = ["sync-wrappers", "vm-memory"] } [target.'cfg(target_os = "macos")'.dependencies] @@ -46,7 +46,7 @@ hvf = { path = "../hvf" } lru = ">=0.9" [target.'cfg(target_os = "linux")'.dependencies] -rutabaga_gfx = { path = "../rutabaga_gfx", features = ["x"], optional = true } +rutabaga_gfx = { version = "0.1.76-libkrun.1", features = ["virgl_renderer"], optional = true } caps = "0.5.5" kvm-bindings = { version = ">=0.11", features = ["fam-wrappers"] } kvm-ioctls = ">=0.21" diff --git a/src/devices/src/virtio/gpu/virtio_gpu.rs b/src/devices/src/virtio/gpu/virtio_gpu.rs index dc4bc99d6..a82598623 100644 --- a/src/devices/src/virtio/gpu/virtio_gpu.rs +++ b/src/devices/src/virtio/gpu/virtio_gpu.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::env; use std::io::IoSliceMut; #[cfg(target_os = "linux")] -use std::os::fd::AsRawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; @@ -19,20 +19,20 @@ use krun_display::{ }; use libc::c_void; #[cfg(target_os = "macos")] -use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_APPLE; +use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_DMABUF; #[cfg(all(not(feature = "virgl_resource_map2"), target_os = "linux"))] -use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD; +use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD; #[cfg(all(feature = "virgl_resource_map2", target_os = "linux"))] -use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_SHM; +use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_SHM; use rutabaga_gfx::{ - ResourceCreate3D, ResourceCreateBlob, Rutabaga, RutabagaBuilder, RutabagaChannel, - RutabagaFence, RutabagaFenceHandler, RutabagaIovec, Transfer3D, RUTABAGA_CHANNEL_TYPE_WAYLAND, - RUTABAGA_MAP_CACHE_MASK, + ResourceCreate3D, ResourceCreateBlob, Rutabaga, RutabagaBuilder, RutabagaDescriptor, + RutabagaFence, RutabagaFenceHandler, RutabagaIovec, RutabagaPath, RutabagaResult, Transfer3D, + VirtioFsLookup, RUTABAGA_MAP_CACHE_MASK, RUTABAGA_PATH_TYPE_WAYLAND, }; #[cfg(target_os = "linux")] use rutabaga_gfx::{ - RUTABAGA_CHANNEL_TYPE_PW, RUTABAGA_CHANNEL_TYPE_X11, RUTABAGA_MAP_ACCESS_MASK, - RUTABAGA_MAP_ACCESS_READ, RUTABAGA_MAP_ACCESS_RW, RUTABAGA_MAP_ACCESS_WRITE, + RUTABAGA_MAP_ACCESS_MASK, RUTABAGA_MAP_ACCESS_READ, RUTABAGA_MAP_ACCESS_RW, + RUTABAGA_MAP_ACCESS_WRITE, RUTABAGA_PATH_TYPE_PIPEWIRE, RUTABAGA_PATH_TYPE_X11, }; #[cfg(target_os = "macos")] use utils::worker_message::WorkerMessage; @@ -44,6 +44,33 @@ use crate::virtio::fs::ExportTable; use crate::virtio::gpu::protocol::VIRTIO_GPU_FLAG_INFO_RING_IDX; use crate::virtio::{InterruptTransport, VirtioShmRegion}; +struct ExportTableLookup { + table: ExportTable, +} + +impl ExportTableLookup { + fn new(table: ExportTable) -> Self { + Self { table } + } +} + +impl VirtioFsLookup for ExportTableLookup { + fn get_exported_descriptor( + &self, + fs_id: u64, + handle: u64, + ) -> RutabagaResult { + let table = self.table.lock().unwrap(); + let file = table + .get(&(fs_id, handle)) + .ok_or(rutabaga_gfx::RutabagaError::InvalidResourceId)? + .try_clone() + .map_err(|_| rutabaga_gfx::RutabagaError::InvalidResourceId)?; + + Ok(file.into()) + } +} + fn sglist_to_rutabaga_iovecs( vecs: &[(GuestAddress, usize)], mem: &GuestMemoryMmap, @@ -229,18 +256,18 @@ impl VirtioGpu { let path = PathBuf::from(format!("{xdg_runtime_dir}/{wayland_display}")); #[allow(unused_mut)] - let mut rutabaga_channels: Vec = vec![RutabagaChannel { - base_channel: path, - channel_type: RUTABAGA_CHANNEL_TYPE_WAYLAND, + let mut rutabaga_paths: Vec = vec![RutabagaPath { + path, + path_type: RUTABAGA_PATH_TYPE_WAYLAND, }]; #[cfg(target_os = "linux")] if let Ok(x_display) = env::var("DISPLAY") { if let Some(x_display) = x_display.strip_prefix(":") { let x_path = PathBuf::from(format!("/tmp/.X11-unix/X{x_display}")); - rutabaga_channels.push(RutabagaChannel { - base_channel: x_path, - channel_type: RUTABAGA_CHANNEL_TYPE_X11, + rutabaga_paths.push(RutabagaPath { + path: x_path, + path_type: RUTABAGA_PATH_TYPE_X11, }); } } @@ -252,28 +279,61 @@ impl VirtioGpu { 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, + rutabaga_paths.push(RutabagaPath { + path: pw_path, + path_type: RUTABAGA_PATH_TYPE_PIPEWIRE, }); } - let rutabaga_channels_opt = Some(rutabaga_channels); - - let builder = RutabagaBuilder::new( - rutabaga_gfx::RutabagaComponentType::VirglRenderer, - virgl_flags, - 0, - ) - .set_rutabaga_channels(rutabaga_channels_opt); - let builder = if let Some(export_table) = export_table { - builder.set_export_table(export_table) - } else { - builder - }; + let rutabaga_paths_opt = Some(rutabaga_paths); let fence = Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt); - builder.clone().build(fence.clone(), None).ok() + + // Translate virgl_flags to capset_mask and builder configuration + // These constants match libkrun.h FFI definitions and rutabaga_gfx's internal constants. + // They're redefined here because rutabaga_gfx doesn't export them publicly. + // TODO: Consider making these public in rutabaga_gfx to avoid duplication. + const VIRGLRENDERER_USE_EGL: u32 = 1 << 0; + const VIRGLRENDERER_USE_GLES: u32 = 1 << 4; + const VIRGLRENDERER_VENUS: u32 = 1 << 6; + const VIRGLRENDERER_NO_VIRGL: u32 = 1 << 7; + const VIRGLRENDERER_RENDER_SERVER: u32 = 1 << 9; + const VIRGLRENDERER_DRM: u32 = 1 << 10; + + let mut capset_mask: u64 = 0; + + if virgl_flags & VIRGLRENDERER_NO_VIRGL == 0 { + capset_mask |= 1 << rutabaga_gfx::RUTABAGA_CAPSET_VIRGL; + capset_mask |= 1 << rutabaga_gfx::RUTABAGA_CAPSET_VIRGL2; + } + + // Enable Venus if requested (requires render server mode) + if virgl_flags & VIRGLRENDERER_VENUS != 0 { + capset_mask |= 1 << rutabaga_gfx::RUTABAGA_CAPSET_VENUS; + } + + // Enable DRM native context if requested (in-process mode) + if virgl_flags & VIRGLRENDERER_DRM != 0 { + capset_mask |= 1 << rutabaga_gfx::RUTABAGA_CAPSET_DRM; + } + + let use_egl = virgl_flags & VIRGLRENDERER_USE_EGL != 0; + let use_gles = virgl_flags & VIRGLRENDERER_USE_GLES != 0; + let use_render_server = virgl_flags & VIRGLRENDERER_RENDER_SERVER != 0; + + let mut builder = RutabagaBuilder::new(capset_mask, fence) + .set_default_component(rutabaga_gfx::RutabagaComponentType::VirglRenderer) + .set_use_egl(use_egl) + .set_use_gles(use_gles) + .set_rutabaga_paths(rutabaga_paths_opt) + .set_use_render_server(use_render_server); + + if let Some(export_table) = export_table { + let lookup: Arc = Arc::new(ExportTableLookup::new(export_table)); + builder = builder.set_virtiofs_lookup(lookup); + } + + builder.build().ok() } pub fn create_fallback_rutabaga( @@ -282,16 +342,16 @@ impl VirtioGpu { interrupt: InterruptTransport, fence_state: Arc>, ) -> Option { - const VIRGLRENDERER_NO_VIRGL: u32 = 1 << 7; - let builder = RutabagaBuilder::new( - rutabaga_gfx::RutabagaComponentType::VirglRenderer, - VIRGLRENDERER_NO_VIRGL, - 0, - ); - let fence = Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt); - builder.clone().build(fence.clone(), None).ok() + + // Fallback with minimal capset mask and no EGL/GLES + let capset_mask = 0; + + RutabagaBuilder::new(capset_mask, fence) + .set_default_component(rutabaga_gfx::RutabagaComponentType::VirglRenderer) + .build() + .ok() } #[allow(clippy::too_many_arguments)] @@ -346,7 +406,7 @@ impl VirtioGpu { // Non-public function -- no doc comment needed! fn result_from_query(&mut self, resource_id: u32) -> GpuResponse { - match self.rutabaga.query(resource_id) { + match self.rutabaga.resource3d_info(resource_id) { Ok(query) => { let mut plane_info = Vec::with_capacity(4); for plane_index in 0..4 { @@ -567,7 +627,7 @@ impl VirtioGpu { transfer: Transfer3D, ) -> VirtioGpuResult { self.rutabaga - .transfer_write(ctx_id, resource_id, transfer)?; + .transfer_write(ctx_id, resource_id, transfer, None)?; Ok(OkNoData) } @@ -764,33 +824,37 @@ impl VirtioGpu { let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?; if let Ok(export) = self.rutabaga.export_blob(resource_id) { - if export.handle_type != RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD { - let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK { - RUTABAGA_MAP_ACCESS_READ => libc::PROT_READ, - RUTABAGA_MAP_ACCESS_WRITE => libc::PROT_WRITE, - RUTABAGA_MAP_ACCESS_RW => libc::PROT_READ | libc::PROT_WRITE, - _ => panic!("unexpected prot mode for mapping"), - }; - - if offset + resource.size > shm_region.size as u64 { - error!("mapping DOES NOT FIT"); - } - let addr = shm_region.host_addr + offset; - debug!( - "mapping: host_addr={:x}, addr={:x}, size={}", - shm_region.host_addr, addr, resource.size - ); - let ret = unsafe { - libc::mmap( - addr as *mut libc::c_void, - resource.size as usize, - prot, - libc::MAP_SHARED | libc::MAP_FIXED, - export.os_handle.as_raw_fd(), - 0 as libc::off_t, - ) - }; - if ret == libc::MAP_FAILED { + if let Some(mesa_handle) = export.as_mesa_handle() { + if mesa_handle.handle_type != RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD { + let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK { + RUTABAGA_MAP_ACCESS_READ => libc::PROT_READ, + RUTABAGA_MAP_ACCESS_WRITE => libc::PROT_WRITE, + RUTABAGA_MAP_ACCESS_RW => libc::PROT_READ | libc::PROT_WRITE, + _ => panic!("unexpected prot mode for mapping"), + }; + + if offset + resource.size > shm_region.size as u64 { + error!("mapping DOES NOT FIT"); + } + let addr = shm_region.host_addr + offset; + debug!( + "mapping: host_addr={:x}, addr={:x}, size={}", + shm_region.host_addr, addr, resource.size + ); + let ret = unsafe { + libc::mmap( + addr as *mut libc::c_void, + resource.size as usize, + prot, + libc::MAP_SHARED | libc::MAP_FIXED, + mesa_handle.os_handle.as_fd().as_raw_fd(), + 0 as libc::off_t, + ) + }; + if ret == libc::MAP_FAILED { + return Err(ErrUnspec); + } + } else { return Err(ErrUnspec); } } else { @@ -834,29 +898,33 @@ impl VirtioGpu { let addr = shm_region.host_addr + offset; if let Ok(export) = self.rutabaga.export_blob(resource_id) { - if export.handle_type == RUTABAGA_MEM_HANDLE_TYPE_SHM { - let ret = unsafe { - libc::mmap( - addr as *mut libc::c_void, - resource.size as usize, + if let Some(mesa_handle) = export.as_mesa_handle() { + if mesa_handle.handle_type == RUTABAGA_HANDLE_TYPE_MEM_SHM { + let ret = unsafe { + libc::mmap( + addr as *mut libc::c_void, + resource.size as usize, + prot, + libc::MAP_SHARED | libc::MAP_FIXED, + mesa_handle.os_handle.as_fd().as_raw_fd(), + 0 as libc::off_t, + ) + }; + if ret == libc::MAP_FAILED { + error!("failed to mmap resource in shm region"); + return Err(ErrUnspec); + } + } else { + self.rutabaga.resource_map( + resource_id, + addr, + resource.size, prot, libc::MAP_SHARED | libc::MAP_FIXED, - export.os_handle.as_raw_fd(), - 0 as libc::off_t, - ) - }; - if ret == libc::MAP_FAILED { - error!("failed to mmap resource in shm region"); - return Err(ErrUnspec); + )?; } } else { - self.rutabaga.resource_map( - resource_id, - addr, - resource.size, - prot, - libc::MAP_SHARED | libc::MAP_FIXED, - )?; + return Err(ErrUnspec); } } @@ -879,31 +947,35 @@ impl VirtioGpu { .ok_or(ErrInvalidResourceId)?; let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?; - let map_ptr = self.rutabaga.map_ptr(resource_id).map_err(|_| ErrUnspec)?; + let map_ptr = self.rutabaga.map(resource_id).map_err(|_| ErrUnspec)?.ptr; if let Ok(export) = self.rutabaga.export_blob(resource_id) { - if export.handle_type == RUTABAGA_MEM_HANDLE_TYPE_APPLE { - if offset + resource.size > shm_region.size as u64 { - error!("mapping DOES NOT FIT"); - return Err(ErrUnspec); - } + if let Some(mesa_handle) = export.as_mesa_handle() { + if mesa_handle.handle_type == RUTABAGA_HANDLE_TYPE_MEM_DMABUF { + if offset + resource.size > shm_region.size as u64 { + error!("mapping DOES NOT FIT"); + return Err(ErrUnspec); + } - let guest_addr = shm_region.guest_addr + offset; - debug!( - "mapping: map_ptr={:x}, guest_addr={:x}, size={}", - map_ptr, guest_addr, resource.size - ); - - let (reply_sender, reply_receiver) = unbounded(); - self.map_sender - .send(WorkerMessage::GpuAddMapping( - reply_sender, - map_ptr, - guest_addr, - resource.size, - )) - .unwrap(); - if !reply_receiver.recv().unwrap() { + let guest_addr = shm_region.guest_addr + offset; + debug!( + "mapping: map_ptr={:x}, guest_addr={:x}, size={}", + map_ptr, guest_addr, resource.size + ); + + let (reply_sender, reply_receiver) = unbounded(); + self.map_sender + .send(WorkerMessage::GpuAddMapping( + reply_sender, + map_ptr, + guest_addr, + resource.size, + )) + .unwrap(); + if !reply_receiver.recv().unwrap() { + return Err(ErrUnspec); + } + } else { return Err(ErrUnspec); } } else { diff --git a/src/devices/src/virtio/gpu/worker.rs b/src/devices/src/virtio/gpu/worker.rs index e00186c46..b2d0776e9 100644 --- a/src/devices/src/virtio/gpu/worker.rs +++ b/src/devices/src/virtio/gpu/worker.rs @@ -162,7 +162,13 @@ impl Worker { } GpuCommand::TransferToHost2d(info) => { let resource_id = info.resource_id; - let transfer = Transfer3D::new_2d(info.r.x, info.r.y, info.r.width, info.r.height); + let transfer = Transfer3D::new_2d( + info.r.x, + info.r.y, + info.r.width, + info.r.height, + info.offset, + ); virtio_gpu.transfer_write(0, resource_id, transfer) } GpuCommand::ResourceAttachBacking(info) => { diff --git a/src/rutabaga_gfx/Cargo.toml b/src/rutabaga_gfx/Cargo.toml deleted file mode 100644 index 6c8357b76..000000000 --- a/src/rutabaga_gfx/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "rutabaga_gfx" -version = "0.1.2" -authors = ["The ChromiumOS Authors + Android Open Source Project"] -edition = "2021" -description = "[highly unstable] Handling virtio-gpu protocols" -license-file = "LICENSE" - -[features] -gfxstream = [] -gfxstream_stub = [] -gpu = [] -virgl_renderer = [] -virgl_renderer_next = [] -virgl_resource_map2 = [] -minigbm = [] -# To try out Vulkano, delete the following line and uncomment the line in "dependencies". Vulkano -# features are just a prototype and not integrated yet into the ChromeOS build system. -vulkano = [] -x = [] - -[dependencies] -cfg-if = "1.0.0" -libc = "0.2.116" -remain = "0.2" -thiserror = "1.0.23" -zerocopy = { version = "0.8.26", features = ["derive"] } -log = "0.4" -vmm-sys-util = ">=0.14" - -[target.'cfg(unix)'.dependencies] -nix = { version = "0.30.1", features = ["event", "feature", "fs", "mman", "socket", "uio", "ioctl"] } - -[target.'cfg(windows)'.dependencies] -winapi = "0.3" - -# To build latest Vulkano, change version to git = "https:/github.com/vulkano-rs/vulkano.git" -# vulkano = { version = "0.31.0", optional = true } - -[build-dependencies] -pkg-config = "0.3" -anyhow = "1.0.57" diff --git a/src/rutabaga_gfx/LICENSE b/src/rutabaga_gfx/LICENSE deleted file mode 100644 index e33747247..000000000 --- a/src/rutabaga_gfx/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/rutabaga_gfx/build.rs b/src/rutabaga_gfx/build.rs deleted file mode 100644 index 9599f5881..000000000 --- a/src/rutabaga_gfx/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Copyright 2023 Red Hat, Inc. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::env; - -fn main() -> Result<(), pkg_config::Error> { - // Skip installing dependencies when generating documents. - if env::var("CARGO_DOC").is_ok() { - return Ok(()); - } - - #[cfg(feature = "gpu")] - { - pkg_config::Config::new().probe("epoxy")?; - #[cfg(target_os = "linux")] - pkg_config::Config::new().probe("libdrm")?; - pkg_config::Config::new().probe("virglrenderer")?; - } - - Ok(()) -} diff --git a/src/rutabaga_gfx/ffi/Cargo.toml b/src/rutabaga_gfx/ffi/Cargo.toml deleted file mode 100644 index 497fe7fde..000000000 --- a/src/rutabaga_gfx/ffi/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "rutabaga_gfx_ffi" -version = "0.1.2" -authors = ["The ChromiumOS Authors + Android Open Source Project"] -edition = "2021" -description = "Handling virtio-gpu protocols with C API" -license-file = "LICENSE" - -[lib] -name = "rutabaga_gfx_ffi" -crate-type = ["cdylib", "staticlib"] - -[dependencies] -rutabaga_gfx = { path = "../", version = "0.1.2"} -libc = "0.2.93" -log = "0.4" -once_cell = "1.7" - -[features] -minigbm = ["rutabaga_gfx/minigbm"] -gfxstream = ["rutabaga_gfx/gfxstream"] -virgl_renderer = ["rutabaga_gfx/virgl_renderer"] -virgl_renderer_next = ["rutabaga_gfx/virgl_renderer_next"] -vulkano = ["rutabaga_gfx/vulkano"] - -[profile.dev] -lto = true -incremental = false - -[workspace] diff --git a/src/rutabaga_gfx/ffi/LICENSE b/src/rutabaga_gfx/ffi/LICENSE deleted file mode 100644 index e33747247..000000000 --- a/src/rutabaga_gfx/ffi/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/rutabaga_gfx/ffi/Makefile b/src/rutabaga_gfx/ffi/Makefile deleted file mode 100644 index 304974a39..000000000 --- a/src/rutabaga_gfx/ffi/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2023 The ChromiumOS Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -prefix ?= /usr/local -libdir = ${prefix}/lib -includedir = ${prefix}/include/rutabaga_gfx - -UNAME := $(shell uname -s) - -GFXSTREAM_DEP = gfxstream_backend - -ifdef debug - release := - target :=debug - extension :=debug - OUT = target/debug -else - release :=--release - target :=release - extension := - OUT = target/release -endif - -# Remember to change ffi/build.rs if this changes. -RUTABAGA_VERSION_MAJOR := 0 -SRC ?= src - -ifeq ($(UNAME), Linux) -LIB_NAME := librutabaga_gfx_ffi.so -endif - -ifeq ($(UNAME), Darwin) -LIB_NAME := librutabaga_gfx_ffi.dylib -endif - -gfxstream_feature := -ifeq ($(shell pkg-config --exists $(GFXSTREAM_DEP) && echo 1),1) - gfxstream_feature :=--features=gfxstream -endif - -RUTABAGA_VERSION := $(RUTABAGA_VERSION_MAJOR).1.2 - -all: build - -build: - cargo build $(gfxstream_feature) $(release) - -install: build -ifeq ($(UNAME), Linux) - install -D -m 755 $(OUT)/$(LIB_NAME) $(DESTDIR)$(libdir)/$(LIB_NAME).$(RUTABAGA_VERSION) -endif -ifeq ($(UNAME), Darwin) - install_name_tool -id $(DESTDIR)$(libdir)/$(LIB_NAME).$(RUTABAGA_VERSION) $(DESTDIR)$(libdir)/$(LIB_NAME) -endif - - ln -sf $(LIB_NAME).$(RUTABAGA_VERSION) $(DESTDIR)$(libdir)/$(LIB_NAME).$(RUTABAGA_VERSION_MAJOR) - ln -sf $(LIB_NAME).$(RUTABAGA_VERSION) $(DESTDIR)$(libdir)/$(LIB_NAME) - -ifeq ($(UNAME), Linux) - install -D -m 0644 $(SRC)/share/rutabaga_gfx_ffi.pc $(DESTDIR)$(libdir)/pkgconfig/rutabaga_gfx_ffi.pc - install -D -m 0644 $(SRC)/include/rutabaga_gfx_ffi.h $(DESTDIR)$(includedir)/rutabaga_gfx_ffi.h -endif -ifeq ($(UNAME), Darwin) - install -m 0644 $(SRC)/share/rutabaga_gfx_ffi.pc $(DESTDIR)$(libdir)/pkgconfig/rutabaga_gfx_ffi.pc - install -m 0644 $(SRC)/include/rutabaga_gfx_ffi.h $(DESTDIR)$(includedir)/rutabaga_gfx_ffi.h -endif - -clean: - cargo clean $(release) - -distclean: - cargo clean $(release) - -help: - @echo "usage: make $(prog) [debug=1]" diff --git a/src/rutabaga_gfx/ffi/build.rs b/src/rutabaga_gfx/ffi/build.rs deleted file mode 100644 index aa9edf9bf..000000000 --- a/src/rutabaga_gfx/ffi/build.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -fn main() { - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); - - if target_os.contains("linux") || target_os.contains("nto") { - println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,librutabaga_gfx_ffi.so.0"); - } -} diff --git a/src/rutabaga_gfx/ffi/src/.clang-format b/src/rutabaga_gfx/ffi/src/.clang-format deleted file mode 100644 index 05120f017..000000000 --- a/src/rutabaga_gfx/ffi/src/.clang-format +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2021 The ChromiumOS Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -BasedOnStyle: LLVM -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -BreakBeforeBraces: Linux -ColumnLimit: 100 -IndentWidth: 4 -TabWidth: 4 -UseTab: Never -Cpp11BracedListStyle: false -IndentCaseLabels: false diff --git a/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h b/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h deleted file mode 100644 index 8ab5ad706..000000000 --- a/src/rutabaga_gfx/ffi/src/include/rutabaga_gfx_ffi.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2021 The ChromiumOS Authors - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -struct iovec; -#else -#include -#endif - -#ifndef RUTABAGA_GFX_FFI_H -#define RUTABAGA_GFX_FFI_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Versioning - */ -#define RUTABAGA_VERSION_MAJOR 0 -#define RUTABAGA_VERSION_MINOR 1 -#define RUTABAGA_VERSION_PATCH 2 - -/** - * Rutabaga capsets. - */ -#define RUTABAGA_CAPSET_VIRGL 1 -#define RUTABAGA_CAPSET_VIRGL2 2 -#define RUTABAGA_CAPSET_GFXSTREAM_VULKAN 3 -#define RUTABAGA_CAPSET_VENUS 4 -#define RUTABAGA_CAPSET_CROSS_DOMAIN 5 -#define RUTABAGA_CAPSET_DRM 6 -#define RUTABAGA_CAPSET_GFXSTREAM_MAGMA 7 -#define RUTABAGA_CAPSET_GFXSTREAM_GLES 8 -#define RUTABAGA_CAPSET_GFXSTREAM_COMPOSER 9 - -/** - * Blob resource creation parameters. - */ -#define RUTABAGA_BLOB_MEM_GUEST 1 -#define RUTABAGA_BLOB_MEM_HOST3D 2 -#define RUTABAGA_BLOB_MEM_HOST3D_GUEST 3 - -#define RUTABAGA_BLOB_FLAG_USE_MAPPABLE 1 -#define RUTABAGA_BLOB_FLAG_USE_SHAREABLE 2 -#define RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE 4 - -/** - * Mapped memory caching flags (see virtio_gpu spec) - */ -#define RUTABAGA_MAP_CACHE_MASK 0x0f -#define RUTABAGA_MAP_CACHE_CACHED 0x01 -#define RUTABAGA_MAP_CACHE_UNCACHED 0x02 -#define RUTABAGA_MAP_CACHE_WC 0x03 - -/** - * Mapped memory access flags (not in virtio_gpu spec) - */ -#define RUTABAGA_MAP_ACCESS_MASK 0xf0 -#define RUTABAGA_MAP_ACCESS_READ 0x10 -#define RUTABAGA_MAP_ACCESS_WRITE 0x20 -#define RUTABAGA_MAP_ACCESS_RW 0x30 - -/** - * Rutabaga handle types - */ -#define RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD 0x1 -#define RUTABAGA_MEM_HANDLE_TYPE_DMABUF 0x2 -#define RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_WIN32 0x3 -#define RUTABAGA_MEM_HANDLE_TYPE_SHM 0x4 -#define RUTABAGA_MEM_HANDLE_TYPE_ZIRCON 0x5 - -#define RUTABAGA_FENCE_HANDLE_TYPE_OPAQUE_FD 0x6 -#define RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD 0x7 -#define RUTABAGA_FENCE_HANDLE_TYPE_OPAQUE_WIN32 0x8 -#define RUTABAGA_FENCE_HANDLE_TYPE_ZIRCON 0x9 - -/** - * Rutabaga channel types - */ -#define RUTABAGA_CHANNEL_TYPE_WAYLAND 1 -#define RUTABAGA_CHANNEL_TYPE_PW 0x10 -#define RUTABAGA_CHANNEL_TYPE_X11 0x11 - -/** - * Rutabaga WSI - */ -#define RUTABAGA_WSI_SURFACELESS 0x1 - -/** - * Rutabaga flags for creating fences. - */ -#define RUTABAGA_FLAG_FENCE (1 << 0) -#define RUTABAGA_FLAG_INFO_RING_IDX (1 << 1) -#define RUTABAGA_FLAG_FENCE_SHAREABLE (1 << 2) - -/** - * Rutabaga Debug - */ -#define RUTABAGA_DEBUG_ERROR 0x1 -#define RUTABAGA_DEBUG_WARN 0x2 -#define RUTABAGA_DEBUG_INFO 0x3 - -struct rutabaga; - -struct rutabaga_create_blob { - uint32_t blob_mem; - uint32_t blob_flags; - uint64_t blob_id; - uint64_t size; -}; - -struct rutabaga_create_3d { - uint32_t target; - uint32_t format; - uint32_t bind; - uint32_t width; - uint32_t height; - uint32_t depth; - uint32_t array_size; - uint32_t last_level; - uint32_t nr_samples; - uint32_t flags; -}; - -struct rutabaga_transfer { - uint32_t x; - uint32_t y; - uint32_t z; - uint32_t w; - uint32_t h; - uint32_t d; - uint32_t level; - uint32_t stride; - uint32_t layer_stride; - uint64_t offset; -}; - -struct rutabaga_iovecs { - struct iovec *iovecs; - size_t num_iovecs; -}; - -struct rutabaga_handle { - int64_t os_handle; - uint32_t handle_type; -}; - -struct rutabaga_mapping { - void *ptr; - uint64_t size; -}; - -struct rutabaga_command { - uint32_t ctx_id; - uint32_t cmd_size; - uint8_t *cmd; - - /** - * Unstable, don't use until version > 0.1.2 - */ - uint32_t num_in_fences; - uint64_t *fence_ids; -}; - -/** - * Assumes null-terminated C-string. - */ -struct rutabaga_channel { - const char *channel_name; - uint32_t channel_type; -}; - -struct rutabaga_channels { - struct rutabaga_channel *channels; - size_t num_channels; -}; - -struct rutabaga_fence { - uint32_t flags; - uint64_t fence_id; - uint32_t ctx_id; - uint32_t ring_idx; -}; - -struct rutabaga_debug { - uint32_t debug_type; - const char *message; -}; - -/** - * Throwing an exception inside this callback is not allowed. - */ -typedef void (*rutabaga_fence_callback)(uint64_t user_data, const struct rutabaga_fence *fence); - -/** - * # Safety - * - Throwing an exception inside this callback is not allowed. - * - `rutabaga_debug` and contained values only valid for the duration of callback. - */ -typedef void (*rutabaga_debug_callback)(uint64_t user_data, const struct rutabaga_debug *debug); - -struct rutabaga_builder { - // Required for correct functioning - uint64_t user_data; - uint64_t capset_mask; - uint64_t wsi; - rutabaga_fence_callback fence_cb; - - // Optional for debugging. - rutabaga_debug_callback debug_cb; - - // Optional and platform specific - struct rutabaga_channels *channels; -}; - -/** - * Expects `capset_names` to delimited by a colon, i.e.: "gfxstream:cross_domain:magma". - * - * # Safety - * - - `capset_names` must be a null-terminated C-string. - */ -int32_t rutabaga_calculate_capset_mask(const char *capset_names, uint64_t *capset_mask); - -/** - * # Safety - * - If `(*builder).channels` is not null, the caller must ensure `(*channels).channels` points to - * a valid array of `struct rutabaga_channel` of size `(*channels).num_channels`. - * - The `channel_name` field of `struct rutabaga_channel` must be a null-terminated C-string. - */ -int32_t rutabaga_init(const struct rutabaga_builder *builder, struct rutabaga **ptr); - -/** - * # Safety - * - `ptr` must have been created by `rutabaga_init`. - */ -int32_t rutabaga_finish(struct rutabaga **ptr); - -int32_t rutabaga_get_num_capsets(struct rutabaga *ptr, uint32_t *num_capsets); - -int32_t rutabaga_get_capset_info(struct rutabaga *ptr, uint32_t capset_index, uint32_t *capset_id, - uint32_t *capset_version, uint32_t *capset_size); - -/** - * # Safety - * - `capset` must point an array of bytes of size `capset_size`. - */ -int32_t rutabaga_get_capset(struct rutabaga *ptr, uint32_t capset_id, uint32_t version, - uint8_t *capset, uint32_t capset_size); - -/** - * # Safety - * - `context_name` must either be NULL or a valid pointer to an array of at least - * `context_name_len` bytes encoding a UTF-8 string. - */ -int32_t rutabaga_context_create(struct rutabaga *ptr, uint32_t ctx_id, uint32_t context_init, - const char *context_name, uint32_t context_name_len); - -int32_t rutabaga_context_destroy(struct rutabaga *ptr, uint32_t ctx_id); - -int32_t rutabaga_context_attach_resource(struct rutabaga *ptr, uint32_t ctx_id, - uint32_t resource_id); - -int32_t rutabaga_context_detach_resource(struct rutabaga *ptr, uint32_t ctx_id, - uint32_t resource_id); - -int32_t rutabaga_resource_create_3d(struct rutabaga *ptr, uint32_t resource_id, - const struct rutabaga_create_3d *create_3d); - -/** - * # Safety - * - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of - * iovecs of size `(*iovecs).num_iovecs`. - * - Each iovec must point to valid memory starting at `iov_base` with length `iov_len`. - * - Each iovec must valid until the resource's backing is explicitly detached or the resource is - * is unreferenced. - */ -int32_t rutabaga_resource_attach_backing(struct rutabaga *ptr, uint32_t resource_id, - const struct rutabaga_iovecs *iovecs); - -int32_t rutabaga_resource_detach_backing(struct rutabaga *ptr, uint32_t resource_id); - -/** - * # Safety - * - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of - * iovecs of size `(*iovecs).num_iovecs`. - */ -int32_t rutabaga_resource_transfer_read(struct rutabaga *ptr, uint32_t ctx_id, uint32_t resource_id, - const struct rutabaga_transfer *transfer, - const struct iovec *iovec); - -int32_t rutabaga_resource_transfer_write(struct rutabaga *ptr, uint32_t ctx_id, - uint32_t resource_id, - const struct rutabaga_transfer *transfer); - -/** - * # Safety - * - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of - * iovecs of size `(*iovecs).num_iovecs`. - * - If `handle` is not null, the caller must ensure it is a valid OS-descriptor. Ownership is - * transferred to rutabaga. - * - Each iovec must valid until the resource's backing is explicitly detached or the resource is - * is unreferenced. - */ -int32_t rutabaga_resource_create_blob(struct rutabaga *ptr, uint32_t ctx_id, uint32_t resource_id, - const struct rutabaga_create_blob *rutabaga_create_blob, - const struct rutabaga_iovecs *iovecs, - const struct rutabaga_handle *handle); - -int32_t rutabaga_resource_unref(struct rutabaga *ptr, uint32_t resource_id); - -/** - * # Safety - * Caller owns raw descriptor on success and is responsible for closing it. - */ -int32_t rutabaga_resource_export_blob(struct rutabaga *ptr, uint32_t resource_id, - struct rutabaga_handle *handle); - -int32_t rutabaga_resource_map(struct rutabaga *ptr, uint32_t resource_id, - struct rutabaga_mapping *mapping); - -int32_t rutabaga_resource_unmap(struct rutabaga *ptr, uint32_t resource_id); - -int32_t rutabaga_resource_map_info(struct rutabaga *ptr, uint32_t resource_id, uint32_t *map_info); - -/** - * # Safety - * - `cmd` must be not null - * - `cmd->cmd` point to a contiguous memory region of `cmd_size` bytes. - * - `cmd->fence_ids` must point to a contiguous array of `num_in_fences` elements - */ -int32_t rutabaga_submit_command(struct rutabaga *ptr, struct rutabaga_command *cmd); - -int32_t rutabaga_create_fence(struct rutabaga *ptr, const struct rutabaga_fence *fence); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/rutabaga_gfx/ffi/src/lib.rs b/src/rutabaga_gfx/ffi/src/lib.rs deleted file mode 100644 index ff5f83ce5..000000000 --- a/src/rutabaga_gfx/ffi/src/lib.rs +++ /dev/null @@ -1,596 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -///! C-bindings for the rutabaga_gfx crate -extern crate rutabaga_gfx; - -use std::convert::TryInto; -use std::ffi::CStr; -use std::ffi::CString; -use std::io::IoSliceMut; -use std::os::raw::c_char; -use std::os::raw::c_void; -use std::panic::catch_unwind; -use std::panic::AssertUnwindSafe; -use std::path::PathBuf; -use std::ptr::copy_nonoverlapping; -use std::ptr::null; -use std::ptr::null_mut; -use std::slice::from_raw_parts; -use std::slice::from_raw_parts_mut; -use std::sync::Mutex; - -use libc::iovec; -use libc::EINVAL; -use libc::ESRCH; -use once_cell::sync::OnceCell; -use rutabaga_gfx::*; - -const NO_ERROR: i32 = 0; -const RUTABAGA_WSI_SURFACELESS: u64 = 1; - -static S_DEBUG_HANDLER: OnceCell> = OnceCell::new(); - -fn log_error(debug_string: String) { - // Although this should be only called from a single-thread environment, add locking to - // to reduce the amount of unsafe code blocks. - if let Some(ref handler_mutex) = S_DEBUG_HANDLER.get() { - let cstring = CString::new(debug_string.as_str()).expect("CString creation failed"); - - let debug = RutabagaDebug { - debug_type: RUTABAGA_DEBUG_ERROR, - message: cstring.as_ptr(), - }; - - let handler = handler_mutex.lock().unwrap(); - handler.call(debug); - } -} - -fn return_result(result: RutabagaResult) -> i32 { - if let Err(e) = result { - log_error(e.to_string()); - -EINVAL - } else { - NO_ERROR - } -} - -macro_rules! return_on_error { - ($result:expr) => { - match $result { - Ok(t) => t, - Err(e) => { - log_error(e.to_string()); - return -EINVAL; - } - } - }; -} - -#[allow(non_camel_case_types)] -type rutabaga = Rutabaga; - -#[allow(non_camel_case_types)] -type rutabaga_create_blob = ResourceCreateBlob; - -#[allow(non_camel_case_types)] -type rutabaga_create_3d = ResourceCreate3D; - -#[allow(non_camel_case_types)] -type rutabaga_transfer = Transfer3D; - -#[allow(non_camel_case_types)] -type rutabaga_fence = RutabagaFence; - -#[allow(non_camel_case_types)] -type rutabaga_debug = RutabagaDebug; - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct rutabaga_iovecs { - pub iovecs: *mut iovec, - pub num_iovecs: usize, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct rutabaga_handle { - pub os_handle: i64, - pub handle_type: u32, -} - -#[repr(C)] -pub struct rutabaga_mapping { - pub ptr: *mut c_void, - pub size: u64, -} - -#[repr(C)] -pub struct rutabaga_channel { - pub channel_name: *const c_char, - pub channel_type: u32, -} - -#[repr(C)] -pub struct rutabaga_channels { - pub channels: *const rutabaga_channel, - pub num_channels: usize, -} - -#[repr(C)] -pub struct rutabaga_command { - pub ctx_id: u32, - pub cmd_size: u32, - pub cmd: *mut u8, - pub num_in_fences: u32, - pub fence_ids: *mut u64, -} - -#[allow(non_camel_case_types)] -pub type rutabaga_fence_callback = extern "C" fn(user_data: u64, fence: &rutabaga_fence); - -#[allow(non_camel_case_types)] -pub type rutabaga_debug_callback = extern "C" fn(user_data: u64, debug: &rutabaga_debug); - -#[repr(C)] -pub struct rutabaga_builder<'a> { - pub user_data: u64, - pub capset_mask: u64, - pub wsi: u64, - pub fence_cb: rutabaga_fence_callback, - pub debug_cb: Option, - pub channels: Option<&'a rutabaga_channels>, -} - -fn create_ffi_fence_handler( - user_data: u64, - fence_cb: rutabaga_fence_callback, -) -> RutabagaFenceHandler { - RutabagaFenceHandler::new(move |completed_fence| fence_cb(user_data, &completed_fence)) -} - -fn create_ffi_debug_handler( - user_data: u64, - debug_cb: rutabaga_debug_callback, -) -> RutabagaDebugHandler { - RutabagaDebugHandler::new(move |rutabaga_debug| debug_cb(user_data, &rutabaga_debug)) -} - -#[no_mangle] -/// # Safety -/// - `capset_names` must be a null-terminated C-string. -pub unsafe extern "C" fn rutabaga_calculate_capset_mask( - capset_names: *const c_char, - capset_mask: &mut u64, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - if capset_names == null() { - return -EINVAL; - } - - let c_str_slice = CStr::from_ptr(capset_names); - let result = c_str_slice.to_str(); - let str_slice = return_on_error!(result); - *capset_mask = rutabaga_gfx::calculate_capset_mask(str_slice.split(':')); - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - If `(*builder).channels` is not null, the caller must ensure `(*channels).channels` points to -/// a valid array of `struct rutabaga_channel` of size `(*channels).num_channels`. -/// - The `channel_name` field of `struct rutabaga_channel` must be a null-terminated C-string. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let fence_handler = create_ffi_fence_handler((*builder).user_data, (*builder).fence_cb); - let mut debug_handler_opt: Option = None; - - if let Some(func) = (*builder).debug_cb { - let debug_handler = create_ffi_debug_handler((*builder).user_data, func); - S_DEBUG_HANDLER - .set(Mutex::new(debug_handler.clone())) - .expect("once_cell set failed"); - debug_handler_opt = Some(debug_handler); - } - - let mut rutabaga_channels_opt = None; - if let Some(channels) = (*builder).channels { - let mut rutabaga_channels: Vec = Vec::new(); - let channels_slice = from_raw_parts(channels.channels, channels.num_channels); - - for channel in channels_slice { - let c_str_slice = CStr::from_ptr(channel.channel_name); - let result = c_str_slice.to_str(); - let str_slice = return_on_error!(result); - let string = str_slice.to_owned(); - let path = PathBuf::from(&string); - - rutabaga_channels.push(RutabagaChannel { - base_channel: path, - channel_type: channel.channel_type, - }); - } - - rutabaga_channels_opt = Some(rutabaga_channels); - } - - let mut component_type = RutabagaComponentType::CrossDomain; - if (*builder).capset_mask == 0 { - component_type = RutabagaComponentType::Rutabaga2D; - } - - let rutabaga_wsi = match (*builder).wsi { - RUTABAGA_WSI_SURFACELESS => RutabagaWsi::Surfaceless, - _ => return -EINVAL, - }; - - let result = RutabagaBuilder::new(component_type, (*builder).capset_mask) - .set_use_external_blob(false) - .set_use_egl(true) - .set_wsi(rutabaga_wsi) - .set_debug_handler(debug_handler_opt) - .set_rutabaga_channels(rutabaga_channels_opt) - .build(fence_handler, None); - - let rtbg = return_on_error!(result); - *ptr = Box::into_raw(Box::new(rtbg)) as _; - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - `ptr` must have been created by `rutabaga_init`. -#[no_mangle] -pub extern "C" fn rutabaga_finish(ptr: &mut *mut rutabaga) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - unsafe { Box::from_raw(*ptr) }; - *ptr = null_mut(); - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_get_num_capsets(ptr: &mut rutabaga, num_capsets: &mut u32) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - *num_capsets = ptr.get_num_capsets(); - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_get_capset_info( - ptr: &mut rutabaga, - capset_index: u32, - capset_id: &mut u32, - capset_version: &mut u32, - capset_size: &mut u32, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.get_capset_info(capset_index); - let info = return_on_error!(result); - *capset_id = info.0; - *capset_version = info.1; - *capset_size = info.2; - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - `capset` must point an array of bytes of size `capset_size`. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_get_capset( - ptr: &mut rutabaga, - capset_id: u32, - version: u32, - capset: *mut u8, - capset_size: u32, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let size: usize = capset_size.try_into().map_err(|_e| -EINVAL).unwrap(); - let result = ptr.get_capset(capset_id, version); - let vec = return_on_error!(result); - copy_nonoverlapping(vec.as_ptr(), capset, size); - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_context_create( - ptr: &mut rutabaga, - ctx_id: u32, - context_init: u32, - context_name: *const c_char, - context_name_len: u32, -) -> i32 { - let mut name: Option<&str> = None; - if !context_name.is_null() && context_name_len > 0 { - // Safe because context_name is not NULL and len is a positive integer, so the caller - // is expected to provide a valid pointer to an array of bytes at least as long as the - // passed length. If the provided byte array doesn't contain valid utf-8, name will be - // None. - let view = unsafe { - std::slice::from_raw_parts(context_name as *const u8, context_name_len as usize) - }; - name = std::str::from_utf8(view).ok(); - } - - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.create_context(ctx_id, context_init, name); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.destroy_context(ctx_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_context_attach_resource( - ptr: &mut rutabaga, - ctx_id: u32, - resource_id: u32, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.context_attach_resource(ctx_id, resource_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_context_detach_resource( - ptr: &mut rutabaga, - ctx_id: u32, - resource_id: u32, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.context_detach_resource(ctx_id, resource_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_create_3d( - ptr: &mut rutabaga, - resource_id: u32, - create_3d: &rutabaga_create_3d, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.resource_create_3d(resource_id, *create_3d); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of -/// iovecs of size `(*iovecs).num_iovecs`. -/// - Each iovec must point to valid memory starting at `iov_base` with length `iov_len`. -/// - Each iovec must valid until the resource's backing is explictly detached or the resource is -/// is unreferenced. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_resource_attach_backing( - ptr: &mut rutabaga, - resource_id: u32, - iovecs: &rutabaga_iovecs, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let slice = from_raw_parts((*iovecs).iovecs, (*iovecs).num_iovecs); - let vecs = slice - .iter() - .map(|iov| RutabagaIovec { - base: iov.iov_base, - len: iov.iov_len, - }) - .collect(); - - let result = ptr.attach_backing(resource_id, vecs); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.detach_backing(resource_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of -/// iovecs of size `(*iovecs).num_iovecs`. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_resource_transfer_read( - ptr: &mut rutabaga, - ctx_id: u32, - resource_id: u32, - transfer: &rutabaga_transfer, - buf: Option<&iovec>, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let mut slice_opt = None; - if let Some(iovec) = buf { - slice_opt = Some(IoSliceMut::new(std::slice::from_raw_parts_mut( - iovec.iov_base as *mut u8, - iovec.iov_len, - ))); - } - - let result = ptr.transfer_read(ctx_id, resource_id, *transfer, slice_opt); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_transfer_write( - ptr: &mut rutabaga, - ctx_id: u32, - resource_id: u32, - transfer: &rutabaga_transfer, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.transfer_write(ctx_id, resource_id, *transfer); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of -/// iovecs of size `(*iovecs).num_iovecs`. -/// - If `handle` is not null, the caller must ensure it is a valid OS-descriptor. Ownership is -/// transfered to rutabaga. -/// - Each iovec must valid until the resource's backing is explictly detached or the resource is -/// is unreferenced. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_resource_create_blob( - ptr: &mut rutabaga, - ctx_id: u32, - resource_id: u32, - create_blob: &rutabaga_create_blob, - iovecs: Option<&rutabaga_iovecs>, - handle: Option<&rutabaga_handle>, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let mut iovecs_opt: Option> = None; - if let Some(iovs) = iovecs { - let slice = from_raw_parts((*iovs).iovecs, (*iovs).num_iovecs); - let vecs = slice - .iter() - .map(|iov| RutabagaIovec { - base: iov.iov_base, - len: iov.iov_len, - }) - .collect(); - iovecs_opt = Some(vecs); - } - - let mut handle_opt: Option = None; - if let Some(hnd) = handle { - handle_opt = Some(RutabagaHandle { - os_handle: RutabagaDescriptor::from_raw_descriptor( - (*hnd).os_handle.try_into().unwrap(), - ), - handle_type: (*hnd).handle_type, - }); - } - - let result = - ptr.resource_create_blob(ctx_id, resource_id, *create_blob, iovecs_opt, handle_opt); - - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.unref_resource(resource_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// Caller owns raw descriptor on success and is responsible for closing it. -#[no_mangle] -pub extern "C" fn rutabaga_resource_export_blob( - ptr: &mut rutabaga, - resource_id: u32, - handle: &mut rutabaga_handle, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.export_blob(resource_id); - let hnd = return_on_error!(result); - - (*handle).handle_type = hnd.handle_type; - (*handle).os_handle = hnd.os_handle.into_raw_descriptor() as i64; - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_map( - ptr: &mut rutabaga, - resource_id: u32, - mapping: &mut rutabaga_mapping, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.map(resource_id); - let internal_map = return_on_error!(result); - (*mapping).ptr = internal_map.ptr as *mut c_void; - (*mapping).size = internal_map.size; - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_unmap(ptr: &mut rutabaga, resource_id: u32) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.unmap(resource_id); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_resource_map_info( - ptr: &mut rutabaga, - resource_id: u32, - map_info: &mut u32, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.map_info(resource_id); - *map_info = return_on_error!(result); - NO_ERROR - })) - .unwrap_or(-ESRCH) -} - -/// # Safety -/// - `commands` must point to a contiguous memory region of `size` bytes. -#[no_mangle] -pub unsafe extern "C" fn rutabaga_submit_command( - ptr: &mut rutabaga, - cmd: &rutabaga_command, -) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let cmd_slice = from_raw_parts_mut(cmd.cmd, cmd.cmd_size as usize); - let fence_ids = from_raw_parts(cmd.fence_ids, cmd.num_in_fences as usize); - let result = ptr.submit_command(cmd.ctx_id, cmd_slice, fence_ids); - return_result(result) - })) - .unwrap_or(-ESRCH) -} - -#[no_mangle] -pub extern "C" fn rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32 { - catch_unwind(AssertUnwindSafe(|| { - let result = ptr.create_fence(*fence); - return_result(result) - })) - .unwrap_or(-ESRCH) -} diff --git a/src/rutabaga_gfx/ffi/src/share/rutabaga_gfx_ffi.pc b/src/rutabaga_gfx/ffi/src/share/rutabaga_gfx_ffi.pc deleted file mode 100644 index 00e1ce49f..000000000 --- a/src/rutabaga_gfx/ffi/src/share/rutabaga_gfx_ffi.pc +++ /dev/null @@ -1,10 +0,0 @@ -prefix=/usr -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib - -Name: rutabaga_gfx_ffi -Description: C FFI bindings to Rutabaga VGI -Version: 0.1.2 -Cflags: -I${includedir} -Libs: -L${libdir} -lrutabaga_gfx_ffi diff --git a/src/rutabaga_gfx/ffi/src/tests/Makefile b/src/rutabaga_gfx/ffi/src/tests/Makefile deleted file mode 100644 index e681f32c1..000000000 --- a/src/rutabaga_gfx/ffi/src/tests/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2021 The ChromiumOS Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -RUTABAGA_TEST = rutabaga_test -SOURCES += rutabaga_test.c -OBJS = $(SOURCES:.c=.o) -DEPS = $(SOURCES:.c=.d) -PKG_CONFIG ?= pkg-config -CFLAGS += -O0 -ggdb3 -CCFLAGS += $(shell $(PKG_CONFIG) --cflags rutabaga_gfx_ffi) -LDLIBS += $(PC_LIBS) -LDLIBS += $(shell $(PKG_CONFIG) --libs rutabaga_gfx_ffi) -.PHONY: all clean -all: $(RUTABAGA_TEST) -$(RUTABAGA_TEST): $(OBJS) -clean: - $(RM) $(RUTABAGA_TEST) - $(RM) $(OBJS) $(DEPS) - $(RM) *.o *.d .version -$(RUTABAGA_TEST): - $(CC) $(CCFLAGS) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) -$(OBJS): %.o: %.c - $(CC) $(CCFLAGS) $(CFLAGS) -c $< -o $@ -MMD --include $(DEPS) diff --git a/src/rutabaga_gfx/ffi/src/tests/rutabaga_test.c b/src/rutabaga_gfx/ffi/src/tests/rutabaga_test.c deleted file mode 100644 index 7169797cd..000000000 --- a/src/rutabaga_gfx/ffi/src/tests/rutabaga_test.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright 2021 The ChromiumOS Authors - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "virtgpu_cross_domain_protocol.h" - -#define CHECK_RESULT(result) \ - do { \ - if (result) { \ - printf("CHECK_RESULT failed in %s() %s:%d\n", __func__, __FILE__, __LINE__); \ - return result; \ - } \ - } while (0) - -#define CHECK(cond) \ - do { \ - if (!(cond)) { \ - printf("CHECK failed in %s() %s:%d\n", __func__, __FILE__, __LINE__); \ - return -EINVAL; \ - } \ - } while (0) - -#define DEFAULT_BUFFER_SIZE 4096 -#define WIDTH 512 -#define HEIGHT 512 -#define NUM_ITERATIONS 4 - -#define GBM_BO_USE_LINEAR (1 << 4) -#define GBM_BO_USE_SCANOUT (1 << 5) -#define fourcc_code(a, b, c, d) \ - ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) -#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4'); - -#define PIPE_TEXTURE_2D 2 -#define PIPE_BIND_RENDER_TARGET 2 -#define VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM 1 - -static int s_resource_id = 1; -static int s_fence_id = 1; - -#if defined(__linux__) -static char *s_wayland_path = "/run/user/1000/wayland-0"; -#elif defined(__Fuchsia__) -#endif - -struct rutabaga_test { - struct rutabaga *rutabaga; - uint32_t ctx_id; - uint64_t value; - uint32_t query_ring_id; - uint32_t channel_ring_id; - struct iovec *query_iovecs; - struct iovec *channel_iovecs; -}; - -static void rutabaga_test_write_fence(uint64_t user_data, const struct rutabaga_fence *fence) -{ - struct rutabaga_test *test = (void *)(uintptr_t)user_data; - test->value = fence->fence_id; -} - -static void rutabaga_test_debug_cb(uint64_t user_data, const struct rutabaga_debug *debug) -{ - if (debug->message) { - printf("The debug message is %s\n", debug->message); - } -} - -static int test_capset_mask_calculation(void) -{ - int result; - uint64_t capset_mask; - - result = rutabaga_calculate_capset_mask("cross-domain:gfxstream-vulkan", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == - ((1 << RUTABAGA_CAPSET_CROSS_DOMAIN) | (1 << RUTABAGA_CAPSET_GFXSTREAM_VULKAN))); - - result = rutabaga_calculate_capset_mask(":gfxstream-vulkan", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == (1 << RUTABAGA_CAPSET_GFXSTREAM_VULKAN)); - - result = rutabaga_calculate_capset_mask("cross-domain:", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)); - - result = rutabaga_calculate_capset_mask("cross-domain", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)); - - result = rutabaga_calculate_capset_mask(":", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == 0); - - result = rutabaga_calculate_capset_mask("fake", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == 0); - - result = rutabaga_calculate_capset_mask("", &capset_mask); - CHECK_RESULT(result); - CHECK(capset_mask == 0); - - result = rutabaga_calculate_capset_mask(NULL, &capset_mask); - CHECK(result != 0); - - return 0; -} - -static int test_rutabaga_init(struct rutabaga_test *test, uint64_t capset_mask) -{ - int result; - struct rutabaga_builder builder = { 0 }; - struct rutabaga_channels channels = { 0 }; - - builder.fence_cb = rutabaga_test_write_fence; - builder.debug_cb = rutabaga_test_debug_cb; - builder.capset_mask = capset_mask; - builder.wsi = RUTABAGA_WSI_SURFACELESS; - if (capset_mask & (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)) { - builder.user_data = (uint64_t)(uintptr_t *)(void *)test; - channels.channels = (struct rutabaga_channel *)calloc(1, sizeof(struct rutabaga_channel)); - channels.num_channels = 1; - - channels.channels[0].channel_name = s_wayland_path; - channels.channels[0].channel_type = RUTABAGA_CHANNEL_TYPE_WAYLAND; - - builder.channels = &channels; - } - - result = rutabaga_init(&builder, &test->rutabaga); - - if (capset_mask & (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)) - free(channels.channels); - - CHECK_RESULT(result); - return 0; -} - -static int test_create_context(struct rutabaga_test *test, const char *context_name) -{ - int result; - uint32_t num_capsets; - uint32_t capset_id, capset_version, capset_size; - bool found_cross_domain = false; - struct CrossDomainCapabilities *capset; - - result = rutabaga_get_num_capsets(test->rutabaga, &num_capsets); - CHECK_RESULT(result); - CHECK(num_capsets == 1); - - for (uint32_t i = 0; i < num_capsets; i++) { - result = - rutabaga_get_capset_info(test->rutabaga, i, &capset_id, &capset_version, &capset_size); - CHECK_RESULT(result); - if (capset_id == RUTABAGA_CAPSET_CROSS_DOMAIN) { - found_cross_domain = true; - CHECK(capset_size == (uint32_t)sizeof(struct CrossDomainCapabilities)); - } - } - - CHECK(found_cross_domain); - CHECK_RESULT(result); - - capset = (struct CrossDomainCapabilities *)calloc(1, capset_size); - result = rutabaga_get_capset(test->rutabaga, RUTABAGA_CAPSET_CROSS_DOMAIN, 0, (uint8_t *)capset, - capset_size); - CHECK_RESULT(result); - - CHECK(capset->version == 1); - free(capset); - - size_t context_name_len = 0; - if (context_name) - context_name_len = strlen(context_name); - - result = rutabaga_context_create(test->rutabaga, test->ctx_id, RUTABAGA_CAPSET_CROSS_DOMAIN, - context_name, context_name_len); - CHECK_RESULT(result); - - return 0; -} - -static int test_init_context(struct rutabaga_test *test) -{ - int result; - struct rutabaga_create_blob rc_blob = { 0 }; - struct rutabaga_iovecs vecs = { 0 }; - struct rutabaga_command cmd = { 0 }; - struct CrossDomainInit cmd_init = { { 0 } }; - - struct iovec *query_iovecs = (struct iovec *)calloc(1, sizeof(struct iovec)); - query_iovecs[0].iov_base = calloc(1, DEFAULT_BUFFER_SIZE); - query_iovecs[0].iov_len = DEFAULT_BUFFER_SIZE; - - test->query_iovecs = query_iovecs; - rc_blob.blob_mem = RUTABAGA_BLOB_MEM_GUEST; - rc_blob.blob_flags = RUTABAGA_BLOB_FLAG_USE_MAPPABLE; - rc_blob.size = DEFAULT_BUFFER_SIZE; - - vecs.iovecs = query_iovecs; - vecs.num_iovecs = 1; - - result = rutabaga_resource_create_blob(test->rutabaga, 0, test->query_ring_id, &rc_blob, &vecs, - NULL); - CHECK_RESULT(result); - - result = rutabaga_context_attach_resource(test->rutabaga, test->ctx_id, test->query_ring_id); - CHECK_RESULT(result); - - struct iovec *channel_iovecs = (struct iovec *)calloc(1, sizeof(struct iovec)); - channel_iovecs[0].iov_base = calloc(1, DEFAULT_BUFFER_SIZE); - channel_iovecs[0].iov_len = DEFAULT_BUFFER_SIZE; - - test->channel_iovecs = channel_iovecs; - rc_blob.blob_mem = RUTABAGA_BLOB_MEM_GUEST; - rc_blob.blob_flags = RUTABAGA_BLOB_FLAG_USE_MAPPABLE; - rc_blob.size = DEFAULT_BUFFER_SIZE; - - vecs.iovecs = channel_iovecs; - vecs.num_iovecs = 1; - - result = rutabaga_resource_create_blob(test->rutabaga, 0, test->channel_ring_id, &rc_blob, - &vecs, NULL); - CHECK_RESULT(result); - - result = rutabaga_context_attach_resource(test->rutabaga, test->ctx_id, test->channel_ring_id); - CHECK_RESULT(result); - - cmd_init.hdr.cmd = CROSS_DOMAIN_CMD_INIT; - cmd_init.hdr.cmd_size = sizeof(struct CrossDomainInit); - cmd_init.query_ring_id = test->query_ring_id; - cmd_init.channel_ring_id = test->channel_ring_id; - cmd_init.channel_type = CROSS_DOMAIN_CHANNEL_TYPE_WAYLAND; - - cmd.ctx_id = test->ctx_id; - cmd.cmd = (uint8_t *)&cmd_init; - cmd.cmd_size = cmd_init.hdr.cmd_size; - - result = rutabaga_submit_command(test->rutabaga, &cmd); - CHECK_RESULT(result); - return 0; -} - -static int test_command_submission(struct rutabaga_test *test) -{ - int result; - struct CrossDomainGetImageRequirements cmd_get_reqs = { 0 }; - struct CrossDomainImageRequirements *image_reqs = (void *)test->query_iovecs[0].iov_base; - struct rutabaga_create_blob rc_blob = { 0 }; - struct rutabaga_fence fence; - struct rutabaga_handle handle = { 0 }; - struct rutabaga_command cmd = { 0 }; - uint32_t map_info; - - fence.flags = RUTABAGA_FLAG_FENCE | RUTABAGA_FLAG_INFO_RING_IDX; - fence.ctx_id = test->ctx_id; - fence.ring_idx = 0; - - cmd_get_reqs.hdr.cmd = CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS; - cmd_get_reqs.hdr.cmd_size = sizeof(struct CrossDomainGetImageRequirements); - - for (uint32_t i = 0; i < NUM_ITERATIONS; i++) { - for (uint32_t j = 0; j < NUM_ITERATIONS; j++) { - fence.fence_id = s_fence_id; - map_info = 0; - - cmd_get_reqs.width = WIDTH * i; - cmd_get_reqs.height = HEIGHT * j; - cmd_get_reqs.drm_format = DRM_FORMAT_XRGB8888; - - cmd_get_reqs.flags = GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT; - - cmd.ctx_id = test->ctx_id; - cmd.cmd = (uint8_t *)&cmd_get_reqs; - cmd.cmd_size = cmd_get_reqs.hdr.cmd_size; - - result = rutabaga_submit_command(test->rutabaga, &cmd); - - CHECK(test->value < fence.fence_id); - result = rutabaga_create_fence(test->rutabaga, &fence); - - CHECK_RESULT(result); - for (;;) { - if (fence.fence_id == test->value) - break; - } - - CHECK(image_reqs->strides[0] >= cmd_get_reqs.width * 4); - CHECK(image_reqs->size >= (cmd_get_reqs.width * 4) * cmd_get_reqs.height); - - rc_blob.blob_mem = RUTABAGA_BLOB_MEM_HOST3D; - rc_blob.blob_flags = RUTABAGA_BLOB_FLAG_USE_MAPPABLE | RUTABAGA_BLOB_FLAG_USE_SHAREABLE; - rc_blob.blob_id = image_reqs->blob_id; - rc_blob.size = image_reqs->size; - - result = rutabaga_resource_create_blob(test->rutabaga, test->ctx_id, s_resource_id, - &rc_blob, NULL, NULL); - CHECK_RESULT(result); - - result = rutabaga_context_attach_resource(test->rutabaga, test->ctx_id, s_resource_id); - CHECK_RESULT(result); - - result = rutabaga_resource_map_info(test->rutabaga, s_resource_id, &map_info); - CHECK_RESULT(result); - CHECK(map_info > 0); - - result = rutabaga_resource_export_blob(test->rutabaga, s_resource_id, &handle); - CHECK_RESULT(result); - CHECK(handle.os_handle >= 0); - - result = close(handle.os_handle); - CHECK_RESULT(result); - - result = rutabaga_context_detach_resource(test->rutabaga, test->ctx_id, s_resource_id); - CHECK_RESULT(result); - - result = rutabaga_resource_unref(test->rutabaga, s_resource_id); - CHECK_RESULT(result); - - s_resource_id++; - s_fence_id++; - } - } - - return 0; -} - -static int test_context_finish(struct rutabaga_test *test) -{ - int result; - - result = rutabaga_context_detach_resource(test->rutabaga, test->ctx_id, test->query_ring_id); - CHECK_RESULT(result); - - result = rutabaga_resource_unref(test->rutabaga, test->query_ring_id); - CHECK_RESULT(result); - - free(test->query_iovecs[0].iov_base); - - result = rutabaga_context_detach_resource(test->rutabaga, test->ctx_id, test->channel_ring_id); - CHECK_RESULT(result); - - result = rutabaga_resource_unref(test->rutabaga, test->channel_ring_id); - CHECK_RESULT(result); - - free(test->channel_iovecs[0].iov_base); - - result = rutabaga_context_destroy(test->rutabaga, test->ctx_id); - CHECK_RESULT(result); - - return 0; -} - -static int test_rutabaga_2d(struct rutabaga_test *test) -{ - struct rutabaga_create_3d rc_3d = { 0 }; - struct rutabaga_transfer transfer = { 0 }; - int result; - uint32_t resource_id = s_resource_id++; - - struct rutabaga_iovecs vecs = { 0 }; - struct iovec *iovecs = (struct iovec *)calloc(1, sizeof(struct iovec)); - uint8_t *test_data; - struct iovec result_iovec; - - iovecs[0].iov_base = calloc(1, DEFAULT_BUFFER_SIZE); - iovecs[0].iov_len = DEFAULT_BUFFER_SIZE; - result_iovec.iov_base = calloc(1, DEFAULT_BUFFER_SIZE); - result_iovec.iov_len = DEFAULT_BUFFER_SIZE; - test_data = (uint8_t *)result_iovec.iov_base; - - vecs.iovecs = iovecs; - vecs.num_iovecs = 1; - - rc_3d.target = PIPE_TEXTURE_2D; - rc_3d.bind = PIPE_BIND_RENDER_TARGET; - rc_3d.format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; - rc_3d.width = DEFAULT_BUFFER_SIZE / 16; - rc_3d.height = 4; - - transfer.w = DEFAULT_BUFFER_SIZE / 16; - transfer.h = 4; - transfer.d = 1; - - result = rutabaga_resource_create_3d(test->rutabaga, resource_id, &rc_3d); - CHECK_RESULT(result); - - result = rutabaga_resource_attach_backing(test->rutabaga, resource_id, &vecs); - CHECK_RESULT(result); - - memset(iovecs[0].iov_base, 8, DEFAULT_BUFFER_SIZE); - - result = - rutabaga_resource_transfer_read(test->rutabaga, 0, resource_id, &transfer, &result_iovec); - CHECK_RESULT(result); - - CHECK(test_data[0] == 0); - - result = rutabaga_resource_transfer_write(test->rutabaga, 0, resource_id, &transfer); - CHECK_RESULT(result); - - result = - rutabaga_resource_transfer_read(test->rutabaga, 0, resource_id, &transfer, &result_iovec); - CHECK_RESULT(result); - - CHECK(test_data[0] == 8); - - result = rutabaga_resource_detach_backing(test->rutabaga, resource_id); - CHECK_RESULT(result); - - result = rutabaga_resource_unref(test->rutabaga, resource_id); - CHECK_RESULT(result); - - free(iovecs[0].iov_base); - free(iovecs); - free(test_data); - return 0; -} - -static int test_rutabaga_finish(struct rutabaga_test *test) -{ - int result; - - result = rutabaga_finish(&test->rutabaga); - CHECK_RESULT(result); - CHECK(test->rutabaga == NULL); - return 0; -} - -int main(int argc, char *argv[]) -{ - struct rutabaga_test test = { 0 }; - test.ctx_id = 1; - test.query_ring_id = s_resource_id++; - test.channel_ring_id = s_resource_id++; - - int result; - - const char *context_names[] = { - NULL, - "test_context", - }; - const uint32_t num_context_names = 2; - - for (uint32_t i = 0; i < num_context_names; i++) { - const char *context_name = context_names[i]; - for (uint32_t j = 0; j < NUM_ITERATIONS; j++) { - result = test_capset_mask_calculation(); - CHECK_RESULT(result); - - result = test_rutabaga_init(&test, 1 << RUTABAGA_CAPSET_CROSS_DOMAIN); - CHECK_RESULT(result); - - result |= test_create_context(&test, context_name); - CHECK_RESULT(result); - - result |= test_init_context(&test); - CHECK_RESULT(result); - - result |= test_command_submission(&test); - CHECK_RESULT(result); - - result |= test_context_finish(&test); - CHECK_RESULT(result); - - result |= test_rutabaga_finish(&test); - CHECK_RESULT(result); - } - } - - for (uint32_t i = 0; i < NUM_ITERATIONS; i++) { - result = test_rutabaga_init(&test, 0); - CHECK_RESULT(result); - - result |= test_rutabaga_2d(&test); - CHECK_RESULT(result); - - result |= test_rutabaga_finish(&test); - CHECK_RESULT(result); - } - - printf("[ PASSED ] rutabaga_test success\n"); - return 0; -} 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 deleted file mode 100644 index 71e95b58e..000000000 --- a/src/rutabaga_gfx/ffi/src/tests/virtgpu_cross_domain_protocol.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef VIRTGPU_CROSS_DOMAIN_PROTOCOL_H -#define VIRTGPU_CROSS_DOMAIN_PROTOCOL_H - -#include - -// Cross-domain commands (only a maximum of 255 supported) -#define CROSS_DOMAIN_CMD_INIT 1 -#define CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS 2 -#define CROSS_DOMAIN_CMD_POLL 3 -#define CROSS_DOMAIN_CMD_SEND 4 -#define CROSS_DOMAIN_CMD_RECEIVE 5 -#define CROSS_DOMAIN_CMD_READ 6 -#define CROSS_DOMAIN_CMD_WRITE 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) -#define CROSS_DOMAIN_MAX_IDENTIFIERS 4 - -// virtgpu memory resource ID. Also works with non-blob memory resources, -// despite the name. -#define CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB 1 - -// virtgpu synchronization resource id. -#define CROSS_DOMAIN_ID_TYPE_VIRTGPU_SYNC 2 - -// ID for Wayland pipe used for reading. The reading is done by the guest proxy -// and the host proxy. The host sends the write end of the proxied pipe over -// the host Wayland socket. -#define CROSS_DOMAIN_ID_TYPE_READ_PIPE 3 - -// ID for Wayland pipe used for writing. The writing is done by the guest and -// the host proxy. The host receives the write end of the pipe over the host -// Wayland socket. -#define CROSS_DOMAIN_ID_TYPE_WRITE_PIPE 4 - -// No ring used -#define CROSS_DOMAIN_RING_NONE 0xffffffff -// A ring for metadata queries. -#define CROSS_DOMAIN_QUERY_RING 0 -// A ring based on this particular context's channel. -#define CROSS_DOMAIN_CHANNEL_RING 1 - -// Read pipe IDs start at this value. -#define CROSS_DOMAIN_PIPE_READ_START 0x80000000 - -struct CrossDomainCapabilities { - uint32_t version; - uint32_t supported_channels; - uint32_t supports_dmabuf; - uint32_t supports_external_gpu_memory; -}; - -struct CrossDomainImageRequirements { - uint32_t strides[4]; - uint32_t offsets[4]; - uint64_t modifier; - uint64_t size; - uint32_t blob_id; - uint32_t map_info; - int32_t memory_idx; - int32_t physical_device_idx; -}; - -struct CrossDomainHeader { - uint8_t cmd; - uint8_t fence_ctx_idx; - uint16_t cmd_size; - uint32_t pad; -}; - -struct CrossDomainInit { - struct CrossDomainHeader hdr; - uint32_t query_ring_id; - uint32_t channel_ring_id; - uint32_t channel_type; -}; - -struct CrossDomainGetImageRequirements { - struct CrossDomainHeader hdr; - uint32_t width; - uint32_t height; - uint32_t drm_format; - uint32_t flags; -}; - -struct CrossDomainPoll { - struct CrossDomainHeader hdr; - uint64_t pad; -}; - -struct CrossDomainSendReceive { - struct CrossDomainHeader hdr; - uint32_t num_identifiers; - uint32_t opaque_data_size; - uint32_t identifiers[CROSS_DOMAIN_MAX_IDENTIFIERS]; - uint32_t identifier_types[CROSS_DOMAIN_MAX_IDENTIFIERS]; - uint32_t identifier_sizes[CROSS_DOMAIN_MAX_IDENTIFIERS]; -}; - -struct CrossDomainReadWrite { - struct CrossDomainHeader hdr; - uint32_t identifier; - uint32_t hang_up; - uint32_t opaque_data_size; - uint32_t pad; -}; - -#endif diff --git a/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs b/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs deleted file mode 100644 index 5085050a6..000000000 --- a/src/rutabaga_gfx/src/cross_domain/cross_domain_protocol.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Hand-written protocol for the cross-domain context type. Intended to be shared with C/C++ -//! components. - -#![allow(dead_code)] - -use zerocopy::FromBytes; -use zerocopy::Immutable; -use zerocopy::IntoBytes; - -/// Cross-domain commands (only a maximum of 255 supported) -pub const CROSS_DOMAIN_CMD_INIT: u8 = 1; -pub const CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS: u8 = 2; -pub const CROSS_DOMAIN_CMD_POLL: u8 = 3; -pub const CROSS_DOMAIN_CMD_SEND: u8 = 4; -pub const CROSS_DOMAIN_CMD_RECEIVE: u8 = 5; -pub const CROSS_DOMAIN_CMD_READ: u8 = 6; -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 (must match what sommelier expects) -pub const CROSS_DOMAIN_MAX_IDENTIFIERS: 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; -/// virtgpu synchronization resource id. -pub const CROSS_DOMAIN_ID_TYPE_VIRTGPU_SYNC: u32 = 2; -/// ID for Wayland pipe used for reading. The reading is done by the guest proxy and the host -/// proxy. The host sends the write end of the proxied pipe over the host Wayland socket. -pub const CROSS_DOMAIN_ID_TYPE_READ_PIPE: u32 = 3; -/// ID for Wayland pipe used for writing. The writing is done by the guest and the host proxy. -/// The host receives the write end of the pipe over the host Wayland socket. -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; -/// A ring for metadata queries. -pub const CROSS_DOMAIN_QUERY_RING: u32 = 0; -/// A ring based on this particular context's channel. -pub const CROSS_DOMAIN_CHANNEL_RING: u32 = 1; - -/// Read pipe IDs start at this value. -pub const CROSS_DOMAIN_PIPE_READ_START: u32 = 0x80000000; - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainCapabilities { - pub version: u32, - pub supported_channels: u32, - pub supports_dmabuf: u32, - pub supports_external_gpu_memory: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainImageRequirements { - pub strides: [u32; 4], - pub offsets: [u32; 4], - pub modifier: u64, - pub size: u64, - pub blob_id: u32, - pub map_info: u32, - pub memory_idx: i32, - pub physical_device_idx: i32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainHeader { - pub cmd: u8, - pub ring_idx: u8, - pub cmd_size: u16, - pub pad: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -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, IntoBytes, Immutable, 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)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainGetImageRequirements { - pub hdr: CrossDomainHeader, - pub width: u32, - pub height: u32, - pub drm_format: u32, - pub flags: u32, -} - -pub trait CrossDomainSendReceiveBase: - Copy + Clone + Default + IntoBytes + Immutable + 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; -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainSendReceive { - pub hdr: CrossDomainHeader, - pub num_identifiers: u32, - pub opaque_data_size: u32, - pub identifiers: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], - pub identifier_types: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], - 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 { - 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, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainSendReceiveV2 { - pub hdr: CrossDomainHeader, - pub num_identifiers: u32, - pub opaque_data_size: u32, - pub identifiers: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], - pub identifier_types: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], - pub identifier_sizes: [u32; CROSS_DOMAIN_MAX_IDENTIFIERS], - // Data of size "opaque data size follows" -} - -impl CrossDomainSendReceiveBase for CrossDomainSendReceiveV2 { - 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 { - 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, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainReadWrite { - pub hdr: CrossDomainHeader, - pub identifier: u32, - pub hang_up: u32, - pub opaque_data_size: u32, - pub pad: u32, - // Data of size "opaque data size follows" -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainFutexNew { - pub hdr: CrossDomainHeader, - pub fs_id: u64, - pub handle: u64, - pub id: u32, - pub pad: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainFutexSignal { - pub hdr: CrossDomainHeader, - pub id: u32, - pub pad: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -pub struct CrossDomainFutexDestroy { - pub hdr: CrossDomainHeader, - pub id: u32, - pub pad: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, 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 deleted file mode 100644 index 5a36f081e..000000000 --- a/src/rutabaga_gfx/src/cross_domain/mod.rs +++ /dev/null @@ -1,1433 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! The cross-domain component type, specialized for allocating and sharing resources across domain -//! boundaries. - -#[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; -use std::collections::BTreeMap as Map; -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::AsFd; -use std::os::fd::AsRawFd; -#[cfg(feature = "x")] -use std::ptr; -use std::ptr::NonNull; -use std::sync::atomic::AtomicBool; -#[cfg(feature = "x")] -use std::sync::atomic::{AtomicU32, Ordering}; -use std::sync::Arc; -use std::sync::Condvar; -use std::sync::Mutex; -use std::thread; -use zerocopy::FromBytes; -use zerocopy::Immutable; -use zerocopy::IntoBytes; - -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::read_volatile; -use crate::cross_domain::sys::write_volatile; -use crate::cross_domain::sys::Receiver; -use crate::cross_domain::sys::Sender; -use crate::cross_domain::sys::SystemStream; -use crate::cross_domain::sys::WaitContext; -use crate::rutabaga_core::ExportTable; -use crate::rutabaga_core::RutabagaComponent; -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; - -mod cross_domain_protocol; -mod sys; - -#[allow(dead_code)] -const WAIT_CONTEXT_MAX: usize = 16; - -pub struct CrossDomainEvent { - token: CrossDomainToken, - hung_up: bool, - readable: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum CrossDomainToken { - ContextChannel, - WaylandReadPipe(u32), - Resample, - 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::(); -const CROSS_DOMAIN_MAX_SEND_RECV_SIZE_V2: usize = - CROSS_DOMAIN_DEFAULT_BUFFER_SIZE - size_of::(); - -pub(crate) enum CrossDomainItem { - ImageRequirements(ImageMemoryRequirements), - ShmBlob(SafeDescriptor), - DmaBuf(SafeDescriptor), - #[allow(dead_code)] // `WaylandReadPipe` is never constructed on Windows. - WaylandReadPipe(File), - WaylandWritePipe(File), - Eventfd(File), -} - -pub(crate) enum CrossDomainJob { - HandleFence(RutabagaFence), - #[allow(dead_code)] // `AddReadPipe` is never constructed on Windows. - AddReadPipe(u32), - Finish, - #[cfg(feature = "x")] - AddFutex(u32, Arc), - AddReadEventfd(u32), -} - -enum RingWrite<'a, T> { - Write(T, Option<&'a [u8]>), - WriteFromFile(CrossDomainReadWrite, &'a mut File, bool), -} - -pub(crate) type CrossDomainResources = Arc>>; -type CrossDomainJobs = Mutex>>; -pub(crate) type CrossDomainItemState = Arc>; -pub(crate) type CrossDomainFutexes = Arc>>; - -pub(crate) struct CrossDomainResource { - #[allow(dead_code)] // `handle` is never used on Windows. - pub handle: Option>, - pub backing_iovecs: Option>, -} - -pub(crate) struct CrossDomainItems { - descriptor_id: u32, - read_pipe_id: u32, - table: Map, -} - -pub(crate) struct CrossDomainState { - context_resources: CrossDomainResources, - #[allow(dead_code)] // `futexes` is never used on macOS. - futexes: CrossDomainFutexes, - query_ring_id: u32, - channel_ring_id: u32, - #[allow(dead_code)] // `connection` is never used on Windows. - pub(crate) connection: Option, - jobs: CrossDomainJobs, - jobs_cvar: Condvar, -} - -struct CrossDomainWorker { - wait_ctx: WaitContext, - state: Arc, - pub(crate) item_state: CrossDomainItemState, - fence_handler: RutabagaFenceHandler, - protocol_version: u32, -} - -#[allow(dead_code)] // Never used on macOS -struct FutexPtr(NonNull); -unsafe impl Send for FutexPtr {} - -#[allow(dead_code)] // Never used on macOS -pub(crate) struct CrossDomainFutex { - address: FutexPtr, - handle: SafeDescriptor, - watcher_thread: Option>, - shutdown: Arc, - evt: Arc, -} - -#[cfg(feature = "x")] -impl CrossDomainFutex { - fn watcher_thread( - address: FutexPtr, - shutdown: Arc, - sender: Sender, - initial_value: u32, - ) { - let op = libc::FUTEX_WAIT_BITSET; - let timeout = ptr::null::<()>(); - let uaddr2 = ptr::null::<()>(); - let val3 = 1u32; - let address = address.0; - let atomic_val = unsafe { AtomicU32::from_ptr(address.as_ptr() as *mut u32) }; - // The goal of this code is to ensure that the other side observes at least - // the latest wake event along with the value that the futex had when that - // wake event was signaled. - - // The initial value is passed in from the futex creation, and therefore - // was loaded synchronously with the cross domain operation that created - // the futex, so it cannot have an associated wake event yet. - let mut val = initial_value; - let _ = channel_signal(&sender); - loop { - // This returns when the futex is woken up OR if the value has changed. - unsafe { - libc::syscall(libc::SYS_futex, address, op, val, timeout, uaddr2, val3); - } - // Load the new value, which the other side is guaranteed to observe. - val = atomic_val.load(Ordering::SeqCst); - // If this wake was triggered by the shutdown code below, just bail. - // If the shutdown command is issued after this point, then it will - // change the futex value, which will disagree with the one we read - // above, so we will still not block in SYS_futex. - if shutdown.load(Ordering::SeqCst) { - // Signal the futex to process the shutdown and remove it from - // the waiter table - let _ = channel_signal(&sender); - break; - } - // Signal the other side after the load. If another change occurs and - // another wake is signaled here, we will miss the wake, but the - // disagreeing value will cause SYS_futex to return early. - if channel_signal(&sender).is_err() { - break; - } - } - } - - fn shutdown(&mut self) { - self.shutdown.store(true, Ordering::SeqCst); - let atomic_val = unsafe { AtomicU32::from_ptr(self.address.0.as_ptr() as *mut u32) }; - let v = atomic_val.load(Ordering::SeqCst); - atomic_val.store(!v, Ordering::SeqCst); - unsafe { - libc::syscall( - libc::SYS_futex, - self.address.0, - libc::FUTEX_WAKE, - i32::MAX, - ptr::null::<()>(), - ptr::null::<()>(), - 0, - ); - } - self.watcher_thread.take().unwrap().join().unwrap(); - } - - fn is_shutdown(&self) -> bool { - self.shutdown.load(Ordering::Relaxed) - } -} - -#[cfg(feature = "x")] -impl Drop for CrossDomainFutex { - fn drop(&mut self) { - if !self.is_shutdown() { - log::info!("Futex dropped without shutdown"); - self.shutdown(); - } - unsafe { - munmap(self.address.0, 4).unwrap(); - } - } -} - -pub(crate) struct CrossDomainContext { - #[allow(dead_code)] // `channels` is unused on Windows. - pub(crate) channels: Option>, - gralloc: Arc>, - pub(crate) state: Option>, - pub(crate) context_resources: CrossDomainResources, - pub(crate) item_state: CrossDomainItemState, - pub(crate) futexes: CrossDomainFutexes, - fence_handler: RutabagaFenceHandler, - #[allow(dead_code)] // `export_table` is never used on macOS. - export_table: Option, - worker_thread: Option>>, - pub(crate) resample_evt: Option, - kill_evt: Option, - protocol_version: u32, -} - -/// The CrossDomain component contains a list of channels that the guest may connect to and the -/// ability to allocate memory. -pub struct CrossDomain { - channels: Option>, - gralloc: Arc>, - fence_handler: RutabagaFenceHandler, - export_table: Option, -} - -// TODO(gurchetansingh): optimize the item tracker. Each requirements blob is long-lived and can -// be stored in a Slab or vector. Descriptors received from the Wayland socket *seem* to come one -// at a time, and can be stored as options. Need to confirm. -pub(crate) fn add_item(item_state: &CrossDomainItemState, item: CrossDomainItem) -> u32 { - let mut items = item_state.lock().unwrap(); - - let item_id = match item { - CrossDomainItem::WaylandReadPipe(_) => { - items.read_pipe_id += 1; - max(items.read_pipe_id, CROSS_DOMAIN_PIPE_READ_START) - } - _ => { - items.descriptor_id += 1; - items.descriptor_id - } - }; - - items.table.insert(item_id, item); - - item_id -} - -impl Default for CrossDomainItems { - fn default() -> Self { - // Odd for descriptors, and even for requirement blobs. - CrossDomainItems { - descriptor_id: 1, - read_pipe_id: CROSS_DOMAIN_PIPE_READ_START, - table: Default::default(), - } - } -} - -impl CrossDomainState { - fn new( - query_ring_id: u32, - channel_ring_id: u32, - context_resources: CrossDomainResources, - futexes: CrossDomainFutexes, - connection: Option, - ) -> CrossDomainState { - CrossDomainState { - query_ring_id, - channel_ring_id, - context_resources, - futexes, - connection, - jobs: Mutex::new(Some(VecDeque::new())), - jobs_cvar: Condvar::new(), - } - } - - pub(crate) fn add_job(&self, job: CrossDomainJob) { - let mut jobs = self.jobs.lock().unwrap(); - if let Some(queue) = jobs.as_mut() { - queue.push_back(job); - self.jobs_cvar.notify_one(); - } - } - - fn wait_for_job(&self) -> Option { - let mut jobs = self.jobs.lock().unwrap(); - loop { - match jobs.as_mut()?.pop_front() { - Some(job) => return Some(job), - None => jobs = self.jobs_cvar.wait(jobs).unwrap(), - } - } - } - - fn write_to_ring(&self, mut ring_write: RingWrite, ring_id: u32) -> RutabagaResult - where - T: FromBytes + IntoBytes + Immutable, - { - let mut context_resources = self.context_resources.lock().unwrap(); - let mut bytes_read: usize = 0; - - let resource = context_resources - .get_mut(&ring_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - let iovecs = resource - .backing_iovecs - .as_mut() - .ok_or(RutabagaError::InvalidIovec)?; - - // Safe because we've verified the iovecs are attached and owned only by this context. - let slice = - unsafe { std::slice::from_raw_parts_mut(iovecs[0].base as *mut u8, iovecs[0].len) }; - - match ring_write { - RingWrite::Write(cmd, opaque_data_opt) => { - if slice.len() < size_of::() { - return Err(RutabagaError::InvalidIovec); - } - let (cmd_slice, opaque_data_slice) = slice.split_at_mut(size_of::()); - cmd_slice.copy_from_slice(cmd.as_bytes()); - if let Some(opaque_data) = opaque_data_opt { - if opaque_data_slice.len() < opaque_data.len() { - return Err(RutabagaError::InvalidIovec); - } - opaque_data_slice[..opaque_data.len()].copy_from_slice(opaque_data); - } - } - RingWrite::WriteFromFile(mut cmd_read, ref mut file, readable) => { - if slice.len() < size_of::() { - return Err(RutabagaError::InvalidIovec); - } - let (cmd_slice, opaque_data_slice) = - slice.split_at_mut(size_of::()); - - if readable { - bytes_read = read_volatile(file, opaque_data_slice)?; - } - - if bytes_read == 0 { - cmd_read.hang_up = 1; - } - - cmd_read.opaque_data_size = bytes_read.try_into()?; - cmd_slice.copy_from_slice(cmd_read.as_bytes()); - } - } - - Ok(bytes_read) - } -} - -impl CrossDomainWorker { - fn new( - wait_ctx: WaitContext, - state: Arc, - item_state: CrossDomainItemState, - fence_handler: RutabagaFenceHandler, - protocol_version: u32, - ) -> CrossDomainWorker { - CrossDomainWorker { - wait_ctx, - state, - item_state, - fence_handler, - protocol_version, - } - } - - // Handles the fence according the the token according to the event token. On success, a - // boolean value indicating whether the worker thread should be stopped is returned. - fn handle_fence( - &mut self, - fence: RutabagaFence, - thread_resample_evt: &Receiver, - receive_buf: &mut [u8], - ) -> RutabagaResult<()> { - let events = self.wait_ctx.wait()?; - - // The worker thread must: - // - // (1) Poll the ContextChannel (usually Wayland) - // (2) Poll a number of WaylandReadPipes - // (3) handle jobs from the virtio-gpu thread. - // - // We can only process one event at a time, because each `handle_fence` call is associated - // with a guest virtio-gpu fence. Signaling the fence means it's okay for the guest to - // access ring data. If two events are available at the same time (say a ContextChannel - // event and a WaylandReadPipe event), and we write responses for both using the same guest - // fence data, that will break the expected order of events. We need the guest to generate - // a new fence before we can resume polling. - // - // The CrossDomainJob queue gurantees a new fence has been generated before polling is - // resumed. - if let Some(event) = events.first() { - match event.token { - CrossDomainToken::ContextChannel => { - if self.protocol_version == 0 { - self.process_receive::(fence, receive_buf)?; - } else { - self.process_receive::(fence, receive_buf)?; - } - } - CrossDomainToken::Resample => { - // The resample event is triggered when the job queue is in the following state: - // - // [CrossDomain::AddReadPipe(..)] -> END - // - // After this event, the job queue will be the following state: - // - // [CrossDomain::AddReadPipe(..)] -> [CrossDomain::HandleFence(..)] -> END - // - // Fence handling is tied to some new data transfer across a pollable - // descriptor. When we're adding new descriptors, we stop polling. - channel_wait(thread_resample_evt)?; - self.state.add_job(CrossDomainJob::HandleFence(fence)); - } - CrossDomainToken::WaylandReadPipe(pipe_id) => { - let mut items = self.item_state.lock().unwrap(); - let mut cmd_read: CrossDomainReadWrite = Default::default(); - let bytes_read; - - cmd_read.hdr.cmd = CROSS_DOMAIN_CMD_READ; - cmd_read.identifier = pipe_id; - - let item = items - .table - .get_mut(&pipe_id) - .ok_or(RutabagaError::InvalidCrossDomainItemId)?; - - match item { - CrossDomainItem::WaylandReadPipe(ref mut file) => { - let ring_write = - RingWrite::WriteFromFile(cmd_read, file, event.readable); - bytes_read = self.state.write_to_ring::( - ring_write, - self.state.channel_ring_id, - )?; - - // Zero bytes read indicates end-of-file on POSIX. - if event.hung_up && bytes_read == 0 { - self.wait_ctx - .delete(CrossDomainToken::WaylandReadPipe(pipe_id), file)?; - } - } - _ => return Err(RutabagaError::InvalidCrossDomainItemType), - } - - if event.hung_up && bytes_read == 0 { - items.table.remove(&pipe_id); - } - - 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::( - 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(); - let mut remove = false; - let mut fence = Some(fence); - - if let Some(ftx) = futexes.get(&id) { - if ftx.is_shutdown() { - self.wait_ctx - .delete(CrossDomainToken::Futex(id), ftx.evt.as_ref())?; - remove = true; - } else { - channel_wait(&ftx.evt)?; - - let mut cmd_ftx: CrossDomainFutexSignal = Default::default(); - cmd_ftx.hdr.cmd = CROSS_DOMAIN_CMD_FUTEX_SIGNAL; - cmd_ftx.id = id; - self.state.write_to_ring( - RingWrite::Write(cmd_ftx, None), - self.state.channel_ring_id, - )?; - self.fence_handler.call(fence.take().unwrap()); - } - }; - - if let Some(fence) = fence { - self.state.add_job(CrossDomainJob::HandleFence(fence)); - } - - if remove { - futexes.remove(&id); - } - } - CrossDomainToken::Kill => { - self.fence_handler.call(fence); - } - } - } - - Ok(()) - } - - fn process_receive( - &mut self, - fence: RutabagaFence, - receive_buf: &mut [u8], - ) -> RutabagaResult<()> { - let (len, files) = self.state.receive_msg::(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 {fd_path:?}, treating as a shmem blob"); - add_item(&self.item_state, CrossDomainItem::ShmBlob(file.into())) - }; - } else { - let flags = fcntl(&file, 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, - thread_resample_evt: Receiver, - ) -> RutabagaResult<()> { - self.wait_ctx - .add(CrossDomainToken::Resample, &thread_resample_evt)?; - self.wait_ctx - .add(CrossDomainToken::Kill, &thread_kill_evt)?; - 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 = vec![0; buf_size]; - - while let Some(job) = self.state.wait_for_job() { - match job { - CrossDomainJob::HandleFence(fence) => { - match self.handle_fence(fence, &thread_resample_evt, &mut receive_buf) { - Ok(()) => (), - Err(e) => { - error!("Worker halting due to: {e}"); - return Err(e); - } - } - } - CrossDomainJob::AddReadPipe(read_pipe_id) => { - let items = self.item_state.lock().unwrap(); - let item = items - .table - .get(&read_pipe_id) - .ok_or(RutabagaError::InvalidCrossDomainItemId)?; - - match item { - CrossDomainItem::WaylandReadPipe(file) => self - .wait_ctx - .add(CrossDomainToken::WaylandReadPipe(read_pipe_id), file)?, - _ => 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)?; - } - CrossDomainJob::Finish => return Ok(()), - } - } - - Ok(()) - } -} - -impl CrossDomain { - /// Initializes the cross-domain component by taking the the rutabaga channels (if any) and - /// initializing rutabaga gralloc. - pub fn init( - channels: Option>, - fence_handler: RutabagaFenceHandler, - export_table: Option, - ) -> RutabagaResult> { - let gralloc = RutabagaGralloc::new()?; - Ok(Box::new(CrossDomain { - channels, - gralloc: Arc::new(Mutex::new(gralloc)), - fence_handler, - export_table, - })) - } -} - -impl CrossDomainContext { - fn initialize(&mut self, cmd_init: &CrossDomainInitV1) -> RutabagaResult<()> { - if !self - .context_resources - .lock() - .unwrap() - .contains_key(&cmd_init.query_ring_id) - { - return Err(RutabagaError::InvalidResourceId); - } - - let query_ring_id = cmd_init.query_ring_id; - 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 { - if !self - .context_resources - .lock() - .unwrap() - .contains_key(&cmd_init.channel_ring_id) - { - return Err(RutabagaError::InvalidResourceId); - } - - let connection = self.get_connection(cmd_init)?; - - let (kill_evt, thread_kill_evt) = channel()?; - let (resample_evt, thread_resample_evt) = channel()?; - - let mut wait_ctx = WaitContext::new()?; - match &connection { - Some(connection) => { - wait_ctx.add(CrossDomainToken::ContextChannel, connection)?; - } - None => return Err(RutabagaError::Unsupported), - }; - - let state = Arc::new(CrossDomainState::new( - query_ring_id, - channel_ring_id, - context_resources, - futexes, - connection, - )); - - let thread_state = state.clone(); - let thread_items = self.item_state.clone(); - let thread_fence_handler = self.fence_handler.clone(); - - let worker_result = thread::Builder::new() - .name("cross domain".to_string()) - .spawn(move || -> RutabagaResult<()> { - CrossDomainWorker::new( - wait_ctx, - thread_state, - thread_items, - thread_fence_handler, - protocol_version, - ) - .run(thread_kill_evt, thread_resample_evt) - }); - - self.worker_thread = Some(worker_result.unwrap()); - self.state = Some(state); - self.resample_evt = Some(resample_evt); - self.kill_evt = Some(kill_evt); - } else { - self.state = Some(Arc::new(CrossDomainState::new( - query_ring_id, - channel_ring_id, - context_resources, - futexes, - None, - ))); - } - - self.protocol_version = protocol_version; - - Ok(()) - } - - fn get_image_requirements( - &mut self, - cmd_get_reqs: &CrossDomainGetImageRequirements, - ) -> RutabagaResult<()> { - let info = ImageAllocationInfo { - width: cmd_get_reqs.width, - height: cmd_get_reqs.height, - drm_format: DrmFormat::from(cmd_get_reqs.drm_format), - flags: RutabagaGrallocFlags::new(cmd_get_reqs.flags), - }; - - let reqs = self - .gralloc - .lock() - .unwrap() - .get_image_memory_requirements(info)?; - - let mut response = CrossDomainImageRequirements { - strides: reqs.strides, - offsets: reqs.offsets, - modifier: reqs.modifier, - size: reqs.size, - blob_id: 0, - map_info: reqs.map_info, - memory_idx: -1, - physical_device_idx: -1, - }; - - if let Some(ref vk_info) = reqs.vulkan_info { - response.memory_idx = vk_info.memory_idx as i32; - // We return -1 for now since physical_device_idx is deprecated. If this backend is - // put back into action, it should be using device_id from the request instead. - response.physical_device_idx = -1; - } - - if let Some(state) = &self.state { - response.blob_id = add_item(&self.item_state, CrossDomainItem::ImageRequirements(reqs)); - state.write_to_ring(RingWrite::Write(response, None), state.query_ring_id)?; - Ok(()) - } else { - Err(RutabagaError::InvalidCrossDomainState) - } - } - - #[cfg(feature = "x")] - fn futex_signal(&mut self, cmd_futex_signal: &CrossDomainFutexSignal) -> RutabagaResult<()> { - let futexes = self.futexes.lock().unwrap(); - if let Some(ftx) = futexes.get(&cmd_futex_signal.id) { - let uaddr = ftx.address.0; - let op = FUTEX_WAKE_BITSET; - let val = c_int::MAX; - let timeout = ptr::null::<()>(); - let uaddr2 = ptr::null::<()>(); - let val3 = !1u32; - unsafe { - libc::syscall(libc::SYS_futex, uaddr, op, val, timeout, uaddr2, val3); - } - Ok(()) - } else { - Err(RutabagaError::InvalidCrossDomainItemId) - } - } - - #[cfg(feature = "x")] - fn futex_destroy(&mut self, cmd_futex_destroy: &CrossDomainFutexDestroy) -> RutabagaResult<()> { - let mut futexes = self.futexes.lock().unwrap(); - futexes - .get_mut(&cmd_futex_destroy.id) - .ok_or(RutabagaError::InvalidCrossDomainItemId)? - .shutdown(); - Ok(()) - } - - #[cfg(feature = "x")] - fn futex_new(&mut self, cmd_futex_new: &CrossDomainFutexNew) -> RutabagaResult<()> { - let exports = self - .export_table - .as_ref() - .ok_or(RutabagaError::InvalidCrossDomainItemId)? - .lock() - .unwrap(); - - let mut futexes = self.futexes.lock().unwrap(); - if futexes.contains_key(&cmd_futex_new.id) { - return Err(RutabagaError::AlreadyInUse); - } - - let file = exports - .get(&(cmd_futex_new.fs_id, cmd_futex_new.handle)) - .ok_or(RutabagaError::InvalidCrossDomainItemId)? - .try_clone()?; - - let handle = SafeDescriptor::from(file); - let address = unsafe { - mmap( - None, - 4.try_into().unwrap(), - ProtFlags::PROT_WRITE | ProtFlags::PROT_READ, - MapFlags::MAP_SHARED, - handle.as_fd(), - 0, - )? - }; - - let shutdown = Arc::new(AtomicBool::new(false)); - let (ftx_evt, thread_ftx_evt) = channel()?; - - let thread_ftx_evt = Arc::new(thread_ftx_evt); - - let id = cmd_futex_new.id; - - let state = self - .state - .as_ref() - .ok_or(RutabagaError::InvalidCrossDomainState)?; - state.add_job(CrossDomainJob::AddFutex(id, thread_ftx_evt.clone())); - - let shutdown2 = shutdown.clone(); - let fptr = FutexPtr(address); - let initial_value = - unsafe { AtomicU32::from_ptr(address.as_ptr() as *mut u32) }.load(Ordering::SeqCst); - let watcher_thread = Some( - thread::Builder::new() - .name(format!("futexw {}", cmd_futex_new.id)) - .spawn(move || { - CrossDomainFutex::watcher_thread(fptr, shutdown2, ftx_evt, initial_value); - })?, - ); - - futexes.insert( - id, - CrossDomainFutex { - handle, - address: FutexPtr(address), - watcher_thread, - shutdown, - evt: thread_ftx_evt, - }, - ); - - 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(); - - // Most of the time, hang-up and writing will be paired. In lieu of this, remove the - // item rather than getting a reference. In case of an error, there's not much to do - // besides reporting it. - let item = items - .table - .remove(&cmd_write.identifier) - .ok_or(RutabagaError::InvalidCrossDomainItemId)?; - - let len: usize = cmd_write.opaque_data_size.try_into()?; - match item { - CrossDomainItem::WaylandWritePipe(file) => { - if len != 0 { - write_volatile(&file, opaque_data)?; - } - - if cmd_write.hang_up == 0 { - items.table.insert( - cmd_write.identifier, - CrossDomainItem::WaylandWritePipe(file), - ); - } - - Ok(()) - } - _ => Err(RutabagaError::InvalidCrossDomainItemType), - } - } - - fn process_cmd_send( - &mut self, - commands: &mut [u8], - ) -> RutabagaResult<()> { - let opaque_data_offset = size_of::(); - let (mut cmd_send, _) = T::read_from_prefix(commands.as_bytes()) - .map_err(|_| 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::(&mut cmd_send, opaque_data)?; - Ok(()) - } -} - -impl Drop for CrossDomainContext { - fn drop(&mut self) { - if let Some(state) = &self.state { - state.add_job(CrossDomainJob::Finish); - } - - if let Some(kill_evt) = self.kill_evt.take() { - // Log the error, but still try to join the worker thread - match channel_signal(&kill_evt) { - Ok(_) => (), - Err(e) => { - error!("failed to write cross domain kill event: {e}"); - } - } - - if let Some(worker_thread) = self.worker_thread.take() { - let _ = worker_thread.join(); - } - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Default, IntoBytes, Immutable, FromBytes)] -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, - resource_id: u32, - resource_create_blob: ResourceCreateBlob, - handle_opt: Option, - ) -> RutabagaResult { - let item_id = resource_create_blob.blob_id as u32; - - let mut items = self.item_state.lock().unwrap(); - let item = items - .table - .get_mut(&item_id) - .ok_or(RutabagaError::InvalidCrossDomainItemId)?; - - // Items that are kept in the table after usage. - if let CrossDomainItem::ImageRequirements(reqs) = item { - if reqs.size != resource_create_blob.size { - return Err(RutabagaError::SpecViolation("blob size mismatch")); - } - - // Strictly speaking, it's against the virtio-gpu spec to allocate memory in the context - // create blob function, which says "the actual allocation is done via - // VIRTIO_GPU_CMD_SUBMIT_3D." However, atomic resource creation is easiest for the - // cross-domain use case, so whatever. - let hnd = match handle_opt { - Some(handle) => handle, - None => self.gralloc.lock().unwrap().allocate_memory(*reqs)?, - }; - - let info_3d = Resource3DInfo { - width: reqs.info.width, - height: reqs.info.height, - drm_fourcc: reqs.info.drm_format.into(), - strides: reqs.strides, - offsets: reqs.offsets, - modifier: reqs.modifier, - }; - - // Keep ImageRequirements items and return immediately, since they can be used for subsequent allocations. - return Ok(RutabagaResource { - resource_id, - handle: Some(Arc::new(hnd)), - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: Some(reqs.map_info | RUTABAGA_MAP_ACCESS_RW), - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: Some(info_3d), - vulkan_info: reqs.vulkan_info, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::CrossDomain as u8), - size: resource_create_blob.size, - mapping: None, - }); - } - - let item = items - .table - .remove(&item_id) - .ok_or(RutabagaError::InvalidCrossDomainItemId)?; - - fn access_mode(descriptor: &SafeDescriptor) -> RutabagaResult { - Ok( - match fcntl(descriptor.as_fd(), FcntlArg::F_GETFL)? & libc::O_ACCMODE { - libc::O_RDWR => RUTABAGA_MAP_ACCESS_RW, - libc::O_WRONLY => RUTABAGA_MAP_ACCESS_WRITE, - _ => RUTABAGA_MAP_ACCESS_READ, - // (there is a "secret fourth option" O_WRONLY|O_RDWR meaning "no access", very unlikely we'd see it) - }, - ) - } - - // Items that are removed from the table after one usage. - match item { - CrossDomainItem::ShmBlob(descriptor) => { - #[allow(unused_mut)] - let mut access = access_mode(&descriptor)?; - // Some compositors actually do send descriptors that are O_RDWR but write-sealed (!) - #[cfg(target_os = "linux")] - if fcntl(&descriptor, FcntlArg::F_GET_SEALS)? & libc::F_SEAL_WRITE != 0 { - access &= !RUTABAGA_MAP_ACCESS_WRITE; - } - - let hnd = RutabagaHandle { - os_handle: descriptor, - handle_type: RUTABAGA_MEM_HANDLE_TYPE_SHM, - }; - - Ok(RutabagaResource { - resource_id, - handle: Some(Arc::new(hnd)), - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: Some(RUTABAGA_MAP_CACHE_CACHED | access), - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: None, - vulkan_info: None, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::CrossDomain as u8), - size: resource_create_blob.size, - mapping: None, - }) - } - CrossDomainItem::DmaBuf(descriptor) => { - let access = access_mode(&descriptor)?; - - let hnd = RutabagaHandle { - os_handle: descriptor, - handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, - }; - - Ok(RutabagaResource { - resource_id, - handle: Some(Arc::new(hnd)), - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: Some(RUTABAGA_MAP_CACHE_CACHED | access), - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: None, - vulkan_info: None, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::CrossDomain as u8), - size: resource_create_blob.size, - mapping: None, - }) - } - _ => Err(RutabagaError::InvalidCrossDomainItemType), - } - } - - fn submit_cmd(&mut self, mut commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> { - if !fence_ids.is_empty() { - return Err(RutabagaError::Unsupported); - } - - while !commands.is_empty() { - let (hdr, _) = CrossDomainHeader::read_from_prefix(commands) - .map_err(|_e| RutabagaError::InvalidCommandBuffer)?; - - match hdr.cmd { - CROSS_DOMAIN_CMD_INIT => { - let cmd_init = CrossDomainInitV1::read_from_prefix(commands.as_bytes()) - .map(|(x, _)| x) - .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()) - }) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - self.initialize(&cmd_init)?; - } - CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS => { - let (cmd_get_reqs, _) = - CrossDomainGetImageRequirements::read_from_prefix(commands.as_bytes()) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - - self.get_image_requirements(&cmd_get_reqs)?; - } - CROSS_DOMAIN_CMD_SEND => { - if self.protocol_version == 0 { - self.process_cmd_send::(commands)?; - } else { - self.process_cmd_send::(commands)?; - } - } - CROSS_DOMAIN_CMD_POLL => { - // Actual polling is done in the subsequent when creating a fence. - } - CROSS_DOMAIN_CMD_WRITE => { - let opaque_data_offset = size_of::(); - let (cmd_write, _) = - CrossDomainReadWrite::read_from_prefix(commands.as_bytes()) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - - let opaque_data = commands - .get_mut( - opaque_data_offset - ..opaque_data_offset + cmd_write.opaque_data_size as usize, - ) - .ok_or(RutabagaError::InvalidCommandSize( - cmd_write.opaque_data_size as usize, - ))?; - - self.write(&cmd_write, opaque_data)?; - } - #[cfg(feature = "x")] - CROSS_DOMAIN_CMD_FUTEX_NEW => { - let (cmd_new_futex, _) = - CrossDomainFutexNew::read_from_prefix(commands.as_bytes()) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - self.futex_new(&cmd_new_futex)?; - } - #[cfg(feature = "x")] - CROSS_DOMAIN_CMD_FUTEX_SIGNAL => { - let (cmd_futex_signal, _) = - CrossDomainFutexSignal::read_from_prefix(commands.as_bytes()) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - self.futex_signal(&cmd_futex_signal)?; - } - #[cfg(feature = "x")] - CROSS_DOMAIN_CMD_FUTEX_DESTROY => { - let (cmd_futex_destroy, _) = - CrossDomainFutexDestroy::read_from_prefix(commands.as_bytes()) - .map_err(|_| 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()) - .map_err(|_| RutabagaError::InvalidCommandBuffer)?; - self.read_eventfd_new(&cmd_new_efd)?; - } - _ => return Err(RutabagaError::SpecViolation("invalid cross domain command")), - } - - commands = commands - .get_mut(hdr.cmd_size as usize..) - .ok_or(RutabagaError::InvalidCommandSize(hdr.cmd_size as usize))?; - } - - Ok(()) - } - - fn attach(&mut self, resource: &mut RutabagaResource) { - if resource.blob_mem == RUTABAGA_BLOB_MEM_GUEST { - self.context_resources.lock().unwrap().insert( - resource.resource_id, - CrossDomainResource { - handle: None, - backing_iovecs: resource.backing_iovecs.take(), - }, - ); - } else if let Some(ref handle) = resource.handle { - self.context_resources.lock().unwrap().insert( - resource.resource_id, - CrossDomainResource { - handle: Some(handle.clone()), - backing_iovecs: None, - }, - ); - } - } - - fn detach(&mut self, resource: &RutabagaResource) { - self.context_resources - .lock() - .unwrap() - .remove(&resource.resource_id); - } - - fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - match fence.ring_idx as u32 { - CROSS_DOMAIN_QUERY_RING => self.fence_handler.call(fence), - CROSS_DOMAIN_CHANNEL_RING => { - if let Some(state) = &self.state { - state.add_job(CrossDomainJob::HandleFence(fence)); - } - } - _ => return Err(RutabagaError::SpecViolation("unexpected ring type")), - } - - Ok(()) - } - - fn component_type(&self) -> RutabagaComponentType { - RutabagaComponentType::CrossDomain - } -} - -impl RutabagaComponent for CrossDomain { - fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) { - (0u32, size_of::() as u32) - } - - fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec { - let mut caps: CrossDomainCapabilities = Default::default(); - if let Some(ref channels) = self.channels { - for channel in channels { - caps.supported_channels |= 1 << channel.channel_type; - } - } - - if self.gralloc.lock().unwrap().supports_dmabuf() { - caps.supports_dmabuf = 1; - } - - if self.gralloc.lock().unwrap().supports_external_gpu_memory() { - caps.supports_external_gpu_memory = 1; - } - - // Version 1 supports all commands up to and including CROSS_DOMAIN_CMD_WRITE. - caps.version = 1; - caps.as_bytes().to_vec() - } - - fn create_blob( - &mut self, - _ctx_id: u32, - resource_id: u32, - resource_create_blob: ResourceCreateBlob, - iovec_opt: Option>, - _handle_opt: Option, - ) -> RutabagaResult { - if resource_create_blob.blob_mem != RUTABAGA_BLOB_MEM_GUEST - && resource_create_blob.blob_flags != RUTABAGA_BLOB_FLAG_USE_MAPPABLE - { - return Err(RutabagaError::SpecViolation( - "expected only guest memory blobs", - )); - } - - Ok(RutabagaResource { - resource_id, - handle: None, - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: None, - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: None, - vulkan_info: None, - backing_iovecs: iovec_opt, - component_mask: 1 << (RutabagaComponentType::CrossDomain as u8), - size: resource_create_blob.size, - mapping: None, - }) - } - - fn create_context( - &self, - _ctx_id: u32, - _context_init: u32, - _context_name: Option<&str>, - fence_handler: RutabagaFenceHandler, - ) -> RutabagaResult> { - Ok(Box::new(CrossDomainContext { - channels: self.channels.clone(), - gralloc: self.gralloc.clone(), - state: None, - context_resources: Arc::new(Mutex::new(Default::default())), - item_state: Arc::new(Mutex::new(Default::default())), - fence_handler, - worker_thread: None, - resample_evt: None, - kill_evt: None, - export_table: self.export_table.clone(), - futexes: Arc::new(Mutex::new(Default::default())), - protocol_version: 0, - })) - } - - // With "drm/virtio: Conditionally allocate virtio_gpu_fence" in the kernel, global fences for - // cross-domain aren't created. However, that change is projected to land in the v6.6 kernel. - // For older kernels, signal the fence immediately on creation. - fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - self.fence_handler.call(fence); - Ok(()) - } -} diff --git a/src/rutabaga_gfx/src/cross_domain/sys/epoll_internal.rs b/src/rutabaga_gfx/src/cross_domain/sys/epoll_internal.rs deleted file mode 100644 index 5c2c2206c..000000000 --- a/src/rutabaga_gfx/src/cross_domain/sys/epoll_internal.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::mem; -use std::os::unix::io::AsFd; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::FromRawFd; -use std::os::unix::io::OwnedFd; - -use libc::c_int; -use nix::errno::Errno; -use nix::sys::epoll::EpollCreateFlags; -use nix::sys::epoll::EpollFlags; -use nix::sys::epoll::EpollOp; -use nix::Result; - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[repr(transparent)] -pub struct EpollEvent { - event: libc::epoll_event, -} - -impl EpollEvent { - pub fn new(events: EpollFlags, data: u64) -> Self { - EpollEvent { - event: libc::epoll_event { - events: events.bits() as u32, - u64: data, - }, - } - } - - pub fn empty() -> Self { - unsafe { mem::zeroed::() } - } - - pub fn events(&self) -> EpollFlags { - EpollFlags::from_bits(self.event.events as c_int).unwrap() - } - - pub fn data(&self) -> u64 { - self.event.u64 - } -} - -// This is a function is unreleased nix 0.27 -- when it is released, we can delete this. -#[derive(Debug)] -pub struct Epoll(pub OwnedFd); -impl Epoll { - /// Creates a new epoll instance and returns a file descriptor referring to that instance. - /// - /// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html). - pub fn new(flags: EpollCreateFlags) -> Result { - let res = unsafe { libc::epoll_create1(flags.bits()) }; - let fd = Errno::result(res)?; - let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) }; - Ok(Self(owned_fd)) - } - /// Add an entry to the interest list of the epoll file descriptor for - /// specified in events. - /// - /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`. - pub fn add(&self, fd: Fd, mut event: EpollEvent) -> Result<()> { - self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event) - } - /// Remove (deregister) the target file descriptor `fd` from the interest list. - /// - /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` . - pub fn delete(&self, fd: Fd) -> Result<()> { - self.epoll_ctl(EpollOp::EpollCtlDel, fd, None) - } - /// Change the settings associated with `fd` in the interest list to the new settings specified - /// in `event`. - /// - /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`. - #[allow(dead_code)] - pub fn modify(&self, fd: Fd, event: &mut EpollEvent) -> Result<()> { - self.epoll_ctl(EpollOp::EpollCtlMod, fd, event) - } - /// Waits for I/O events, blocking the calling thread if no events are currently available. - /// (This can be thought of as fetching items from the ready list of the epoll instance.) - /// - /// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html) - pub fn wait(&self, events: &mut [EpollEvent], timeout: isize) -> Result { - let res = unsafe { - libc::epoll_wait( - self.0.as_raw_fd(), - events.as_mut_ptr() as *mut libc::epoll_event, - events.len() as c_int, - timeout as c_int, - ) - }; - - Errno::result(res).map(|r| r as usize) - } - /// This system call is used to add, modify, or remove entries in the interest list of the epoll - /// instance referred to by `self`. It requests that the operation `op` be performed for the - /// target file descriptor, `fd`. - /// - /// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`]. - /// - /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) - fn epoll_ctl<'a, Fd: AsFd, T>(&self, op: EpollOp, fd: Fd, event: T) -> Result<()> - where - T: Into>, - { - let event: Option<&mut EpollEvent> = event.into(); - let ptr = event - .map(|x| &mut x.event as *mut libc::epoll_event) - .unwrap_or(std::ptr::null_mut()); - unsafe { - Errno::result(libc::epoll_ctl( - self.0.as_raw_fd(), - op as c_int, - fd.as_fd().as_raw_fd(), - ptr, - )) - .map(drop) - } - } -} diff --git a/src/rutabaga_gfx/src/cross_domain/sys/mod.rs b/src/rutabaga_gfx/src/cross_domain/sys/mod.rs deleted file mode 100644 index f42316e28..000000000 --- a/src/rutabaga_gfx/src/cross_domain/sys/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { - pub(crate) mod unix; - mod epoll_internal; - use unix as platform; - } else if #[cfg(any(target_os = "fuchsia",target_os = "windows", target_os = "macos"))] { - pub(crate) mod stub; - use stub as platform; - } else { - compile_error!("Unsupported platform"); - } -} - -pub use platform::channel; -pub use platform::channel_signal; -pub use platform::channel_wait; -pub use platform::read_volatile; -pub use platform::write_volatile; -pub use platform::Receiver; -pub use platform::Sender; -pub use platform::SystemStream; -pub use platform::WaitContext; diff --git a/src/rutabaga_gfx/src/cross_domain/sys/stub.rs b/src/rutabaga_gfx/src/cross_domain/sys/stub.rs deleted file mode 100644 index 150631f2c..000000000 --- a/src/rutabaga_gfx/src/cross_domain/sys/stub.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::fs::File; -use std::sync::Arc; - -use super::super::cross_domain_protocol::CrossDomainInitV1; -use super::super::cross_domain_protocol::CrossDomainSendReceiveBase; -use super::super::CrossDomainContext; -use super::super::CrossDomainState; -use crate::cross_domain::CrossDomainEvent; -use crate::cross_domain::CrossDomainToken; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -pub struct Stub(()); -pub type SystemStream = Stub; - -impl CrossDomainState { - pub(crate) fn receive_msg( - &self, - _opaque_data: &mut [u8], - ) -> RutabagaResult<(usize, Vec)> { - Err(RutabagaError::Unsupported) - } -} - -impl CrossDomainContext { - pub(crate) fn get_connection( - &mut self, - _cmd_init: &CrossDomainInitV1, - ) -> RutabagaResult> { - Err(RutabagaError::Unsupported) - } - - pub(crate) fn send( - &self, - _cmd_send: &mut T, - _opaque_data: &[u8], - ) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } -} - -pub type Sender = Stub; -pub type Receiver = Stub; - -pub fn channel_signal(_sender: &Sender) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) -} - -pub fn channel_wait(_receiver: &Receiver) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) -} - -pub fn read_volatile(_file: &File, _opaque_data: &mut [u8]) -> RutabagaResult { - Err(RutabagaError::Unsupported) -} - -pub fn write_volatile(_file: &File, _opaque_data: &[u8]) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) -} - -pub fn channel() -> RutabagaResult<(Sender, Receiver)> { - Err(RutabagaError::Unsupported) -} - -pub type WaitContext = Stub; - -pub trait WaitTrait {} -impl WaitTrait for Stub {} -impl WaitTrait for &Stub {} -impl WaitTrait for Arc {} -impl WaitTrait for File {} -impl WaitTrait for &File {} -impl WaitTrait for &mut File {} - -impl WaitContext { - pub fn new() -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - pub fn add( - &mut self, - _token: CrossDomainToken, - _waitable: Waitable, - ) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } - - pub fn wait(&mut self) -> RutabagaResult> { - Err(RutabagaError::Unsupported) - } - - pub fn delete( - &mut self, - _token: CrossDomainToken, - _waitable: Waitable, - ) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } -} diff --git a/src/rutabaga_gfx/src/cross_domain/sys/unix.rs b/src/rutabaga_gfx/src/cross_domain/sys/unix.rs deleted file mode 100644 index e889456e7..000000000 --- a/src/rutabaga_gfx/src/cross_domain/sys/unix.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::fs::File; -use std::io::IoSlice; -use std::io::IoSliceMut; -use std::os::unix::io::AsRawFd; -use std::os::unix::prelude::AsFd; - -use nix::cmsg_space; -use nix::sys::epoll::EpollCreateFlags; -use nix::sys::epoll::EpollFlags; -use nix::sys::eventfd::EfdFlags; -use nix::sys::eventfd::EventFd; -use nix::sys::socket::connect; -use nix::sys::socket::recvmsg; -use nix::sys::socket::sendmsg; -use nix::sys::socket::socket; -use nix::sys::socket::AddressFamily; -use nix::sys::socket::ControlMessage; -use nix::sys::socket::ControlMessageOwned; -use nix::sys::socket::MsgFlags; -use nix::sys::socket::SockFlag; -use nix::sys::socket::SockType; -use nix::sys::socket::UnixAddr; -use nix::unistd::pipe; -use nix::unistd::read; -use nix::unistd::write; - -use super::super::add_item; -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::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::{ - CrossDomainInitV1, CrossDomainSendReceiveBase, CROSS_DOMAIN_ID_TYPE_SHM, -}; -use crate::cross_domain::CrossDomainEvent; -use crate::cross_domain::CrossDomainToken; -use crate::cross_domain::WAIT_CONTEXT_MAX; -use crate::rutabaga_os::AsRawDescriptor; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::RutabagaError; -use crate::RutabagaResult; - -pub type SystemStream = File; - -impl CrossDomainState { - fn send_msg(&self, opaque_data: &[u8], descriptors: &[RawDescriptor]) -> RutabagaResult { - let cmsg = ControlMessage::ScmRights(descriptors); - if let Some(connection) = &self.connection { - let bytes_sent = sendmsg::<()>( - connection.as_raw_descriptor(), - &[IoSlice::new(opaque_data)], - &[cmsg], - MsgFlags::empty(), - None, - )?; - - return Ok(bytes_sent); - } - - Err(RutabagaError::InvalidCrossDomainChannel) - } - - pub(crate) fn receive_msg( - &self, - opaque_data: &mut [u8], - ) -> RutabagaResult<(usize, Vec)> { - // 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; MAX_IDENTIFIERS]); - let flags = MsgFlags::empty(); - - if let Some(connection) = &self.connection { - let r = recvmsg::<()>( - connection.as_raw_descriptor(), - &mut iovecs, - Some(&mut cmsgspace), - flags, - )?; - let len = r.bytes; - - let files = match r.cmsgs()?.next() { - Some(ControlMessageOwned::ScmRights(fds)) => { - fds.into_iter() - .map(|fd| { - // Safe since the descriptors from recv_with_fds(..) are owned by us and valid. - unsafe { File::from_raw_descriptor(fd) } - }) - .collect() - } - Some(_) => return Err(RutabagaError::Unsupported), - None => Vec::new(), - }; - - Ok((len, files)) - } else { - Err(RutabagaError::InvalidCrossDomainChannel) - } - } -} - -impl CrossDomainContext { - pub(crate) fn get_connection( - &mut self, - cmd_init: &CrossDomainInitV1, - ) -> RutabagaResult> { - let channels = self - .channels - .take() - .ok_or(RutabagaError::InvalidCrossDomainChannel)?; - let base_channel = &channels - .iter() - .find(|channel| channel.channel_type == cmd_init.channel_type) - .ok_or(RutabagaError::InvalidCrossDomainChannel)? - .base_channel; - - let socket_fd = socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::SOCK_CLOEXEC, - None, - )?; - - let unix_addr = UnixAddr::new(base_channel)?; - connect(socket_fd.as_raw_fd(), &unix_addr)?; - let stream = socket_fd.into(); - Ok(Some(stream)) - } - - pub(crate) fn send( - &self, - cmd_send: &mut T, - opaque_data: &[u8], - ) -> RutabagaResult<()> { - let mut descriptors = [0; MAX_IDENTIFIERS]; - - let mut write_pipe_opt: Option = None; - let mut read_pipe_id_opt: Option = None; - - let num_identifiers = (*cmd_send.num_identifiers_mut()).try_into()?; - - if num_identifiers > MAX_IDENTIFIERS { - return Err(RutabagaError::SpecViolation( - "max cross domain identifiers exceeded", - )); - } - - let iter = cmd_send - .iter_over_identifiers() - .zip(descriptors.iter_mut()) - .take(num_identifiers); - - for ((identifier, identifier_type, _), descriptor) in iter { - if *identifier_type == CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB { - let context_resources = self.context_resources.lock().unwrap(); - - let context_resource = context_resources - .get(identifier) - .ok_or(RutabagaError::InvalidResourceId)?; - - if let Some(ref handle) = context_resource.handle { - *descriptor = handle.os_handle.as_raw_descriptor(); - } else { - return Err(RutabagaError::InvalidRutabagaHandle); - } - } else if *identifier_type == CROSS_DOMAIN_ID_TYPE_READ_PIPE { - // In practice, just 1 pipe pair per send is observed. If we encounter - // more, this can be changed later. - if write_pipe_opt.is_some() { - return Err(RutabagaError::SpecViolation("expected just one pipe pair")); - } - - let (raw_read_pipe, raw_write_pipe) = pipe()?; - let read_pipe = File::from(raw_read_pipe); - let write_pipe = File::from(raw_write_pipe); - - *descriptor = write_pipe.as_raw_descriptor(); - let read_pipe_id: u32 = add_item( - &self.item_state, - CrossDomainItem::WaylandReadPipe(read_pipe), - ); - - // For Wayland read pipes, the guest guesses which identifier the host will use to - // avoid waiting for the host to generate one. Validate guess here. This works - // because of the way Sommelier copy + paste works. If the Sommelier sequence of events - // changes, it's always possible to wait for the host response. - if read_pipe_id != *identifier { - return Err(RutabagaError::InvalidCrossDomainItemId); - } - - // The write pipe needs to be dropped after the send_msg(..) call is complete, so the read pipe - // can receive subsequent hang-up events. - write_pipe_opt = Some(write_pipe); - read_pipe_id_opt = Some(read_pipe_id); - } else if *identifier_type == CROSS_DOMAIN_ID_TYPE_SHM { - if let Some(ftx) = self.futexes.lock().unwrap().get(identifier) { - *descriptor = ftx.handle.as_raw_descriptor(); - } else { - return Err(RutabagaError::InvalidCrossDomainItemId); - } - } else { - // Don't know how to handle anything else yet. - return Err(RutabagaError::InvalidCrossDomainItemType); - } - } - - if let (Some(state), Some(resample_evt)) = (&self.state, &self.resample_evt) { - state.send_msg(opaque_data, &descriptors[..num_identifiers])?; - - if let Some(read_pipe_id) = read_pipe_id_opt { - state.add_job(CrossDomainJob::AddReadPipe(read_pipe_id)); - channel_signal(resample_evt)?; - } - } else { - return Err(RutabagaError::InvalidCrossDomainState); - } - - Ok(()) - } -} - -pub type Sender = EventFd; -pub type Receiver = File; - -pub fn channel_signal(sender: &Sender) -> RutabagaResult<()> { - sender.write(1)?; - Ok(()) -} - -pub fn channel_wait(receiver: &Receiver) -> RutabagaResult<()> { - read(receiver, &mut 1u64.to_ne_bytes())?; - Ok(()) -} - -pub fn read_volatile(file: &File, opaque_data: &mut [u8]) -> RutabagaResult { - let bytes_read = read(file, opaque_data)?; - Ok(bytes_read) -} - -pub fn write_volatile(file: &File, opaque_data: &[u8]) -> RutabagaResult<()> { - write(file, opaque_data)?; - Ok(()) -} - -pub fn channel() -> RutabagaResult<(Sender, Receiver)> { - let sender = EventFd::from_flags(EfdFlags::empty())?; - let receiver = sender.as_fd().try_clone_to_owned()?.into(); - Ok((sender, receiver)) -} - -pub struct WaitContext { - epoll_ctx: Epoll, - data: u64, - vec: Vec<(u64, CrossDomainToken)>, -} - -impl WaitContext { - pub fn new() -> RutabagaResult { - let epoll = Epoll::new(EpollCreateFlags::empty())?; - Ok(WaitContext { - epoll_ctx: epoll, - data: 0, - vec: Default::default(), - }) - } - - pub fn add( - &mut self, - token: CrossDomainToken, - waitable: Waitable, - ) -> RutabagaResult<()> { - self.data += 1; - self.epoll_ctx - .add(waitable, EpollEvent::new(EpollFlags::EPOLLIN, self.data))?; - self.vec.push((self.data, token)); - Ok(()) - } - - fn calculate_token(&self, data: u64) -> RutabagaResult { - if let Some(item) = self.vec.iter().find(|item| item.0 == data) { - return Ok(item.1); - } - - Err(RutabagaError::SpecViolation("unable to find token")) - } - - pub fn wait(&mut self) -> RutabagaResult> { - let mut events = [EpollEvent::empty(); WAIT_CONTEXT_MAX]; - let count = loop { - break match self.epoll_ctx.wait(&mut events, isize::MAX) { - Err(nix::errno::Errno::EINTR) => continue, - a => a, - }; - }?; - let events = events[0..count] - .iter() - .map(|e| CrossDomainEvent { - token: self.calculate_token(e.data()).unwrap(), - readable: e.events() & EpollFlags::EPOLLIN == EpollFlags::EPOLLIN, - hung_up: e.events() & EpollFlags::EPOLLHUP == EpollFlags::EPOLLHUP - || e.events() & EpollFlags::EPOLLRDHUP == EpollFlags::EPOLLRDHUP, - }) - .collect(); - - Ok(events) - } - - pub fn delete( - &mut self, - token: CrossDomainToken, - waitable: Waitable, - ) -> RutabagaResult<()> { - self.epoll_ctx.delete(waitable)?; - self.vec.retain(|item| item.1 != token); - Ok(()) - } -} diff --git a/src/rutabaga_gfx/src/generated/generate b/src/rutabaga_gfx/src/generated/generate deleted file mode 120000 index d4bb1f140..000000000 --- a/src/rutabaga_gfx/src/generated/generate +++ /dev/null @@ -1 +0,0 @@ -generate.py \ No newline at end of file diff --git a/src/rutabaga_gfx/src/generated/generate.py b/src/rutabaga_gfx/src/generated/generate.py deleted file mode 100755 index 457f11ce3..000000000 --- a/src/rutabaga_gfx/src/generated/generate.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2018 The ChromiumOS Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Generates bindings that are used gpu_renderer. - -A sysroot and virglrenderer source checkout is required. The defaults to the -root directory. -""" - -from __future__ import print_function -import argparse -import multiprocessing.pool -import os -import subprocess -import sys -import tempfile - -# Bright green. -PASS_COLOR = "\033[1;32m" -# Bright red. -FAIL_COLOR = "\033[1;31m" -# Default color. -END_COLOR = "\033[0m" - -verbose = False - - -def generate_module( - module_name, allowlist, blocklist, header, clang_args, lib_name, derive_default -): - args = [ - "bindgen", - "--no-layout-tests", - "--allowlist-function", - allowlist, - "--allowlist-var", - allowlist, - "--allowlist-type", - allowlist, - "--blocklist-function", - blocklist, - "--blocklist-item", - blocklist, - "--blocklist-type", - blocklist, - "--no-prepend-enum-name", - "-o", - module_name + "_bindings.rs", - ] - - if lib_name: - args.extend(["--raw-line", '#[cfg(feature = "{}")]'.format(module_name)]) - args.extend(["--raw-line", '#[link(name = "{}")] extern {{}}'.format(lib_name)]) - - if derive_default: - args.append("--with-derive-default") - - args.extend([header, "--"]) - args.extend(clang_args) - - if verbose: - print(" ".join(args)) - - if subprocess.Popen(args).wait() == 0: - return "pass" - else: - return "bindgen failed" - - -def download_virgl(src, dst, branch): - virgl_src = tempfile.TemporaryDirectory(prefix="virglrenderer-src") - - args = ["git", "clone"] - - if branch: - args.extend(["-b", branch]) - - args.extend([src, dst]) - - if verbose: - print(" ".join(args)) - - if subprocess.Popen(args).wait() == 0: - return True - else: - return False - - -def get_parser(): - """Gets the argument parser""" - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--sysroot", default="/", help="sysroot directory (default=%(default)s)") - parser.add_argument("--virglrenderer", help="virglrenderer src dir/repo (default=%(default)s)") - parser.add_argument( - "--virgl_branch", - default="master", - help="virglrenderer branch name (default=%(default)s)", - ) - parser.add_argument( - "--verbose", - "-v", - action="store_true", - help="enable verbose output (default=%(default)s)", - ) - return parser - - -def main(argv): - global verbose - os.chdir(os.path.dirname(sys.argv[0])) - opts = get_parser().parse_args(argv) - if opts.verbose: - verbose = True - - if opts.virglrenderer: - if "://" in opts.virglrenderer: - virgl_src_dir_temp = tempfile.TemporaryDirectory(prefix="virglrenderer-src") - virgl_src_dir = virgl_src_dir_temp.name - if not download_virgl(opts.virglrenderer, virgl_src_dir, opts.virgl_branch): - print("failed to clone '{}' to '{}'".format(virgl_src_dir, opts.virgl_branch)) - sys.exit(1) - else: - virgl_src_dir = opts.virglrenderer - - header = os.path.join(virgl_src_dir, "src/virglrenderer.h") - else: - header = os.path.join(opts.sysroot, "usr/include/virgl/virglrenderer.h") - - clang_args = [ - "-I", - os.path.join(opts.sysroot, "usr/include"), - "-D", - "VIRGL_RENDERER_UNSTABLE_APIS", - ] - - modules = ( - ( - "virgl_renderer", - "(virgl|VIRGL)_.+", # allowlist - ".*(va_list|debug_callback).*", # blocklist - header, - clang_args, - "virglrenderer", - True, - ), - ) - - pool = multiprocessing.pool.Pool(len(modules)) - results = pool.starmap(generate_module, modules, 1) - - return_fail = False - print("---") - print("generate module summary:") - for module, result in zip(modules, results): - result_color = FAIL_COLOR - if result == "pass": - result_color = PASS_COLOR - else: - return_fail = True - - print("%15s: %s%s%s" % (module[0], result_color, result, END_COLOR)) - - if return_fail: - sys.exit(1) - - with open("mod.rs", "w") as f: - print("/* generated by generate.py */", file=f) - print("#![allow(dead_code)]", file=f) - print("#![allow(non_camel_case_types)]", file=f) - print("#![allow(non_snake_case)]", file=f) - print("#![allow(non_upper_case_globals)]", file=f) - print("pub mod virgl_debug_callback_bindings;", file=f) - for module in modules: - print("pub mod", module[0] + "_bindings;", file=f) - - -if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) diff --git a/src/rutabaga_gfx/src/generated/mod.rs b/src/rutabaga_gfx/src/generated/mod.rs deleted file mode 100644 index 159f71707..000000000 --- a/src/rutabaga_gfx/src/generated/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* generated by generate.py */ -#![allow(dead_code)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -pub mod virgl_debug_callback_bindings; -pub mod virgl_renderer_bindings; diff --git a/src/rutabaga_gfx/src/generated/virgl_debug_callback_bindings.rs b/src/rutabaga_gfx/src/generated/virgl_debug_callback_bindings.rs deleted file mode 100644 index a6d4e3819..000000000 --- a/src/rutabaga_gfx/src/generated/virgl_debug_callback_bindings.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * automatically generated by rust-bindgen - * $ bindgen /usr/include/stdio.h \ - * --no-layout-tests \ - * --allowlist-function vsnprintf \ - * -- \ - * -target - */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub mod stdio { - extern "C" { - pub fn vsnprintf( - __s: *mut ::std::os::raw::c_char, - __maxlen: ::std::os::raw::c_ulong, - __format: *const ::std::os::raw::c_char, - __arg: *mut __va_list_tag, - ) -> ::std::os::raw::c_int; - } - #[repr(C)] - #[derive(Debug, Copy, Clone)] - pub struct __va_list_tag { - pub gp_offset: ::std::os::raw::c_uint, - pub fp_offset: ::std::os::raw::c_uint, - pub overflow_arg_area: *mut ::std::os::raw::c_void, - pub reg_save_area: *mut ::std::os::raw::c_void, - } - - pub type va_list = *mut __va_list_tag; -} -#[cfg(target_arch = "arm")] -pub mod stdio { - extern "C" { - pub fn vsnprintf( - __s: *mut ::std::os::raw::c_char, - __maxlen: ::std::os::raw::c_uint, - __format: *const ::std::os::raw::c_char, - __arg: __builtin_va_list, - ) -> ::std::os::raw::c_int; - } - pub type __builtin_va_list = __va_list; - #[repr(C)] - #[derive(Debug, Copy, Clone)] - pub struct __va_list { - pub __ap: *mut ::std::os::raw::c_void, - } - - pub type va_list = __builtin_va_list; -} -#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] -pub mod stdio { - extern "C" { - pub fn vsnprintf( - __s: *mut ::std::os::raw::c_char, - __maxlen: ::std::os::raw::c_ulong, - __format: *const ::std::os::raw::c_char, - __arg: __builtin_va_list, - ) -> ::std::os::raw::c_int; - } - pub type __builtin_va_list = __va_list; - #[repr(C)] - #[derive(Debug, Copy, Clone)] - pub struct __va_list { - pub __ap: *mut ::std::os::raw::c_void, - } - - pub type va_list = __builtin_va_list; -} - -pub type virgl_debug_callback_type = ::std::option::Option< - unsafe extern "C" fn(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list), ->; - -extern "C" { - pub fn virgl_set_debug_callback(cb: virgl_debug_callback_type) -> virgl_debug_callback_type; -} diff --git a/src/rutabaga_gfx/src/generated/virgl_renderer_bindings.rs b/src/rutabaga_gfx/src/generated/virgl_renderer_bindings.rs deleted file mode 100644 index 970e500d1..000000000 --- a/src/rutabaga_gfx/src/generated/virgl_renderer_bindings.rs +++ /dev/null @@ -1,447 +0,0 @@ -/* automatically generated by rust-bindgen 0.59.2 */ - -#[cfg(feature = "virgl_renderer")] -#[link(name = "virglrenderer")] -extern "C" {} - -pub const VIRGL_RENDERER_CALLBACKS_VERSION: u32 = 3; -pub const VIRGL_RENDERER_USE_EGL: u32 = 1; -pub const VIRGL_RENDERER_THREAD_SYNC: u32 = 2; -pub const VIRGL_RENDERER_USE_GLX: u32 = 4; -pub const VIRGL_RENDERER_USE_SURFACELESS: u32 = 8; -pub const VIRGL_RENDERER_USE_GLES: u32 = 16; -pub const VIRGL_RENDERER_USE_EXTERNAL_BLOB: u32 = 32; -pub const VIRGL_RENDERER_VENUS: u32 = 64; -pub const VIRGL_RENDERER_NO_VIRGL: u32 = 128; -pub const VIRGL_RENDERER_ASYNC_FENCE_CB: u32 = 256; -pub const VIRGL_RENDERER_RENDER_SERVER: u32 = 512; -pub const VIRGL_RES_BIND_DEPTH_STENCIL: u32 = 1; -pub const VIRGL_RES_BIND_RENDER_TARGET: u32 = 2; -pub const VIRGL_RES_BIND_SAMPLER_VIEW: u32 = 8; -pub const VIRGL_RES_BIND_VERTEX_BUFFER: u32 = 16; -pub const VIRGL_RES_BIND_INDEX_BUFFER: u32 = 32; -pub const VIRGL_RES_BIND_CONSTANT_BUFFER: u32 = 64; -pub const VIRGL_RES_BIND_STREAM_OUTPUT: u32 = 2048; -pub const VIRGL_RES_BIND_CURSOR: u32 = 65536; -pub const VIRGL_RES_BIND_CUSTOM: u32 = 131072; -pub const VIRGL_RES_BIND_SCANOUT: u32 = 262144; -pub const VIRGL_RES_BIND_SHARED: u32 = 1048576; -pub const VIRGL_RENDERER_CONTEXT_FLAG_CAPSET_ID_MASK: u32 = 255; -pub const VIRGL_RENDERER_BLOB_MEM_GUEST: u32 = 1; -pub const VIRGL_RENDERER_BLOB_MEM_HOST3D: u32 = 2; -pub const VIRGL_RENDERER_BLOB_MEM_HOST3D_GUEST: u32 = 3; -pub const VIRGL_RENDERER_BLOB_FLAG_USE_MAPPABLE: u32 = 1; -pub const VIRGL_RENDERER_BLOB_FLAG_USE_SHAREABLE: u32 = 2; -pub const VIRGL_RENDERER_BLOB_FLAG_USE_CROSS_DEVICE: u32 = 4; -pub const VIRGL_RENDERER_MAP_CACHE_MASK: u32 = 15; -pub const VIRGL_RENDERER_MAP_CACHE_NONE: u32 = 0; -pub const VIRGL_RENDERER_MAP_CACHE_CACHED: u32 = 1; -pub const VIRGL_RENDERER_MAP_CACHE_UNCACHED: u32 = 2; -pub const VIRGL_RENDERER_MAP_CACHE_WC: u32 = 3; -pub const VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF: u32 = 1; -pub const VIRGL_RENDERER_BLOB_FD_TYPE_OPAQUE: u32 = 2; -pub const VIRGL_RENDERER_BLOB_FD_TYPE_SHM: u32 = 3; -pub const VIRGL_RENDERER_BLOB_FD_TYPE_APPLE: u32 = 4; -pub const VIRGL_RENDERER_FENCE_FLAG_MERGEABLE: u32 = 1; -pub type __int32_t = ::std::os::raw::c_int; -pub type __uint32_t = ::std::os::raw::c_uint; -pub type __uint64_t = ::std::os::raw::c_ulong; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct virgl_box { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct iovec { - _unused: [u8; 0], -} -pub type virgl_renderer_gl_context = *mut ::std::os::raw::c_void; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_gl_ctx_param { - pub version: ::std::os::raw::c_int, - pub shared: bool, - pub major_ver: ::std::os::raw::c_int, - pub minor_ver: ::std::os::raw::c_int, -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_callbacks { - pub version: ::std::os::raw::c_int, - pub write_fence: ::std::option::Option< - unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, fence: u32), - >, - pub create_gl_context: ::std::option::Option< - unsafe extern "C" fn( - cookie: *mut ::std::os::raw::c_void, - scanout_idx: ::std::os::raw::c_int, - param: *mut virgl_renderer_gl_ctx_param, - ) -> virgl_renderer_gl_context, - >, - pub destroy_gl_context: ::std::option::Option< - unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, ctx: virgl_renderer_gl_context), - >, - pub make_current: ::std::option::Option< - unsafe extern "C" fn( - cookie: *mut ::std::os::raw::c_void, - scanout_idx: ::std::os::raw::c_int, - ctx: virgl_renderer_gl_context, - ) -> ::std::os::raw::c_int, - >, - pub get_drm_fd: ::std::option::Option< - unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int, - >, - pub write_context_fence: ::std::option::Option< - unsafe extern "C" fn( - cookie: *mut ::std::os::raw::c_void, - ctx_id: u32, - queue_id: u64, - fence_id: u64, - ), - >, - pub get_server_fd: ::std::option::Option< - unsafe extern "C" fn( - cookie: *mut ::std::os::raw::c_void, - version: u32, - ) -> ::std::os::raw::c_int, - >, -} -extern "C" { - pub fn virgl_renderer_init( - cookie: *mut ::std::os::raw::c_void, - flags: ::std::os::raw::c_int, - cb: *mut virgl_renderer_callbacks, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_poll(); -} -extern "C" { - pub fn virgl_renderer_get_cursor_data( - resource_id: u32, - width: *mut u32, - height: *mut u32, - ) -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn virgl_renderer_get_rect( - resource_id: ::std::os::raw::c_int, - iov: *mut iovec, - num_iovs: ::std::os::raw::c_uint, - offset: u32, - x: ::std::os::raw::c_int, - y: ::std::os::raw::c_int, - width: ::std::os::raw::c_int, - height: ::std::os::raw::c_int, - ); -} -extern "C" { - pub fn virgl_renderer_get_fd_for_texture( - tex_id: u32, - fd: *mut ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_get_fd_for_texture2( - tex_id: u32, - fd: *mut ::std::os::raw::c_int, - stride: *mut ::std::os::raw::c_int, - offset: *mut ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -pub const VIRGL_RENDERER_STRUCTURE_TYPE_NONE: virgl_renderer_structure_type_v0 = 0; -pub const VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY: virgl_renderer_structure_type_v0 = 1; -pub const VIRGL_RENDERER_STRUCTURE_TYPE_SUPPORTED_STRUCTURES: virgl_renderer_structure_type_v0 = 2; -pub type virgl_renderer_structure_type_v0 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_resource_create_args { - pub handle: u32, - pub target: u32, - pub format: u32, - pub bind: u32, - pub width: u32, - pub height: u32, - pub depth: u32, - pub array_size: u32, - pub last_level: u32, - pub nr_samples: u32, - pub flags: u32, -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_hdr { - pub stype: u32, - pub stype_version: u32, - pub size: u32, -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_export_query { - pub hdr: virgl_renderer_hdr, - pub in_resource_id: u32, - pub out_num_fds: u32, - pub in_export_fds: u32, - pub out_fourcc: u32, - pub pad: u32, - pub out_fds: [i32; 4usize], - pub out_strides: [u32; 4usize], - pub out_offsets: [u32; 4usize], - pub out_modifier: u64, -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_supported_structures { - pub hdr: virgl_renderer_hdr, - pub in_stype_version: u32, - pub out_supported_structures_mask: u32, -} -extern "C" { - pub fn virgl_renderer_resource_create( - args: *mut virgl_renderer_resource_create_args, - iov: *mut iovec, - num_iovs: u32, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_import_eglimage( - args: *mut virgl_renderer_resource_create_args, - image: *mut ::std::os::raw::c_void, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_unref(res_handle: u32); -} -extern "C" { - pub fn virgl_renderer_resource_set_priv(res_handle: u32, priv_: *mut ::std::os::raw::c_void); -} -extern "C" { - pub fn virgl_renderer_resource_get_priv(res_handle: u32) -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn virgl_renderer_context_create( - handle: u32, - nlen: u32, - name: *const ::std::os::raw::c_char, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_context_destroy(handle: u32); -} -extern "C" { - pub fn virgl_renderer_submit_cmd( - buffer: *mut ::std::os::raw::c_void, - ctx_id: ::std::os::raw::c_int, - ndw: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_transfer_read_iov( - handle: u32, - ctx_id: u32, - level: u32, - stride: u32, - layer_stride: u32, - box_: *mut virgl_box, - offset: u64, - iov: *mut iovec, - iovec_cnt: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_transfer_write_iov( - handle: u32, - ctx_id: u32, - level: ::std::os::raw::c_int, - stride: u32, - layer_stride: u32, - box_: *mut virgl_box, - offset: u64, - iovec: *mut iovec, - iovec_cnt: ::std::os::raw::c_uint, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32); -} -extern "C" { - pub fn virgl_renderer_fill_caps(set: u32, version: u32, caps: *mut ::std::os::raw::c_void); -} -extern "C" { - pub fn virgl_renderer_resource_attach_iov( - res_handle: ::std::os::raw::c_int, - iov: *mut iovec, - num_iovs: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_detach_iov( - res_handle: ::std::os::raw::c_int, - iov: *mut *mut iovec, - num_iovs: *mut ::std::os::raw::c_int, - ); -} -extern "C" { - pub fn virgl_renderer_create_fence( - client_fence_id: ::std::os::raw::c_int, - ctx_id: u32, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_force_ctx_0(); -} -extern "C" { - pub fn virgl_renderer_ctx_attach_resource( - ctx_id: ::std::os::raw::c_int, - res_handle: ::std::os::raw::c_int, - ); -} -extern "C" { - pub fn virgl_renderer_ctx_detach_resource( - ctx_id: ::std::os::raw::c_int, - res_handle: ::std::os::raw::c_int, - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_resource_info { - pub handle: u32, - pub virgl_format: u32, - pub width: u32, - pub height: u32, - pub depth: u32, - pub flags: u32, - pub tex_id: u32, - pub stride: u32, - pub drm_fourcc: ::std::os::raw::c_int, -} -extern "C" { - pub fn virgl_renderer_resource_get_info( - res_handle: ::std::os::raw::c_int, - info: *mut virgl_renderer_resource_info, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_cleanup(cookie: *mut ::std::os::raw::c_void); -} -extern "C" { - pub fn virgl_renderer_reset(); -} -extern "C" { - pub fn virgl_renderer_get_poll_fd() -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_execute( - execute_args: *mut ::std::os::raw::c_void, - execute_size: u32, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_context_create_with_flags( - ctx_id: u32, - ctx_flags: u32, - nlen: u32, - name: *const ::std::os::raw::c_char, - ) -> ::std::os::raw::c_int; -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct virgl_renderer_resource_create_blob_args { - pub res_handle: u32, - pub ctx_id: u32, - pub blob_mem: u32, - pub blob_flags: u32, - pub blob_id: u64, - pub size: u64, - pub iovecs: *const iovec, - pub num_iovs: u32, -} -impl Default for virgl_renderer_resource_create_blob_args { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -extern "C" { - pub fn virgl_renderer_resource_create_blob( - args: *const virgl_renderer_resource_create_blob_args, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_map( - res_handle: u32, - map: *mut *mut ::std::os::raw::c_void, - out_size: *mut u64, - ) -> ::std::os::raw::c_int; -} -#[cfg(feature = "virgl_resource_map2")] -extern "C" { - pub fn virgl_renderer_resource_map2( - res_handle: u32, - map: *const ::std::os::raw::c_void, - size: u64, - prot: i32, - flags: i32, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_unmap(res_handle: u32) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_get_map_info( - res_handle: u32, - map_info: *mut u32, - ) -> ::std::os::raw::c_int; -} -#[cfg(target_os = "macos")] -extern "C" { - pub fn virgl_renderer_resource_get_map_ptr( - res_handle: u32, - map_ptr: *mut u64, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_resource_export_blob( - res_id: u32, - fd_type: *mut u32, - fd: *mut ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct virgl_renderer_resource_import_blob_args { - pub res_handle: u32, - pub blob_mem: u32, - pub fd_type: u32, - pub fd: ::std::os::raw::c_int, - pub size: u64, -} -extern "C" { - pub fn virgl_renderer_resource_import_blob( - args: *const virgl_renderer_resource_import_blob_args, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_export_fence( - client_fence_id: u32, - fd: *mut ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_context_create_fence( - ctx_id: u32, - flags: u32, - queue_id: u64, - fence_id: u64, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn virgl_renderer_context_poll(ctx_id: u32); -} -extern "C" { - pub fn virgl_renderer_context_get_poll_fd(ctx_id: u32) -> ::std::os::raw::c_int; -} diff --git a/src/rutabaga_gfx/src/gfxstream.rs b/src/rutabaga_gfx/src/gfxstream.rs deleted file mode 100644 index 2b13c2706..000000000 --- a/src/rutabaga_gfx/src/gfxstream.rs +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! gfxstream: Handles 3D virtio-gpu hypercalls using gfxstream. -//! -//! External code found at . - -#![cfg(feature = "gfxstream")] - -use std::convert::TryInto; -use std::io::IoSliceMut; -use std::mem::size_of; -use std::os::raw::c_char; -use std::os::raw::c_int; -use std::os::raw::c_uint; -use std::os::raw::c_void; -use std::panic::catch_unwind; -use std::process::abort; -use std::ptr::null; -use std::ptr::null_mut; -use std::sync::Arc; - -use crate::generated::virgl_renderer_bindings::iovec; -use crate::generated::virgl_renderer_bindings::virgl_box; -use crate::generated::virgl_renderer_bindings::virgl_renderer_resource_create_args; -use crate::renderer_utils::*; -use crate::rutabaga_core::RutabagaComponent; -use crate::rutabaga_core::RutabagaContext; -use crate::rutabaga_core::RutabagaResource; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_os::IntoRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::*; - -// See `virtgpu-gfxstream-renderer.h` for definitions -const STREAM_RENDERER_PARAM_NULL: u64 = 0; -const STREAM_RENDERER_PARAM_USER_DATA: u64 = 1; -const STREAM_RENDERER_PARAM_RENDERER_FLAGS: u64 = 2; -const STREAM_RENDERER_PARAM_FENCE_CALLBACK: u64 = 3; -const STREAM_RENDERER_PARAM_WIN0_WIDTH: u64 = 4; -const STREAM_RENDERER_PARAM_WIN0_HEIGHT: u64 = 5; -const STREAM_RENDERER_PARAM_DEBUG_CALLBACK: u64 = 6; - -const STREAM_RENDERER_MAX_PARAMS: usize = 6; - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct stream_renderer_param { - key: u64, - value: u64, -} - -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub struct stream_renderer_handle { - pub os_handle: i64, - pub handle_type: u32, -} - -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub struct stream_renderer_vulkan_info { - pub memory_index: u32, - pub device_uuid: [u8; 16], - pub driver_uuid: [u8; 16], -} - -#[repr(C)] -pub struct stream_renderer_command { - pub ctx_id: u32, - pub cmd_size: u32, - pub cmd: *const u8, - pub num_in_fences: u32, - pub in_fence_descriptors: *const u64, -} - -#[allow(non_camel_case_types)] -pub type stream_renderer_create_blob = ResourceCreateBlob; - -#[allow(non_camel_case_types)] -pub type stream_renderer_resource_create_args = virgl_renderer_resource_create_args; - -#[allow(non_camel_case_types)] -pub type stream_renderer_box = virgl_box; - -#[allow(non_camel_case_types)] -pub type stream_renderer_fence = RutabagaFence; - -#[allow(non_camel_case_types)] -pub type stream_renderer_debug = RutabagaDebug; - -extern "C" { - // Entry point for the stream renderer. - fn stream_renderer_init( - stream_renderer_params: *mut stream_renderer_param, - num_params: u64, - ) -> c_int; - - // Shutdown entry point for the renderer. - fn stream_renderer_teardown(); - - // virtio-gpu-3d ioctl functions (begin) - - // In gfxstream, the resource create/transfer ioctls correspond to creating buffers for API - // forwarding and the notification of new API calls forwarded by the guest, unless they - // correspond to minigbm resource targets (PIPE_TEXTURE_2D), in which case they create globally - // visible shared GL textures to support gralloc. - fn stream_renderer_resource_create( - args: *mut stream_renderer_resource_create_args, - iov: *mut iovec, - num_iovs: u32, - ) -> c_int; - - fn stream_renderer_resource_unref(res_handle: u32); - fn stream_renderer_context_destroy(handle: u32); - fn stream_renderer_transfer_read_iov( - handle: u32, - ctx_id: u32, - level: u32, - stride: u32, - layer_stride: u32, - box_: *mut stream_renderer_box, - offset: u64, - iov: *mut iovec, - iovec_cnt: c_int, - ) -> c_int; - fn stream_renderer_transfer_write_iov( - handle: u32, - ctx_id: u32, - level: c_int, - stride: u32, - layer_stride: u32, - box_: *mut stream_renderer_box, - offset: u64, - iovec: *mut iovec, - iovec_cnt: c_uint, - ) -> c_int; - fn stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int; - fn stream_renderer_resource_attach_iov( - res_handle: c_int, - iov: *mut iovec, - num_iovs: c_int, - ) -> c_int; - fn stream_renderer_resource_detach_iov( - res_handle: c_int, - iov: *mut *mut iovec, - num_iovs: *mut c_int, - ); - fn stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int; - fn stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int); - fn stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int); - fn stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32); - fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void); - - fn stream_renderer_flush(res_handle: u32); - fn stream_renderer_create_blob( - ctx_id: u32, - res_handle: u32, - create_blob: *const stream_renderer_create_blob, - iovecs: *const iovec, - num_iovs: u32, - handle: *const stream_renderer_handle, - ) -> c_int; - - fn stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int; - fn stream_renderer_resource_map( - res_handle: u32, - map: *mut *mut c_void, - out_size: *mut u64, - ) -> c_int; - fn stream_renderer_resource_unmap(res_handle: u32) -> c_int; - fn stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int; - fn stream_renderer_vulkan_info( - res_handle: u32, - vulkan_info: *mut stream_renderer_vulkan_info, - ) -> c_int; - fn stream_renderer_context_create( - handle: u32, - nlen: u32, - name: *const c_char, - context_init: u32, - ) -> c_int; -} - -/// The virtio-gpu backend state tracker which supports accelerated rendering. -pub struct Gfxstream { - /// Cookie used by Gfxstream, should be held as long as the renderer is alive. - _cookie: Box, -} - -struct GfxstreamContext { - ctx_id: u32, - fence_handler: RutabagaFenceHandler, -} - -impl RutabagaContext for GfxstreamContext { - fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> { - if !fence_ids.is_empty() { - return Err(RutabagaError::Unsupported); - } - - if commands.len() % size_of::() != 0 { - return Err(RutabagaError::InvalidCommandSize(commands.len())); - } - - let ret = unsafe { - let cmd = stream_renderer_command { - ctx_id: self.ctx_id, - cmd_size: commands.len().try_into()?, - cmd: commands.as_mut_ptr(), - num_in_fences: 0, - in_fence_descriptors: null(), - }; - - stream_renderer_submit_cmd(&cmd as *const stream_renderer_command) - }; - ret_to_res(ret) - } - - fn attach(&mut self, resource: &mut RutabagaResource) { - // The context id and resource id must be valid because the respective instances ensure - // their lifetime. - unsafe { - stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32); - } - } - - fn detach(&mut self, resource: &RutabagaResource) { - // The context id and resource id must be valid because the respective instances ensure - // their lifetime. - unsafe { - stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32); - } - } - - fn component_type(&self) -> RutabagaComponentType { - RutabagaComponentType::Gfxstream - } - - fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - if fence.ring_idx as u32 == 1 { - self.fence_handler.call(fence); - return Ok(()); - } - - // Safe because RutabagaFences and stream_renderer_fence are ABI identical - let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) }; - - ret_to_res(ret) - } -} - -impl Drop for GfxstreamContext { - fn drop(&mut self) { - // The context is safe to destroy because nothing else can be referencing it. - unsafe { - stream_renderer_context_destroy(self.ctx_id); - } - } -} - -extern "C" fn write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence) { - catch_unwind(|| { - assert!(!cookie.is_null()); - let cookie = unsafe { &*(cookie as *mut RutabagaCookie) }; - if let Some(handler) = &cookie.fence_handler { - // We trust gfxstream not give a dangling pointer - unsafe { handler.call(*fence) }; - } - }) - .unwrap_or_else(|_| abort()) -} - -extern "C" fn gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug) { - catch_unwind(|| { - assert!(!cookie.is_null()); - let cookie = unsafe { &*(cookie as *mut RutabagaCookie) }; - if let Some(handler) = &cookie.debug_handler { - // We trust gfxstream not give a dangling pointer - unsafe { handler.call(*debug) }; - } - }) - .unwrap_or_else(|_| abort()) -} - -impl Gfxstream { - pub fn init( - display_width: u32, - display_height: u32, - gfxstream_flags: GfxstreamFlags, - fence_handler: RutabagaFenceHandler, - debug_handler: Option, - ) -> RutabagaResult> { - let use_debug = debug_handler.is_some(); - let mut cookie = Box::new(RutabagaCookie { - render_server_fd: None, - fence_handler: Some(fence_handler), - debug_handler, - }); - - let mut stream_renderer_params: [stream_renderer_param; STREAM_RENDERER_MAX_PARAMS] = [ - stream_renderer_param { - key: STREAM_RENDERER_PARAM_USER_DATA, - // Safe as cookie outlives the stream renderer (stream_renderer_teardown called - // at Gfxstream Drop) - value: &mut *cookie as *mut RutabagaCookie as u64, - }, - stream_renderer_param { - key: STREAM_RENDERER_PARAM_RENDERER_FLAGS, - value: gfxstream_flags.into(), - }, - stream_renderer_param { - key: STREAM_RENDERER_PARAM_FENCE_CALLBACK, - value: write_context_fence as usize as u64, - }, - stream_renderer_param { - key: STREAM_RENDERER_PARAM_WIN0_WIDTH, - value: display_width as u64, - }, - stream_renderer_param { - key: STREAM_RENDERER_PARAM_WIN0_HEIGHT, - value: display_height as u64, - }, - if use_debug { - stream_renderer_param { - key: STREAM_RENDERER_PARAM_DEBUG_CALLBACK, - value: gfxstream_debug_callback as usize as u64, - } - } else { - stream_renderer_param { - key: STREAM_RENDERER_PARAM_NULL, - value: 0, - } - }, - ]; - - unsafe { - ret_to_res(stream_renderer_init( - stream_renderer_params.as_mut_ptr(), - stream_renderer_params.len() as u64, - ))?; - } - - Ok(Box::new(Gfxstream { _cookie: cookie })) - } - - fn map_info(&self, resource_id: u32) -> RutabagaResult { - let mut map_info = 0; - // Safe because `map_info` is a local stack variable owned by us. - let ret = unsafe { stream_renderer_resource_map_info(resource_id, &mut map_info) }; - ret_to_res(ret)?; - - Ok(map_info | RUTABAGA_MAP_ACCESS_RW) - } - - fn vulkan_info(&self, resource_id: u32) -> RutabagaResult { - let mut vulkan_info: stream_renderer_vulkan_info = Default::default(); - // Safe because `vulkan_info` is a local stack variable owned by us. - let ret = unsafe { stream_renderer_vulkan_info(resource_id, &mut vulkan_info) }; - ret_to_res(ret)?; - - Ok(VulkanInfo { - memory_idx: vulkan_info.memory_index, - device_id: DeviceId { - device_uuid: vulkan_info.device_uuid, - driver_uuid: vulkan_info.driver_uuid, - }, - }) - } - - fn export_blob(&self, resource_id: u32) -> RutabagaResult> { - let mut stream_handle: stream_renderer_handle = Default::default(); - let ret = unsafe { stream_renderer_export_blob(resource_id, &mut stream_handle) }; - ret_to_res(ret)?; - - // Safe because the handle was just returned by a successful gfxstream call so it must be - // valid and owned by us. - let raw_descriptor = stream_handle.os_handle as RawDescriptor; - let handle = unsafe { SafeDescriptor::from_raw_descriptor(raw_descriptor) }; - - Ok(Arc::new(RutabagaHandle { - os_handle: handle, - handle_type: stream_handle.handle_type, - })) - } -} - -impl Drop for Gfxstream { - fn drop(&mut self) { - // SAFETY: Safe because Gfxstream was succesfully initialized. - unsafe { - stream_renderer_teardown(); - } - } -} - -impl RutabagaComponent for Gfxstream { - fn get_capset_info(&self, capset_id: u32) -> (u32, u32) { - let mut version = 0; - let mut size = 0; - // Safe because gfxstream is initialized by now and properly size stack variables are - // used for the pointers. - unsafe { - stream_renderer_get_cap_set(capset_id, &mut version, &mut size); - } - (version, size) - } - - fn get_capset(&self, capset_id: u32, version: u32) -> Vec { - let (_, max_size) = self.get_capset_info(capset_id); - let mut buf = vec![0u8; max_size as usize]; - // Safe because gfxstream is initialized by now and the given buffer is sized properly - // for the given cap id/version. - unsafe { - stream_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void); - } - - buf - } - - fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - // Safe because RutabagaFences and stream_renderer_fence are ABI identical - let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) }; - ret_to_res(ret) - } - - fn create_3d( - &self, - resource_id: u32, - resource_create_3d: ResourceCreate3D, - ) -> RutabagaResult { - let mut args = virgl_renderer_resource_create_args { - handle: resource_id, - target: resource_create_3d.target, - format: resource_create_3d.format, - bind: resource_create_3d.bind, - width: resource_create_3d.width, - height: resource_create_3d.height, - depth: resource_create_3d.depth, - array_size: resource_create_3d.array_size, - last_level: resource_create_3d.last_level, - nr_samples: resource_create_3d.nr_samples, - flags: resource_create_3d.flags, - }; - - // Safe because gfxstream is initialized by now, and the return value is checked before - // returning a new resource. The backing buffers are not supplied with this call. - let ret = unsafe { stream_renderer_resource_create(&mut args, null_mut(), 0) }; - ret_to_res(ret)?; - - Ok(RutabagaResource { - resource_id, - handle: None, - blob: false, - blob_mem: 0, - blob_flags: 0, - map_info: None, - info_2d: None, - info_3d: None, - vulkan_info: None, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::Gfxstream as u8), - size: 0, - mapping: None, - }) - } - - fn attach_backing( - &self, - resource_id: u32, - vecs: &mut Vec, - ) -> RutabagaResult<()> { - let ret = unsafe { - stream_renderer_resource_attach_iov( - resource_id as i32, - vecs.as_mut_ptr() as *mut iovec, - vecs.len() as i32, - ) - }; - ret_to_res(ret) - } - - fn detach_backing(&self, resource_id: u32) { - unsafe { - stream_renderer_resource_detach_iov( - resource_id as i32, - std::ptr::null_mut(), - std::ptr::null_mut(), - ); - } - } - - fn unref_resource(&self, resource_id: u32) { - // The resource is safe to unreference destroy because no user of these bindings can still - // be holding a reference. - unsafe { - stream_renderer_resource_unref(resource_id); - } - } - - fn transfer_write( - &self, - ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - ) -> RutabagaResult<()> { - if transfer.is_empty() { - return Ok(()); - } - - let mut transfer_box = VirglBox { - x: transfer.x, - y: transfer.y, - z: transfer.z, - w: transfer.w, - h: transfer.h, - d: transfer.d, - }; - - // Safe because only stack variables of the appropriate type are used. - let ret = unsafe { - stream_renderer_transfer_write_iov( - resource.resource_id, - ctx_id, - transfer.level as i32, - transfer.stride, - transfer.layer_stride, - &mut transfer_box as *mut VirglBox as *mut stream_renderer_box, - transfer.offset, - null_mut(), - 0, - ) - }; - ret_to_res(ret) - } - - fn transfer_read( - &self, - ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - buf: Option, - ) -> RutabagaResult<()> { - if transfer.is_empty() { - return Ok(()); - } - - let mut transfer_box = VirglBox { - x: transfer.x, - y: transfer.y, - z: transfer.z, - w: transfer.w, - h: transfer.h, - d: transfer.d, - }; - - let mut iov = RutabagaIovec { - base: null_mut(), - len: 0, - }; - - let (iovecs, num_iovecs) = match buf { - Some(mut buf) => { - iov.base = buf.as_mut_ptr() as *mut c_void; - iov.len = buf.len(); - (&mut iov as *mut RutabagaIovec as *mut iovec, 1) - } - None => (null_mut(), 0), - }; - - // Safe because only stack variables of the appropriate type are used. - let ret = unsafe { - stream_renderer_transfer_read_iov( - resource.resource_id, - ctx_id, - transfer.level, - transfer.stride, - transfer.layer_stride, - &mut transfer_box as *mut VirglBox as *mut stream_renderer_box, - transfer.offset, - iovecs, - num_iovecs, - ) - }; - ret_to_res(ret) - } - - fn resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()> { - unsafe { - stream_renderer_flush(resource.resource_id); - } - Ok(()) - } - - fn create_blob( - &mut self, - ctx_id: u32, - resource_id: u32, - resource_create_blob: ResourceCreateBlob, - mut iovec_opt: Option>, - handle_opt: Option, - ) -> RutabagaResult { - let mut iovec_ptr = null_mut(); - let mut num_iovecs = 0; - if let Some(ref mut iovecs) = iovec_opt { - iovec_ptr = iovecs.as_mut_ptr(); - num_iovecs = iovecs.len() as u32; - } - - let mut handle_ptr = null(); - let mut stream_handle: stream_renderer_handle = Default::default(); - if let Some(handle) = handle_opt { - stream_handle.handle_type = handle.handle_type; - stream_handle.os_handle = handle.os_handle.into_raw_descriptor() as i64; - handle_ptr = &stream_handle; - } - - let ret = unsafe { - stream_renderer_create_blob( - ctx_id, - resource_id, - &resource_create_blob as *const stream_renderer_create_blob, - iovec_ptr as *const iovec, - num_iovecs, - handle_ptr as *const stream_renderer_handle, - ) - }; - - ret_to_res(ret)?; - - Ok(RutabagaResource { - resource_id, - handle: self.export_blob(resource_id).ok(), - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: self.map_info(resource_id).ok(), - info_2d: None, - info_3d: None, - vulkan_info: self.vulkan_info(resource_id).ok(), - backing_iovecs: iovec_opt, - component_mask: 1 << (RutabagaComponentType::Gfxstream as u8), - size: resource_create_blob.size, - mapping: None, - }) - } - - fn map(&self, resource_id: u32) -> RutabagaResult { - let mut map: *mut c_void = null_mut(); - let mut size: u64 = 0; - - // Safe because the Stream renderer wraps and validates use of vkMapMemory. - let ret = unsafe { stream_renderer_resource_map(resource_id, &mut map, &mut size) }; - if ret != 0 { - return Err(RutabagaError::MappingFailed(ret)); - } - Ok(RutabagaMapping { - ptr: map as u64, - size, - }) - } - - fn unmap(&self, resource_id: u32) -> RutabagaResult<()> { - let ret = unsafe { stream_renderer_resource_unmap(resource_id) }; - ret_to_res(ret) - } - - fn create_context( - &self, - ctx_id: u32, - context_init: u32, - context_name: Option<&str>, - fence_handler: RutabagaFenceHandler, - ) -> RutabagaResult> { - let mut name: &str = "gpu_renderer"; - if let Some(name_string) = context_name.filter(|s| !s.is_empty()) { - name = name_string; - } - - // Safe because gfxstream is initialized by now and the context name is statically - // allocated. The return value is checked before returning a new context. - let ret = unsafe { - stream_renderer_context_create( - ctx_id, - name.len() as u32, - name.as_ptr() as *const c_char, - context_init, - ) - }; - ret_to_res(ret)?; - Ok(Box::new(GfxstreamContext { - ctx_id, - fence_handler, - })) - } -} diff --git a/src/rutabaga_gfx/src/gfxstream_stub.rs b/src/rutabaga_gfx/src/gfxstream_stub.rs deleted file mode 100644 index 2a7eb47b3..000000000 --- a/src/rutabaga_gfx/src/gfxstream_stub.rs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Stub impplementation of the native interface of gfxstream_backend.so. -//! -//! This implementation is used to enable the gfxstream feature of crosvm to be compiled without -//! gfxstream_backend.so available. It is only used for testing purposes and not functional -//! at runtime. - -#![cfg(feature = "gfxstream_stub")] - -use std::os::raw::c_char; -use std::os::raw::c_int; -use std::os::raw::c_uint; -use std::os::raw::c_void; - -use crate::generated::virgl_renderer_bindings::iovec; -use crate::gfxstream::stream_renderer_box; -use crate::gfxstream::stream_renderer_command; -use crate::gfxstream::stream_renderer_create_blob; -use crate::gfxstream::stream_renderer_fence; -use crate::gfxstream::stream_renderer_handle; -use crate::gfxstream::stream_renderer_param; -use crate::gfxstream::stream_renderer_resource_create_args; -use crate::gfxstream::stream_renderer_vulkan_info; - -#[no_mangle] -extern "C" fn stream_renderer_init( - _stream_renderer_params: *mut stream_renderer_param, - _num_params: u64, -) -> c_int { - unimplemented!(); -} - -#[no_mangle] -extern "C" fn stream_renderer_teardown() { - unimplemented!(); -} - -#[no_mangle] -extern "C" fn stream_renderer_resource_create( - _args: *mut stream_renderer_resource_create_args, - _iov: *mut iovec, - _num_iovs: u32, -) -> c_int { - unimplemented!(); -} - -#[no_mangle] -extern "C" fn stream_renderer_resource_unref(_res_handle: u32) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_context_destroy(_handle: u32) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_transfer_read_iov( - _handle: u32, - _ctx_id: u32, - _level: u32, - _stride: u32, - _layer_stride: u32, - _box_: *mut stream_renderer_box, - _offset: u64, - _iov: *mut iovec, - _iovec_cnt: c_int, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_transfer_write_iov( - _handle: u32, - _ctx_id: u32, - _level: c_int, - _stride: u32, - _layer_stride: u32, - _box_: *mut stream_renderer_box, - _offset: u64, - _iovec: *mut iovec, - _iovec_cnt: c_uint, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_submit_cmd(_cmd: *const stream_renderer_command) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_resource_attach_iov( - _res_handle: c_int, - _iov: *mut iovec, - _num_iovs: c_int, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_resource_detach_iov( - _res_handle: c_int, - _iov: *mut *mut iovec, - _num_iovs: *mut c_int, -) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_ctx_attach_resource(_ctx_id: c_int, _res_handle: c_int) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_ctx_detach_resource(_ctx_id: c_int, _res_handle: c_int) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_get_cap_set(_set: u32, _max_ver: *mut u32, _max_size: *mut u32) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_fill_caps(_set: u32, _version: u32, _caps: *mut c_void) { - unimplemented!(); -} - -#[no_mangle] -extern "C" fn stream_renderer_flush(_res_handle: u32) { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_create_blob( - _ctx_id: u32, - _res_handle: u32, - _create_blob: *const stream_renderer_create_blob, - _iovecs: *const iovec, - _num_iovs: u32, - _handle: *const stream_renderer_handle, -) -> c_int { - unimplemented!(); -} - -#[no_mangle] -extern "C" fn stream_renderer_export_blob( - _res_handle: u32, - _handle: *mut stream_renderer_handle, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_resource_map( - _res_handle: u32, - _map: *mut *mut c_void, - _out_size: *mut u64, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_resource_unmap(_res_handle: u32) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_resource_map_info(_res_handle: u32, _map_info: *mut u32) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_vulkan_info( - _res_handle: u32, - _vulkan_info: *mut stream_renderer_vulkan_info, -) -> c_int { - unimplemented!(); -} -#[no_mangle] -extern "C" fn stream_renderer_context_create( - _handle: u32, - _nlen: u32, - _name: *const c_char, - _context_init: u32, -) -> c_int { - unimplemented!(); -} -#[no_mangle] - -extern "C" fn stream_renderer_create_fence(_fence: *const stream_renderer_fence) -> c_int { - unimplemented!(); -} diff --git a/src/rutabaga_gfx/src/lib.rs b/src/rutabaga_gfx/src/lib.rs deleted file mode 100644 index 06c75ae2f..000000000 --- a/src/rutabaga_gfx/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! A crate for handling 2D and 3D virtio-gpu hypercalls, along with graphics -//! swapchain allocation and mapping. - -mod cross_domain; -mod generated; -mod gfxstream; -mod gfxstream_stub; -#[macro_use] -mod macros; -#[cfg(any(feature = "gfxstream", feature = "virgl_renderer"))] -mod renderer_utils; -mod rutabaga_2d; -mod rutabaga_core; -mod rutabaga_gralloc; -mod rutabaga_os; -mod rutabaga_snapshot; -mod rutabaga_utils; -mod virgl_renderer; - -pub use crate::rutabaga_core::calculate_capset_mask; -pub use crate::rutabaga_core::calculate_capset_names; -pub use crate::rutabaga_core::Rutabaga; -pub use crate::rutabaga_core::RutabagaBuilder; -pub use crate::rutabaga_gralloc::DrmFormat; -pub use crate::rutabaga_gralloc::ImageAllocationInfo; -pub use crate::rutabaga_gralloc::ImageMemoryRequirements; -pub use crate::rutabaga_gralloc::RutabagaGralloc; -pub use crate::rutabaga_gralloc::RutabagaGrallocFlags; -pub use crate::rutabaga_os::AsRawDescriptor; -pub use crate::rutabaga_os::FromRawDescriptor as RutabagaFromRawDescriptor; -pub use crate::rutabaga_os::IntoRawDescriptor as RutabagaIntoRawDescriptor; -pub use crate::rutabaga_os::MappedRegion as RutabagaMappedRegion; -pub use crate::rutabaga_os::SafeDescriptor as RutabagaDescriptor; -pub use crate::rutabaga_utils::*; diff --git a/src/rutabaga_gfx/src/macros.rs b/src/rutabaga_gfx/src/macros.rs deleted file mode 100644 index e813bd788..000000000 --- a/src/rutabaga_gfx/src/macros.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Macros for rutabaga_gfx. - -#[macro_export] -macro_rules! checked_range { - ($x:expr; <= $y:expr) => { - if $x <= $y { - Ok(()) - } else { - Err(RutabagaError::CheckedRange { - field1: (stringify!($x), $x as usize), - field2: (stringify!($y), $y as usize), - }) - } - }; - ($x:ident <= $y:ident) => { - check_range!($x; <= $y) - }; -} - -#[macro_export] -macro_rules! checked_arithmetic { - ($x:ident $op:ident $y:ident $op_name:expr) => { - $x.$op($y).ok_or_else(|| RutabagaError::CheckedArithmetic { - field1: (stringify!($x), $x as usize), - field2: (stringify!($y), $y as usize), - op: $op_name, - }) - }; - ($x:ident + $y:ident) => { - checked_arithmetic!($x checked_add $y "+") - }; - ($x:ident - $y:ident) => { - checked_arithmetic!($x checked_sub $y "-") - }; - ($x:ident * $y:ident) => { - checked_arithmetic!($x checked_mul $y "*") - }; - ($x:ident / $y:ident) => { - checked_arithmetic!($x checked_div $y "/") - }; -} diff --git a/src/rutabaga_gfx/src/renderer_utils.rs b/src/rutabaga_gfx/src/renderer_utils.rs deleted file mode 100644 index 3458e013d..000000000 --- a/src/rutabaga_gfx/src/renderer_utils.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! renderer_utils: Utility functions and structs used by virgl_renderer and gfxstream. - -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaDebugHandler; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaFenceHandler; -use crate::rutabaga_utils::RutabagaResult; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct VirglBox { - pub x: u32, - pub y: u32, - pub z: u32, - pub w: u32, - pub h: u32, - pub d: u32, -} - -pub fn ret_to_res(ret: i32) -> RutabagaResult<()> { - match ret { - 0 => Ok(()), - _ => Err(RutabagaError::ComponentError(ret)), - } -} - -#[allow(dead_code)] -pub struct RutabagaCookie { - pub render_server_fd: Option, - pub fence_handler: Option, - pub debug_handler: Option, -} diff --git a/src/rutabaga_gfx/src/rutabaga_2d.rs b/src/rutabaga_gfx/src/rutabaga_2d.rs deleted file mode 100644 index 417e67952..000000000 --- a/src/rutabaga_gfx/src/rutabaga_2d.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! rutabaga_2d: Handles 2D virtio-gpu hypercalls. - -use std::cmp::max; -use std::cmp::min; -use std::cmp::Ordering; -use std::io::IoSliceMut; - -use crate::rutabaga_core::Rutabaga2DInfo; -use crate::rutabaga_core::RutabagaComponent; -use crate::rutabaga_core::RutabagaResource; -use crate::rutabaga_utils::*; - -/// Transfers a resource from potentially many chunked src slices to a dst slice. -#[allow(clippy::too_many_arguments)] -fn transfer_2d( - resource_w: u32, - resource_h: u32, - rect_x: u32, - rect_y: u32, - rect_w: u32, - rect_h: u32, - dst_stride: u32, - dst_offset: u64, - mut dst: IoSliceMut, - src_stride: u32, - src_offset: u64, - srcs: &[&[u8]], -) -> RutabagaResult<()> { - if rect_w == 0 || rect_h == 0 { - return Ok(()); - } - - checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?; - checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?; - - let bytes_per_pixel = 4u64; - - let rect_x = rect_x as u64; - let rect_y = rect_y as u64; - let rect_w = rect_w as u64; - let rect_h = rect_h as u64; - - let dst_stride = dst_stride as u64; - let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel); - - let src_stride = src_stride as u64; - let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel); - - let mut next_src; - let mut next_line; - let mut current_height = 0u64; - let mut srcs = srcs.iter(); - let mut src_opt = srcs.next(); - - // Cumulative start offset of the current src. - let mut src_start_offset = 0u64; - while let Some(src) = src_opt { - if current_height >= rect_h { - break; - } - - let src_size = src.len() as u64; - - // Cumulative end offset of the current src. - let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?; - - let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?; - let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?; - - // Cumulative start/end offsets of the next line to copy within all srcs. - let src_line_start_offset = - checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?; - let src_line_end_offset = - checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?; - - // Clamp the line start/end offset to be inside the current src. - let src_copyable_start_offset = max(src_line_start_offset, src_start_offset); - let src_copyable_end_offset = min(src_line_end_offset, src_end_offset); - - if src_copyable_start_offset < src_copyable_end_offset { - let copyable_size = - checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?; - - let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset); - - match src_line_end_offset.cmp(&src_end_offset) { - Ordering::Greater => { - next_src = true; - next_line = false; - } - Ordering::Equal => { - next_src = true; - next_line = true; - } - Ordering::Less => { - next_src = false; - next_line = true; - } - } - - let src_end = offset_within_src + copyable_size; - let src_subslice = src - .get(offset_within_src as usize..src_end as usize) - .ok_or(RutabagaError::InvalidIovec)?; - - let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?; - let dst_line_horizontal_offset = - checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?; - let dst_line_offset = - checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?; - let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?; - - let dst_end_offset = dst_start_offset + copyable_size; - let dst_subslice = dst - .get_mut(dst_start_offset as usize..dst_end_offset as usize) - .ok_or(RutabagaError::InvalidIovec)?; - - dst_subslice.copy_from_slice(src_subslice); - } else if src_line_start_offset >= src_start_offset { - next_src = true; - next_line = false; - } else { - next_src = false; - next_line = true; - }; - - if next_src { - src_start_offset = checked_arithmetic!(src_start_offset + src_size)?; - src_opt = srcs.next(); - } - - if next_line { - current_height += 1; - } - } - - Ok(()) -} - -pub struct Rutabaga2D { - fence_handler: RutabagaFenceHandler, -} - -impl Rutabaga2D { - pub fn init(fence_handler: RutabagaFenceHandler) -> RutabagaResult> { - Ok(Box::new(Rutabaga2D { fence_handler })) - } -} - -impl RutabagaComponent for Rutabaga2D { - fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - self.fence_handler.call(fence); - Ok(()) - } - - fn create_3d( - &self, - resource_id: u32, - resource_create_3d: ResourceCreate3D, - ) -> RutabagaResult { - // All virtio formats are 4 bytes per pixel. - let resource_bpp = 4; - let resource_stride = resource_bpp * resource_create_3d.width; - let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize); - let info_2d = Rutabaga2DInfo { - width: resource_create_3d.width, - height: resource_create_3d.height, - host_mem: vec![0; resource_size], - }; - - Ok(RutabagaResource { - resource_id, - handle: None, - blob: false, - blob_mem: 0, - blob_flags: 0, - map_info: None, - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: Some(info_2d), - info_3d: None, - vulkan_info: None, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8), - size: resource_size as u64, - mapping: None, - }) - } - - fn transfer_write( - &self, - _ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - ) -> RutabagaResult<()> { - if transfer.is_empty() { - return Ok(()); - } - - let mut info_2d = resource - .info_2d - .take() - .ok_or(RutabagaError::Invalid2DInfo)?; - - let iovecs = resource - .backing_iovecs - .take() - .ok_or(RutabagaError::InvalidIovec)?; - - // All offical virtio_gpu formats are 4 bytes per pixel. - let resource_bpp = 4; - let mut src_slices = Vec::with_capacity(iovecs.len()); - for iovec in &iovecs { - // Safe because Rutabaga users should have already checked the iovecs. - let slice = unsafe { std::slice::from_raw_parts(iovec.base as *mut u8, iovec.len) }; - src_slices.push(slice); - } - - let src_stride = resource_bpp * info_2d.width; - let src_offset = transfer.offset; - - let dst_stride = resource_bpp * info_2d.width; - let dst_offset = 0; - - transfer_2d( - info_2d.width, - info_2d.height, - transfer.x, - transfer.y, - transfer.w, - transfer.h, - dst_stride, - dst_offset, - IoSliceMut::new(info_2d.host_mem.as_mut_slice()), - src_stride, - src_offset, - &src_slices, - )?; - - resource.info_2d = Some(info_2d); - resource.backing_iovecs = Some(iovecs); - Ok(()) - } - - fn transfer_read( - &self, - _ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - buf: Option, - ) -> RutabagaResult<()> { - let mut info_2d = resource - .info_2d - .take() - .ok_or(RutabagaError::Invalid2DInfo)?; - - // All offical virtio_gpu formats are 4 bytes per pixel. - let resource_bpp = 4; - let src_stride = resource_bpp * info_2d.width; - let src_offset = 0; - let dst_offset = 0; - - let dst_slice = buf.ok_or(RutabagaError::SpecViolation( - "need a destination slice for transfer read", - ))?; - - transfer_2d( - info_2d.width, - info_2d.height, - transfer.x, - transfer.y, - transfer.w, - transfer.h, - transfer.stride, - dst_offset, - dst_slice, - src_stride, - src_offset, - &[info_2d.host_mem.as_mut_slice()], - )?; - - resource.info_2d = Some(info_2d); - Ok(()) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_core.rs b/src/rutabaga_gfx/src/rutabaga_core.rs deleted file mode 100644 index f5b349a93..000000000 --- a/src/rutabaga_gfx/src/rutabaga_core.rs +++ /dev/null @@ -1,1387 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! rutabaga_core: Cross-platform, Rust-based, Wayland and Vulkan centric GPU virtualization. -use std::collections::BTreeMap as Map; -use std::convert::TryInto; -use std::fs::File; -use std::io::IoSliceMut; -use std::io::Read; -use std::io::Write; -use std::sync::{Arc, Mutex}; - -use crate::cross_domain::CrossDomain; - -#[cfg(feature = "gfxstream")] -use crate::gfxstream::Gfxstream; - -use crate::rutabaga_2d::Rutabaga2D; -use crate::rutabaga_os::MemoryMapping; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_snapshot::RutabagaResourceSnapshot; -use crate::rutabaga_snapshot::RutabagaSnapshot; -use crate::rutabaga_utils::*; - -#[cfg(feature = "virgl_renderer")] -use crate::virgl_renderer::VirglRenderer; - -const RUTABAGA_DEFAULT_WIDTH: u32 = 1280; -const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024; - -pub type ExportTable = Arc>>; - -/// Information required for 2D functionality. -pub struct Rutabaga2DInfo { - pub width: u32, - pub height: u32, - pub host_mem: Vec, -} - -/// A Rutabaga resource, supporting 2D and 3D rutabaga features. Assumes a single-threaded library. -pub struct RutabagaResource { - pub resource_id: u32, - pub handle: Option>, - pub blob: bool, - pub blob_mem: u32, - pub blob_flags: u32, - pub map_info: Option, - #[cfg(target_os = "macos")] - pub map_ptr: Option, - pub info_2d: Option, - pub info_3d: Option, - pub vulkan_info: Option, - pub backing_iovecs: Option>, - - /// Bitmask of components that have already imported this resource - pub component_mask: u8, - pub size: u64, - pub mapping: Option, -} - -/// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI). Each component -/// on it's own is sufficient to virtualize graphics on many Google products. These components wrap -/// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype -/// functionality. -/// -/// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for -/// the given command. -pub trait RutabagaComponent { - /// Implementations should return the version and size of the given capset_id. (0, 0) is - /// returned by default. - fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) { - (0, 0) - } - - /// Implementations should return the capabilites of given a `capset_id` and `version`. A - /// zero-sized array is returned by default. - fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec { - Vec::new() - } - - /// Implementations should set their internal context to be the reserved context 0. - fn force_ctx_0(&self) {} - - /// Implementations must create a fence that represents the completion of prior work. This is - /// required for synchronization with the guest kernel. - fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> { - Ok(()) - } - - /// Used only by VirglRenderer to poll when its poll_descriptor is signaled. - fn event_poll(&self) {} - - /// Used only by VirglRenderer to return a poll_descriptor that is signaled when a poll() is - /// necessary. - fn poll_descriptor(&self) -> Option { - None - } - - /// Implementations must create a resource with the given metadata. For 2D rutabaga components, - /// this a system memory allocation. For 3D components, this is typically a GL texture or - /// buffer. Vulkan components should use blob resources instead. - fn create_3d( - &self, - resource_id: u32, - _resource_create_3d: ResourceCreate3D, - ) -> RutabagaResult { - Ok(RutabagaResource { - resource_id, - handle: None, - blob: false, - blob_mem: 0, - blob_flags: 0, - map_info: None, - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: None, - vulkan_info: None, - backing_iovecs: None, - component_mask: 0, - size: 0, - mapping: None, - }) - } - - /// Implementations must attach `vecs` to the resource. - fn attach_backing( - &self, - _resource_id: u32, - _vecs: &mut Vec, - ) -> RutabagaResult<()> { - Ok(()) - } - - /// Implementations must detach `vecs` from the resource. - fn detach_backing(&self, _resource_id: u32) {} - - /// Implementations must release the guest kernel reference on the resource. - fn unref_resource(&self, _resource_id: u32) {} - - /// Implementations must perform the transfer write operation. For 2D rutabaga components, this - /// done via memcpy(). For 3D components, this is typically done via glTexSubImage(..). - fn transfer_write( - &self, - _ctx_id: u32, - _resource: &mut RutabagaResource, - _transfer: Transfer3D, - ) -> RutabagaResult<()> { - Ok(()) - } - - /// Implementations must perform the transfer read operation. For 2D rutabaga components, this - /// done via memcpy(). For 3D components, this is typically done via glReadPixels(..). - fn transfer_read( - &self, - _ctx_id: u32, - _resource: &mut RutabagaResource, - _transfer: Transfer3D, - _buf: Option, - ) -> RutabagaResult<()> { - Ok(()) - } - - /// Implementations must flush the given resource to the display. - fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } - - /// Implementations must create a blob resource on success. The memory parameters, size, and - /// usage of the blob resource is given by `resource_create_blob`. - fn create_blob( - &mut self, - _ctx_id: u32, - _resource_id: u32, - _resource_create_blob: ResourceCreateBlob, - _iovec_opt: Option>, - _handle_opt: Option, - ) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Implementations must map the blob resource on success, on the specified address and - /// honoring prot and flags. - fn resource_map( - &self, - _resource_id: u32, - _addr: u64, - _size: u64, - _prot: i32, - _flags: i32, - ) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } - - /// Implementations must map the blob resource on success. This is typically done by - /// glMapBufferRange(...) or vkMapMemory. - fn map(&self, _resource_id: u32) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Implementations must unmap the blob resource on success. This is typically done by - /// glUnmapBuffer(...) or vkUnmapMemory. - fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } - - /// Implementations must return a RutabagaHandle of the fence on success. - fn export_fence(&self, _fence_id: u32) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Implementations must create a context for submitting commands. The command stream of the - /// context is determined by `context_init`. For virgl contexts, it is a Gallium/TGSI command - /// stream. For gfxstream contexts, it's an autogenerated Vulkan or GLES streams. - fn create_context( - &self, - _ctx_id: u32, - _context_init: u32, - _context_name: Option<&str>, - _fence_handler: RutabagaFenceHandler, - ) -> RutabagaResult> { - Err(RutabagaError::Unsupported) - } -} - -pub trait RutabagaContext { - /// Implementations must return a RutabagaResource given the `resource_create_blob` parameters. - fn context_create_blob( - &mut self, - _resource_id: u32, - _resource_create_blob: ResourceCreateBlob, - _handle_opt: Option, - ) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Implementations must handle the context-specific command stream. - fn submit_cmd(&mut self, _commands: &mut [u8], _fence_ids: &[u64]) -> RutabagaResult<()>; - - /// Implementations may use `resource` in this context's command stream. - fn attach(&mut self, _resource: &mut RutabagaResource); - - /// Implementations must stop using `resource` in this context's command stream. - fn detach(&mut self, _resource: &RutabagaResource); - - /// Implementations must create a fence on specified `ring_idx` in `fence`. This - /// allows for multiple synchronizations timelines per RutabagaContext. - fn context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> { - Err(RutabagaError::Unsupported) - } - - /// Implementations must return the component type associated with the context. - fn component_type(&self) -> RutabagaComponentType; -} - -#[derive(Copy, Clone)] -struct RutabagaCapsetInfo { - pub capset_id: u32, - pub component: RutabagaComponentType, - pub name: &'static str, -} - -const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 9] = [ - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_VIRGL, - component: RutabagaComponentType::VirglRenderer, - name: "virgl", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_VIRGL2, - component: RutabagaComponentType::VirglRenderer, - name: "virgl2", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_GFXSTREAM_VULKAN, - component: RutabagaComponentType::Gfxstream, - name: "gfxstream-vulkan", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_VENUS, - component: RutabagaComponentType::VirglRenderer, - name: "venus", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN, - component: RutabagaComponentType::CrossDomain, - name: "cross-domain", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_DRM, - component: RutabagaComponentType::VirglRenderer, - name: "drm", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_GFXSTREAM_MAGMA, - component: RutabagaComponentType::Gfxstream, - name: "gfxstream-magma", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_GFXSTREAM_GLES, - component: RutabagaComponentType::Gfxstream, - name: "gfxstream-gles", - }, - RutabagaCapsetInfo { - capset_id: RUTABAGA_CAPSET_GFXSTREAM_COMPOSER, - component: RutabagaComponentType::Gfxstream, - name: "gfxstream-composer", - }, -]; - -pub fn calculate_capset_mask<'a, I: Iterator>(context_names: I) -> u64 { - let mut capset_mask = 0; - for name in context_names { - if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) { - capset_mask |= 1 << capset.capset_id; - }; - } - - capset_mask -} - -pub fn calculate_capset_names(capset_mask: u64) -> Vec { - RUTABAGA_CAPSETS - .iter() - .filter(|capset| capset_mask & (1 << capset.capset_id) != 0) - .map(|capset| capset.name.to_string()) - .collect() -} - -fn calculate_component(component_mask: u8) -> RutabagaResult { - if component_mask.count_ones() != 1 { - return Err(RutabagaError::SpecViolation("can't infer single component")); - } - - match component_mask.trailing_zeros() { - 0 => Ok(RutabagaComponentType::Rutabaga2D), - 1 => Ok(RutabagaComponentType::VirglRenderer), - 2 => Ok(RutabagaComponentType::Gfxstream), - 3 => Ok(RutabagaComponentType::CrossDomain), - _ => Err(RutabagaError::InvalidComponent), - } -} - -/// The global libary handle used to query capability sets, create resources and contexts. -/// -/// Currently, Rutabaga only supports one default component. Many components running at the -/// same time is a stretch goal of Rutabaga GFX. -/// -/// Not thread-safe, but can be made so easily. Making non-Rutabaga, C/C++ components -/// thread-safe is more difficult. -pub struct Rutabaga { - resources: Map, - contexts: Map>, - // Declare components after resources and contexts such that it is dropped last. - components: Map>, - default_component: RutabagaComponentType, - capset_info: Vec, - fence_handler: RutabagaFenceHandler, -} - -impl Rutabaga { - /// Take a snapshot of Rutabaga's current state. The snapshot is serialized into an opaque byte - /// stream and written to `w`. - /// - /// Only supports Mode2D. - pub fn snapshot(&self, w: &mut impl Write) -> RutabagaResult<()> { - // We current only support snapshotting Rutabaga2D. - if !(self.contexts.is_empty() - && self - .components - .keys() - .all(|t| *t == RutabagaComponentType::Rutabaga2D) - && self.default_component == RutabagaComponentType::Rutabaga2D - && self.capset_info.is_empty()) - { - return Err(RutabagaError::Unsupported); - } - let snapshot = RutabagaSnapshot { - resources: self - .resources - .iter() - .map(|(i, r)| { - if !(r.handle.is_none() - && !r.blob - && r.blob_mem == 0 - && r.blob_flags == 0 - && r.map_info.is_none() - && r.info_3d.is_none() - && r.vulkan_info.is_none() - && r.component_mask == 1 << (RutabagaComponentType::Rutabaga2D as u8) - && r.mapping.is_none()) - { - return Err(RutabagaError::Unsupported); - } - let info = r.info_2d.as_ref().ok_or(RutabagaError::Unsupported)?; - assert_eq!( - usize::try_from(info.width * info.height * 4).unwrap(), - info.host_mem.len() - ); - assert_eq!(usize::try_from(r.size).unwrap(), info.host_mem.len()); - let s = RutabagaResourceSnapshot { - resource_id: r.resource_id, - width: info.width, - height: info.height, - }; - Ok((*i, s)) - }) - .collect::>()?, - }; - - snapshot.serialize_to(w).map_err(RutabagaError::IoError) - } - - /// Restore Rutabaga to a previously snapshot'd state. - /// - /// Snapshotting on one host machine and then restoring on another ("host migration") might - /// work for very similar machines but isn't explicitly supported yet. - /// - /// Rutabaga will recreate resources internally, but it's the VMM's responsibility to re-attach - /// backing iovecs and re-map the memory after re-creation. Specifically: - /// - /// * Mode2D - /// * The VMM must call `Rutabaga::attach_backing` calls for all resources that had backing - /// memory at the time of the snapshot. - /// * ModeVirglRenderer - /// * Not supported. - /// * ModeGfxstream - /// * Not supported. - /// - /// NOTES: This is required because the pointers to backing memory aren't stable, help from the - /// VMM is necessary. In an alternative approach, the VMM could supply Rutabaga with callbacks - /// to translate to/from stable guest physical addresses, but it is unclear how well that - /// approach would scale to support 3D modes, which have others problems that require VMM help, - /// like resource handles. - pub fn restore(&mut self, r: &mut impl Read) -> RutabagaResult<()> { - let snapshot = RutabagaSnapshot::deserialize_from(r).map_err(RutabagaError::IoError)?; - - // We currently only support restoring to a fresh Rutabaga2D instance. - if !(self.resources.is_empty() - && self.contexts.is_empty() - && self - .components - .keys() - .all(|t| *t == RutabagaComponentType::Rutabaga2D) - && self.default_component == RutabagaComponentType::Rutabaga2D - && self.capset_info.is_empty()) - { - return Err(RutabagaError::Unsupported); - } - self.resources = snapshot - .resources - .into_iter() - .map(|(i, s)| { - let size = u64::from(s.width * s.height * 4); - let r = RutabagaResource { - resource_id: s.resource_id, - handle: None, - blob: false, - blob_mem: 0, - blob_flags: 0, - map_info: None, - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: Some(Rutabaga2DInfo { - width: s.width, - height: s.height, - host_mem: vec![0; usize::try_from(size).unwrap()], - }), - info_3d: None, - vulkan_info: None, - // NOTE: `RutabagaResource::backing_iovecs` isn't snapshotted because the - // pointers won't be valid at restore time, see the `Rutabaga::restore` doc. If - // the client doesn't attach new iovecs, the restored resource will behave as - // if they had been detached (instead of segfaulting on the stale iovec - // pointers). - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8), - size, - mapping: None, - }; - (i, r) - }) - .collect(); - - Ok(()) - } - - fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult { - let component = self - .capset_info - .iter() - .find(|capset_info| capset_info.capset_id == capset_id) - .ok_or(RutabagaError::InvalidCapset)? - .component; - - Ok(component) - } - - fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult { - let idx = index as usize; - if idx >= self.capset_info.len() { - return Err(RutabagaError::InvalidCapset); - } - - Ok(self.capset_info[idx]) - } - - /// Gets the version and size for the capabilty set `index`. - pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> { - let capset_info = self.capset_index_to_component_info(index)?; - - let component = self - .components - .get(&capset_info.component) - .ok_or(RutabagaError::InvalidComponent)?; - - let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id); - Ok((capset_info.capset_id, capset_version, capset_size)) - } - - /// Gets the capability set for the `capset_id` and `version`. - /// Each capability set is associated with a context type, which is associated - /// with a rutabaga component. - pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult> { - // The default workaround is just until context types are fully supported in all - // Google kernels. - let component_type = self - .capset_id_to_component_type(capset_id) - .unwrap_or(self.default_component); - - let component = self - .components - .get(&component_type) - .ok_or(RutabagaError::InvalidComponent)?; - - Ok(component.get_capset(capset_id, version)) - } - - /// Gets the number of capsets - pub fn get_num_capsets(&self) -> u32 { - self.capset_info.len() as u32 - } - - /// Forces context zero for the default rutabaga component. - pub fn force_ctx_0(&self) { - if let Some(component) = self.components.get(&self.default_component) { - component.force_ctx_0(); - } - } - - /// Creates a fence with the given `fence`. - /// If the flags include RUTABAGA_FLAG_INFO_RING_IDX, then the fence is created on a - /// specific timeline on the specific context. - pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 { - let ctx = self - .contexts - .get_mut(&fence.ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - - ctx.context_create_fence(fence)?; - } else { - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - component.create_fence(fence)?; - } - - Ok(()) - } - - /// Polls the default rutabaga component. - pub fn event_poll(&self) { - if let Some(component) = self.components.get(&self.default_component) { - component.event_poll(); - } - } - - /// Returns a pollable descriptor for the default rutabaga component. In practice, it is only - /// not None if the default component is virglrenderer. - pub fn poll_descriptor(&self) -> Option { - let component = self.components.get(&self.default_component).or(None)?; - component.poll_descriptor() - } - - /// Creates a resource with the `resource_create_3d` metadata. - pub fn resource_create_3d( - &mut self, - resource_id: u32, - resource_create_3d: ResourceCreate3D, - ) -> RutabagaResult<()> { - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - if self.resources.contains_key(&resource_id) { - return Err(RutabagaError::InvalidResourceId); - } - - let resource = component.create_3d(resource_id, resource_create_3d)?; - self.resources.insert(resource_id, resource); - Ok(()) - } - - /// Attaches `vecs` to the resource. - pub fn attach_backing( - &mut self, - resource_id: u32, - mut vecs: Vec, - ) -> RutabagaResult<()> { - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.attach_backing(resource_id, &mut vecs)?; - resource.backing_iovecs = Some(vecs); - Ok(()) - } - - /// Detaches any previously attached iovecs from the resource. - pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> { - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.detach_backing(resource_id); - resource.backing_iovecs = None; - Ok(()) - } - - /// Releases guest kernel reference on the resource. - pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> { - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - self.resources - .remove(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.unref_resource(resource_id); - Ok(()) - } - - /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource. For - /// HOST3D resources, this may flush caches, though this feature is unused by guest userspace. - pub fn transfer_write( - &mut self, - ctx_id: u32, - resource_id: u32, - transfer: Transfer3D, - ) -> RutabagaResult<()> { - let component = self - .components - .get(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.transfer_write(ctx_id, resource, transfer) - } - - /// 1) If specified, copies to `buf` from the host resource. - /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host - /// resource. For HOST3D resources, this may invalidate caches, though this feature is - /// unused by guest userspace. - pub fn transfer_read( - &mut self, - ctx_id: u32, - resource_id: u32, - transfer: Transfer3D, - buf: Option, - ) -> RutabagaResult<()> { - let component = self - .components - .get(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.transfer_read(ctx_id, resource, transfer, buf) - } - - pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> { - let component = self - .components - .get(&self.default_component) - .ok_or(RutabagaError::Unsupported)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - component.resource_flush(resource) - } - - /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata. - /// Associates `iovecs` with the resource, if there are any. Associates externally - /// created `handle` with the resource, if there is any. - pub fn resource_create_blob( - &mut self, - ctx_id: u32, - resource_id: u32, - resource_create_blob: ResourceCreateBlob, - iovecs: Option>, - handle: Option, - ) -> RutabagaResult<()> { - if self.resources.contains_key(&resource_id) { - return Err(RutabagaError::InvalidResourceId); - } - - let component = self - .components - .get_mut(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - let mut context = None; - // For the cross-domain context, we'll need to create the blob resource via a home-grown - // rutabaga context rather than one from an external C/C++ component. Use `ctx_id` and - // the component type if it happens to be a cross-domain context. - if ctx_id > 0 { - let ctx = self - .contexts - .get_mut(&ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - - if ctx.component_type() == RutabagaComponentType::CrossDomain { - context = Some(ctx); - } - } - - let resource = match context { - Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?, - None => { - component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)? - } - }; - - self.resources.insert(resource_id, resource); - Ok(()) - } - - pub fn resource_map( - &mut self, - resource_id: u32, - addr: u64, - size: u64, - prot: i32, - flags: i32, - ) -> RutabagaResult<()> { - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - let component_type = calculate_component(resource.component_mask)?; - if component_type == RutabagaComponentType::CrossDomain { - resource.mapping = None; - return Ok(()); - } - - let component = self - .components - .get(&component_type) - .ok_or(RutabagaError::InvalidComponent)?; - - component.resource_map(resource_id, addr, size, prot, flags) - } - - /// Returns a memory mapping of the blob resource. - pub fn map(&mut self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - let component_type = calculate_component(resource.component_mask)?; - if component_type == RutabagaComponentType::CrossDomain { - let handle_opt = resource.handle.take(); - match handle_opt { - Some(handle) => { - if handle.handle_type != RUTABAGA_MEM_HANDLE_TYPE_SHM { - return Err(RutabagaError::SpecViolation( - "expected a shared memory handle", - )); - } - - let clone = handle.try_clone()?; - let resource_size: usize = resource.size.try_into()?; - let map_info = resource - .map_info - .ok_or(RutabagaError::SpecViolation("no map info available"))?; - - // Creating the mapping closes the cloned descriptor. - let mapping = MemoryMapping::from_safe_descriptor( - clone.os_handle, - resource_size, - map_info, - )?; - let rutabaga_mapping = mapping.as_rutabaga_mapping(); - resource.handle = Some(handle); - resource.mapping = Some(mapping); - - return Ok(rutabaga_mapping); - } - None => return Err(RutabagaError::SpecViolation("expected a handle to map")), - } - } - - let component = self - .components - .get(&component_type) - .ok_or(RutabagaError::InvalidComponent)?; - - component.map(resource_id) - } - - /// Unmaps the blob resource from the default component - pub fn unmap(&mut self, resource_id: u32) -> RutabagaResult<()> { - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - let component_type = calculate_component(resource.component_mask)?; - if component_type == RutabagaComponentType::CrossDomain { - resource.mapping = None; - return Ok(()); - } - - let component = self - .components - .get(&component_type) - .ok_or(RutabagaError::InvalidComponent)?; - - component.unmap(resource_id) - } - - /// Returns the `map_info` of the blob resource. The valid values for `map_info` - /// are defined in the virtio-gpu spec. - pub fn map_info(&self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - resource - .map_info - .ok_or(RutabagaError::SpecViolation("no map info available")) - } - - /// Returns the `map_ptr` of the blob resource. - #[cfg(target_os = "macos")] - pub fn map_ptr(&self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - resource - .map_ptr - .ok_or(RutabagaError::SpecViolation("no map ptr available")) - } - - /// Returns the `vulkan_info` of the blob resource, which consists of the physical device - /// index and memory index associated with the resource. - pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo) - } - - /// Returns the 3D info associated with the resource, if any. - pub fn query(&self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - resource - .info_3d - .ok_or(RutabagaError::SpecViolation("no 3d info available")) - } - - /// Exports a blob resource. See virtio-gpu spec for blob flag use flags. - pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult { - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - // We can inspect blob flags only once guest minigbm is fully transitioned to blob. - let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE; - let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob; - - let opt = resource.handle.take(); - - match (opt, shareable) { - (Some(handle), true) => { - let clone = handle.try_clone()?; - resource.handle = Some(handle); - Ok(clone) - } - (Some(handle), false) => { - // Exactly one strong reference in this case. - let hnd = - Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?; - Ok(hnd) - } - _ => Err(RutabagaError::InvalidRutabagaHandle), - } - } - - /// Exports the given fence for import into other processes. - pub fn export_fence(&self, fence_id: u32) -> RutabagaResult { - let component = self - .components - .get(&self.default_component) - .ok_or(RutabagaError::InvalidComponent)?; - - component.export_fence(fence_id) - } - - /// Creates a context with the given `ctx_id` and `context_init` variable. - /// `context_init` is used to determine which rutabaga component creates the context. - pub fn create_context( - &mut self, - ctx_id: u32, - context_init: u32, - context_name: Option<&str>, - ) -> RutabagaResult<()> { - // The default workaround is just until context types are fully supported in all - // Google kernels. - let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK; - let component_type = self - .capset_id_to_component_type(capset_id) - .unwrap_or(self.default_component); - - let component = self - .components - .get_mut(&component_type) - .ok_or(RutabagaError::InvalidComponent)?; - - if self.contexts.contains_key(&ctx_id) { - return Err(RutabagaError::InvalidContextId); - } - - let ctx = component.create_context( - ctx_id, - context_init, - context_name, - self.fence_handler.clone(), - )?; - self.contexts.insert(ctx_id, ctx); - Ok(()) - } - - /// Destroys the context given by `ctx_id`. - pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> { - self.contexts - .remove(&ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - Ok(()) - } - - /// Attaches the resource given by `resource_id` to the context given by `ctx_id`. - pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> { - let ctx = self - .contexts - .get_mut(&ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - ctx.attach(resource); - Ok(()) - } - - /// Detaches the resource given by `resource_id` from the context given by `ctx_id`. - pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> { - let ctx = self - .contexts - .get_mut(&ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - - let resource = self - .resources - .get_mut(&resource_id) - .ok_or(RutabagaError::InvalidResourceId)?; - - ctx.detach(resource); - Ok(()) - } - - /// submits `commands` to the context given by `ctx_id`. - pub fn submit_command( - &mut self, - ctx_id: u32, - commands: &mut [u8], - fence_ids: &[u64], - ) -> RutabagaResult<()> { - let ctx = self - .contexts - .get_mut(&ctx_id) - .ok_or(RutabagaError::InvalidContextId)?; - - ctx.submit_cmd(commands, fence_ids) - } -} - -/// Rutabaga Builder, following the Rust builder pattern. -#[derive(Clone)] -pub struct RutabagaBuilder { - display_width: u32, - display_height: u32, - default_component: RutabagaComponentType, - gfxstream_flags: GfxstreamFlags, - virglrenderer_flags: VirglRendererFlags, - capset_mask: u64, - channels: Option>, - debug_handler: Option, - export_table: Option, -} - -impl RutabagaBuilder { - /// Create new a RutabagaBuilder. - pub fn new( - default_component: RutabagaComponentType, - virgl_flags: u32, - capset_mask: u64, - ) -> RutabagaBuilder { - let gfxstream_flags = GfxstreamFlags::new(); - RutabagaBuilder { - display_width: RUTABAGA_DEFAULT_WIDTH, - display_height: RUTABAGA_DEFAULT_HEIGHT, - default_component, - gfxstream_flags, - virglrenderer_flags: virgl_flags.into(), - capset_mask, - channels: None, - debug_handler: None, - export_table: None, - } - } - - /// Set display width for the RutabagaBuilder - pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder { - self.display_width = display_width; - self - } - - /// Set display height for the RutabagaBuilder - pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder { - self.display_height = display_height; - self - } - - /// Sets use EGL flags in gfxstream + virglrenderer. - pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_egl(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v); - self - } - - /// Sets use GLES in gfxstream + virglrenderer. - pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_gles(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v); - self - } - - /// Sets use GLX flags in gfxstream + virglrenderer. - pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_glx(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v); - self - } - - /// Sets use surfaceless flags in gfxstream + virglrenderer. - pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v); - self - } - - /// Sets use Vulkan in gfxstream + virglrenderer. - pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v); - self - } - - /// Sets use external blob in gfxstream + virglrenderer. - pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v); - self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v); - self - } - - /// Sets use system blob in gfxstream. - pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v); - self - } - - /// Sets use render server in virglrenderer. - pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder { - self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v); - self - } - - /// Sets use drm in virglrenderer. - pub fn set_use_drm(mut self, v: bool) -> RutabagaBuilder { - self.virglrenderer_flags = self.virglrenderer_flags.use_drm(v); - self - } - - /// Use the Vulkan swapchain to draw on the host window for gfxstream. - pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder { - self.gfxstream_flags = self.gfxstream_flags.set_wsi(v); - self - } - - /// Set rutabaga channels for the RutabagaBuilder - pub fn set_rutabaga_channels( - mut self, - channels: Option>, - ) -> RutabagaBuilder { - self.channels = channels; - self - } - - /// Set rutabaga channels for the RutabagaBuilder - pub fn set_debug_handler( - mut self, - debug_handler: Option, - ) -> RutabagaBuilder { - self.debug_handler = debug_handler; - self - } - - pub fn set_export_table(mut self, export_table: ExportTable) -> RutabagaBuilder { - self.export_table = Some(export_table); - self - } - - /// Builds Rutabaga and returns a handle to it. - /// - /// This should be only called once per every virtual machine instance. Rutabaga tries to - /// intialize all 3D components which have been built. In 2D mode, only the 2D component is - /// initialized. - pub fn build( - mut self, - fence_handler: RutabagaFenceHandler, - #[allow(unused_variables)] rutabaga_server_descriptor: Option, - ) -> RutabagaResult { - let mut rutabaga_components: Map> = - Default::default(); - - #[allow(unused_mut)] - let mut rutabaga_capsets: Vec = Default::default(); - - let capset_enabled = - |capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 }; - - let mut push_capset = |capset_id: u32| { - if let Some(capset) = RUTABAGA_CAPSETS - .iter() - .find(|capset| capset_id == capset.capset_id) - { - if self.capset_mask != 0 { - if capset_enabled(capset.capset_id) { - rutabaga_capsets.push(*capset); - } - } else { - // Unconditionally push capset -- this should eventually be deleted when context types are - // always specified by crosvm launchers. - rutabaga_capsets.push(*capset); - } - }; - }; - - if self.capset_mask != 0 { - let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN) - | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_MAGMA) - | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES) - | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER); - let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2) - | capset_enabled(RUTABAGA_CAPSET_VENUS) - | capset_enabled(RUTABAGA_CAPSET_DRM); - - if supports_gfxstream { - self.default_component = RutabagaComponentType::Gfxstream; - } else if supports_virglrenderer { - self.default_component = RutabagaComponentType::VirglRenderer; - } else { - self.default_component = RutabagaComponentType::CrossDomain; - } - - self.virglrenderer_flags = self - .virglrenderer_flags - .use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2)) - .use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS)) - .use_drm(capset_enabled(RUTABAGA_CAPSET_DRM)); - - self.gfxstream_flags = self - .gfxstream_flags - .use_gles(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES)) - .use_vulkan(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN)) - } - - // Make sure that disabled components are not used as default. - #[cfg(not(feature = "virgl_renderer"))] - if self.default_component == RutabagaComponentType::VirglRenderer { - return Err(RutabagaError::InvalidRutabagaBuild( - "virgl renderer feature not enabled", - )); - } - #[cfg(not(feature = "gfxstream"))] - if self.default_component == RutabagaComponentType::Gfxstream { - return Err(RutabagaError::InvalidRutabagaBuild( - "gfxstream feature not enabled", - )); - } - - if self.default_component == RutabagaComponentType::Rutabaga2D { - let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?; - rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d); - } else { - #[cfg(feature = "virgl_renderer")] - if self.default_component == RutabagaComponentType::VirglRenderer { - #[cfg(not(feature = "virgl_renderer_next"))] - let rutabaga_server_descriptor = None; - - let virgl = VirglRenderer::init( - self.virglrenderer_flags, - fence_handler.clone(), - rutabaga_server_descriptor, - )?; - rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl); - - push_capset(RUTABAGA_CAPSET_VIRGL); - push_capset(RUTABAGA_CAPSET_VIRGL2); - push_capset(RUTABAGA_CAPSET_VENUS); - push_capset(RUTABAGA_CAPSET_DRM); - } - - #[cfg(feature = "gfxstream")] - if self.default_component == RutabagaComponentType::Gfxstream { - let gfxstream = Gfxstream::init( - self.display_width, - self.display_height, - self.gfxstream_flags, - fence_handler.clone(), - self.debug_handler.clone(), - )?; - - rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream); - - push_capset(RUTABAGA_CAPSET_GFXSTREAM_VULKAN); - push_capset(RUTABAGA_CAPSET_GFXSTREAM_MAGMA); - push_capset(RUTABAGA_CAPSET_GFXSTREAM_GLES); - push_capset(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER); - } - - let cross_domain = CrossDomain::init( - self.channels, - fence_handler.clone(), - self.export_table.take(), - )?; - rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain); - push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN); - } - - Ok(Rutabaga { - resources: Default::default(), - contexts: Default::default(), - components: rutabaga_components, - default_component: self.default_component, - capset_info: rutabaga_capsets, - fence_handler, - }) - } -} - -#[cfg(test)] -mod tests { - use crate::*; - - fn new_2d() -> Rutabaga { - RutabagaBuilder::new(RutabagaComponentType::Rutabaga2D, 0, 0) - .build(RutabagaHandler::new(|_| {}), None) - .unwrap() - } - - #[test] - fn snapshot_restore_2d_no_resources() { - let mut buffer = std::io::Cursor::new(Vec::new()); - - let rutabaga1 = new_2d(); - rutabaga1.snapshot(&mut buffer).unwrap(); - - let mut rutabaga1 = new_2d(); - rutabaga1.restore(&mut &buffer.get_ref()[..]).unwrap(); - } - - #[test] - fn snapshot_restore_2d_one_resource() { - let resource_id = 123; - let resource_create_3d = ResourceCreate3D { - target: RUTABAGA_PIPE_TEXTURE_2D, - format: 1, - bind: RUTABAGA_PIPE_BIND_RENDER_TARGET, - width: 100, - height: 200, - depth: 1, - array_size: 1, - last_level: 0, - nr_samples: 0, - flags: 0, - }; - - let mut buffer = std::io::Cursor::new(Vec::new()); - - let mut rutabaga1 = new_2d(); - rutabaga1 - .resource_create_3d(resource_id, resource_create_3d) - .unwrap(); - rutabaga1 - .attach_backing( - resource_id, - vec![RutabagaIovec { - base: std::ptr::null_mut(), - len: 456, - }], - ) - .unwrap(); - rutabaga1.snapshot(&mut buffer).unwrap(); - - let mut rutabaga2 = new_2d(); - rutabaga2.restore(&mut &buffer.get_ref()[..]).unwrap(); - - assert_eq!(rutabaga2.resources.len(), 1); - let rutabaga_resource = rutabaga2.resources.get(&resource_id).unwrap(); - assert_eq!(rutabaga_resource.resource_id, resource_id); - assert_eq!( - rutabaga_resource.info_2d.as_ref().unwrap().width, - resource_create_3d.width - ); - assert_eq!( - rutabaga_resource.info_2d.as_ref().unwrap().height, - resource_create_3d.height - ); - // NOTE: We attached an backing iovec, but it should be gone post-restore. - assert!(rutabaga_resource.backing_iovecs.is_none()); - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/formats.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/formats.rs deleted file mode 100644 index ebfc311ba..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/formats.rs +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! formats: Utility file for dealing with DRM and VK formats, and canonical -//! size calculations. - -use std::fmt; - -#[cfg(feature = "vulkano")] -use vulkano::format::Format as VulkanFormat; -#[cfg(feature = "vulkano")] -use vulkano::image::ImageAspect as VulkanImageAspect; - -use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo; -use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements; -use crate::rutabaga_utils::*; - -/* - * This list is based on Sommelier / cros_gralloc guest userspace. Formats that are never - * used by guest userspace (i.e, DRM_FORMAT_RGB332) are left out for simplicity. - */ - -pub const DRM_FORMAT_R8: [u8; 4] = [b'R', b'8', b' ', b' ']; - -pub const DRM_FORMAT_RGB565: [u8; 4] = [b'R', b'G', b'1', b'6']; -pub const DRM_FORMAT_BGR888: [u8; 4] = [b'B', b'G', b'2', b'4']; - -pub const DRM_FORMAT_XRGB8888: [u8; 4] = [b'X', b'R', b'2', b'4']; -pub const DRM_FORMAT_XBGR8888: [u8; 4] = [b'X', b'B', b'2', b'4']; - -pub const DRM_FORMAT_ARGB8888: [u8; 4] = [b'A', b'R', b'2', b'4']; -pub const DRM_FORMAT_ABGR8888: [u8; 4] = [b'A', b'B', b'2', b'4']; - -pub const DRM_FORMAT_XRGB2101010: [u8; 4] = [b'X', b'R', b'3', b'0']; -pub const DRM_FORMAT_XBGR2101010: [u8; 4] = [b'X', b'B', b'3', b'0']; -pub const DRM_FORMAT_ARGB2101010: [u8; 4] = [b'A', b'R', b'3', b'0']; -pub const DRM_FORMAT_ABGR2101010: [u8; 4] = [b'A', b'B', b'3', b'0']; - -pub const DRM_FORMAT_ABGR16161616F: [u8; 4] = [b'A', b'B', b'4', b'H']; - -pub const DRM_FORMAT_NV12: [u8; 4] = [b'N', b'V', b'1', b'2']; -pub const DRM_FORMAT_YVU420: [u8; 4] = [b'Y', b'V', b'1', b'2']; - -/// A [fourcc](https://en.wikipedia.org/wiki/FourCC) format identifier. -#[derive(Copy, Clone, Eq, PartialEq, Default)] -pub struct DrmFormat(pub u32); - -/// Planar properties associated with each `DrmFormat`. Copied from helpers.c in minigbm. -#[derive(Copy, Clone)] -pub struct PlanarLayout { - pub num_planes: usize, - horizontal_subsampling: [u32; 3], - vertical_subsampling: [u32; 3], - bytes_per_pixel: [u32; 3], -} - -static PACKED_1BPP: PlanarLayout = PlanarLayout { - num_planes: 1, - horizontal_subsampling: [1, 0, 0], - vertical_subsampling: [1, 0, 0], - bytes_per_pixel: [1, 0, 0], -}; - -static PACKED_2BPP: PlanarLayout = PlanarLayout { - num_planes: 1, - horizontal_subsampling: [1, 0, 0], - vertical_subsampling: [1, 0, 0], - bytes_per_pixel: [2, 0, 0], -}; - -static PACKED_3BPP: PlanarLayout = PlanarLayout { - num_planes: 1, - horizontal_subsampling: [1, 0, 0], - vertical_subsampling: [1, 0, 0], - bytes_per_pixel: [3, 0, 0], -}; - -static PACKED_4BPP: PlanarLayout = PlanarLayout { - num_planes: 1, - horizontal_subsampling: [1, 0, 0], - vertical_subsampling: [1, 0, 0], - bytes_per_pixel: [4, 0, 0], -}; - -static PACKED_8BPP: PlanarLayout = PlanarLayout { - num_planes: 1, - horizontal_subsampling: [1, 0, 0], - vertical_subsampling: [1, 0, 0], - bytes_per_pixel: [8, 0, 0], -}; - -static BIPLANAR_YUV420: PlanarLayout = PlanarLayout { - num_planes: 2, - horizontal_subsampling: [1, 2, 0], - vertical_subsampling: [1, 2, 0], - bytes_per_pixel: [1, 2, 0], -}; - -static TRIPLANAR_YUV420: PlanarLayout = PlanarLayout { - num_planes: 3, - horizontal_subsampling: [1, 2, 2], - vertical_subsampling: [1, 2, 2], - bytes_per_pixel: [1, 1, 1], -}; - -impl DrmFormat { - /// Constructs a format identifer using a fourcc byte sequence. - #[inline(always)] - pub fn new(a: u8, b: u8, c: u8, d: u8) -> DrmFormat { - DrmFormat((a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)) - } - - /// Returns the fourcc code as a sequence of bytes. - #[inline(always)] - pub fn to_bytes(&self) -> [u8; 4] { - let f = self.0; - [f as u8, (f >> 8) as u8, (f >> 16) as u8, (f >> 24) as u8] - } - - /// Returns the planar layout of the format. - pub fn planar_layout(&self) -> RutabagaResult { - match self.to_bytes() { - DRM_FORMAT_R8 => Ok(PACKED_1BPP), - DRM_FORMAT_RGB565 => Ok(PACKED_2BPP), - DRM_FORMAT_BGR888 => Ok(PACKED_3BPP), - DRM_FORMAT_ABGR2101010 - | DRM_FORMAT_ABGR8888 - | DRM_FORMAT_XBGR2101010 - | DRM_FORMAT_XBGR8888 - | DRM_FORMAT_ARGB2101010 - | DRM_FORMAT_ARGB8888 - | DRM_FORMAT_XRGB2101010 - | DRM_FORMAT_XRGB8888 => Ok(PACKED_4BPP), - DRM_FORMAT_ABGR16161616F => Ok(PACKED_8BPP), - DRM_FORMAT_NV12 => Ok(BIPLANAR_YUV420), - DRM_FORMAT_YVU420 => Ok(TRIPLANAR_YUV420), - _ => Err(RutabagaError::InvalidGrallocDrmFormat), - } - } - - #[cfg(feature = "vulkano")] - /// Returns the Vulkan format from the DrmFormat. - pub fn vulkan_format(&self) -> RutabagaResult { - match self.to_bytes() { - DRM_FORMAT_R8 => Ok(VulkanFormat::R8_UNORM), - DRM_FORMAT_RGB565 => Ok(VulkanFormat::R5G6B5_UNORM_PACK16), - DRM_FORMAT_BGR888 => Ok(VulkanFormat::R8G8B8_UNORM), - DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => { - Ok(VulkanFormat::A2R10G10B10_UNORM_PACK32) - } - DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(VulkanFormat::R8G8B8A8_UNORM), - DRM_FORMAT_ARGB2101010 | DRM_FORMAT_XRGB2101010 => { - Ok(VulkanFormat::A2B10G10R10_UNORM_PACK32) - } - DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(VulkanFormat::B8G8R8A8_UNORM), - DRM_FORMAT_ABGR16161616F => Ok(VulkanFormat::R16G16B16A16_SFLOAT), - DRM_FORMAT_NV12 => Ok(VulkanFormat::G8_B8R8_2PLANE_420_UNORM), - DRM_FORMAT_YVU420 => Ok(VulkanFormat::G8_B8_R8_3PLANE_420_UNORM), - _ => Err(RutabagaError::InvalidGrallocDrmFormat), - } - } - - #[cfg(feature = "vulkano")] - /// Returns the Vulkan format from the DrmFormat. - pub fn vulkan_image_aspect(&self, plane: usize) -> RutabagaResult { - match self.to_bytes() { - DRM_FORMAT_R8 - | DRM_FORMAT_RGB565 - | DRM_FORMAT_BGR888 - | DRM_FORMAT_ABGR2101010 - | DRM_FORMAT_ABGR8888 - | DRM_FORMAT_XBGR2101010 - | DRM_FORMAT_XBGR8888 - | DRM_FORMAT_ARGB2101010 - | DRM_FORMAT_ARGB8888 - | DRM_FORMAT_XRGB2101010 - | DRM_FORMAT_XRGB8888 => Ok(VulkanImageAspect::Color), - DRM_FORMAT_NV12 => match plane { - 0 => Ok(VulkanImageAspect::Plane0), - 1 => Ok(VulkanImageAspect::Plane1), - _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes), - }, - DRM_FORMAT_YVU420 => match plane { - 0 => Ok(VulkanImageAspect::Plane0), - 1 => Ok(VulkanImageAspect::Plane1), - 2 => Ok(VulkanImageAspect::Plane2), - _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes), - }, - _ => Err(RutabagaError::InvalidGrallocDrmFormat), - } - } -} - -impl From for DrmFormat { - fn from(u: u32) -> DrmFormat { - DrmFormat(u) - } -} - -impl From for u32 { - fn from(f: DrmFormat) -> u32 { - f.0 - } -} - -impl fmt::Debug for DrmFormat { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let b = self.to_bytes(); - if b.iter().all(u8::is_ascii_graphic) { - write!( - f, - "fourcc({}{}{}{})", - b[0] as char, b[1] as char, b[2] as char, b[3] as char - ) - } else { - write!( - f, - "fourcc(0x{:02x}{:02x}{:02x}{:02x})", - b[0], b[1], b[2], b[3] - ) - } - } -} - -fn stride_from_layout(layout: &PlanarLayout, width: u32, plane: usize) -> RutabagaResult { - let bytes_per_pixel = layout.bytes_per_pixel[plane]; - let horizontal_subsampling = layout.horizontal_subsampling[plane]; - let subsampled_width = checked_arithmetic!(width / horizontal_subsampling)?; - let stride = checked_arithmetic!(bytes_per_pixel * subsampled_width)?; - Ok(stride) -} - -pub fn canonical_image_requirements( - info: ImageAllocationInfo, -) -> RutabagaResult { - let mut image_requirements: ImageMemoryRequirements = Default::default(); - let mut size: u32 = 0; - let layout = info.drm_format.planar_layout()?; - for plane in 0..layout.num_planes { - let plane_stride = stride_from_layout(&layout, info.width, plane)?; - image_requirements.strides[plane] = plane_stride; - if plane > 0 { - image_requirements.offsets[plane] = size; - } - - let height = info.height; - let vertical_subsampling = layout.vertical_subsampling[plane]; - let subsampled_height = checked_arithmetic!(height / vertical_subsampling)?; - let plane_size = checked_arithmetic!(subsampled_height * plane_stride)?; - size = checked_arithmetic!(size + plane_size)?; - } - - image_requirements.info = info; - image_requirements.size = size as u64; - Ok(image_requirements) -} - -#[cfg(test)] -mod tests { - use std::fmt::Write; - - use super::*; - use crate::rutabaga_gralloc::RutabagaGrallocFlags; - - #[test] - fn format_debug() { - let f = DrmFormat::new(b'X', b'R', b'2', b'4'); - let mut buf = String::new(); - write!(&mut buf, "{f:?}").unwrap(); - assert_eq!(buf, "fourcc(XR24)"); - - let f = DrmFormat::new(0, 1, 2, 16); - let mut buf = String::new(); - write!(&mut buf, "{f:?}").unwrap(); - assert_eq!(buf, "fourcc(0x00010210)"); - } - - #[test] - fn canonical_formats() { - let mut info = ImageAllocationInfo { - width: 10, - height: 10, - drm_format: DrmFormat::new(b'R', b'8', b' ', b' '), - flags: RutabagaGrallocFlags::empty(), - }; - - let r8_reqs = canonical_image_requirements(info).unwrap(); - - assert_eq!(r8_reqs.info.width, 10); - assert_eq!(r8_reqs.info.height, 10); - assert_eq!(r8_reqs.strides[0], 10); - assert_eq!(r8_reqs.strides[1], 0); - assert_eq!(r8_reqs.strides[2], 0); - - assert_eq!(r8_reqs.offsets[0], 0); - assert_eq!(r8_reqs.offsets[1], 0); - assert_eq!(r8_reqs.offsets[2], 0); - - assert_eq!(r8_reqs.size, 100); - - info.drm_format = DrmFormat::new(b'X', b'R', b'2', b'4'); - let xr24_reqs = canonical_image_requirements(info).unwrap(); - - assert_eq!(xr24_reqs.info.width, 10); - assert_eq!(xr24_reqs.info.height, 10); - assert_eq!(xr24_reqs.strides[0], 40); - assert_eq!(xr24_reqs.strides[1], 0); - assert_eq!(xr24_reqs.strides[2], 0); - - assert_eq!(xr24_reqs.offsets[0], 0); - assert_eq!(xr24_reqs.offsets[1], 0); - assert_eq!(xr24_reqs.offsets[2], 0); - - assert_eq!(xr24_reqs.size, 400); - } - - #[test] - fn canonical_planar_formats() { - let mut info = ImageAllocationInfo { - width: 10, - height: 10, - drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'), - flags: RutabagaGrallocFlags::empty(), - }; - - let nv12_reqs = canonical_image_requirements(info).unwrap(); - - assert_eq!(nv12_reqs.info.width, 10); - assert_eq!(nv12_reqs.info.height, 10); - assert_eq!(nv12_reqs.strides[0], 10); - assert_eq!(nv12_reqs.strides[1], 10); - assert_eq!(nv12_reqs.strides[2], 0); - - assert_eq!(nv12_reqs.offsets[0], 0); - assert_eq!(nv12_reqs.offsets[1], 100); - assert_eq!(nv12_reqs.offsets[2], 0); - - assert_eq!(nv12_reqs.size, 150); - - info.drm_format = DrmFormat::new(b'Y', b'V', b'1', b'2'); - let yv12_reqs = canonical_image_requirements(info).unwrap(); - - assert_eq!(yv12_reqs.info.width, 10); - assert_eq!(yv12_reqs.info.height, 10); - assert_eq!(yv12_reqs.strides[0], 10); - assert_eq!(yv12_reqs.strides[1], 5); - assert_eq!(yv12_reqs.strides[2], 5); - - assert_eq!(yv12_reqs.offsets[0], 0); - assert_eq!(yv12_reqs.offsets[1], 100); - assert_eq!(yv12_reqs.offsets[2], 125); - - assert_eq!(yv12_reqs.size, 150); - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs deleted file mode 100644 index 5002716e7..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! gralloc: Cross-platform, Rust-based, Vulkan centric GPU allocation and -//! mapping. - -use std::collections::BTreeMap as Map; - -#[cfg(feature = "vulkano")] -use log::error; - -use crate::rutabaga_gralloc::formats::*; -#[cfg(feature = "minigbm")] -use crate::rutabaga_gralloc::minigbm::MinigbmDevice; -use crate::rutabaga_gralloc::system_gralloc::SystemGralloc; -#[cfg(feature = "vulkano")] -use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc; -use crate::rutabaga_os::round_up_to_page_size; -use crate::rutabaga_os::MappedRegion; -use crate::rutabaga_utils::*; - -/* - * Rutabaga gralloc flags are copied from minigbm, but redundant legacy flags are left out. - * For example, USE_WRITE / USE_CURSOR_64X64 / USE_CURSOR don't add much value. - */ -const RUTABAGA_GRALLOC_USE_SCANOUT: u32 = 1 << 0; -const RUTABAGA_GRALLOC_USE_RENDERING: u32 = 1 << 2; -const RUTABAGA_GRALLOC_USE_LINEAR: u32 = 1 << 4; -const RUTABAGA_GRALLOC_USE_TEXTURING: u32 = 1 << 5; -const RUTABAGA_GRALLOC_USE_CAMERA_WRITE: u32 = 1 << 6; -const RUTABAGA_GRALLOC_USE_CAMERA_READ: u32 = 1 << 7; -#[allow(dead_code)] -const RUTABAGA_GRALLOC_USE_PROTECTED: u32 = 1 << 8; - -/* SW_{WRITE,READ}_RARELY omitted since not even Android uses this much. */ -const RUTABAGA_GRALLOC_USE_SW_READ_OFTEN: u32 = 1 << 9; -const RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN: u32 = 1 << 11; - -#[allow(dead_code)] -const RUTABAGA_GRALLOC_VIDEO_DECODER: u32 = 1 << 13; -#[allow(dead_code)] -const RUTABAGA_GRALLOC_VIDEO_ENCODER: u32 = 1 << 14; - -/// Usage flags for constructing a buffer object. -#[derive(Copy, Clone, Eq, PartialEq, Default)] -pub struct RutabagaGrallocFlags(pub u32); - -impl RutabagaGrallocFlags { - /// Returns empty set of flags. - #[inline(always)] - pub fn empty() -> RutabagaGrallocFlags { - RutabagaGrallocFlags(0) - } - - /// Returns the given set of raw `RUTABAGA_GRALLOC` flags wrapped in a RutabagaGrallocFlags - /// struct. - #[inline(always)] - pub fn new(raw: u32) -> RutabagaGrallocFlags { - RutabagaGrallocFlags(raw) - } - - /// Sets the scanout flag's presence. - #[inline(always)] - pub fn use_scanout(self, e: bool) -> RutabagaGrallocFlags { - if e { - RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SCANOUT) - } else { - RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SCANOUT) - } - } - - /// Sets the rendering flag's presence. - #[inline(always)] - pub fn use_rendering(self, e: bool) -> RutabagaGrallocFlags { - if e { - RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_RENDERING) - } else { - RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_RENDERING) - } - } - - /// Sets the linear flag's presence. - #[inline(always)] - pub fn use_linear(self, e: bool) -> RutabagaGrallocFlags { - if e { - RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_LINEAR) - } else { - RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_LINEAR) - } - } - - /// Sets the SW write flag's presence. - #[inline(always)] - pub fn use_sw_write(self, e: bool) -> RutabagaGrallocFlags { - if e { - RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN) - } else { - RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN) - } - } - - /// Sets the SW read flag's presence. - #[inline(always)] - pub fn use_sw_read(self, e: bool) -> RutabagaGrallocFlags { - if e { - RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_READ_OFTEN) - } else { - RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_READ_OFTEN) - } - } - - /// Returns true if the texturing flag is set. - #[inline(always)] - pub fn uses_texturing(self) -> bool { - self.0 & RUTABAGA_GRALLOC_USE_TEXTURING != 0 - } - - /// Returns true if the rendering flag is set. - #[inline(always)] - pub fn uses_rendering(self) -> bool { - self.0 & RUTABAGA_GRALLOC_USE_RENDERING != 0 - } - - /// Returns true if the memory will accessed by the CPU or an IP block that prefers host - /// visible allocations (i.e, camera). - #[inline(always)] - pub fn host_visible(self) -> bool { - self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0 - || self.0 & RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN != 0 - || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_WRITE != 0 - || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0 - } - - /// Returns true if the memory will read by the CPU or an IP block that prefers cached - /// allocations (i.e, camera). - #[inline(always)] - pub fn host_cached(self) -> bool { - self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0 - || self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0 - } -} - -/// Information required to allocate a swapchain image. -#[derive(Copy, Clone, Default)] -pub struct ImageAllocationInfo { - pub width: u32, - pub height: u32, - pub drm_format: DrmFormat, - pub flags: RutabagaGrallocFlags, -} - -/// The memory requirements, compression and layout of a swapchain image. -#[derive(Copy, Clone, Default)] -pub struct ImageMemoryRequirements { - pub info: ImageAllocationInfo, - pub map_info: u32, - pub strides: [u32; 4], - pub offsets: [u32; 4], - pub modifier: u64, - pub size: u64, - pub vulkan_info: Option, -} - -/// Trait that needs to be implemented to service graphics memory requests. Two step allocation -/// process: -/// -/// (1) Get memory requirements for a given allocation request. -/// (2) Allocate using those requirements. -pub trait Gralloc: Send { - /// This function must return true if the implementation can: - /// - /// (1) allocate GPU memory and - /// (2) {export to}/{import from} into a OS-specific RutabagaHandle. - fn supports_external_gpu_memory(&self) -> bool; - - /// This function must return true the implementation can {export to}/{import from} a Linux - /// dma-buf. This often used for sharing with the scanout engine or multimedia subsystems. - fn supports_dmabuf(&self) -> bool; - - /// Implementations must return the resource layout, compression, and caching properties of - /// an allocation request. - fn get_image_memory_requirements( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult; - - /// Implementations must allocate memory given the requirements and return a RutabagaHandle - /// upon success. - fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult; - - /// Implementations must import the given `handle` and return a mapping, suitable for use with - /// KVM and other hypervisors. This is optional and only works with the Vulkano backend. - fn import_and_map( - &mut self, - _handle: RutabagaHandle, - _vulkan_info: VulkanInfo, - _size: u64, - ) -> RutabagaResult> { - Err(RutabagaError::Unsupported) - } -} - -/// Enumeration of possible allocation backends. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] -pub enum GrallocBackend { - #[allow(dead_code)] - Vulkano, - #[allow(dead_code)] - Minigbm, - System, -} - -/// A container for a variety of allocation backends. -pub struct RutabagaGralloc { - grallocs: Map>, -} - -impl RutabagaGralloc { - /// Returns a new RutabagaGralloc instance upon success. All allocation backends that have - /// been built are initialized. The default system allocator is always initialized. - pub fn new() -> RutabagaResult { - let mut grallocs: Map> = Default::default(); - - let system = SystemGralloc::init()?; - grallocs.insert(GrallocBackend::System, system); - - #[cfg(feature = "minigbm")] - { - // crosvm integration tests build with the "wl-dmabuf" feature, which translates in - // rutabaga to the "minigbm" feature. These tests run on hosts where a rendernode is - // not present, and minigbm can not be initialized. - // - // Thus, to keep kokoro happy, allow minigbm initialization to fail silently for now. - if let Ok(gbm_device) = MinigbmDevice::init() { - grallocs.insert(GrallocBackend::Minigbm, gbm_device); - } - } - - #[cfg(feature = "vulkano")] - { - match VulkanoGralloc::init() { - Ok(vulkano) => { - grallocs.insert(GrallocBackend::Vulkano, vulkano); - } - Err(e) => { - error!("failed to init Vulkano gralloc: {:?}", e); - } - } - } - - Ok(RutabagaGralloc { grallocs }) - } - - /// Returns true if one of the allocation backends supports GPU external memory. - pub fn supports_external_gpu_memory(&self) -> bool { - for gralloc in self.grallocs.values() { - if gralloc.supports_external_gpu_memory() { - return true; - } - } - - false - } - - /// Returns true if one of the allocation backends supports dma_buf. - pub fn supports_dmabuf(&self) -> bool { - for gralloc in self.grallocs.values() { - if gralloc.supports_dmabuf() { - return true; - } - } - - false - } - - /// Returns the best allocation backend to service a particular request. - fn determine_optimal_backend(&self, _info: ImageAllocationInfo) -> GrallocBackend { - // This function could be more sophisticated and consider the allocation info. For example, - // nobody has ever tried Mali allocated memory + a mediatek/rockchip display and as such it - // probably doesn't work. In addition, YUV calculations in minigbm have yet to make it - // towards the Vulkan api. This function allows for a variety of quirks, but for now just - // choose the most shiny backend that the user has built. The rationale is "why would you - // build it if you don't want to use it". - #[allow(clippy::let_and_return)] - let mut _backend = GrallocBackend::System; - - #[cfg(feature = "minigbm")] - { - // See note on "wl-dmabuf" and Kokoro in Gralloc::new(). - if self.grallocs.contains_key(&GrallocBackend::Minigbm) { - _backend = GrallocBackend::Minigbm; - } - } - - #[cfg(feature = "vulkano")] - { - _backend = GrallocBackend::Vulkano; - } - - _backend - } - - /// Returns a image memory requirements for the given `info` upon success. - pub fn get_image_memory_requirements( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult { - let backend = self.determine_optimal_backend(info); - - let gralloc = self - .grallocs - .get_mut(&backend) - .ok_or(RutabagaError::InvalidGrallocBackend)?; - - let mut reqs = gralloc.get_image_memory_requirements(info)?; - reqs.size = round_up_to_page_size(reqs.size)?; - Ok(reqs) - } - - /// Allocates memory given the particular `reqs` upon success. - pub fn allocate_memory( - &mut self, - reqs: ImageMemoryRequirements, - ) -> RutabagaResult { - let backend = self.determine_optimal_backend(reqs.info); - - let gralloc = self - .grallocs - .get_mut(&backend) - .ok_or(RutabagaError::InvalidGrallocBackend)?; - - gralloc.allocate_memory(reqs) - } - - /// Imports the `handle` using the given `vulkan_info`. Returns a mapping using Vulkano upon - /// success. Should not be used with minigbm or system gralloc backends. - pub fn import_and_map( - &mut self, - handle: RutabagaHandle, - vulkan_info: VulkanInfo, - size: u64, - ) -> RutabagaResult> { - let gralloc = self - .grallocs - .get_mut(&GrallocBackend::Vulkano) - .ok_or(RutabagaError::InvalidGrallocBackend)?; - - gralloc.import_and_map(handle, vulkan_info, size) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[cfg_attr(target_os = "windows", ignore)] - fn create_render_target() { - let gralloc_result = RutabagaGralloc::new(); - if gralloc_result.is_err() { - return; - } - - let mut gralloc = gralloc_result.unwrap(); - - let info = ImageAllocationInfo { - width: 512, - height: 1024, - drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'), - flags: RutabagaGrallocFlags::empty().use_scanout(true), - }; - - let reqs = gralloc.get_image_memory_requirements(info).unwrap(); - let min_reqs = canonical_image_requirements(info).unwrap(); - - assert!(reqs.strides[0] >= min_reqs.strides[0]); - assert!(reqs.size >= min_reqs.size); - - let _handle = gralloc.allocate_memory(reqs).unwrap(); - - // Reallocate with same requirements - let _handle2 = gralloc.allocate_memory(reqs).unwrap(); - } - - #[test] - #[cfg_attr(target_os = "windows", ignore)] - fn create_video_buffer() { - let gralloc_result = RutabagaGralloc::new(); - if gralloc_result.is_err() { - return; - } - - let mut gralloc = gralloc_result.unwrap(); - - let info = ImageAllocationInfo { - width: 512, - height: 1024, - drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'), - flags: RutabagaGrallocFlags::empty().use_linear(true), - }; - - let reqs = gralloc.get_image_memory_requirements(info).unwrap(); - let min_reqs = canonical_image_requirements(info).unwrap(); - - assert!(reqs.strides[0] >= min_reqs.strides[0]); - assert!(reqs.strides[1] >= min_reqs.strides[1]); - assert_eq!(reqs.strides[2], 0); - assert_eq!(reqs.strides[3], 0); - - assert!(reqs.offsets[0] >= min_reqs.offsets[0]); - assert!(reqs.offsets[1] >= min_reqs.offsets[1]); - assert_eq!(reqs.offsets[2], 0); - assert_eq!(reqs.offsets[3], 0); - - assert!(reqs.size >= min_reqs.size); - - let _handle = gralloc.allocate_memory(reqs).unwrap(); - - // Reallocate with same requirements - let _handle2 = gralloc.allocate_memory(reqs).unwrap(); - } - - #[test] - #[cfg_attr(target_os = "windows", ignore)] - fn export_and_map() { - let gralloc_result = RutabagaGralloc::new(); - if gralloc_result.is_err() { - return; - } - - let mut gralloc = gralloc_result.unwrap(); - - let info = ImageAllocationInfo { - width: 512, - height: 1024, - drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'), - flags: RutabagaGrallocFlags::empty() - .use_linear(true) - .use_sw_write(true) - .use_sw_read(true), - }; - - let mut reqs = gralloc.get_image_memory_requirements(info).unwrap(); - - // Anything else can use the mmap(..) system call. - if reqs.vulkan_info.is_none() { - return; - } - - let handle = gralloc.allocate_memory(reqs).unwrap(); - let vulkan_info = reqs.vulkan_info.take().unwrap(); - - let mapping = gralloc - .import_and_map(handle, vulkan_info, reqs.size) - .unwrap(); - - let addr = mapping.as_ptr(); - let size = mapping.size(); - - assert_eq!(size as u64, reqs.size); - assert_ne!(addr as *const u8, std::ptr::null()); - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs deleted file mode 100644 index 346b6bfa4..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2018 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! minigbm: implements swapchain allocation using ChromeOS's minigbm library. -//! -//! External code found at . - -#![cfg(feature = "minigbm")] - -use std::ffi::CStr; -use std::fs::File; -use std::io::Error; -use std::io::Seek; -use std::io::SeekFrom; -use std::os::raw::c_char; -use std::sync::Arc; - -use crate::rutabaga_gralloc::formats::DrmFormat; -use crate::rutabaga_gralloc::gralloc::Gralloc; -use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo; -use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements; -use crate::rutabaga_gralloc::minigbm_bindings::*; -use crate::rutabaga_gralloc::rendernode; -use crate::rutabaga_os::AsRawDescriptor; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_utils::*; - -struct MinigbmDeviceInner { - _fd: File, - gbm: *mut gbm_device, -} - -// Safe because minigbm handles synchronization internally. -unsafe impl Send for MinigbmDeviceInner {} -unsafe impl Sync for MinigbmDeviceInner {} - -impl Drop for MinigbmDeviceInner { - fn drop(&mut self) { - // Safe because MinigbmDeviceInner is only constructed with a valid minigbm_device. - unsafe { - gbm_device_destroy(self.gbm); - } - } -} - -/// A device capable of allocating `MinigbmBuffer`. -#[derive(Clone)] -pub struct MinigbmDevice { - minigbm_device: Arc, - last_buffer: Option>, - device_name: &'static str, -} - -impl MinigbmDevice { - /// Returns a new `MinigbmDevice` if there is a rendernode in `/dev/dri/` that is accepted by - /// the minigbm library. - pub fn init() -> RutabagaResult> { - let undesired: &[&str] = &["vgem", "pvr"]; - let fd = rendernode::open_device(undesired)?; - - // gbm_create_device is safe to call with a valid fd, and we check that a valid one is - // returned. If the fd does not refer to a DRM device, gbm_create_device will reject it. - let gbm = unsafe { gbm_create_device(fd.as_raw_descriptor()) }; - if gbm.is_null() { - return Err(RutabagaError::IoError(Error::last_os_error())); - } - - // Safe because a valid minigbm device has a statically allocated string associated with - // it, which is valid for the lifetime of the process. - let backend_name: *const c_char = unsafe { gbm_device_get_backend_name(gbm) }; - let c_str: &CStr = unsafe { CStr::from_ptr(backend_name) }; - let device_name: &str = c_str.to_str()?; - - Ok(Box::new(MinigbmDevice { - minigbm_device: Arc::new(MinigbmDeviceInner { _fd: fd, gbm }), - last_buffer: None, - device_name, - })) - } -} - -impl Gralloc for MinigbmDevice { - fn supports_external_gpu_memory(&self) -> bool { - true - } - - fn supports_dmabuf(&self) -> bool { - true - } - - fn get_image_memory_requirements( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult { - let bo = unsafe { - gbm_bo_create( - self.minigbm_device.gbm, - info.width, - info.height, - info.drm_format.0, - info.flags.0, - ) - }; - if bo.is_null() { - return Err(RutabagaError::IoError(Error::last_os_error())); - } - - let mut reqs: ImageMemoryRequirements = Default::default(); - let gbm_buffer = MinigbmBuffer(bo, self.clone()); - - // Intel GPUs typically only use cached memory buffers. This will change with dGPUs, but - // perhaps minigbm will be deprecated by then. Other display drivers (rockchip, mediatek, - // amdgpu) typically use write combine memory. We can also consider use flags too if this - // heuristic proves insufficient. - if self.device_name == "i915" { - reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; - } else { - reqs.map_info = RUTABAGA_MAP_CACHE_WC; - } - - reqs.modifier = gbm_buffer.format_modifier(); - for plane in 0..gbm_buffer.num_planes() { - reqs.strides[plane] = gbm_buffer.plane_stride(plane); - reqs.offsets[plane] = gbm_buffer.plane_offset(plane); - } - - let mut fd = gbm_buffer.export()?; - let size = fd.seek(SeekFrom::End(0))?; - - // minigbm does have the ability to query image requirements without allocating memory - // via the TEST_ALLOC flag. However, support has only been added in i915. Until this - // flag is supported everywhere, do the actual allocation here and stash it away. - if self.last_buffer.is_some() { - return Err(RutabagaError::AlreadyInUse); - } - - self.last_buffer = Some(Arc::new(gbm_buffer)); - reqs.info = info; - reqs.size = size; - Ok(reqs) - } - - fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult { - let last_buffer = self.last_buffer.take(); - if let Some(gbm_buffer) = last_buffer { - if gbm_buffer.width() != reqs.info.width - || gbm_buffer.height() != reqs.info.height - || gbm_buffer.format() != reqs.info.drm_format - { - return Err(RutabagaError::InvalidGrallocDimensions); - } - - let dmabuf = gbm_buffer.export()?.into(); - return Ok(RutabagaHandle { - os_handle: dmabuf, - handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, - }); - } - - let bo = unsafe { - gbm_bo_create( - self.minigbm_device.gbm, - reqs.info.width, - reqs.info.height, - reqs.info.drm_format.0, - reqs.info.flags.0, - ) - }; - - if bo.is_null() { - return Err(RutabagaError::IoError(Error::last_os_error())); - } - - let gbm_buffer = MinigbmBuffer(bo, self.clone()); - let dmabuf = gbm_buffer.export()?.into(); - Ok(RutabagaHandle { - os_handle: dmabuf, - handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, - }) - } -} - -/// An allocation from a `MinigbmDevice`. -pub struct MinigbmBuffer(*mut gbm_bo, MinigbmDevice); - -// Safe because minigbm handles synchronization internally. -unsafe impl Send for MinigbmBuffer {} -unsafe impl Sync for MinigbmBuffer {} - -impl MinigbmBuffer { - /// Width in pixels. - pub fn width(&self) -> u32 { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_width(self.0) } - } - - /// Height in pixels. - pub fn height(&self) -> u32 { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_height(self.0) } - } - - /// `DrmFormat` of the buffer. - pub fn format(&self) -> DrmFormat { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { DrmFormat(gbm_bo_get_format(self.0)) } - } - - /// DrmFormat modifier flags for the buffer. - pub fn format_modifier(&self) -> u64 { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_modifier(self.0) } - } - - /// Number of planes present in this buffer. - pub fn num_planes(&self) -> usize { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_plane_count(self.0) as usize } - } - - /// Offset in bytes for the given plane. - pub fn plane_offset(&self, plane: usize) -> u32 { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_offset(self.0, plane) } - } - - /// Length in bytes of one row for the given plane. - pub fn plane_stride(&self, plane: usize) -> u32 { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_get_stride_for_plane(self.0, plane) } - } - - /// Exports a new dmabuf/prime file descriptor. - pub fn export(&self) -> RutabagaResult { - // This is always safe to call with a valid gbm_bo pointer. - match unsafe { gbm_bo_get_fd(self.0) } { - fd if fd >= 0 => { - let dmabuf = unsafe { File::from_raw_descriptor(fd) }; - Ok(dmabuf) - } - ret => Err(RutabagaError::ComponentError(ret)), - } - } -} - -impl Drop for MinigbmBuffer { - fn drop(&mut self) { - // This is always safe to call with a valid gbm_bo pointer. - unsafe { gbm_bo_destroy(self.0) } - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm_bindings.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm_bindings.rs deleted file mode 100644 index 8ed422114..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/minigbm_bindings.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2018 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Generated with bindgen --allowlist-function='gbm_.*' --allowlist-type='gbm_.*' minigbm/gbm.h -// Then modified manually - -#![cfg(feature = "minigbm")] -/* Added below line manually */ -#![allow(dead_code, non_camel_case_types)] - -/* Added below line manually */ -use std::os::raw::c_char; -use std::os::raw::c_int; -use std::os::raw::c_uint; -use std::os::raw::c_void; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_device { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_bo { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_surface { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union gbm_bo_handle { - pub ptr: *mut c_void, - pub s32: i32, - pub u32: u32, - pub s64: i64, - pub u64: u64, - _bindgen_union_align: u64, -} -pub const GBM_BO_FORMAT_XRGB8888: gbm_bo_format = 0; -pub const GBM_BO_FORMAT_ARGB8888: gbm_bo_format = 1; -pub type gbm_bo_format = u32; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_format_name_desc { - pub name: [c_char; 5usize], -} -pub const GBM_BO_USE_SCANOUT: gbm_bo_flags = 1; -pub const GBM_BO_USE_CURSOR: gbm_bo_flags = 2; -pub const GBM_BO_USE_CURSOR_64X64: gbm_bo_flags = 2; -pub const GBM_BO_USE_RENDERING: gbm_bo_flags = 4; -pub const GBM_BO_USE_WRITE: gbm_bo_flags = 8; -pub const GBM_BO_USE_LINEAR: gbm_bo_flags = 16; -pub const GBM_BO_USE_TEXTURING: gbm_bo_flags = 32; -pub const GBM_BO_USE_CAMERA_WRITE: gbm_bo_flags = 64; -pub const GBM_BO_USE_CAMERA_READ: gbm_bo_flags = 128; -pub const GBM_BO_USE_PROTECTED: gbm_bo_flags = 256; -pub const GBM_BO_USE_SW_READ_OFTEN: gbm_bo_flags = 512; -pub const GBM_BO_USE_SW_READ_RARELY: gbm_bo_flags = 1024; -pub const GBM_BO_USE_SW_WRITE_OFTEN: gbm_bo_flags = 2048; -pub const GBM_BO_USE_SW_WRITE_RARELY: gbm_bo_flags = 4096; -pub const GBM_BO_USE_HW_VIDEO_DECODER: gbm_bo_flags = 8192; -pub const GBM_BO_USE_HW_VIDEO_ENCODER: gbm_bo_flags = 16384; -/* Added below line manually */ -#[allow(non_camel_case_types)] -pub type gbm_bo_flags = u32; -/* Added below line manually */ -#[link(name = "gbm")] -extern "C" { - pub fn gbm_device_get_fd(gbm: *mut gbm_device) -> c_int; -} -extern "C" { - pub fn gbm_device_get_backend_name(gbm: *mut gbm_device) -> *const c_char; -} -extern "C" { - pub fn gbm_device_is_format_supported(gbm: *mut gbm_device, format: u32, usage: u32) -> c_int; -} -extern "C" { - pub fn gbm_device_get_format_modifier_plane_count( - gbm: *mut gbm_device, - format: u32, - modifier: u64, - ) -> c_int; -} -extern "C" { - pub fn gbm_device_destroy(gbm: *mut gbm_device); -} -extern "C" { - pub fn gbm_create_device(fd: c_int) -> *mut gbm_device; -} -extern "C" { - pub fn gbm_bo_create( - gbm: *mut gbm_device, - width: u32, - height: u32, - format: u32, - flags: u32, - ) -> *mut gbm_bo; -} -extern "C" { - pub fn gbm_bo_create_with_modifiers( - gbm: *mut gbm_device, - width: u32, - height: u32, - format: u32, - modifiers: *const u64, - count: c_uint, - ) -> *mut gbm_bo; -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_import_fd_data { - pub fd: c_int, - pub width: u32, - pub height: u32, - pub stride: u32, - pub format: u32, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct gbm_import_fd_modifier_data { - pub width: u32, - pub height: u32, - pub format: u32, - pub num_fds: u32, - pub fds: [c_int; 4usize], - pub strides: [c_int; 4usize], - pub offsets: [c_int; 4usize], - pub modifier: u64, -} -extern "C" { - pub fn gbm_bo_import( - gbm: *mut gbm_device, - type_: u32, - buffer: *mut c_void, - usage: u32, - ) -> *mut gbm_bo; -} -pub const GBM_BO_TRANSFER_READ: gbm_bo_transfer_flags = 1; -pub const GBM_BO_TRANSFER_WRITE: gbm_bo_transfer_flags = 2; -pub const GBM_BO_TRANSFER_READ_WRITE: gbm_bo_transfer_flags = 3; - -/* Added below line manually */ -#[allow(non_camel_case_types)] -pub type gbm_bo_transfer_flags = u32; -extern "C" { - pub fn gbm_bo_unmap(bo: *mut gbm_bo, map_data: *mut c_void); -} -extern "C" { - pub fn gbm_bo_get_width(bo: *mut gbm_bo) -> u32; -} -extern "C" { - pub fn gbm_bo_get_height(bo: *mut gbm_bo) -> u32; -} -extern "C" { - pub fn gbm_bo_get_stride(bo: *mut gbm_bo) -> u32; -} -extern "C" { - pub fn gbm_bo_get_stride_for_plane(bo: *mut gbm_bo, plane: usize) -> u32; -} -extern "C" { - pub fn gbm_bo_get_format(bo: *mut gbm_bo) -> u32; -} -extern "C" { - pub fn gbm_bo_get_bpp(bo: *mut gbm_bo) -> u32; -} -extern "C" { - pub fn gbm_bo_get_offset(bo: *mut gbm_bo, plane: usize) -> u32; -} -extern "C" { - pub fn gbm_bo_get_device(bo: *mut gbm_bo) -> *mut gbm_device; -} -extern "C" { - pub fn gbm_bo_get_handle(bo: *mut gbm_bo) -> gbm_bo_handle; -} -extern "C" { - pub fn gbm_bo_get_fd(bo: *mut gbm_bo) -> c_int; -} -extern "C" { - pub fn gbm_bo_get_modifier(bo: *mut gbm_bo) -> u64; -} -extern "C" { - pub fn gbm_bo_get_plane_count(bo: *mut gbm_bo) -> c_int; -} -extern "C" { - pub fn gbm_bo_get_handle_for_plane(bo: *mut gbm_bo, plane: usize) -> gbm_bo_handle; -} -extern "C" { - pub fn gbm_bo_write(bo: *mut gbm_bo, buf: *const c_void, count: usize) -> c_int; -} -extern "C" { - pub fn gbm_bo_set_user_data( - bo: *mut gbm_bo, - data: *mut c_void, - destroy_user_data: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut gbm_bo, arg2: *mut c_void), - >, - ); -} -extern "C" { - pub fn gbm_bo_get_user_data(bo: *mut gbm_bo) -> *mut c_void; -} -extern "C" { - pub fn gbm_bo_destroy(bo: *mut gbm_bo); -} -extern "C" { - pub fn gbm_surface_create( - gbm: *mut gbm_device, - width: u32, - height: u32, - format: u32, - flags: u32, - ) -> *mut gbm_surface; -} -extern "C" { - pub fn gbm_surface_create_with_modifiers( - gbm: *mut gbm_device, - width: u32, - height: u32, - format: u32, - modifiers: *const u64, - count: c_uint, - ) -> *mut gbm_surface; -} -extern "C" { - pub fn gbm_surface_lock_front_buffer(surface: *mut gbm_surface) -> *mut gbm_bo; -} -extern "C" { - pub fn gbm_surface_release_buffer(surface: *mut gbm_surface, bo: *mut gbm_bo); -} -extern "C" { - pub fn gbm_surface_has_free_buffers(surface: *mut gbm_surface) -> c_int; -} -extern "C" { - pub fn gbm_surface_destroy(surface: *mut gbm_surface); -} -extern "C" { - pub fn gbm_format_get_name(gbm_format: u32, desc: *mut gbm_format_name_desc) -> *mut c_char; -} -extern "C" { - pub fn gbm_bo_get_plane_size(bo: *mut gbm_bo, plane: usize) -> u32; -} -extern "C" { - pub fn gbm_bo_get_plane_fd(bo: *mut gbm_bo, plane: usize) -> c_int; -} -extern "C" { - pub fn gbm_bo_map( - bo: *mut gbm_bo, - x: u32, - y: u32, - width: u32, - height: u32, - flags: u32, - stride: *mut u32, - map_data: *mut *mut c_void, - plane: usize, - ) -> *mut c_void; -} -extern "C" { - pub fn gbm_bo_map2( - bo: *mut gbm_bo, - x: u32, - y: u32, - width: u32, - height: u32, - flags: u32, - stride: *mut u32, - map_data: *mut *mut c_void, - plane: c_int, - ) -> *mut c_void; -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/mod.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/mod.rs deleted file mode 100644 index c4ceb2e5c..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! This module implements cross-platform allocation of window system buffers. -//! In addition, it may perform mappings of GPU buffers. This is based on -//! "gralloc", a well-known Android hardware abstaction layer (HAL). -//! -//! - -mod formats; -mod gralloc; -mod minigbm; -mod minigbm_bindings; -mod rendernode; -mod system_gralloc; -mod vulkano_gralloc; - -pub use formats::DrmFormat; -pub use gralloc::ImageAllocationInfo; -pub use gralloc::ImageMemoryRequirements; -pub use gralloc::RutabagaGralloc; -pub use gralloc::RutabagaGrallocFlags; diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs deleted file mode 100644 index e7863fbc3..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#![cfg(feature = "minigbm")] - -use std::ffi::CString; -use std::fs::File; -use std::fs::OpenOptions; -use std::os::raw::c_char; -use std::os::raw::c_int; -use std::os::raw::c_uint; -#[cfg(target_pointer_width = "64")] -use std::os::raw::c_ulong; -use std::os::unix::io::AsRawFd; -use std::path::Path; -use std::ptr::null_mut; - -use nix::ioctl_readwrite; - -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -// Consistent with __kernel_size_t in include/uapi/asm-generic/posix_types.h. -#[cfg(not(target_pointer_width = "64"))] -#[allow(non_camel_case_types)] -type __kernel_size_t = c_uint; -#[cfg(target_pointer_width = "64")] -#[allow(non_camel_case_types)] -type __kernel_size_t = c_ulong; - -const DRM_IOCTL_BASE: c_uint = 0x64; -const DRM_IOCTL_VERSION: c_uint = 0x00; - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct drm_version { - version_major: c_int, - version_minor: c_int, - version_patchlevel: c_int, - name_len: __kernel_size_t, - name: *mut c_char, - date_len: __kernel_size_t, - date: *mut c_char, - desc_len: __kernel_size_t, - desc: *mut c_char, -} - -ioctl_readwrite!( - drm_get_version, - DRM_IOCTL_BASE, - DRM_IOCTL_VERSION, - drm_version -); - -fn get_drm_device_name(fd: &File) -> RutabagaResult { - let mut version = drm_version { - version_major: 0, - version_minor: 0, - version_patchlevel: 0, - name_len: 0, - name: null_mut(), - date_len: 0, - date: null_mut(), - desc_len: 0, - desc: null_mut(), - }; - - // Get the length of the device name. - unsafe { - drm_get_version(fd.as_raw_fd(), &mut version)?; - } - - // Enough bytes to hold the device name and terminating null character. - let mut name_bytes: Vec = vec![0; (version.name_len + 1) as usize]; - let mut version = drm_version { - version_major: 0, - version_minor: 0, - version_patchlevel: 0, - name_len: name_bytes.len() as __kernel_size_t, - name: name_bytes.as_mut_ptr() as *mut c_char, - date_len: 0, - date: null_mut(), - desc_len: 0, - desc: null_mut(), - }; - - // Safe as no more than name_len + 1 bytes will be written to name. - unsafe { - drm_get_version(fd.as_raw_fd(), &mut version)?; - } - - CString::new(&name_bytes[..(version.name_len as usize)])? - .into_string() - .map_err(|_| RutabagaError::SpecViolation("couldn't convert string")) -} - -/// Returns a `fd` for an opened rendernode device, while filtering out specified -/// undesired drivers. -pub fn open_device(undesired: &[&str]) -> RutabagaResult { - const DRM_DIR_NAME: &str = "/dev/dri"; - const DRM_MAX_MINOR: u32 = 15; - const RENDER_NODE_START: u32 = 128; - - for n in RENDER_NODE_START..=RENDER_NODE_START + DRM_MAX_MINOR { - let path = Path::new(DRM_DIR_NAME).join(format!("renderD{}", n)); - - if let Ok(fd) = OpenOptions::new().read(true).write(true).open(path) { - if let Ok(name) = get_drm_device_name(&fd) { - if !undesired.iter().any(|item| *item == name) { - return Ok(fd); - } - } - } - } - - Err(RutabagaError::SpecViolation("no DRM rendernode opened")) -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/system_gralloc.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/system_gralloc.rs deleted file mode 100644 index dda684960..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/system_gralloc.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Utility file for allocating exportable system memory. On Linux systems, -//! this is is often done with memfd. - -use crate::rutabaga_gralloc::formats::canonical_image_requirements; -use crate::rutabaga_gralloc::gralloc::Gralloc; -use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo; -use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements; -use crate::rutabaga_os::SharedMemory; -use crate::rutabaga_utils::*; - -/// A gralloc implementation capable of allocation from system memory. -pub struct SystemGralloc(()); - -impl SystemGralloc { - fn new() -> Self { - SystemGralloc(()) - } - - /// Returns a new `SystemGralloc` instance. - pub fn init() -> RutabagaResult> { - Ok(Box::new(SystemGralloc::new())) - } -} - -impl Gralloc for SystemGralloc { - fn supports_external_gpu_memory(&self) -> bool { - false - } - - fn supports_dmabuf(&self) -> bool { - false - } - - fn get_image_memory_requirements( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult { - let mut reqs = canonical_image_requirements(info)?; - reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; - Ok(reqs) - } - - fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult { - let shm = SharedMemory::new("rutabaga_gralloc", reqs.size)?; - Ok(RutabagaHandle { - os_handle: shm.into(), - handle_type: RUTABAGA_MEM_HANDLE_TYPE_SHM, - }) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs deleted file mode 100644 index 5ed01e1bb..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright 2021 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! vulkano_gralloc: Implements swapchain allocation and memory mapping -//! using Vulkano. -//! -//! External code found at https://github.com/vulkano-rs/vulkano. - -#![cfg(feature = "vulkano")] - -mod sys; - -use std::collections::HashMap as Map; -use std::convert::TryInto; -use std::sync::Arc; - -use log::warn; -use vulkano::device::physical::PhysicalDeviceType; -use vulkano::device::Device; -use vulkano::device::DeviceCreateInfo; -use vulkano::device::DeviceCreationError; -use vulkano::device::QueueCreateInfo; -use vulkano::image; -use vulkano::image::ImageCreationError; -use vulkano::image::ImageDimensions; -use vulkano::image::ImageUsage; -use vulkano::image::SampleCount; -use vulkano::instance::Instance; -use vulkano::instance::InstanceCreateInfo; -use vulkano::instance::InstanceCreationError; -use vulkano::instance::InstanceExtensions; -use vulkano::instance::Version; -use vulkano::memory::pool::AllocFromRequirementsFilter; -use vulkano::memory::DedicatedAllocation; -use vulkano::memory::DeviceMemory; -use vulkano::memory::DeviceMemoryError; -use vulkano::memory::ExternalMemoryHandleType; -use vulkano::memory::ExternalMemoryHandleTypes; -use vulkano::memory::MappedDeviceMemory; -use vulkano::memory::MemoryAllocateInfo; -use vulkano::memory::MemoryMapError; -use vulkano::memory::MemoryRequirements; -use vulkano::memory::MemoryType; -use vulkano::sync::Sharing; -use vulkano::LoadingError; -use vulkano::VulkanError; -use vulkano::VulkanLibrary; - -use crate::rutabaga_gralloc::gralloc::Gralloc; -use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo; -use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements; -use crate::rutabaga_os::MappedRegion; -use crate::rutabaga_utils::*; - -/// A gralloc implementation capable of allocation `VkDeviceMemory`. -pub struct VulkanoGralloc { - devices: Map>, - device_by_id: Map>, - has_integrated_gpu: bool, -} - -struct VulkanoMapping { - mapped_memory: MappedDeviceMemory, - size: usize, -} - -impl VulkanoMapping { - pub fn new(mapped_memory: MappedDeviceMemory, size: usize) -> VulkanoMapping { - VulkanoMapping { - mapped_memory, - size, - } - } -} - -unsafe impl MappedRegion for VulkanoMapping { - /// Used for passing this region for hypervisor memory mappings. We trust crosvm to use this - /// safely. - fn as_ptr(&self) -> *mut u8 { - unsafe { - // Will not panic since the requested range of the device memory was verified on - // creation - let x = self.mapped_memory.write(0..self.size as u64).unwrap(); - x.as_mut_ptr() - } - } - - /// Returns the size of the memory region in bytes. - fn size(&self) -> usize { - self.size - } -} - -trait DeviceExt { - /// Get a unique identifier for the device. - fn get_id(&self) -> DeviceId; -} - -impl DeviceExt for Device { - fn get_id(&self) -> DeviceId { - let properties = self.physical_device().properties(); - DeviceId { - device_uuid: properties.device_uuid.expect("Vulkan should support uuid"), - driver_uuid: properties.driver_uuid.expect("Vulkan should support uuid"), - } - } -} - -impl VulkanoGralloc { - /// Returns a new `VulkanGralloc' instance upon success. - pub fn init() -> RutabagaResult> { - // Initialization copied from triangle.rs in Vulkano. Look there for a more detailed - // explanation of VK initialization. - let library = VulkanLibrary::new()?; - - let instance_extensions = InstanceExtensions { - khr_external_memory_capabilities: true, - khr_get_physical_device_properties2: true, - ..InstanceExtensions::empty() - }; - let instance = Instance::new( - library, - InstanceCreateInfo { - enabled_extensions: instance_extensions, - max_api_version: Some(Version::V1_1), - ..Default::default() - }, - )?; - - let mut devices: Map> = Default::default(); - let mut device_by_id: Map> = Default::default(); - let mut has_integrated_gpu = false; - - for physical in instance.enumerate_physical_devices()? { - let queue_family_index = match physical.queue_family_properties().iter().position(|q| { - // We take the first queue family that supports graphics. - q.queue_flags.graphics - }) { - Some(family) => family, - None => { - warn!( - "Skipping Vulkano for {} due to no graphics queue", - physical.properties().device_name - ); - continue; - } - }; - - let supported_extensions = physical.supported_extensions(); - - let desired_extensions = Self::get_desired_device_extensions(); - - let intersection = supported_extensions.intersection(&desired_extensions); - - if let Ok((device, mut _queues)) = Device::new( - physical.clone(), - DeviceCreateInfo { - enabled_extensions: intersection, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index: queue_family_index as u32, - ..Default::default() - }], - ..Default::default() - }, - ) { - let device_type = device.physical_device().properties().device_type; - if device_type == PhysicalDeviceType::IntegratedGpu { - has_integrated_gpu = true - } - - // If we have two devices of the same type (two integrated GPUs), the old value is - // dropped. Vulkano is verbose enough such that a keener selection algorithm may - // be used, but the need for such complexity does not seem to exist now. - devices.insert(device_type, device.clone()); - device_by_id.insert(device.get_id(), device); - }; - } - - if devices.is_empty() { - return Err(RutabagaError::SpecViolation( - "no matching VK devices available", - )); - } - - Ok(Box::new(VulkanoGralloc { - devices, - device_by_id, - has_integrated_gpu, - })) - } - - // This function is used safely in this module because gralloc does not: - // - // (1) bind images to any memory. - // (2) transition the layout of images. - // (3) transfer ownership of images between queues. - // - // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec. - // TODO(tutankhamen): Do we still need a separate MemoryRequirements? - unsafe fn create_image( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult<(Arc, MemoryRequirements)> { - let device = if self.has_integrated_gpu { - self.devices - .get(&PhysicalDeviceType::IntegratedGpu) - .ok_or(RutabagaError::InvalidGrallocGpuType)? - } else { - self.devices - .get(&PhysicalDeviceType::DiscreteGpu) - .ok_or(RutabagaError::InvalidGrallocGpuType)? - }; - - let usage = match info.flags.uses_rendering() { - true => ImageUsage { - color_attachment: true, - ..ImageUsage::empty() - }, - false => ImageUsage { - sampled: true, - ..ImageUsage::empty() - }, - }; - - // Reasonable bounds on image width. - if info.width == 0 || info.width > 4096 { - return Err(RutabagaError::InvalidGrallocDimensions); - } - - // Reasonable bounds on image height. - if info.height == 0 || info.height > 4096 { - return Err(RutabagaError::InvalidGrallocDimensions); - } - - let vulkan_format = info.drm_format.vulkan_format()?; - let unsafe_image = image::sys::UnsafeImage::new( - device.clone(), - image::sys::UnsafeImageCreateInfo { - dimensions: ImageDimensions::Dim2d { - width: info.width, - height: info.height, - array_layers: 1, - }, - format: Some(vulkan_format), - samples: SampleCount::Sample1, - usage, - mip_levels: 1, - sharing: Sharing::Exclusive, - ..Default::default() - }, - )?; - - let memory_requirements = unsafe_image.memory_requirements(); - - Ok((unsafe_image, memory_requirements)) - } -} - -impl Gralloc for VulkanoGralloc { - fn supports_external_gpu_memory(&self) -> bool { - for device in self.devices.values() { - if !device.enabled_extensions().khr_external_memory { - return false; - } - } - - true - } - - fn supports_dmabuf(&self) -> bool { - for device in self.devices.values() { - if !device.enabled_extensions().ext_external_memory_dma_buf { - return false; - } - } - - true - } - - fn get_image_memory_requirements( - &mut self, - info: ImageAllocationInfo, - ) -> RutabagaResult { - let mut reqs: ImageMemoryRequirements = Default::default(); - - let (unsafe_image, memory_requirements) = unsafe { self.create_image(info)? }; - - let device_type = if self.has_integrated_gpu { - &PhysicalDeviceType::IntegratedGpu - } else { - &PhysicalDeviceType::DiscreteGpu - }; - - let device = self - .devices - .get(device_type) - .ok_or(RutabagaError::InvalidGrallocGpuType)?; - - let planar_layout = info.drm_format.planar_layout()?; - - // Safe because we created the image with the linear bit set and verified the format is - // not a depth or stencil format. We are also using the correct image aspect. Vulkano - // will panic if we are not. - for plane in 0..planar_layout.num_planes { - let aspect = info.drm_format.vulkan_image_aspect(plane)?; - let layout = unsafe { unsafe_image.multiplane_color_layout(aspect) }; - reqs.strides[plane] = layout.row_pitch as u32; - reqs.offsets[plane] = layout.offset as u32; - } - - let need_visible = info.flags.host_visible(); - let want_cached = info.flags.host_cached(); - - let (memory_type_index, memory_type) = { - let filter = |current_type: &MemoryType| { - if need_visible && !current_type.property_flags.host_visible { - return AllocFromRequirementsFilter::Forbidden; - } - - if !need_visible && current_type.property_flags.device_local { - return AllocFromRequirementsFilter::Preferred; - } - - if need_visible && want_cached && current_type.property_flags.host_cached { - return AllocFromRequirementsFilter::Preferred; - } - - if need_visible - && !want_cached - && current_type.property_flags.host_coherent - && !current_type.property_flags.host_cached - { - return AllocFromRequirementsFilter::Preferred; - } - - AllocFromRequirementsFilter::Allowed - }; - - let first_loop = device - .physical_device() - .memory_properties() - .memory_types - .iter() - .enumerate() - .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Preferred)); - let second_loop = device - .physical_device() - .memory_properties() - .memory_types - .iter() - .enumerate() - .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Allowed)); - let found_type = first_loop - .chain(second_loop) - .filter(|&(i, _, _)| (memory_requirements.memory_type_bits & (1 << i)) != 0) - .find(|&(_, t, rq)| filter(t) == rq) - .ok_or(RutabagaError::SpecViolation( - "unable to find required memory type", - ))?; - (found_type.0, found_type.1) - }; - - reqs.info = info; - reqs.size = memory_requirements.size as u64; - - if memory_type.property_flags.host_visible { - if memory_type.property_flags.host_cached { - reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; - } else if memory_type.property_flags.host_coherent { - reqs.map_info = RUTABAGA_MAP_CACHE_WC; - } - } - - reqs.vulkan_info = Some(VulkanInfo { - memory_idx: memory_type_index as u32, - device_id: device.get_id(), - }); - - Ok(reqs) - } - - fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult { - let (unsafe_image, memory_requirements) = unsafe { self.create_image(reqs.info)? }; - - let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?; - - let device = if self.has_integrated_gpu { - self.devices - .get(&PhysicalDeviceType::IntegratedGpu) - .ok_or(RutabagaError::InvalidGrallocGpuType)? - } else { - self.devices - .get(&PhysicalDeviceType::DiscreteGpu) - .ok_or(RutabagaError::InvalidGrallocGpuType)? - }; - - if vulkan_info.memory_idx as usize - >= device - .physical_device() - .memory_properties() - .memory_types - .len() - { - return Err(RutabagaError::InvalidVulkanInfo); - } - - let (export_handle_type, export_handle_types, rutabaga_type) = - match device.enabled_extensions().ext_external_memory_dma_buf { - true => ( - ExternalMemoryHandleType::DmaBuf, - ExternalMemoryHandleTypes { - dma_buf: true, - ..ExternalMemoryHandleTypes::empty() - }, - RUTABAGA_MEM_HANDLE_TYPE_DMABUF, - ), - false => ( - ExternalMemoryHandleType::OpaqueFd, - ExternalMemoryHandleTypes { - opaque_fd: true, - ..ExternalMemoryHandleTypes::empty() - }, - RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD, - ), - }; - - let dedicated_allocation = match device.enabled_extensions().khr_dedicated_allocation { - true => { - if memory_requirements.prefer_dedicated { - Some(DedicatedAllocation::Image(&unsafe_image)) - } else { - None - } - } - false => None, - }; - - let device_memory = DeviceMemory::allocate( - device.clone(), - MemoryAllocateInfo { - allocation_size: reqs.size, - memory_type_index: vulkan_info.memory_idx, - dedicated_allocation, - export_handle_types, - ..Default::default() - }, - )?; - - let descriptor = device_memory.export_fd(export_handle_type)?.into(); - - Ok(RutabagaHandle { - os_handle: descriptor, - handle_type: rutabaga_type, - }) - } - - /// Implementations must map the memory associated with the `resource_id` upon success. - fn import_and_map( - &mut self, - handle: RutabagaHandle, - vulkan_info: VulkanInfo, - size: u64, - ) -> RutabagaResult> { - let device = self - .device_by_id - .get(&vulkan_info.device_id) - .ok_or(RutabagaError::InvalidVulkanInfo)?; - - let device_memory = unsafe { - VulkanoGralloc::import_memory( - device.clone(), - MemoryAllocateInfo { - allocation_size: size, - memory_type_index: vulkan_info.memory_idx, - ..Default::default() - }, - handle, - )? - }; - - let mapped_memory = MappedDeviceMemory::new(device_memory, 0..size)?; - - Ok(Box::new(VulkanoMapping::new( - mapped_memory, - size.try_into()?, - ))) - } -} - -// Vulkano should really define an universal type that wraps all these errors, say -// "VulkanoError(e)". -impl From for RutabagaError { - fn from(e: InstanceCreationError) -> RutabagaError { - RutabagaError::VkInstanceCreationError(e) - } -} - -impl From for RutabagaError { - fn from(e: ImageCreationError) -> RutabagaError { - RutabagaError::VkImageCreationError(e) - } -} - -impl From for RutabagaError { - fn from(e: DeviceCreationError) -> RutabagaError { - RutabagaError::VkDeviceCreationError(e) - } -} - -impl From for RutabagaError { - fn from(e: DeviceMemoryError) -> RutabagaError { - RutabagaError::VkDeviceMemoryError(e) - } -} - -impl From for RutabagaError { - fn from(e: MemoryMapError) -> RutabagaError { - RutabagaError::VkMemoryMapError(e) - } -} - -impl From for RutabagaError { - fn from(e: LoadingError) -> RutabagaError { - RutabagaError::VkLoadingError(e) - } -} - -impl From for RutabagaError { - fn from(e: VulkanError) -> RutabagaError { - RutabagaError::VkError(e) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys.rs deleted file mode 100644 index 7ca32b7e1..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cfg_if::cfg_if! { - if #[cfg(unix)] { - pub mod unix; - } else if #[cfg(windows)] { - pub mod windows; - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/unix.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/unix.rs deleted file mode 100644 index f949a6567..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/unix.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::fs::File; -use std::sync::Arc; - -use vulkano::device::Device; -use vulkano::device::DeviceExtensions; -use vulkano::memory::DeviceMemory; -use vulkano::memory::ExternalMemoryHandleType; -use vulkano::memory::MemoryAllocateInfo; -use vulkano::memory::MemoryImportInfo; - -use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_os::IntoRawDescriptor; -use crate::rutabaga_utils::RUTABAGA_MEM_HANDLE_TYPE_DMABUF; -use crate::rutabaga_utils::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD; -use crate::RutabagaError; -use crate::RutabagaHandle; -use crate::RutabagaResult; - -impl VulkanoGralloc { - /// Get the extensions that should be enabled. - pub(crate) fn get_desired_device_extensions() -> DeviceExtensions { - DeviceExtensions { - khr_dedicated_allocation: true, - khr_get_memory_requirements2: true, - khr_external_memory: true, - khr_external_memory_fd: true, - ext_external_memory_dma_buf: true, - ..DeviceExtensions::empty() - } - } - - /// Import memory from a handle. - /// - /// # Safety - /// Safe if the memory handle given is an opaque FD or a DMA buffer handle, and the allocation - /// info matches the information at the time the memory was created. - pub(crate) unsafe fn import_memory( - device: Arc, - allocate_info: MemoryAllocateInfo, - handle: RutabagaHandle, - ) -> RutabagaResult { - let import_info = MemoryImportInfo::Fd { - handle_type: match handle.handle_type { - RUTABAGA_MEM_HANDLE_TYPE_DMABUF => ExternalMemoryHandleType::DmaBuf, - RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD => ExternalMemoryHandleType::OpaqueFd, - _ => return Err(RutabagaError::InvalidRutabagaHandle), - }, - // Safe because we own the handle. - file: File::from_raw_descriptor(handle.os_handle.into_raw_descriptor()), - }; - - Ok(DeviceMemory::import(device, allocate_info, import_info)?) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/windows.rs b/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/windows.rs deleted file mode 100644 index 0fcbdf56c..000000000 --- a/src/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc/sys/windows.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::sync::Arc; - -use vulkano::device::Device; -use vulkano::device::DeviceExtensions; -use vulkano::memory::DeviceMemory; -use vulkano::memory::ExternalMemoryHandleType; -use vulkano::memory::MemoryAllocateInfo; -use vulkano::memory::MemoryImportInfo; - -use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc; -use crate::rutabaga_os::AsRawDescriptor; -use crate::rutabaga_utils::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_WIN32; -use crate::RutabagaError; -use crate::RutabagaHandle; -use crate::RutabagaResult; - -impl VulkanoGralloc { - /// Get the extensions that should be enabled. - pub(crate) fn get_desired_device_extensions() -> DeviceExtensions { - DeviceExtensions { - khr_dedicated_allocation: true, - khr_get_memory_requirements2: true, - khr_external_memory: true, - khr_external_memory_win32: true, - ..DeviceExtensions::empty() - } - } - - /// Import memory from a handle. - /// - /// # Safety - /// Safe if the memory handle given is an opaque Win32 handle, and the allocation info matches - /// the information at the time the memory was created. - pub(crate) unsafe fn import_memory( - device: Arc, - allocate_info: MemoryAllocateInfo, - handle: RutabagaHandle, - ) -> RutabagaResult { - let import_info = MemoryImportInfo::Win32 { - handle_type: match handle.handle_type { - RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_WIN32 => ExternalMemoryHandleType::OpaqueWin32, - _ => return Err(RutabagaError::InvalidRutabagaHandle), - }, - handle: handle.os_handle.as_raw_descriptor(), - }; - - Ok(DeviceMemory::import(device, allocate_info, import_info)?) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/descriptor.rs b/src/rutabaga_gfx/src/rutabaga_os/descriptor.rs deleted file mode 100644 index 36d3eab31..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/descriptor.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::fs::File; -use std::mem; -use std::mem::ManuallyDrop; - -use crate::rutabaga_os::RawDescriptor; - -/// Wraps a RawDescriptor and safely closes it when self falls out of scope. -pub struct SafeDescriptor { - pub(crate) descriptor: RawDescriptor, -} - -/// Trait for forfeiting ownership of the current raw descriptor, and returning the raw descriptor -pub trait IntoRawDescriptor { - fn into_raw_descriptor(self) -> RawDescriptor; -} - -/// Trait for returning the underlying raw descriptor, without giving up ownership of the -/// descriptor. -pub trait AsRawDescriptor { - /// Returns the underlying raw descriptor. - /// - /// Since the descriptor is still owned by the provider, callers should not assume that it will - /// remain open for longer than the immediate call of this method. In particular, it is a - /// dangerous practice to store the result of this method for future use: instead, it should be - /// used to e.g. obtain a raw descriptor that is immediately passed to a system call. - /// - /// If you need to use the descriptor for a longer time (and particularly if you cannot reliably - /// track the lifetime of the providing object), you should probably consider using - /// [`SafeDescriptor`] (possibly along with [`trait@IntoRawDescriptor`]) to get full ownership - /// over a descriptor pointing to the same resource. - fn as_raw_descriptor(&self) -> RawDescriptor; -} - -/// A trait similar to `AsRawDescriptor` but supports an arbitrary number of descriptors. -pub trait AsRawDescriptors { - /// Returns the underlying raw descriptors. - /// - /// Please refer to the documentation of [`AsRawDescriptor::as_raw_descriptor`] for limitations - /// and recommended use. - #[allow(dead_code)] - fn as_raw_descriptors(&self) -> Vec; -} - -pub trait FromRawDescriptor { - /// # Safety - /// Safe only if the caller ensures nothing has access to the descriptor after passing it to - /// `from_raw_descriptor` - unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self; -} - -impl AsRawDescriptor for SafeDescriptor { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.descriptor - } -} - -impl AsRawDescriptors for T -where - T: AsRawDescriptor, -{ - fn as_raw_descriptors(&self) -> Vec { - vec![self.as_raw_descriptor()] - } -} - -impl IntoRawDescriptor for SafeDescriptor { - fn into_raw_descriptor(self) -> RawDescriptor { - let descriptor = self.descriptor; - mem::forget(self); - descriptor - } -} - -impl FromRawDescriptor for SafeDescriptor { - unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self { - SafeDescriptor { descriptor } - } -} - -impl TryFrom<&dyn AsRawDescriptor> for SafeDescriptor { - type Error = std::io::Error; - - /// Clones the underlying descriptor (handle), internally creating a new descriptor. - /// - /// WARNING: Windows does NOT support cloning/duplicating all types of handles. DO NOT use this - /// function on IO completion ports, sockets, or pseudo-handles (except those from - /// GetCurrentProcess or GetCurrentThread). See - /// - /// for further details. - /// - /// TODO(b/191800567): this API has sharp edges on Windows. We should evaluate making some - /// adjustments to smooth those edges. - fn try_from(rd: &dyn AsRawDescriptor) -> std::result::Result { - // Safe because the underlying raw descriptor is guaranteed valid by rd's existence. - // - // Note that we are cloning the underlying raw descriptor since we have no guarantee of - // its existence after this function returns. - let rd_as_safe_desc = ManuallyDrop::new(unsafe { - SafeDescriptor::from_raw_descriptor(rd.as_raw_descriptor()) - }); - - // We have to clone rd because we have no guarantee ownership was transferred (rd is - // borrowed). - rd_as_safe_desc - .try_clone() - .map_err(|_| Self::Error::last_os_error()) - } -} - -impl From for SafeDescriptor { - fn from(f: File) -> SafeDescriptor { - // Safe because we own the File at this point. - unsafe { SafeDescriptor::from_raw_descriptor(f.into_raw_descriptor()) } - } -} - -/// For use cases where a simple wrapper around a [`RawDescriptor`] is needed, in order to e.g. -/// implement [`trait@AsRawDescriptor`]. -/// -/// This is a simply a wrapper and does not manage the lifetime of the descriptor. As such it is the -/// responsibility of the user to ensure that the wrapped descriptor will not be closed for as long -/// as the `Descriptor` is alive. -/// -/// Most use-cases should prefer [`SafeDescriptor`] or implementing and using -/// [`trait@AsRawDescriptor`] on the type providing the descriptor. Using this wrapper usually means -/// something can be improved in your code. -/// -/// Valid uses of this struct include: -/// * You only have a valid [`RawDescriptor`] and need to pass something that implements -/// [`trait@AsRawDescriptor`] to a function, -/// * You need to serialize a [`RawDescriptor`], -/// * You need [`trait@Send`] or [`trait@Sync`] for your descriptor and properly handle the case -/// where your descriptor gets closed. -/// -/// Note that with the exception of the last use-case (which requires proper error checking against -/// the descriptor being closed), the `Descriptor` instance would be very short-lived. -#[allow(unused)] -#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct Descriptor(pub RawDescriptor); -impl AsRawDescriptor for Descriptor { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.0 - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/memory_mapping.rs b/src/rutabaga_gfx/src/rutabaga_os/memory_mapping.rs deleted file mode 100644 index 6fadec2ad..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/memory_mapping.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use crate::rutabaga_os::sys::platform::MemoryMapping as PlatformMapping; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaMapping; -use crate::rutabaga_utils::RutabagaResult; - -pub struct MemoryMapping { - mapping: PlatformMapping, -} - -impl MemoryMapping { - pub fn from_safe_descriptor( - descriptor: SafeDescriptor, - size: usize, - map_info: u32, - ) -> RutabagaResult { - let mapping = PlatformMapping::from_safe_descriptor(descriptor, size, map_info)?; - Ok(MemoryMapping { mapping }) - } - - pub fn as_rutabaga_mapping(&self) -> RutabagaMapping { - RutabagaMapping { - ptr: self.mapping.addr.as_ptr() as u64, - size: self.mapping.size as u64, - } - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/mod.rs b/src/rutabaga_gfx/src/rutabaga_os/mod.rs deleted file mode 100644 index 6efd1a876..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -mod descriptor; -mod memory_mapping; -mod shm; -pub mod sys; - -pub use descriptor::AsRawDescriptor; -#[allow(unused_imports)] -pub use descriptor::AsRawDescriptors; -pub use descriptor::FromRawDescriptor; -pub use descriptor::IntoRawDescriptor; -pub use descriptor::SafeDescriptor; -pub use shm::SharedMemory; - -pub use memory_mapping::MemoryMapping; - -pub use sys::platform::descriptor::RawDescriptor; -pub use sys::platform::shm::round_up_to_page_size; - -#[allow(clippy::missing_safety_doc)] -pub unsafe trait MappedRegion: Send + Sync { - /// Returns a pointer to the beginning of the memory region. Should only be - /// used for passing this region to ioctls for setting guest memory. - fn as_ptr(&self) -> *mut u8; - - /// Returns the size of the memory region in bytes. - fn size(&self) -> usize; -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/shm.rs b/src/rutabaga_gfx/src/rutabaga_os/shm.rs deleted file mode 100644 index be6a3326d..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/shm.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::ffi::CString; - -use crate::rutabaga_os::sys::platform::SharedMemory as SysUtilSharedMemory; -use crate::rutabaga_os::AsRawDescriptor; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_os::IntoRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaResult; - -pub struct SharedMemory(pub(crate) SysUtilSharedMemory); -impl SharedMemory { - /// Creates a new shared memory object of the given size. - /// - /// |name| is purely for debugging purposes. It does not need to be unique, and it does - /// not affect any non-debugging related properties of the constructed shared memory. - pub fn new>>(debug_name: T, size: u64) -> RutabagaResult { - let debug_name = CString::new(debug_name)?; - SysUtilSharedMemory::new(&debug_name, size).map(SharedMemory) - } - - pub fn size(&self) -> u64 { - self.0.size() - } -} - -impl AsRawDescriptor for SharedMemory { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.0.as_raw_descriptor() - } -} - -impl IntoRawDescriptor for SharedMemory { - fn into_raw_descriptor(self) -> RawDescriptor { - self.0.into_raw_descriptor() - } -} - -impl From for SafeDescriptor { - fn from(sm: SharedMemory) -> SafeDescriptor { - // Safe because we own the SharedMemory at this point. - unsafe { SafeDescriptor::from_raw_descriptor(sm.into_raw_descriptor()) } - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/mod.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/mod.rs deleted file mode 100644 index dbadaac9e..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#[cfg(any(target_os = "android", target_os = "linux"))] -pub mod unix; - -#[cfg(any(target_os = "fuchsia", target_os = "macos"))] -pub mod stub; - -#[cfg(windows)] -pub mod windows; - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { - pub use unix as platform; - } else if #[cfg(windows)] { - pub use windows as platform; - } else if #[cfg(any(target_os = "fuchsia", target_os = "macos"))] { - pub use stub as platform; - } else { - compile_error!("Unsupported platform"); - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/descriptor.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/stub/descriptor.rs deleted file mode 100644 index 73d41c10e..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/descriptor.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::convert::TryFrom; -use std::fs::File; -use std::os::fd::AsFd; -use std::os::fd::BorrowedFd; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::FromRawFd; -use std::os::unix::io::IntoRawFd; -use std::os::unix::io::OwnedFd; -use std::os::unix::io::RawFd; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::Descriptor; -use crate::rutabaga_os::descriptor::FromRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::descriptor::SafeDescriptor; - -type Error = std::io::Error; -type Result = std::result::Result; - -pub type RawDescriptor = RawFd; - -/// Clones `fd`, returning a new file descriptor that refers to the same open file description as -/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file -/// descriptor flags with `fd`. -fn clone_fd(fd: &dyn AsRawFd) -> Result { - // Safe because this doesn't modify any memory and we check the return value. - let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) }; - if ret < 0 { - Err(Error::last_os_error()) - } else { - Ok(ret) - } -} - -impl Drop for SafeDescriptor { - fn drop(&mut self) { - let _ = unsafe { libc::close(self.descriptor) }; - } -} - -impl AsRawFd for SafeDescriptor { - fn as_raw_fd(&self) -> RawFd { - self.as_raw_descriptor() - } -} - -impl TryFrom<&dyn AsRawFd> for SafeDescriptor { - type Error = std::io::Error; - - fn try_from(fd: &dyn AsRawFd) -> Result { - Ok(SafeDescriptor { - descriptor: clone_fd(fd)?, - }) - } -} - -impl SafeDescriptor { - /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will - /// share the same underlying count within the kernel. - pub fn try_clone(&self) -> Result { - // Safe because this doesn't modify any memory and we check the return value. - let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) }; - if descriptor < 0 { - Err(Error::last_os_error()) - } else { - Ok(SafeDescriptor { descriptor }) - } - } -} - -impl From for File { - fn from(s: SafeDescriptor) -> File { - // Safe because we own the SafeDescriptor at this point. - unsafe { File::from_raw_fd(s.into_raw_descriptor()) } - } -} - -// AsRawFd for interoperability with interfaces that require it. Within crosvm, -// always use AsRawDescriptor when possible. -impl AsRawFd for Descriptor { - fn as_raw_fd(&self) -> RawFd { - self.0 - } -} - -impl AsFd for SafeDescriptor { - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: the `BorrowedFd` we return lives no longer than this `SafeDescriptor`, so the - // descriptor will remain open. - unsafe { BorrowedFd::borrow_raw(self.descriptor) } - } -} - -macro_rules! AsRawDescriptor { - ($name:ident) => { - impl AsRawDescriptor for $name { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.as_raw_fd() - } - } - }; -} - -macro_rules! FromRawDescriptor { - ($name:ident) => { - impl FromRawDescriptor for $name { - unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self { - $name::from_raw_fd(descriptor) - } - } - }; -} - -macro_rules! IntoRawDescriptor { - ($name:ident) => { - impl IntoRawDescriptor for $name { - fn into_raw_descriptor(self) -> RawDescriptor { - self.into_raw_fd() - } - } - }; -} - -// Implementations for File. This enables the File-type to use -// RawDescriptor, but does not mean File should be used as a generic -// descriptor container. That should go to either SafeDescriptor or another more -// relevant container type. -AsRawDescriptor!(File); -FromRawDescriptor!(File); -IntoRawDescriptor!(File); -AsRawDescriptor!(OwnedFd); -FromRawDescriptor!(OwnedFd); -IntoRawDescriptor!(OwnedFd); diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/memory_mapping.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/stub/memory_mapping.rs deleted file mode 100644 index 6f7cc281c..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/memory_mapping.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::ptr::NonNull; - -use libc::c_void; - -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -/// Wraps an anonymous shared memory mapping in the current process. Provides -/// RAII semantics including munmap when no longer needed. -#[derive(Debug)] -pub struct MemoryMapping { - pub addr: NonNull, - pub size: usize, -} - -impl MemoryMapping { - pub fn from_safe_descriptor( - _descriptor: SafeDescriptor, - _size: usize, - _map_info: u32, - ) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/mod.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/stub/mod.rs deleted file mode 100644 index b34581ae0..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -pub mod descriptor; -pub mod memory_mapping; -pub mod shm; - -#[allow(unused_imports)] -pub use shm::round_up_to_page_size; -pub use shm::SharedMemory; - -pub use memory_mapping::MemoryMapping; diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/shm.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/stub/shm.rs deleted file mode 100644 index 9c43d8e8f..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/stub/shm.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::ffi::CStr; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -pub struct SharedMemory { - size: u64, -} - -impl SharedMemory { - /// Creates a new shared memory file descriptor with zero size. - pub fn new(_debug_name: &CStr, _size: u64) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Gets the size in bytes of the shared memory. - /// - /// The size returned here does not reflect changes by other interfaces or users of the shared - /// memory file descriptor.. - pub fn size(&self) -> u64 { - self.size - } -} - -impl AsRawDescriptor for SharedMemory { - fn as_raw_descriptor(&self) -> RawDescriptor { - unimplemented!() - } -} - -impl IntoRawDescriptor for SharedMemory { - fn into_raw_descriptor(self) -> RawDescriptor { - unimplemented!() - } -} - -/// Uses the system's page size in bytes to round the given value up to the nearest page boundary. -pub fn round_up_to_page_size(_v: u64) -> RutabagaResult { - Err(RutabagaError::Unsupported) -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/descriptor.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/descriptor.rs deleted file mode 100644 index 73d41c10e..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/descriptor.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::convert::TryFrom; -use std::fs::File; -use std::os::fd::AsFd; -use std::os::fd::BorrowedFd; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::FromRawFd; -use std::os::unix::io::IntoRawFd; -use std::os::unix::io::OwnedFd; -use std::os::unix::io::RawFd; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::Descriptor; -use crate::rutabaga_os::descriptor::FromRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::descriptor::SafeDescriptor; - -type Error = std::io::Error; -type Result = std::result::Result; - -pub type RawDescriptor = RawFd; - -/// Clones `fd`, returning a new file descriptor that refers to the same open file description as -/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file -/// descriptor flags with `fd`. -fn clone_fd(fd: &dyn AsRawFd) -> Result { - // Safe because this doesn't modify any memory and we check the return value. - let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) }; - if ret < 0 { - Err(Error::last_os_error()) - } else { - Ok(ret) - } -} - -impl Drop for SafeDescriptor { - fn drop(&mut self) { - let _ = unsafe { libc::close(self.descriptor) }; - } -} - -impl AsRawFd for SafeDescriptor { - fn as_raw_fd(&self) -> RawFd { - self.as_raw_descriptor() - } -} - -impl TryFrom<&dyn AsRawFd> for SafeDescriptor { - type Error = std::io::Error; - - fn try_from(fd: &dyn AsRawFd) -> Result { - Ok(SafeDescriptor { - descriptor: clone_fd(fd)?, - }) - } -} - -impl SafeDescriptor { - /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will - /// share the same underlying count within the kernel. - pub fn try_clone(&self) -> Result { - // Safe because this doesn't modify any memory and we check the return value. - let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) }; - if descriptor < 0 { - Err(Error::last_os_error()) - } else { - Ok(SafeDescriptor { descriptor }) - } - } -} - -impl From for File { - fn from(s: SafeDescriptor) -> File { - // Safe because we own the SafeDescriptor at this point. - unsafe { File::from_raw_fd(s.into_raw_descriptor()) } - } -} - -// AsRawFd for interoperability with interfaces that require it. Within crosvm, -// always use AsRawDescriptor when possible. -impl AsRawFd for Descriptor { - fn as_raw_fd(&self) -> RawFd { - self.0 - } -} - -impl AsFd for SafeDescriptor { - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: the `BorrowedFd` we return lives no longer than this `SafeDescriptor`, so the - // descriptor will remain open. - unsafe { BorrowedFd::borrow_raw(self.descriptor) } - } -} - -macro_rules! AsRawDescriptor { - ($name:ident) => { - impl AsRawDescriptor for $name { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.as_raw_fd() - } - } - }; -} - -macro_rules! FromRawDescriptor { - ($name:ident) => { - impl FromRawDescriptor for $name { - unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self { - $name::from_raw_fd(descriptor) - } - } - }; -} - -macro_rules! IntoRawDescriptor { - ($name:ident) => { - impl IntoRawDescriptor for $name { - fn into_raw_descriptor(self) -> RawDescriptor { - self.into_raw_fd() - } - } - }; -} - -// Implementations for File. This enables the File-type to use -// RawDescriptor, but does not mean File should be used as a generic -// descriptor container. That should go to either SafeDescriptor or another more -// relevant container type. -AsRawDescriptor!(File); -FromRawDescriptor!(File); -IntoRawDescriptor!(File); -AsRawDescriptor!(OwnedFd); -FromRawDescriptor!(OwnedFd); -IntoRawDescriptor!(OwnedFd); diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs deleted file mode 100644 index 581d3c971..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::num::NonZeroUsize; -use std::os::fd::AsFd; -use std::ptr::NonNull; - -use libc::c_void; -use nix::sys::mman::mmap; -use nix::sys::mman::munmap; -use nix::sys::mman::MapFlags; -use nix::sys::mman::ProtFlags; - -use crate::rutabaga_os::descriptor::SafeDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -use crate::rutabaga_utils::RUTABAGA_MAP_ACCESS_MASK; -use crate::rutabaga_utils::RUTABAGA_MAP_ACCESS_READ; -use crate::rutabaga_utils::RUTABAGA_MAP_ACCESS_RW; -use crate::rutabaga_utils::RUTABAGA_MAP_ACCESS_WRITE; - -/// Wraps an anonymous shared memory mapping in the current process. Provides -/// RAII semantics including munmap when no longer needed. -#[derive(Debug)] -pub struct MemoryMapping { - pub addr: NonNull, - pub size: usize, -} - -impl Drop for MemoryMapping { - fn drop(&mut self) { - // This is safe because we mmap the area at addr ourselves, and nobody - // else is holding a reference to it. - unsafe { - munmap(self.addr, self.size).unwrap(); - } - } -} - -impl MemoryMapping { - pub fn from_safe_descriptor( - descriptor: SafeDescriptor, - size: usize, - map_info: u32, - ) -> RutabagaResult { - let non_zero_opt = NonZeroUsize::new(size); - let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK { - RUTABAGA_MAP_ACCESS_READ => ProtFlags::PROT_READ, - RUTABAGA_MAP_ACCESS_WRITE => ProtFlags::PROT_WRITE, - RUTABAGA_MAP_ACCESS_RW => ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - _ => return Err(RutabagaError::SpecViolation("incorrect access flags")), - }; - - if let Some(non_zero_size) = non_zero_opt { - let addr = unsafe { - mmap( - None, - non_zero_size, - prot, - MapFlags::MAP_SHARED, - descriptor.as_fd(), - 0, - )? - }; - Ok(MemoryMapping { addr, size }) - } else { - Err(RutabagaError::SpecViolation("zero size mapping")) - } - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/mod.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/mod.rs deleted file mode 100644 index b34581ae0..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -pub mod descriptor; -pub mod memory_mapping; -pub mod shm; - -#[allow(unused_imports)] -pub use shm::round_up_to_page_size; -pub use shm::SharedMemory; - -pub use memory_mapping::MemoryMapping; diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/shm.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/shm.rs deleted file mode 100644 index a01ced204..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/shm.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::convert::TryInto; -use std::ffi::CStr; -use std::os::unix::io::OwnedFd; - -use libc::off_t; -use nix::sys::memfd::memfd_create; -use nix::sys::memfd::MFdFlags; -use nix::unistd::ftruncate; -use nix::unistd::sysconf; -use nix::unistd::SysconfVar; -use vmm_sys_util::align_upwards; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -pub struct SharedMemory { - fd: OwnedFd, - size: u64, -} - -impl SharedMemory { - /// Creates a new shared memory file descriptor with zero size. - /// - /// If a name is given, it will appear in `/proc/self/fd/` for the purposes of - /// debugging. The name does not need to be unique. - /// - /// The file descriptor is opened with the close on exec flag and allows memfd sealing. - pub fn new(debug_name: &CStr, size: u64) -> RutabagaResult { - // Nix will transition to owned fd in future releases, do it locally here. - let fd = memfd_create( - debug_name, - MFdFlags::MFD_CLOEXEC | MFdFlags::MFD_ALLOW_SEALING, - )?; - - let size_off_t: off_t = size.try_into()?; - ftruncate(&fd, size_off_t)?; - - Ok(SharedMemory { fd, size }) - } - - /// Gets the size in bytes of the shared memory. - /// - /// The size returned here does not reflect changes by other interfaces or users of the shared - /// memory file descriptor.. - pub fn size(&self) -> u64 { - self.size - } -} - -impl AsRawDescriptor for SharedMemory { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.fd.as_raw_descriptor() - } -} - -impl IntoRawDescriptor for SharedMemory { - fn into_raw_descriptor(self) -> RawDescriptor { - self.fd.into_raw_descriptor() - } -} - -/// Uses the system's page size in bytes to round the given value up to the nearest page boundary. -pub fn round_up_to_page_size(v: u64) -> RutabagaResult { - let page_size_opt = sysconf(SysconfVar::PAGE_SIZE)?; - if let Some(page_size) = page_size_opt { - let aligned_size = align_upwards!(v, page_size as u64); - Ok(aligned_size) - } else { - Err(RutabagaError::SpecViolation("no page size")) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/descriptor.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/windows/descriptor.rs deleted file mode 100644 index 2b15bdb52..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/descriptor.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::marker::Send; -use std::marker::Sync; -use std::ops::Drop; -use std::os::windows::io::AsRawHandle; -use std::os::windows::io::FromRawHandle; -use std::os::windows::io::IntoRawHandle; -use std::os::windows::io::RawHandle; - -use winapi::shared::minwindef::FALSE; -use winapi::shared::minwindef::TRUE; -use winapi::um::handleapi::CloseHandle; -use winapi::um::handleapi::DuplicateHandle; -use winapi::um::processthreadsapi::GetCurrentProcess; -use winapi::um::winnt::DUPLICATE_SAME_ACCESS; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::Descriptor; -use crate::rutabaga_os::descriptor::FromRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::descriptor::SafeDescriptor; - -type Error = std::io::Error; -type Result = std::result::Result; - -pub type RawDescriptor = RawHandle; - -impl Drop for SafeDescriptor { - fn drop(&mut self) { - unsafe { CloseHandle(self.descriptor) }; - } -} - -impl AsRawHandle for SafeDescriptor { - fn as_raw_handle(&self) -> RawHandle { - self.as_raw_descriptor() - } -} - -pub fn duplicate_handle_from_source_process( - source_process_handle: RawHandle, - hndl: RawHandle, - target_process_handle: RawHandle, -) -> io::Result { - // Safe because: - // 1. We are checking the return code - // 2. new_handle_ptr points to a valid location on the stack - // 3. Caller guarantees hndl is a real valid handle. - unsafe { - let mut new_handle: RawHandle = std::ptr::null_mut(); - let success_flag = DuplicateHandle( - /* hSourceProcessHandle= */ source_process_handle, - /* hSourceHandle= */ hndl, - /* hTargetProcessHandle= */ target_process_handle, - /* lpTargetHandle= */ &mut new_handle, - /* dwDesiredAccess= */ 0, - /* bInheritHandle= */ TRUE, - /* dwOptions= */ DUPLICATE_SAME_ACCESS, - ); - - if success_flag == FALSE { - Err(io::Error::last_os_error()) - } else { - Ok(new_handle) - } - } -} - -fn duplicate_handle_with_target_handle( - hndl: RawHandle, - target_process_handle: RawHandle, -) -> io::Result { - // Safe because `GetCurrentProcess` just gets the current process handle. - duplicate_handle_from_source_process( - unsafe { GetCurrentProcess() }, - hndl, - target_process_handle, - ) -} - -pub fn duplicate_handle(hndl: RawHandle) -> io::Result { - // Safe because `GetCurrentProcess` just gets the current process handle. - duplicate_handle_with_target_handle(hndl, unsafe { GetCurrentProcess() }) -} - -impl TryFrom<&dyn AsRawHandle> for SafeDescriptor { - type Error = std::io::Error; - - fn try_from(handle: &dyn AsRawHandle) -> std::result::Result { - Ok(SafeDescriptor { - descriptor: duplicate_handle(handle.as_raw_handle())?, - }) - } -} - -impl SafeDescriptor { - /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will - /// share the same underlying count within the kernel. - pub fn try_clone(&self) -> Result { - // Safe because `duplicate_handle` will return a valid handle, or at the very least error - // out. - Ok(unsafe { SafeDescriptor::from_raw_descriptor(duplicate_handle(self.descriptor)?) }) - } -} - -// On Windows, RawHandles are represented by raw pointers but are not used as such in -// rust code, and are therefore safe to send between threads. -unsafe impl Send for SafeDescriptor {} -unsafe impl Sync for SafeDescriptor {} - -// On Windows, RawHandles are represented by raw pointers but are opaque to the -// userspace and cannot be derefenced by rust code, and are therefore safe to -// send between threads. -unsafe impl Send for Descriptor {} -unsafe impl Sync for Descriptor {} - -macro_rules! AsRawDescriptor { - ($name:ident) => { - impl AsRawDescriptor for $name { - fn as_raw_descriptor(&self) -> RawDescriptor { - return self.as_raw_handle(); - } - } - }; -} - -macro_rules! FromRawDescriptor { - ($name:ident) => { - impl FromRawDescriptor for $name { - unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self { - return $name::from_raw_handle(descriptor); - } - } - }; -} - -macro_rules! IntoRawDescriptor { - ($name:ident) => { - impl IntoRawDescriptor for $name { - fn into_raw_descriptor(self) -> RawDescriptor { - return self.into_raw_handle(); - } - } - }; -} - -// Implementations for File. This enables the File-type to use the cross-platform -// RawDescriptor, but does not mean File should be used as a generic -// descriptor container. That should go to either SafeDescriptor or another more -// relevant container type. -// TODO(b/148971445): Ensure there are no usages of File that aren't actually files. -AsRawDescriptor!(File); -FromRawDescriptor!(File); -IntoRawDescriptor!(File); diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/memory_mapping.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/windows/memory_mapping.rs deleted file mode 100644 index 6f7cc281c..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/memory_mapping.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::ptr::NonNull; - -use libc::c_void; - -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -/// Wraps an anonymous shared memory mapping in the current process. Provides -/// RAII semantics including munmap when no longer needed. -#[derive(Debug)] -pub struct MemoryMapping { - pub addr: NonNull, - pub size: usize, -} - -impl MemoryMapping { - pub fn from_safe_descriptor( - _descriptor: SafeDescriptor, - _size: usize, - _map_info: u32, - ) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/mod.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/windows/mod.rs deleted file mode 100644 index 22f6db190..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -pub mod descriptor; -pub mod memory_mapping; -pub mod shm; - -pub use shm::round_up_to_page_size; -pub use shm::SharedMemory; - -pub use memory_mapping::MemoryMapping; diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/shm.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/windows/shm.rs deleted file mode 100644 index 2afe7a1f5..000000000 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/windows/shm.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::ffi::CStr; - -use crate::rutabaga_os::descriptor::AsRawDescriptor; -use crate::rutabaga_os::descriptor::IntoRawDescriptor; -use crate::rutabaga_os::RawDescriptor; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::RutabagaError; -use crate::rutabaga_utils::RutabagaResult; - -/// A shared memory file descriptor and its size. -pub struct SharedMemory { - pub descriptor: SafeDescriptor, - pub size: u64, -} - -impl SharedMemory { - /// Creates a new shared memory file mapping with zero size. - pub fn new(_debug_name: &CStr, _size: u64) -> RutabagaResult { - Err(RutabagaError::Unsupported) - } - - /// Gets the size in bytes of the shared memory. - /// - /// The size returned here does not reflect changes by other interfaces or users of the shared - /// memory file descriptor. - pub fn size(&self) -> u64 { - self.size - } -} - -/// USE THIS CAUTIOUSLY. The returned handle is not a file handle and cannot be -/// used as if it were one. It is a handle to a the associated file mapping object -/// and should only be used for memory-mapping the file view. -impl AsRawDescriptor for SharedMemory { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.descriptor.as_raw_descriptor() - } -} - -impl IntoRawDescriptor for SharedMemory { - fn into_raw_descriptor(self) -> RawDescriptor { - self.descriptor.into_raw_descriptor() - } -} - -pub fn round_up_to_page_size(_v: u64) -> RutabagaResult { - Err(RutabagaError::Unsupported) -} diff --git a/src/rutabaga_gfx/src/rutabaga_snapshot.rs b/src/rutabaga_gfx/src/rutabaga_snapshot.rs deleted file mode 100644 index 982267dea..000000000 --- a/src/rutabaga_gfx/src/rutabaga_snapshot.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2023 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::collections::BTreeMap; -use std::io::Read; -use std::io::Write; - -use zerocopy::FromBytes; -use zerocopy::Immutable; -use zerocopy::IntoBytes; - -pub struct RutabagaSnapshot { - pub resources: BTreeMap, -} - -pub struct RutabagaResourceSnapshot { - pub resource_id: u32, - pub width: u32, - pub height: u32, -} - -impl RutabagaSnapshot { - // To avoid adding a build dependency, we use a custom serialization format. It is an internal - // detail, doesn't need to support host migration (e.g. we don't need to care about endianess - // or integer sizes), and isn't expected to be stable across releases. - pub fn serialize_to(&self, w: &mut impl Write) -> std::io::Result<()> { - fn write(w: &mut impl Write, v: impl IntoBytes + Immutable) -> std::io::Result<()> { - w.write_all(v.as_bytes()) - } - - write(w, self.resources.len())?; - for (id, resource) in self.resources.iter() { - assert_eq!(*id, resource.resource_id); - write(w, resource.resource_id)?; - write(w, resource.width)?; - write(w, resource.height)?; - } - - Ok(()) - } - - pub fn deserialize_from(r: &mut impl Read) -> std::io::Result { - fn read( - r: &mut impl Read, - ) -> std::io::Result { - let mut v: T = Default::default(); - r.read_exact(v.as_mut_bytes())?; - Ok(v) - } - - let num_resources: usize = read::(r)?; - let mut resources = BTreeMap::new(); - for _ in 0..num_resources { - let resource_id = read(r)?; - let width = read(r)?; - let height = read(r)?; - resources.insert( - resource_id, - RutabagaResourceSnapshot { - resource_id, - width, - height, - }, - ); - } - - // Verify we have consumed the all the input by checking for EOF. - let mut buf = [0u8]; - if r.read(&mut buf)? != 0 { - return Err(std::io::ErrorKind::InvalidData.into()); - } - - Ok(RutabagaSnapshot { resources }) - } -} diff --git a/src/rutabaga_gfx/src/rutabaga_utils.rs b/src/rutabaga_gfx/src/rutabaga_utils.rs deleted file mode 100644 index 1377ddccf..000000000 --- a/src/rutabaga_gfx/src/rutabaga_utils.rs +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! rutabaga_utils: Utility enums, structs, and implementations needed by the rest of the crate. - -use std::ffi::NulError; -use std::fmt; -use std::io::Error as IoError; -use std::num::TryFromIntError; -use std::os::raw::c_char; -use std::os::raw::c_void; -use std::path::PathBuf; -use std::str::Utf8Error; -use std::sync::Arc; - -#[cfg(unix)] -use nix::Error as NixError; -use remain::sorted; -use thiserror::Error; -#[cfg(feature = "vulkano")] -use vulkano::device::DeviceCreationError; -#[cfg(feature = "vulkano")] -use vulkano::image::ImageCreationError; -#[cfg(feature = "vulkano")] -use vulkano::instance::InstanceCreationError; -#[cfg(feature = "vulkano")] -use vulkano::memory::DeviceMemoryError; -#[cfg(feature = "vulkano")] -use vulkano::memory::MemoryMapError; -#[cfg(feature = "vulkano")] -use vulkano::LoadingError; -#[cfg(feature = "vulkano")] -use vulkano::VulkanError; - -use crate::rutabaga_os::SafeDescriptor; - -/// Represents a buffer. `base` contains the address of a buffer, while `len` contains the length -/// of the buffer. -#[repr(C)] -#[derive(Copy, Clone)] -pub struct RutabagaIovec { - pub base: *mut c_void, - pub len: usize, -} - -unsafe impl Send for RutabagaIovec {} -unsafe impl Sync for RutabagaIovec {} - -/// 3D resource creation parameters. Also used to create 2D resource. Constants based on Mesa's -/// (internal) Gallium interface. Not in the virtio-gpu spec, but should be since dumb resources -/// can't work with gfxstream/virglrenderer without this. -pub const RUTABAGA_PIPE_TEXTURE_2D: u32 = 2; -pub const RUTABAGA_PIPE_BIND_RENDER_TARGET: u32 = 2; -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct ResourceCreate3D { - pub target: u32, - pub format: u32, - pub bind: u32, - pub width: u32, - pub height: u32, - pub depth: u32, - pub array_size: u32, - pub last_level: u32, - pub nr_samples: u32, - pub flags: u32, -} - -/// Blob resource creation parameters. -pub const RUTABAGA_BLOB_MEM_GUEST: u32 = 0x0001; -pub const RUTABAGA_BLOB_MEM_HOST3D: u32 = 0x0002; -pub const RUTABAGA_BLOB_MEM_HOST3D_GUEST: u32 = 0x0003; - -pub const RUTABAGA_BLOB_FLAG_USE_MAPPABLE: u32 = 0x0001; -pub const RUTABAGA_BLOB_FLAG_USE_SHAREABLE: u32 = 0x0002; -pub const RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE: u32 = 0x0004; -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct ResourceCreateBlob { - pub blob_mem: u32, - pub blob_flags: u32, - pub blob_id: u64, - pub size: u64, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct RutabagaMapping { - pub ptr: u64, - pub size: u64, -} - -/// Metadata associated with a swapchain, video or camera image. -#[derive(Default, Copy, Clone, Debug)] -pub struct Resource3DInfo { - pub width: u32, - pub height: u32, - pub drm_fourcc: u32, - pub strides: [u32; 4], - pub offsets: [u32; 4], - pub modifier: u64, -} - -/// A unique identifier for a device. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId { - pub device_uuid: [u8; 16], - pub driver_uuid: [u8; 16], -} - -/// Memory index and physical device id of the associated VkDeviceMemory. -#[derive(Copy, Clone, Default)] -pub struct VulkanInfo { - pub memory_idx: u32, - pub device_id: DeviceId, -} - -/// Rutabaga context init capset id mask. -pub const RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK: u32 = 0x00ff; - -/// Rutabaga flags for creating fences. -pub const RUTABAGA_FLAG_FENCE: u32 = 1 << 0; -pub const RUTABAGA_FLAG_INFO_RING_IDX: u32 = 1 << 1; -pub const RUTABAGA_FLAG_FENCE_SHAREABLE: u32 = 1 << 2; - -/// Convenience struct for Rutabaga fences -#[repr(C)] -#[derive(Copy, Clone)] -pub struct RutabagaFence { - pub flags: u32, - pub fence_id: u64, - pub ctx_id: u32, - pub ring_idx: u8, -} - -/// Rutabaga debug types -pub const RUTABAGA_DEBUG_ERROR: u32 = 0x01; -pub const RUTABAGA_DEBUG_WARNING: u32 = 0x02; -pub const RUTABAGA_DEBUG_INFO: u32 = 0x03; - -/// Convenience struct for debug data -#[repr(C)] -#[derive(Copy, Clone)] -pub struct RutabagaDebug { - pub debug_type: u32, - pub message: *const c_char, -} - -// This is sketchy, since `message` is a C-string and there's no locking + atomics. However, -// the current use case is to mirror the C-API. If the `RutabagaDebugHandler` is used with -// by Rust code, a different struct should be used. -unsafe impl Send for RutabagaDebug {} -unsafe impl Sync for RutabagaDebug {} - -/// Mapped memory caching flags (see virtio_gpu spec) -pub const RUTABAGA_MAP_CACHE_MASK: u32 = 0x0f; -pub const RUTABAGA_MAP_CACHE_CACHED: u32 = 0x01; -pub const RUTABAGA_MAP_CACHE_UNCACHED: u32 = 0x02; -pub const RUTABAGA_MAP_CACHE_WC: u32 = 0x03; -/// Access flags (not in virtio_gpu spec) -pub const RUTABAGA_MAP_ACCESS_MASK: u32 = 0xf0; -pub const RUTABAGA_MAP_ACCESS_READ: u32 = 0x10; -pub const RUTABAGA_MAP_ACCESS_WRITE: u32 = 0x20; -pub const RUTABAGA_MAP_ACCESS_RW: u32 = 0x30; - -/// Rutabaga capsets. -pub const RUTABAGA_CAPSET_VIRGL: u32 = 1; -pub const RUTABAGA_CAPSET_VIRGL2: u32 = 2; -pub const RUTABAGA_CAPSET_GFXSTREAM_VULKAN: u32 = 3; -pub const RUTABAGA_CAPSET_VENUS: u32 = 4; -pub const RUTABAGA_CAPSET_CROSS_DOMAIN: u32 = 5; -pub const RUTABAGA_CAPSET_DRM: u32 = 6; -pub const RUTABAGA_CAPSET_GFXSTREAM_MAGMA: u32 = 7; -pub const RUTABAGA_CAPSET_GFXSTREAM_GLES: u32 = 8; -pub const RUTABAGA_CAPSET_GFXSTREAM_COMPOSER: u32 = 9; - -/// An error generated while using this crate. -#[sorted] -#[derive(Error, Debug)] -pub enum RutabagaError { - /// Indicates `Rutabaga` was already initialized since only one Rutabaga instance per process - /// is allowed. - #[error("attempted to use a rutabaga asset already in use")] - AlreadyInUse, - /// Checked Arithmetic error - #[error("arithmetic failed: {}({}) {op} {}({})", .field1.0, .field1.1, .field2.0, .field2.1)] - CheckedArithmetic { - field1: (&'static str, usize), - field2: (&'static str, usize), - op: &'static str, - }, - /// Checked Range error - #[error("range check failed: {}({}) vs {}({})", .field1.0, .field1.1, .field2.0, .field2.1)] - CheckedRange { - field1: (&'static str, usize), - field2: (&'static str, usize), - }, - /// An internal Rutabaga component error was returned. - #[error("rutabaga component failed with error {0}")] - ComponentError(i32), - /// Invalid 2D info - #[error("invalid 2D info")] - Invalid2DInfo, - /// Invalid Capset - #[error("invalid capset")] - InvalidCapset, - /// A command buffer with insufficient space was submitted. - #[error("invalid command buffer submitted")] - InvalidCommandBuffer, - /// A command size was submitted that was invalid. - #[error("command buffer submitted with invalid size: {0}")] - InvalidCommandSize(usize), - /// Invalid RutabagaComponent - #[error("invalid rutabaga component")] - InvalidComponent, - /// Invalid Context ID - #[error("invalid context id")] - InvalidContextId, - /// Invalid cross domain channel - #[error("invalid cross domain channel")] - InvalidCrossDomainChannel, - /// Invalid cross domain item ID - #[error("invalid cross domain item id")] - InvalidCrossDomainItemId, - /// Invalid cross domain item type - #[error("invalid cross domain item type")] - InvalidCrossDomainItemType, - /// Invalid cross domain state - #[error("invalid cross domain state")] - InvalidCrossDomainState, - /// Invalid gralloc backend. - #[error("invalid gralloc backend")] - InvalidGrallocBackend, - /// Invalid gralloc dimensions. - #[error("invalid gralloc dimensions")] - InvalidGrallocDimensions, - /// Invalid gralloc DRM format. - #[error("invalid gralloc DRM format")] - InvalidGrallocDrmFormat, - /// Invalid GPU type. - #[error("invalid GPU type for gralloc")] - InvalidGrallocGpuType, - /// Invalid number of YUV planes. - #[error("invalid number of YUV planes")] - InvalidGrallocNumberOfPlanes, - /// The indicated region of guest memory is invalid. - #[error("an iovec is outside of guest memory's range")] - InvalidIovec, - /// Invalid Resource ID. - #[error("invalid resource id")] - InvalidResourceId, - /// Indicates an error in the RutabagaBuilder. - #[error("invalid rutabaga build parameters: {0}")] - InvalidRutabagaBuild(&'static str), - /// An error with the RutabagaHandle - #[error("invalid rutabaga handle")] - InvalidRutabagaHandle, - /// Invalid Vulkan info - #[error("invalid vulkan info")] - InvalidVulkanInfo, - /// An input/output error occured. - #[error("an input/output error occur: {0}")] - IoError(IoError), - /// The mapping failed. - #[error("The mapping failed with library error: {0}")] - MappingFailed(i32), - /// Nix crate error. - #[cfg(unix)] - #[error("The errno is {0}")] - NixError(NixError), - #[error("Nul Error occured {0}")] - NulError(NulError), - /// Violation of the Rutabaga spec occured. - #[error("violation of the rutabaga spec: {0}")] - SpecViolation(&'static str), - /// An attempted integer conversion failed. - #[error("int conversion failed: {0}")] - TryFromIntError(TryFromIntError), - /// The command is unsupported. - #[error("the requested function is not implemented")] - Unsupported, - /// Utf8 error. - #[error("an utf8 error occured: {0}")] - Utf8Error(Utf8Error), - /// Device creation error - #[cfg(feature = "vulkano")] - #[error("vulkano device creation failure {0}")] - VkDeviceCreationError(DeviceCreationError), - /// Device memory error - #[cfg(feature = "vulkano")] - #[error("vulkano device memory failure {0}")] - VkDeviceMemoryError(DeviceMemoryError), - /// General Vulkan error - #[cfg(feature = "vulkano")] - #[error("vulkano failure {0}")] - VkError(VulkanError), - /// Image creation error - #[cfg(feature = "vulkano")] - #[error("vulkano image creation failure {0}")] - VkImageCreationError(ImageCreationError), - /// Instance creation error - #[cfg(feature = "vulkano")] - #[error("vulkano instance creation failure {0}")] - VkInstanceCreationError(InstanceCreationError), - /// Loading error - #[cfg(feature = "vulkano")] - #[error("vulkano loading failure {0}")] - VkLoadingError(LoadingError), - /// Memory map error - #[cfg(feature = "vulkano")] - #[error("vulkano memory map failure {0}")] - VkMemoryMapError(MemoryMapError), -} - -#[cfg(unix)] -impl From for RutabagaError { - fn from(e: NixError) -> RutabagaError { - RutabagaError::NixError(e) - } -} - -impl From for RutabagaError { - fn from(e: NulError) -> RutabagaError { - RutabagaError::NulError(e) - } -} - -impl From for RutabagaError { - fn from(e: IoError) -> RutabagaError { - RutabagaError::IoError(e) - } -} - -impl From for RutabagaError { - fn from(e: TryFromIntError) -> RutabagaError { - RutabagaError::TryFromIntError(e) - } -} - -impl From for RutabagaError { - fn from(e: Utf8Error) -> RutabagaError { - RutabagaError::Utf8Error(e) - } -} - -/// The result of an operation in this crate. -pub type RutabagaResult = std::result::Result; - -/// Flags for virglrenderer. Copied from virglrenderer bindings. -const VIRGLRENDERER_USE_EGL: u32 = 1 << 0; -const VIRGLRENDERER_THREAD_SYNC: u32 = 1 << 1; -const VIRGLRENDERER_USE_GLX: u32 = 1 << 2; -const VIRGLRENDERER_USE_SURFACELESS: u32 = 1 << 3; -const VIRGLRENDERER_USE_GLES: u32 = 1 << 4; -const VIRGLRENDERER_USE_EXTERNAL_BLOB: u32 = 1 << 5; -const VIRGLRENDERER_VENUS: u32 = 1 << 6; -const VIRGLRENDERER_NO_VIRGL: u32 = 1 << 7; -const VIRGLRENDERER_USE_ASYNC_FENCE_CB: u32 = 1 << 8; -const VIRGLRENDERER_RENDER_SERVER: u32 = 1 << 9; -const VIRGLRENDERER_DRM: u32 = 1 << 10; - -/// virglrenderer flag struct. -#[derive(Copy, Clone)] -pub struct VirglRendererFlags(u32); - -impl Default for VirglRendererFlags { - fn default() -> VirglRendererFlags { - VirglRendererFlags::new() - .use_virgl(true) - .use_venus(false) - .use_egl(true) - .use_surfaceless(true) - .use_gles(true) - .use_render_server(false) - } -} - -impl From for VirglRendererFlags { - fn from(val: u32) -> Self { - VirglRendererFlags(val) - } -} - -impl From for i32 { - fn from(flags: VirglRendererFlags) -> i32 { - flags.0 as i32 - } -} - -impl VirglRendererFlags { - /// Create new virglrenderer flags. - pub fn new() -> VirglRendererFlags { - VirglRendererFlags(0) - } - - fn set_flag(self, bitmask: u32, set: bool) -> VirglRendererFlags { - if set { - VirglRendererFlags(self.0 | bitmask) - } else { - VirglRendererFlags(self.0 & (!bitmask)) - } - } - - /// Enable virgl support - pub fn use_virgl(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_NO_VIRGL, !v) - } - - /// Enable venus support - pub fn use_venus(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_VENUS, v) - } - - /// Enable drm native context support - pub fn use_drm(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_DRM, v) - } - - /// Use EGL for context creation. - pub fn use_egl(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_EGL, v) - } - - /// Use a dedicated thread for fence synchronization. - pub fn use_thread_sync(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_THREAD_SYNC, v) - } - - /// Use GLX for context creation. - pub fn use_glx(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_GLX, v) - } - - /// No surfaces required when creating context. - pub fn use_surfaceless(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_SURFACELESS, v) - } - - /// Use GLES drivers. - pub fn use_gles(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_GLES, v) - } - - /// Use external memory when creating blob resources. - pub fn use_external_blob(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_EXTERNAL_BLOB, v) - } - - /// Retire fence directly from sync thread. - pub fn use_async_fence_cb(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_USE_ASYNC_FENCE_CB, v) - } - - pub fn use_render_server(self, v: bool) -> VirglRendererFlags { - self.set_flag(VIRGLRENDERER_RENDER_SERVER, v) - } -} - -/// Flags for the gfxstream renderer. -const STREAM_RENDERER_FLAGS_USE_EGL: u32 = 1 << 0; -#[allow(dead_code)] -const STREAM_RENDERER_FLAGS_THREAD_SYNC: u32 = 1 << 1; -const STREAM_RENDERER_FLAGS_USE_GLX: u32 = 1 << 2; -const STREAM_RENDERER_FLAGS_USE_SURFACELESS: u32 = 1 << 3; -const STREAM_RENDERER_FLAGS_USE_GLES: u32 = 1 << 4; -const STREAM_RENDERER_FLAGS_USE_VK_BIT: u32 = 1 << 5; -const STREAM_RENDERER_FLAGS_USE_EXTERNAL_BLOB: u32 = 1 << 6; -const STREAM_RENDERER_FLAGS_USE_SYSTEM_BLOB: u32 = 1 << 7; -const STREAM_RENDERER_FLAGS_VULKAN_NATIVE_SWAPCHAIN_BIT: u32 = 1 << 8; - -/// gfxstream flag struct. -#[derive(Copy, Clone, Default)] -pub struct GfxstreamFlags(u32); - -#[derive(Clone, Debug)] -pub enum RutabagaWsi { - Surfaceless, - VulkanSwapchain, -} - -impl GfxstreamFlags { - /// Create new gfxstream flags. - pub fn new() -> GfxstreamFlags { - GfxstreamFlags(0) - } - - fn set_flag(self, bitmask: u32, set: bool) -> GfxstreamFlags { - if set { - GfxstreamFlags(self.0 | bitmask) - } else { - GfxstreamFlags(self.0 & (!bitmask)) - } - } - - /// Use EGL for context creation. - pub fn use_egl(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_EGL, v) - } - - /// Use GLX for context creation. - pub fn use_glx(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_GLX, v) - } - - /// No surfaces required when creating context. - pub fn use_surfaceless(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_SURFACELESS, v) - } - - /// Use GLES drivers. - pub fn use_gles(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_GLES, v) - } - - /// Support using Vulkan. - pub fn use_vulkan(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_VK_BIT, v) - } - - /// Use the Vulkan swapchain to draw on the host window. - pub fn set_wsi(self, v: RutabagaWsi) -> GfxstreamFlags { - let use_vulkan_swapchain = matches!(v, RutabagaWsi::VulkanSwapchain); - self.set_flag( - STREAM_RENDERER_FLAGS_VULKAN_NATIVE_SWAPCHAIN_BIT, - use_vulkan_swapchain, - ) - } - - /// Use external blob when creating resources. - pub fn use_external_blob(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_EXTERNAL_BLOB, v) - } - - /// Use system blob when creating resources. - pub fn use_system_blob(self, v: bool) -> GfxstreamFlags { - self.set_flag(STREAM_RENDERER_FLAGS_USE_SYSTEM_BLOB, v) - } -} - -impl From for u32 { - fn from(flags: GfxstreamFlags) -> u32 { - flags.0 - } -} - -impl From for i32 { - fn from(flags: GfxstreamFlags) -> i32 { - flags.0 as i32 - } -} - -impl From for u64 { - fn from(flags: GfxstreamFlags) -> u64 { - flags.0 as u64 - } -} - -/// Transfers {to, from} 1D buffers, 2D textures, 3D textures, and cubemaps. -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct Transfer3D { - pub x: u32, - pub y: u32, - pub z: u32, - pub w: u32, - pub h: u32, - pub d: u32, - pub level: u32, - pub stride: u32, - pub layer_stride: u32, - pub offset: u64, -} - -impl Transfer3D { - /// Constructs a 2 dimensional XY box in 3 dimensional space with unit depth and zero - /// displacement on the Z axis. - pub fn new_2d(x: u32, y: u32, w: u32, h: u32) -> Transfer3D { - Transfer3D { - x, - y, - z: 0, - w, - h, - d: 1, - level: 0, - stride: 0, - layer_stride: 0, - offset: 0, - } - } - - /// Returns true if this box represents a volume of zero. - pub fn is_empty(&self) -> bool { - self.w == 0 || self.h == 0 || self.d == 0 - } -} - -/// 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 -/// considered at the moment. -#[derive(Clone)] -pub struct RutabagaChannel { - pub base_channel: PathBuf, - pub channel_type: u32, -} - -/// Enumeration of possible rutabaga components. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] -pub enum RutabagaComponentType { - Rutabaga2D, - VirglRenderer, - Gfxstream, - CrossDomain, -} - -/// Rutabaga handle types (memory and sync in same namespace) -pub const RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD: u32 = 0x0001; -pub const RUTABAGA_MEM_HANDLE_TYPE_DMABUF: u32 = 0x0002; -pub const RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_WIN32: u32 = 0x0003; -pub const RUTABAGA_MEM_HANDLE_TYPE_SHM: u32 = 0x0004; -pub const RUTABAGA_MEM_HANDLE_TYPE_ZIRCON: u32 = 0x0005; -pub const RUTABAGA_MEM_HANDLE_TYPE_APPLE: u32 = 0x0006; - -pub const RUTABAGA_FENCE_HANDLE_TYPE_OPAQUE_FD: u32 = 0x0006; -pub const RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD: u32 = 0x0007; -pub const RUTABAGA_FENCE_HANDLE_TYPE_OPAQUE_WIN32: u32 = 0x0008; -pub const RUTABAGA_FENCE_HANDLE_TYPE_ZIRCON: u32 = 0x0009; - -/// Handle to OS-specific memory or synchronization objects. -pub struct RutabagaHandle { - pub os_handle: SafeDescriptor, - pub handle_type: u32, -} - -impl RutabagaHandle { - /// Clones an existing rutabaga handle, by using OS specific mechanisms. - pub fn try_clone(&self) -> RutabagaResult { - let clone = self - .os_handle - .try_clone() - .map_err(|_| RutabagaError::InvalidRutabagaHandle)?; - Ok(RutabagaHandle { - os_handle: clone, - handle_type: self.handle_type, - }) - } -} - -#[derive(Clone)] -pub struct RutabagaHandler { - closure: Arc, -} - -impl RutabagaHandler -where - S: Send + Sync + Clone + 'static, -{ - pub fn new(closure: impl Fn(S) + Send + Sync + 'static) -> RutabagaHandler { - RutabagaHandler { - closure: Arc::new(closure), - } - } - - pub fn call(&self, data: S) { - (self.closure)(data) - } -} - -impl fmt::Debug for RutabagaHandler { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Closure debug").finish() - } -} - -pub type RutabagaFenceHandler = RutabagaHandler; - -pub type RutabagaDebugHandler = RutabagaHandler; diff --git a/src/rutabaga_gfx/src/virgl_renderer.rs b/src/rutabaga_gfx/src/virgl_renderer.rs deleted file mode 100644 index a2627ca0d..000000000 --- a/src/rutabaga_gfx/src/virgl_renderer.rs +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2020 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! virgl_renderer: Handles 3D virtio-gpu hypercalls using virglrenderer. -//! External code found at . - -#![cfg(feature = "virgl_renderer")] - -use std::cmp::min; -use std::convert::TryFrom; -use std::io::Error as SysError; -use std::io::IoSliceMut; -use std::mem::size_of; -use std::os::raw::c_char; -use std::os::raw::c_int; -use std::os::raw::c_void; -use std::os::unix::io::AsRawFd; -use std::panic::catch_unwind; -use std::process::abort; -use std::ptr::null_mut; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -use log::debug; -use log::error; -use log::warn; - -use crate::generated::virgl_debug_callback_bindings::*; -use crate::generated::virgl_renderer_bindings::*; -use crate::renderer_utils::*; -use crate::rutabaga_core::RutabagaComponent; -use crate::rutabaga_core::RutabagaContext; -use crate::rutabaga_core::RutabagaResource; -use crate::rutabaga_os::FromRawDescriptor; -use crate::rutabaga_os::IntoRawDescriptor; -use crate::rutabaga_os::SafeDescriptor; -use crate::rutabaga_utils::*; - -type Query = virgl_renderer_export_query; - -/// The virtio-gpu backend state tracker which supports accelerated rendering. -pub struct VirglRenderer {} - -struct VirglRendererContext { - ctx_id: u32, -} - -fn import_resource(resource: &mut RutabagaResource) -> RutabagaResult<()> { - if (resource.component_mask & (1 << (RutabagaComponentType::VirglRenderer as u8))) != 0 { - return Ok(()); - } - - if let Some(_handle) = &resource.handle { - #[cfg(target_os = "linux")] - if _handle.handle_type == RUTABAGA_MEM_HANDLE_TYPE_DMABUF { - let dmabuf_fd = _handle.os_handle.try_clone()?.into_raw_descriptor(); - // Safe because we are being passed a valid fd - unsafe { - let dmabuf_size = libc::lseek64(dmabuf_fd, 0, libc::SEEK_END); - libc::lseek64(dmabuf_fd, 0, libc::SEEK_SET); - let args = virgl_renderer_resource_import_blob_args { - res_handle: resource.resource_id, - blob_mem: resource.blob_mem, - fd_type: VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF, - fd: dmabuf_fd, - size: dmabuf_size as u64, - }; - let ret = virgl_renderer_resource_import_blob(&args); - if ret != 0 { - // import_blob can fail if we've previously imported this resource, - // but in any case virglrenderer does not take ownership of the fd - // in error paths - // - // Because of the re-import case we must still fall through to the - // virgl_renderer_ctx_attach_resource() call. - libc::close(dmabuf_fd); - return Ok(()); - } - resource.component_mask |= 1 << (RutabagaComponentType::VirglRenderer as u8); - } - } - } - - Ok(()) -} - -impl RutabagaContext for VirglRendererContext { - fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> { - if !fence_ids.is_empty() { - return Err(RutabagaError::Unsupported); - } - if !commands.len().is_multiple_of(size_of::()) { - return Err(RutabagaError::InvalidCommandSize(commands.len())); - } - let dword_count = (commands.len() / size_of::()) as i32; - // Safe because the context and buffer are valid and virglrenderer will have been - // initialized if there are Context instances. - let ret = unsafe { - virgl_renderer_submit_cmd( - commands.as_mut_ptr() as *mut c_void, - self.ctx_id as i32, - dword_count, - ) - }; - ret_to_res(ret) - } - - fn attach(&mut self, resource: &mut RutabagaResource) { - match import_resource(resource) { - Ok(()) => (), - Err(e) => error!("importing resource failing with {e}"), - } - - // The context id and resource id must be valid because the respective instances ensure - // their lifetime. - unsafe { - virgl_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32); - } - } - - fn detach(&mut self, resource: &RutabagaResource) { - // The context id and resource id must be valid because the respective instances ensure - // their lifetime. - unsafe { - virgl_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32); - } - } - - fn component_type(&self) -> RutabagaComponentType { - RutabagaComponentType::VirglRenderer - } - - fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - // RutabagaFence::flags are not compatible with virglrenderer's fencing API and currently - // virglrenderer context's assume all fences on a single timeline are MERGEABLE, and enforce - // this assumption. - let flags: u32 = VIRGL_RENDERER_FENCE_FLAG_MERGEABLE; - - let ret = unsafe { - virgl_renderer_context_create_fence( - fence.ctx_id, - flags, - fence.ring_idx as u64, - fence.fence_id, - ) - }; - ret_to_res(ret) - } -} - -impl Drop for VirglRendererContext { - fn drop(&mut self) { - // The context is safe to destroy because nothing else can be referencing it. - unsafe { - virgl_renderer_context_destroy(self.ctx_id); - } - } -} - -extern "C" fn debug_callback(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list) { - const BUF_LEN: usize = 256; - let mut v = [b' '; BUF_LEN]; - - let printed_len = unsafe { - let ptr = v.as_mut_ptr() as *mut ::std::os::raw::c_char; - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "riscv64" - ))] - let size = BUF_LEN as ::std::os::raw::c_ulong; - #[cfg(target_arch = "arm")] - let size = BUF_LEN as ::std::os::raw::c_uint; - - stdio::vsnprintf(ptr, size, fmt, ap) - }; - - if printed_len < 0 { - debug!("rutabaga_gfx::virgl_renderer::debug_callback: vsnprintf returned {printed_len}"); - } else { - // vsnprintf returns the number of chars that *would* have been printed - let len = min(printed_len as usize, BUF_LEN - 1); - debug!("{}", String::from_utf8_lossy(&v[..len])); - } -} - -/// TODO(ryanneph): re-evaluate if "ring_idx: u8" can be used instead so we can drop this in favor -/// of the common write_context_fence() from renderer_utils before promoting to -/// cfg(feature = "virgl_renderer"). -#[cfg(feature = "virgl_renderer_next")] -extern "C" fn write_context_fence(cookie: *mut c_void, ctx_id: u32, ring_idx: u64, fence_id: u64) { - catch_unwind(|| { - assert!(!cookie.is_null()); - let cookie = unsafe { &*(cookie as *mut RutabagaCookie) }; - - // Call fence completion callback - if let Some(handler) = &cookie.fence_handler { - handler.call(RutabagaFence { - flags: RUTABAGA_FLAG_FENCE | RUTABAGA_FLAG_INFO_RING_IDX, - fence_id, - ctx_id, - ring_idx: ring_idx as u8, - }); - } - }) - .unwrap_or_else(|_| abort()) -} - -unsafe extern "C" fn write_fence(cookie: *mut c_void, fence: u32) { - catch_unwind(|| { - assert!(!cookie.is_null()); - let cookie = &*(cookie as *mut RutabagaCookie); - - // Call fence completion callback - if let Some(handler) = &cookie.fence_handler { - handler.call(RutabagaFence { - flags: RUTABAGA_FLAG_FENCE, - fence_id: fence as u64, - ctx_id: 0, - ring_idx: 0, - }); - } - }) - .unwrap_or_else(|_| abort()) -} - -#[cfg(feature = "virgl_renderer_next")] -unsafe extern "C" fn get_server_fd(cookie: *mut c_void, version: u32) -> c_int { - catch_unwind(|| { - assert!(!cookie.is_null()); - let cookie = &mut *(cookie as *mut RutabagaCookie); - - if version != 0 { - return -1; - } - - // Transfer the fd ownership to virglrenderer. - cookie - .render_server_fd - .take() - .map(SafeDescriptor::into_raw_descriptor) - .unwrap_or(-1) - }) - .unwrap_or_else(|_| abort()) -} - -const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks = &virgl_renderer_callbacks { - #[cfg(not(feature = "virgl_renderer_next"))] - version: 1, - #[cfg(feature = "virgl_renderer_next")] - version: 3, - write_fence: Some(write_fence), - create_gl_context: None, - destroy_gl_context: None, - make_current: None, - get_drm_fd: None, - #[cfg(not(feature = "virgl_renderer_next"))] - write_context_fence: None, - #[cfg(feature = "virgl_renderer_next")] - write_context_fence: Some(write_context_fence), - #[cfg(not(feature = "virgl_renderer_next"))] - get_server_fd: None, - #[cfg(feature = "virgl_renderer_next")] - get_server_fd: Some(get_server_fd), -}; - -/// Retrieves metadata suitable for export about this resource. If "export_fd" is true, -/// performs an export of this resource so that it may be imported by other processes. -fn export_query(resource_id: u32) -> RutabagaResult { - let mut query: Query = Default::default(); - query.hdr.stype = VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY; - query.hdr.stype_version = 0; - query.hdr.size = size_of::() as u32; - query.in_resource_id = resource_id; - query.in_export_fds = 0; - - // Safe because the image parameters are stack variables of the correct type. - let ret = - unsafe { virgl_renderer_execute(&mut query as *mut _ as *mut c_void, query.hdr.size) }; - - ret_to_res(ret)?; - Ok(query) -} - -impl VirglRenderer { - pub fn init( - virglrenderer_flags: VirglRendererFlags, - fence_handler: RutabagaFenceHandler, - render_server_fd: Option, - ) -> RutabagaResult> { - 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: {}", - SysError::last_os_error() - ); - } - } - - // virglrenderer is a global state backed library that uses thread bound OpenGL contexts. - // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied - // to whichever thread called this function first. - static INIT_ONCE: AtomicBool = AtomicBool::new(false); - if INIT_ONCE - .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) - .is_err() - { - return Err(RutabagaError::AlreadyInUse); - } - - unsafe { virgl_set_debug_callback(Some(debug_callback)) }; - - // Cookie is intentionally never freed because virglrenderer never gets uninitialized. - // Otherwise, Resource and Context would become invalid because their lifetime is not tied - // to the Renderer instance. Doing so greatly simplifies the ownership for users of this - // library. - let cookie = Box::into_raw(Box::new(RutabagaCookie { - render_server_fd, - fence_handler: Some(fence_handler), - debug_handler: None, - })); - - // Safe because a valid cookie and set of callbacks is used and the result is checked for - // error. - let ret = unsafe { - virgl_renderer_init( - cookie as *mut c_void, - virglrenderer_flags.into(), - VIRGL_RENDERER_CALLBACKS as *const virgl_renderer_callbacks - as *mut virgl_renderer_callbacks, - ) - }; - - // If the initialization failed, allow the users to try again. - if ret != 0 { - INIT_ONCE.store(false, Ordering::Release); - } - - ret_to_res(ret)?; - Ok(Box::new(VirglRenderer {})) - } - - #[allow(unused_variables)] - fn map_info(&self, resource_id: u32) -> RutabagaResult { - #[cfg(feature = "virgl_renderer_next")] - { - let mut map_info = 0; - let ret = unsafe { virgl_renderer_resource_get_map_info(resource_id, &mut map_info) }; - ret_to_res(ret)?; - - Ok(map_info | RUTABAGA_MAP_ACCESS_RW) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } - - #[cfg(target_os = "macos")] - fn map_ptr(&self, resource_id: u32) -> RutabagaResult { - let mut map_ptr = 0; - let ret = unsafe { virgl_renderer_resource_get_map_ptr(resource_id, &mut map_ptr) }; - ret_to_res(ret)?; - - Ok(map_ptr) - } - - fn query(&self, resource_id: u32) -> RutabagaResult { - let query = export_query(resource_id)?; - if query.out_num_fds == 0 { - return Err(RutabagaError::Unsupported); - } - - // virglrenderer unfortunately doesn't return the width or height, so map to zero. - Ok(Resource3DInfo { - width: 0, - height: 0, - drm_fourcc: query.out_fourcc, - strides: query.out_strides, - offsets: query.out_offsets, - modifier: query.out_modifier, - }) - } - - #[allow(unused_variables)] - fn export_blob(&self, resource_id: u32) -> RutabagaResult> { - #[cfg(feature = "virgl_renderer_next")] - { - let mut fd_type = 0; - let mut fd = -1; - let ret = - unsafe { virgl_renderer_resource_export_blob(resource_id, &mut fd_type, &mut fd) }; - ret_to_res(ret)?; - - // Safe because the FD was just returned by a successful virglrenderer - // call so it must be valid and owned by us. - let handle = unsafe { SafeDescriptor::from_raw_descriptor(fd) }; - - let handle_type = match fd_type { - VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF => RUTABAGA_MEM_HANDLE_TYPE_DMABUF, - VIRGL_RENDERER_BLOB_FD_TYPE_SHM => RUTABAGA_MEM_HANDLE_TYPE_SHM, - VIRGL_RENDERER_BLOB_FD_TYPE_OPAQUE => RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD, - VIRGL_RENDERER_BLOB_FD_TYPE_APPLE => RUTABAGA_MEM_HANDLE_TYPE_APPLE, - _ => { - return Err(RutabagaError::Unsupported); - } - }; - - Ok(Arc::new(RutabagaHandle { - os_handle: handle, - handle_type, - })) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } -} - -impl Drop for VirglRenderer { - fn drop(&mut self) { - // Safe because virglrenderer is initialized. - // - // This invalidates all context ids and resource ids. It is fine because struct Rutabaga - // makes sure contexts and resources are dropped before this is reached. Even if it did - // not, virglrenderer is designed to deal with invalid ids safely. - unsafe { - virgl_renderer_cleanup(null_mut()); - } - } -} - -impl RutabagaComponent for VirglRenderer { - fn get_capset_info(&self, capset_id: u32) -> (u32, u32) { - let mut version = 0; - let mut size = 0; - // Safe because virglrenderer is initialized by now and properly size stack variables are - // used for the pointers. - unsafe { - virgl_renderer_get_cap_set(capset_id, &mut version, &mut size); - } - (version, size) - } - - fn get_capset(&self, capset_id: u32, version: u32) -> Vec { - let (_, max_size) = self.get_capset_info(capset_id); - let mut buf = vec![0u8; max_size as usize]; - // Safe because virglrenderer is initialized by now and the given buffer is sized properly - // for the given cap id/version. - unsafe { - virgl_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void); - } - buf - } - - fn force_ctx_0(&self) { - unsafe { virgl_renderer_force_ctx_0() }; - } - - fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { - let ret = unsafe { virgl_renderer_create_fence(fence.fence_id as i32, fence.ctx_id) }; - ret_to_res(ret) - } - - fn event_poll(&self) { - unsafe { virgl_renderer_poll() }; - } - - fn poll_descriptor(&self) -> Option { - // Safe because it can be called anytime and returns -1 in the event of an error. - let fd = unsafe { virgl_renderer_get_poll_fd() }; - if fd >= 0 { - if let Ok(dup_fd) = SafeDescriptor::try_from(&fd as &dyn AsRawFd) { - return Some(dup_fd); - } - } - None - } - - fn create_3d( - &self, - resource_id: u32, - resource_create_3d: ResourceCreate3D, - ) -> RutabagaResult { - let mut args = virgl_renderer_resource_create_args { - handle: resource_id, - target: resource_create_3d.target, - format: resource_create_3d.format, - bind: resource_create_3d.bind, - width: resource_create_3d.width, - height: resource_create_3d.height, - depth: resource_create_3d.depth, - array_size: resource_create_3d.array_size, - last_level: resource_create_3d.last_level, - nr_samples: resource_create_3d.nr_samples, - flags: resource_create_3d.flags, - }; - - // Safe because virglrenderer is initialized by now, and the return value is checked before - // returning a new resource. The backing buffers are not supplied with this call. - let ret = unsafe { virgl_renderer_resource_create(&mut args, null_mut(), 0) }; - ret_to_res(ret)?; - - Ok(RutabagaResource { - resource_id, - handle: self.export_blob(resource_id).ok(), - blob: false, - blob_mem: 0, - blob_flags: 0, - map_info: None, - #[cfg(target_os = "macos")] - map_ptr: None, - info_2d: None, - info_3d: self.query(resource_id).ok(), - vulkan_info: None, - backing_iovecs: None, - component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8), - size: 0, - mapping: None, - }) - } - - fn attach_backing( - &self, - resource_id: u32, - vecs: &mut Vec, - ) -> RutabagaResult<()> { - // Safe because the backing is into guest memory that we store a reference count for. - let ret = unsafe { - virgl_renderer_resource_attach_iov( - resource_id as i32, - vecs.as_mut_ptr() as *mut iovec, - vecs.len() as i32, - ) - }; - ret_to_res(ret) - } - - fn detach_backing(&self, resource_id: u32) { - // Safe as we don't need the old backing iovecs returned and the reference to the guest - // memory can be dropped as it will no longer be needed for this resource. - unsafe { - virgl_renderer_resource_detach_iov(resource_id as i32, null_mut(), null_mut()); - } - } - - fn unref_resource(&self, resource_id: u32) { - // The resource is safe to unreference destroy because no user of these bindings can still - // be holding a reference. - unsafe { - virgl_renderer_resource_unref(resource_id); - } - } - - fn transfer_write( - &self, - ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - ) -> RutabagaResult<()> { - if transfer.is_empty() { - return Ok(()); - } - - let mut transfer_box = VirglBox { - x: transfer.x, - y: transfer.y, - z: transfer.z, - w: transfer.w, - h: transfer.h, - d: transfer.d, - }; - - // Safe because only stack variables of the appropriate type are used. - let ret = unsafe { - virgl_renderer_transfer_write_iov( - resource.resource_id, - ctx_id, - transfer.level as i32, - transfer.stride, - transfer.layer_stride, - &mut transfer_box as *mut VirglBox as *mut virgl_box, - transfer.offset, - null_mut(), - 0, - ) - }; - ret_to_res(ret) - } - - fn transfer_read( - &self, - ctx_id: u32, - resource: &mut RutabagaResource, - transfer: Transfer3D, - buf: Option, - ) -> RutabagaResult<()> { - if transfer.is_empty() { - return Ok(()); - } - - let mut transfer_box = VirglBox { - x: transfer.x, - y: transfer.y, - z: transfer.z, - w: transfer.w, - h: transfer.h, - d: transfer.d, - }; - - let mut iov = RutabagaIovec { - base: null_mut(), - len: 0, - }; - - let (iovecs, num_iovecs) = match buf { - Some(mut buf) => { - iov.base = buf.as_mut_ptr() as *mut c_void; - iov.len = buf.len(); - (&mut iov as *mut RutabagaIovec as *mut iovec, 1) - } - None => (null_mut(), 0), - }; - - // Safe because only stack variables of the appropriate type are used. - let ret = unsafe { - virgl_renderer_transfer_read_iov( - resource.resource_id, - ctx_id, - transfer.level, - transfer.stride, - transfer.layer_stride, - &mut transfer_box as *mut VirglBox as *mut virgl_box, - transfer.offset, - iovecs, - num_iovecs, - ) - }; - ret_to_res(ret) - } - - #[allow(unused_variables)] - fn create_blob( - &mut self, - ctx_id: u32, - resource_id: u32, - resource_create_blob: ResourceCreateBlob, - mut iovec_opt: Option>, - _handle_opt: Option, - ) -> RutabagaResult { - #[cfg(feature = "virgl_renderer_next")] - { - let mut iovec_ptr = null_mut(); - let mut num_iovecs = 0; - if let Some(ref mut iovecs) = iovec_opt { - iovec_ptr = iovecs.as_mut_ptr(); - num_iovecs = iovecs.len(); - } - - let resource_create_args = virgl_renderer_resource_create_blob_args { - res_handle: resource_id, - ctx_id, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - blob_id: resource_create_blob.blob_id, - size: resource_create_blob.size, - iovecs: iovec_ptr as *const iovec, - num_iovs: num_iovecs as u32, - }; - - let ret = unsafe { virgl_renderer_resource_create_blob(&resource_create_args) }; - ret_to_res(ret)?; - - // TODO(b/244591751): assign vulkan_info to support opaque_fd mapping via Vulkano when - // sandboxing (hence external_blob) is enabled. - Ok(RutabagaResource { - resource_id, - handle: self.export_blob(resource_id).ok(), - blob: true, - blob_mem: resource_create_blob.blob_mem, - blob_flags: resource_create_blob.blob_flags, - map_info: self.map_info(resource_id).ok(), - #[cfg(target_os = "macos")] - map_ptr: self.map_ptr(resource_id).ok(), - info_2d: None, - info_3d: self.query(resource_id).ok(), - vulkan_info: None, - backing_iovecs: iovec_opt, - component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8), - size: resource_create_blob.size, - mapping: None, - }) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } - - fn resource_map( - &self, - _resource_id: u32, - _addr: u64, - _size: u64, - _prot: i32, - _flags: i32, - ) -> RutabagaResult<()> { - #[cfg(feature = "virgl_resource_map2")] - { - let ret = unsafe { - virgl_renderer_resource_map2( - _resource_id, - _addr as *mut libc::c_void, - _size, - _prot, - _flags, - ) - }; - if ret != 0 { - return Err(RutabagaError::MappingFailed(ret)); - } - - Ok(()) - } - #[cfg(not(feature = "virgl_resource_map2"))] - Err(RutabagaError::Unsupported) - } - - fn map(&self, resource_id: u32) -> RutabagaResult { - #[cfg(feature = "virgl_renderer_next")] - { - let mut map: *mut c_void = null_mut(); - let mut size: u64 = 0; - // Safe because virglrenderer wraps and validates use of GL/VK. - let ret = unsafe { virgl_renderer_resource_map(resource_id, &mut map, &mut size) }; - if ret != 0 { - return Err(RutabagaError::MappingFailed(ret)); - } - - Ok(RutabagaMapping { - ptr: map as u64, - size, - }) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } - - fn unmap(&self, resource_id: u32) -> RutabagaResult<()> { - #[cfg(feature = "virgl_renderer_next")] - { - // Safe because virglrenderer is initialized by now. - let ret = unsafe { virgl_renderer_resource_unmap(resource_id) }; - ret_to_res(ret) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } - - #[allow(unused_variables)] - fn export_fence(&self, fence_id: u32) -> RutabagaResult { - #[cfg(feature = "virgl_renderer_next")] - { - // Safe because the parameters are stack variables of the correct type. - let mut fd: i32 = 0; - let ret = unsafe { virgl_renderer_export_fence(fence_id, &mut fd) }; - ret_to_res(ret)?; - - // Safe because the FD was just returned by a successful virglrenderer call so it must - // be valid and owned by us. - let fence = unsafe { SafeDescriptor::from_raw_descriptor(fd) }; - Ok(RutabagaHandle { - os_handle: fence, - handle_type: RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD, - }) - } - #[cfg(not(feature = "virgl_renderer_next"))] - Err(RutabagaError::Unsupported) - } - - #[allow(unused_variables)] - fn create_context( - &self, - ctx_id: u32, - context_init: u32, - context_name: Option<&str>, - _fence_handler: RutabagaFenceHandler, - ) -> RutabagaResult> { - let mut name: &str = "gpu_renderer"; - if let Some(name_string) = context_name.filter(|s| !s.is_empty()) { - name = name_string; - } - - // Safe because virglrenderer is initialized by now and the context name is statically - // allocated. The return value is checked before returning a new context. - let ret = unsafe { - #[cfg(feature = "virgl_renderer_next")] - match context_init { - 0 => virgl_renderer_context_create( - ctx_id, - name.len() as u32, - name.as_ptr() as *const c_char, - ), - _ => virgl_renderer_context_create_with_flags( - ctx_id, - context_init, - name.len() as u32, - name.as_ptr() as *const c_char, - ), - } - #[cfg(not(feature = "virgl_renderer_next"))] - virgl_renderer_context_create(ctx_id, name.len() as u32, name.as_ptr() as *const c_char) - }; - ret_to_res(ret)?; - Ok(Box::new(VirglRendererContext { ctx_id })) - } -}