From 040b256abd5669a7d7d4765e833208d9d053d56f Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Thu, 2 Jan 2025 16:18:13 -0800 Subject: [PATCH] mesa: import mesa3d_protocols This crate is intended to house zerocopy-based protocols and utilites for them. This is useful for paravirtualization and microkernel-like systems. Currently, the only supported protocol is the Kumquat GPU protocol. https://crosvm.dev/book/appendix/rutabaga_gfx.html#kumquat-media-server And there is a concept of a "KumquatStream". In the future, there would be more protocols, like a "MagmaStream". In fact, this crate would house more generic streams in the future, a gfxstream crate (* hears minds getting blown *) if you would like. Reviewed-by: Aaron Ruby Part-of: --- src/virtio/protocols/ipc/kumquat_stream.rs | 223 +++++++++++++++ src/virtio/protocols/ipc/mod.rs | 5 + src/virtio/protocols/lib.rs | 5 + src/virtio/protocols/meson.build | 11 + .../protocols/kumquat_gpu_protocol.rs | 270 ++++++++++++++++++ src/virtio/protocols/protocols/mod.rs | 4 + 6 files changed, 518 insertions(+) create mode 100644 src/virtio/protocols/ipc/kumquat_stream.rs create mode 100644 src/virtio/protocols/ipc/mod.rs create mode 100644 src/virtio/protocols/lib.rs create mode 100644 src/virtio/protocols/meson.build create mode 100644 src/virtio/protocols/protocols/kumquat_gpu_protocol.rs create mode 100644 src/virtio/protocols/protocols/mod.rs diff --git a/src/virtio/protocols/ipc/kumquat_stream.rs b/src/virtio/protocols/ipc/kumquat_stream.rs new file mode 100644 index 00000000000..af3300bfddb --- /dev/null +++ b/src/virtio/protocols/ipc/kumquat_stream.rs @@ -0,0 +1,223 @@ +// Copyright 2025 Google +// SPDX-License-Identifier: MIT + +use std::collections::VecDeque; +use std::mem::size_of; + +use mesa3d_util::AsBorrowedDescriptor; +use mesa3d_util::MesaError; +use mesa3d_util::MesaHandle; +use mesa3d_util::MesaResult; +use mesa3d_util::OwnedDescriptor; +use mesa3d_util::Reader; +use mesa3d_util::Tube; +use mesa3d_util::Writer; +use mesa3d_util::MESA_HANDLE_TYPE_SIGNAL_EVENT_FD; +use zerocopy::FromBytes; +use zerocopy::Immutable; +use zerocopy::IntoBytes; + +use crate::protocols::kumquat_gpu_protocol::*; + +const MAX_COMMAND_SIZE: usize = 4096; + +pub struct KumquatStream { + stream: Tube, + write_buffer: [u8; MAX_COMMAND_SIZE], + read_buffer: [u8; MAX_COMMAND_SIZE], +} + +impl KumquatStream { + pub fn new(stream: Tube) -> KumquatStream { + KumquatStream { + stream, + write_buffer: [0; MAX_COMMAND_SIZE], + read_buffer: [0; MAX_COMMAND_SIZE], + } + } + + pub fn write( + &mut self, + encode: KumquatGpuProtocolWrite, + ) -> MesaResult<()> { + let mut writer = Writer::new(&mut self.write_buffer); + + let array: &[OwnedDescriptor] = match encode { + KumquatGpuProtocolWrite::Cmd(cmd) => { + writer.write_obj(cmd)?; + &[] + } + KumquatGpuProtocolWrite::CmdWithHandle(cmd, handle) => { + writer.write_obj(cmd)?; + &[handle.os_handle] + } + KumquatGpuProtocolWrite::CmdWithData(cmd, data) => { + writer.write_obj(cmd)?; + writer.write_all(&data)?; + &[] + } + }; + + let bytes_written = writer.bytes_written(); + self.stream + .send(&self.write_buffer[0..bytes_written], array)?; + Ok(()) + } + + pub fn read(&mut self) -> MesaResult> { + let mut vec: Vec = Vec::new(); + let (bytes_read, descriptor_vec) = self.stream.receive(&mut self.read_buffer)?; + let mut descriptors: VecDeque = descriptor_vec.into(); + + if bytes_read == 0 { + vec.push(KumquatGpuProtocol::OkNoData); + return Ok(vec); + } + + let mut reader = Reader::new(&self.read_buffer[0..bytes_read]); + while reader.available_bytes() != 0 { + let hdr = reader.peek_obj::()?; + let protocol = match hdr.type_ { + KUMQUAT_GPU_PROTOCOL_GET_NUM_CAPSETS => { + reader.consume(size_of::()); + KumquatGpuProtocol::GetNumCapsets + } + KUMQUAT_GPU_PROTOCOL_GET_CAPSET_INFO => { + reader.consume(size_of::()); + KumquatGpuProtocol::GetCapsetInfo(hdr.payload) + } + KUMQUAT_GPU_PROTOCOL_GET_CAPSET => { + KumquatGpuProtocol::GetCapset(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_CTX_CREATE => { + KumquatGpuProtocol::CtxCreate(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_CTX_DESTROY => { + reader.consume(size_of::()); + KumquatGpuProtocol::CtxDestroy(hdr.payload) + } + KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE => { + KumquatGpuProtocol::CtxAttachResource(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE => { + KumquatGpuProtocol::CtxDetachResource(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D => { + KumquatGpuProtocol::ResourceCreate3d(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D => { + let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; + let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; + + let handle = MesaHandle { + os_handle, + handle_type: MESA_HANDLE_TYPE_SIGNAL_EVENT_FD, + }; + + KumquatGpuProtocol::TransferToHost3d(resp, handle) + } + KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D => { + let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; + let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; + + let handle = MesaHandle { + os_handle, + handle_type: MESA_HANDLE_TYPE_SIGNAL_EVENT_FD, + }; + + KumquatGpuProtocol::TransferFromHost3d(resp, handle) + } + KUMQUAT_GPU_PROTOCOL_SUBMIT_3D => { + let cmd: kumquat_gpu_protocol_cmd_submit = reader.read_obj()?; + if reader.available_bytes() < cmd.size.try_into()? { + // Large command buffers should handled via shared memory. + return Err(MesaError::Unsupported); + } else if reader.available_bytes() != 0 { + let num_in_fences = cmd.num_in_fences as usize; + let cmd_size = cmd.size as usize; + let mut cmd_buf = vec![0; cmd_size]; + let mut fence_ids: Vec = Vec::with_capacity(num_in_fences); + for _ in 0..num_in_fences { + match reader.read_obj::() { + Ok(fence_id) => { + fence_ids.push(fence_id); + } + Err(_) => return Err(MesaError::Unsupported), + } + } + reader.read_exact(&mut cmd_buf[..])?; + KumquatGpuProtocol::CmdSubmit3d(cmd, cmd_buf, fence_ids) + } else { + KumquatGpuProtocol::CmdSubmit3d(cmd, Vec::new(), Vec::new()) + } + } + KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_BLOB => { + KumquatGpuProtocol::ResourceCreateBlob(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_SNAPSHOT_SAVE => { + reader.consume(size_of::()); + KumquatGpuProtocol::SnapshotSave + } + KUMQUAT_GPU_PROTOCOL_SNAPSHOT_RESTORE => { + reader.consume(size_of::()); + KumquatGpuProtocol::SnapshotRestore + } + KUMQUAT_GPU_PROTOCOL_RESP_NUM_CAPSETS => { + reader.consume(size_of::()); + KumquatGpuProtocol::RespNumCapsets(hdr.payload) + } + KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO => { + KumquatGpuProtocol::RespCapsetInfo(reader.read_obj()?) + } + KUMQUAT_GPU_PROTOCOL_RESP_CAPSET => { + let len: usize = hdr.payload.try_into()?; + reader.consume(size_of::()); + let mut capset: Vec = vec![0; len]; + reader.read_exact(&mut capset)?; + KumquatGpuProtocol::RespCapset(capset) + } + KUMQUAT_GPU_PROTOCOL_RESP_CONTEXT_CREATE => { + reader.consume(size_of::()); + KumquatGpuProtocol::RespContextCreate(hdr.payload) + } + KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE => { + let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; + let resp: kumquat_gpu_protocol_resp_resource_create = reader.read_obj()?; + + let handle = MesaHandle { + os_handle, + handle_type: resp.handle_type, + }; + + KumquatGpuProtocol::RespResourceCreate(resp, handle) + } + KUMQUAT_GPU_PROTOCOL_RESP_CMD_SUBMIT_3D => { + let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; + let resp: kumquat_gpu_protocol_resp_cmd_submit_3d = reader.read_obj()?; + + let handle = MesaHandle { + os_handle, + handle_type: resp.handle_type, + }; + + KumquatGpuProtocol::RespCmdSubmit3d(resp.fence_id, handle) + } + KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT => { + reader.consume(size_of::()); + KumquatGpuProtocol::RespOkSnapshot + } + _ => { + return Err(MesaError::Unsupported); + } + }; + + vec.push(protocol); + } + + Ok(vec) + } + + pub fn as_borrowed_descriptor(&self) -> &OwnedDescriptor { + self.stream.as_borrowed_descriptor() + } +} diff --git a/src/virtio/protocols/ipc/mod.rs b/src/virtio/protocols/ipc/mod.rs new file mode 100644 index 00000000000..aaf441194d4 --- /dev/null +++ b/src/virtio/protocols/ipc/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2025 Google +// SPDX-License-Identifier: MIT + +pub mod kumquat_stream; +pub use kumquat_stream::KumquatStream; diff --git a/src/virtio/protocols/lib.rs b/src/virtio/protocols/lib.rs new file mode 100644 index 00000000000..400b9bdf5f0 --- /dev/null +++ b/src/virtio/protocols/lib.rs @@ -0,0 +1,5 @@ +// Copyright 2025 Google +// SPDX-License-Identifier: MIT + +pub mod ipc; +pub mod protocols; diff --git a/src/virtio/protocols/meson.build b/src/virtio/protocols/meson.build new file mode 100644 index 00000000000..09b17feb9f5 --- /dev/null +++ b/src/virtio/protocols/meson.build @@ -0,0 +1,11 @@ +# Copyright © 2024 Google +# SPDX-License-Identifier: MIT + +libmesa_protocols = static_library( + 'mesa3d_protocols', + 'lib.rs', + gnu_symbol_visibility : 'hidden', + rust_abi : 'rust', + link_with: [libmesa_rust_util], + dependencies: dep_mesa3d_util +) diff --git a/src/virtio/protocols/protocols/kumquat_gpu_protocol.rs b/src/virtio/protocols/protocols/kumquat_gpu_protocol.rs new file mode 100644 index 00000000000..6d24f74bec6 --- /dev/null +++ b/src/virtio/protocols/protocols/kumquat_gpu_protocol.rs @@ -0,0 +1,270 @@ +// Copyright 2025 Google +// SPDX-License-Identifier: MIT + +//! Protocol based on ${crosvm_src}/devices/src/virtio/gpu/protocol.rs + +use mesa3d_util::MesaHandle; +use zerocopy::FromBytes; +use zerocopy::Immutable; +use zerocopy::IntoBytes; + +/// A unique identifier for a device. +#[derive( + Copy, + Clone, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + FromBytes, + IntoBytes, + Immutable, +)] +#[repr(C)] +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, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + FromBytes, + IntoBytes, + Immutable, +)] +#[repr(C)] +pub struct VulkanInfo { + pub memory_idx: u32, + pub device_id: DeviceId, +} + +/* 2d commands */ +pub const KUMQUAT_GPU_PROTOCOL_RESOURCE_UNREF: u32 = 0x100; +pub const KUMQUAT_GPU_PROTOCOL_GET_NUM_CAPSETS: u32 = 0x101; +pub const KUMQUAT_GPU_PROTOCOL_GET_CAPSET_INFO: u32 = 0x102; +pub const KUMQUAT_GPU_PROTOCOL_GET_CAPSET: u32 = 0x103; +pub const KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_BLOB: u32 = 0x104; + +/* 3d commands */ +pub const KUMQUAT_GPU_PROTOCOL_CTX_CREATE: u32 = 0x200; +pub const KUMQUAT_GPU_PROTOCOL_CTX_DESTROY: u32 = 0x201; +pub const KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE: u32 = 0x202; +pub const KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE: u32 = 0x203; +pub const KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D: u32 = 0x204; +pub const KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D: u32 = 0x205; +pub const KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D: u32 = 0x206; +pub const KUMQUAT_GPU_PROTOCOL_SUBMIT_3D: u32 = 0x207; +pub const KUMQUAT_GPU_PROTOCOL_RESOURCE_MAP_BLOB: u32 = 0x208; +pub const KUMQUAT_GPU_PROTOCOL_RESOURCE_UNMAP_BLOB: u32 = 0x209; +pub const KUMQUAT_GPU_PROTOCOL_SNAPSHOT_SAVE: u32 = 0x208; +pub const KUMQUAT_GPU_PROTOCOL_SNAPSHOT_RESTORE: u32 = 0x209; + +/* success responses */ +pub const KUMQUAT_GPU_PROTOCOL_RESP_NODATA: u32 = 0x3001; +pub const KUMQUAT_GPU_PROTOCOL_RESP_NUM_CAPSETS: u32 = 0x3002; +pub const KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO: u32 = 0x3003; +pub const KUMQUAT_GPU_PROTOCOL_RESP_CAPSET: u32 = 0x3004; +pub const KUMQUAT_GPU_PROTOCOL_RESP_CONTEXT_CREATE: u32 = 0x3005; +pub const KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE: u32 = 0x3006; +pub const KUMQUAT_GPU_PROTOCOL_RESP_CMD_SUBMIT_3D: u32 = 0x3007; +pub const KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT: u32 = 0x3008; + +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_ctrl_hdr { + pub type_: u32, + pub payload: u32, +} + +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_box { + pub x: u32, + pub y: u32, + pub z: u32, + pub w: u32, + pub h: u32, + pub d: u32, +} + +/* KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D, KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_transfer_host_3d { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub box_: kumquat_gpu_protocol_box, + pub offset: u64, + pub level: u32, + pub stride: u32, + pub layer_stride: u32, + pub ctx_id: u32, + pub resource_id: u32, + pub padding: u32, +} + +/* KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_resource_create_3d { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + 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, + pub size: u32, + pub stride: u32, + pub ctx_id: u32, +} + +#[derive(Clone, Debug, Copy, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_ctx_create { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub nlen: u32, + pub context_init: u32, + pub debug_name: [u8; 64], +} + +impl Default for kumquat_gpu_protocol_ctx_create { + fn default() -> Self { + // SAFETY: All zero pattern is safe for this particular struct + unsafe { ::std::mem::zeroed() } + } +} + +/* KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE, KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_ctx_resource { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub ctx_id: u32, + pub resource_id: u32, +} + +/* KUMQUAT_GPU_PROTOCOL_SUBMIT_3D */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_cmd_submit { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub ctx_id: u32, + pub pad: u32, + pub size: u32, + + // The in-fence IDs are prepended to the cmd_buf and memory layout + // of the KUMQUAT_GPU_PROTOCOL_SUBMIT_3D buffer looks like this: + // _________________ + // | CMD_SUBMIT_3D | + // ----------------- + // | header | + // | in-fence IDs | + // | cmd_buf | + // ----------------- + // + // This makes in-fence IDs naturally aligned to the sizeof(u64) inside + // of the virtio buffer. + pub num_in_fences: u32, + pub flags: u32, + pub ring_idx: u8, + pub padding: [u8; 3], +} + +/* KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_resp_capset_info { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub capset_id: u32, + pub version: u32, + pub size: u32, + pub padding: u32, +} + +/* KUMQUAT_GPU_PROTOCOL_GET_CAPSET */ +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_get_capset { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub capset_id: u32, + pub capset_version: u32, +} + +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_resource_create_blob { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub ctx_id: u32, + pub blob_mem: u32, + pub blob_flags: u32, + pub padding: u32, + pub blob_id: u64, + pub size: u64, +} + +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_resp_resource_create { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub resource_id: u32, + pub handle_type: u32, + pub vulkan_info: VulkanInfo, +} + +#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct kumquat_gpu_protocol_resp_cmd_submit_3d { + pub hdr: kumquat_gpu_protocol_ctrl_hdr, + pub fence_id: u64, + pub handle_type: u32, + pub padding: u32, +} + +/// A virtio gpu command and associated metadata specific to each command. +#[derive(Debug)] +pub enum KumquatGpuProtocol { + OkNoData, + GetNumCapsets, + GetCapsetInfo(u32), + GetCapset(kumquat_gpu_protocol_get_capset), + CtxCreate(kumquat_gpu_protocol_ctx_create), + CtxDestroy(u32), + CtxAttachResource(kumquat_gpu_protocol_ctx_resource), + CtxDetachResource(kumquat_gpu_protocol_ctx_resource), + ResourceCreate3d(kumquat_gpu_protocol_resource_create_3d), + TransferToHost3d(kumquat_gpu_protocol_transfer_host_3d, MesaHandle), + TransferFromHost3d(kumquat_gpu_protocol_transfer_host_3d, MesaHandle), + CmdSubmit3d(kumquat_gpu_protocol_cmd_submit, Vec, Vec), + ResourceCreateBlob(kumquat_gpu_protocol_resource_create_blob), + SnapshotSave, + SnapshotRestore, + RespNumCapsets(u32), + RespCapsetInfo(kumquat_gpu_protocol_resp_capset_info), + RespCapset(Vec), + RespContextCreate(u32), + RespResourceCreate(kumquat_gpu_protocol_resp_resource_create, MesaHandle), + RespCmdSubmit3d(u64, MesaHandle), + RespOkSnapshot, +} + +pub enum KumquatGpuProtocolWrite { + Cmd(T), + CmdWithHandle(T, MesaHandle), + CmdWithData(T, Vec), +} diff --git a/src/virtio/protocols/protocols/mod.rs b/src/virtio/protocols/protocols/mod.rs new file mode 100644 index 00000000000..b5e56434a3c --- /dev/null +++ b/src/virtio/protocols/protocols/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2025 Google +// SPDX-License-Identifier: MIT + +pub mod kumquat_gpu_protocol;