From cc4a2845e90a9e05f5b14620b028a80a1b56a300 Mon Sep 17 00:00:00 2001 From: Wenyu Huang Date: Sat, 19 Oct 2024 00:15:39 -0400 Subject: [PATCH 1/4] Add desc module and split.rs Add a new module called desc for split and packed descriptor. split.rs represents the split descriptor and packed.rs will represent the packed descriptor. The Descriptor struct in mod.rs represents the memory layout of the split and packed descriptor. Signed-off-by: Wenyu Huang --- virtio-queue/src/desc/mod.rs | 67 ++++++++++ virtio-queue/src/desc/split.rs | 237 +++++++++++++++++++++++++++++++++ virtio-queue/src/lib.rs | 1 + 3 files changed, 305 insertions(+) create mode 100644 virtio-queue/src/desc/mod.rs create mode 100644 virtio-queue/src/desc/split.rs diff --git a/virtio-queue/src/desc/mod.rs b/virtio-queue/src/desc/mod.rs new file mode 100644 index 00000000..317791bd --- /dev/null +++ b/virtio-queue/src/desc/mod.rs @@ -0,0 +1,67 @@ +//! Descriptor types for virtio queue. + +use vm_memory::{ByteValued, Le16, Le32, Le64}; + +pub mod split; + +/// a virtio descriptor +#[deprecated = "Descriptor has been deprecated. Please use RawDescriptor"] +pub type Descriptor = RawDescriptor; + +/// A virtio descriptor's layout constraints with C representation. +/// This is a unified representation of the memory layout order +/// for packed descriptors and split descriptors. +/// This type corresponds to struct virtq_desc, see: +/// https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.html#x1-720008 +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct RawDescriptor(Le64, Le32, Le16, Le16); + +// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and +// all accesses through safe `vm-memory` API will validate any garbage that could be +// included in there. +unsafe impl ByteValued for RawDescriptor {} + +impl From for RawDescriptor { + fn from(desc: split::Descriptor) -> Self { + RawDescriptor( + Le64::from(desc.addr().0), + Le32::from(desc.len()), + Le16::from(desc.flags()), + Le16::from(desc.next()), + ) + } +} + +impl From for split::Descriptor { + fn from(desc: RawDescriptor) -> split::Descriptor { + split::Descriptor::new(desc.0.into(), desc.1.into(), desc.2.into(), desc.3.into()) + } +} + +#[cfg(test)] +mod tests { + use vm_memory::{Le16, Le32, Le64}; + + use super::{split, RawDescriptor}; + + #[test] + fn test_desc_from_split() { + let split_desc = split::Descriptor::new(1, 2, 3, 4); + let desc = RawDescriptor::from(split_desc); + assert_eq!(split_desc.addr().0, desc.0); + assert_eq!(split_desc.len(), desc.1); + assert_eq!(split_desc.flags(), desc.2); + assert_eq!(split_desc.next(), desc.3); + } + + #[test] + fn test_split_from_desc() { + let desc = RawDescriptor(Le64::from(1), Le32::from(2), Le16::from(3), Le16::from(4)); + let split_desc = split::Descriptor::from(desc); + assert_eq!(split_desc.addr().0, desc.0); + assert_eq!(split_desc.len(), desc.1); + assert_eq!(split_desc.flags(), desc.2); + assert_eq!(split_desc.next(), desc.3); + } +} diff --git a/virtio-queue/src/desc/split.rs b/virtio-queue/src/desc/split.rs new file mode 100644 index 00000000..9aa01e4b --- /dev/null +++ b/virtio-queue/src/desc/split.rs @@ -0,0 +1,237 @@ +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. +// +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Copyright © 2019 Intel Corporation +// +// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +//! split descriptor + +use vm_memory::{ByteValued, GuestAddress, Le16, Le32, Le64}; + +use virtio_bindings::bindings::virtio_ring::{ + VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, +}; + +/// A virtio split descriptor constraints with C representation. +#[repr(C)] +#[derive(Default, Clone, Copy, Debug)] +pub struct Descriptor { + /// Guest physical address of device specific data. + addr: Le64, + + /// Length of device specific data. + len: Le32, + + /// Includes next, write, and indirect bits. + flags: Le16, + + /// Index into the descriptor table of the next descriptor if flags has the `next` bit set. + next: Le16, +} + +#[allow(clippy::len_without_is_empty)] +impl Descriptor { + /// Create a new descriptor. + /// + /// # Arguments + /// * `addr` - the guest physical address of the descriptor buffer. + /// * `len` - the length of the descriptor buffer. + /// * `flags` - the `flags` for the descriptor. + /// * `next` - the `next` field of the descriptor. + pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self { + Descriptor { + addr: addr.into(), + len: len.into(), + flags: flags.into(), + next: next.into(), + } + } + + /// Return the guest physical address of the descriptor buffer. + pub fn addr(&self) -> GuestAddress { + GuestAddress(self.addr.into()) + } + + /// Return the length of the descriptor buffer. + pub fn len(&self) -> u32 { + self.len.into() + } + + /// Return the flags for this descriptor, including next, write and indirect bits. + pub fn flags(&self) -> u16 { + self.flags.into() + } + + /// Return the value stored in the `next` field of the descriptor. + pub fn next(&self) -> u16 { + self.next.into() + } + + /// Check whether this descriptor refers to a buffer containing an indirect descriptor table. + pub fn refers_to_indirect_table(&self) -> bool { + self.flags() & VRING_DESC_F_INDIRECT as u16 != 0 + } + + /// Check whether the `VIRTQ_DESC_F_NEXT` is set for the descriptor. + pub fn has_next(&self) -> bool { + self.flags() & VRING_DESC_F_NEXT as u16 != 0 + } + + /// Check if the driver designated this as a write only descriptor. + /// + /// If this is false, this descriptor is read only. + /// Write only means the the emulated device can write and the driver can read. + pub fn is_write_only(&self) -> bool { + self.flags() & VRING_DESC_F_WRITE as u16 != 0 + } +} + +#[cfg(any(test, feature = "test-utils"))] +impl Descriptor { + /// Set the guest physical address of the descriptor buffer. + pub fn set_addr(&mut self, addr: u64) { + self.addr = addr.into(); + } + + /// Set the length of the descriptor buffer. + pub fn set_len(&mut self, len: u32) { + self.len = len.into(); + } + + /// Set the flags for this descriptor. + pub fn set_flags(&mut self, flags: u16) { + self.flags = flags.into(); + } + + /// Set the value stored in the `next` field of the descriptor. + pub fn set_next(&mut self, next: u16) { + self.next = next.into(); + } +} + +// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and +// all accesses through safe `vm-memory` API will validate any garbage that could be +// included in there. +unsafe impl ByteValued for Descriptor {} + +/// Represents the contents of an element from the used virtqueue ring. +// Note that the `ByteValued` implementation of this structure expects the `VirtqUsedElem` to store +// only plain old data types. +#[repr(C)] +#[derive(Clone, Copy, Default, Debug)] +pub struct VirtqUsedElem { + id: Le32, + len: Le32, +} + +impl VirtqUsedElem { + /// Create a new `VirtqUsedElem` instance. + /// + /// # Arguments + /// * `id` - the index of the used descriptor chain. + /// * `len` - the total length of the descriptor chain which was used (written to). + #[allow(unused)] + pub(crate) fn new(id: u32, len: u32) -> Self { + VirtqUsedElem { + id: id.into(), + len: len.into(), + } + } +} + +#[cfg(any(test, feature = "test-utils"))] +#[allow(clippy::len_without_is_empty)] +impl VirtqUsedElem { + /// Get the index of the used descriptor chain. + pub fn id(&self) -> u32 { + self.id.into() + } + + /// Get `length` field of the used ring entry. + pub fn len(&self) -> u32 { + self.len.into() + } +} + +// SAFETY: This is safe because `VirtqUsedElem` contains only wrappers over POD types +// and all accesses through safe `vm-memory` API will validate any garbage that could be +// included in there. +unsafe impl ByteValued for VirtqUsedElem {} + +#[cfg(test)] +mod tests { + use super::*; + use memoffset::offset_of; + use std::mem::{align_of, size_of}; + + #[test] + fn test_descriptor_offset() { + assert_eq!(size_of::(), 16); + assert_eq!(offset_of!(Descriptor, addr), 0); + assert_eq!(offset_of!(Descriptor, len), 8); + assert_eq!(offset_of!(Descriptor, flags), 12); + assert_eq!(offset_of!(Descriptor, next), 14); + assert!(align_of::() <= 16); + } + + #[test] + fn test_descriptor_getter_setter() { + let mut desc = Descriptor::new(0, 0, 0, 0); + + desc.set_addr(0x1000); + assert_eq!(desc.addr(), GuestAddress(0x1000)); + desc.set_len(0x2000); + assert_eq!(desc.len(), 0x2000); + desc.set_flags(VRING_DESC_F_NEXT as u16); + assert_eq!(desc.flags(), VRING_DESC_F_NEXT as u16); + assert!(desc.has_next()); + assert!(!desc.is_write_only()); + assert!(!desc.refers_to_indirect_table()); + desc.set_flags(VRING_DESC_F_WRITE as u16); + assert_eq!(desc.flags(), VRING_DESC_F_WRITE as u16); + assert!(!desc.has_next()); + assert!(desc.is_write_only()); + assert!(!desc.refers_to_indirect_table()); + desc.set_flags(VRING_DESC_F_INDIRECT as u16); + assert_eq!(desc.flags(), VRING_DESC_F_INDIRECT as u16); + assert!(!desc.has_next()); + assert!(!desc.is_write_only()); + assert!(desc.refers_to_indirect_table()); + desc.set_next(3); + assert_eq!(desc.next(), 3); + } + + #[test] + fn test_descriptor_copy() { + let e1 = Descriptor::new(1, 2, VRING_DESC_F_NEXT as u16, 3); + let mut e2 = Descriptor::default(); + + e2.as_mut_slice().copy_from_slice(e1.as_slice()); + assert_eq!(e1.addr(), e2.addr()); + assert_eq!(e1.len(), e2.len()); + assert_eq!(e1.flags(), e2.flags()); + assert_eq!(e1.next(), e2.next()); + } + + #[test] + fn test_used_elem_offset() { + assert_eq!(offset_of!(VirtqUsedElem, id), 0); + assert_eq!(offset_of!(VirtqUsedElem, len), 4); + assert_eq!(size_of::(), 8); + } + + #[test] + fn test_used_elem_copy() { + let e1 = VirtqUsedElem::new(3, 15); + let mut e2 = VirtqUsedElem::new(0, 0); + + e2.as_mut_slice().copy_from_slice(e1.as_slice()); + assert_eq!(e1.id, e2.id); + assert_eq!(e1.len, e2.len); + } +} diff --git a/virtio-queue/src/lib.rs b/virtio-queue/src/lib.rs index df3d1893..0bdae8af 100644 --- a/virtio-queue/src/lib.rs +++ b/virtio-queue/src/lib.rs @@ -30,6 +30,7 @@ pub use self::queue_sync::QueueSync; pub use self::state::QueueState; pub mod defs; +pub mod desc; #[cfg(any(test, feature = "test-utils"))] pub mod mock; From fa06b5c24fd4edda34f582ff9701ac27ace6cedf Mon Sep 17 00:00:00 2001 From: Wenyu Huang Date: Sun, 20 Oct 2024 10:41:24 -0400 Subject: [PATCH 2/4] Use desc/split and remove descriptor.rs Use 'virtio_queue::desc::split::Descriptor' to replace 'virtio_queue::Descriptor'. Signed-off-by: Wenyu Huang --- fuzz/common/src/blk.rs | 4 +- fuzz/common/src/lib.rs | 13 +- fuzz/common/src/virtio_queue.rs | 4 +- fuzz/common/src/vsock.rs | 6 +- fuzz/fuzz_targets/blk.rs | 4 +- fuzz/fuzz_targets/virtio_queue.rs | 4 +- fuzz/fuzz_targets/virtio_queue_ser.rs | 4 +- fuzz/fuzz_targets/vsock.rs | 4 +- virtio-blk/src/request.rs | 129 +++++++++--- virtio-console/src/console.rs | 68 +++++-- virtio-queue/src/chain.rs | 91 ++++++--- virtio-queue/src/desc/split.rs | 40 ++++ virtio-queue/src/descriptor.rs | 276 -------------------------- virtio-queue/src/descriptor_utils.rs | 11 +- virtio-queue/src/lib.rs | 2 - virtio-queue/src/mock.rs | 50 +++-- virtio-queue/src/queue.rs | 72 ++++--- virtio-vsock/src/packet.rs | 254 +++++++++++++++++------- 18 files changed, 550 insertions(+), 486 deletions(-) delete mode 100644 virtio-queue/src/descriptor.rs diff --git a/fuzz/common/src/blk.rs b/fuzz/common/src/blk.rs index 3a4f5fad..c3e49e32 100644 --- a/fuzz/common/src/blk.rs +++ b/fuzz/common/src/blk.rs @@ -19,7 +19,7 @@ pub mod tests { use virtio_bindings::bindings::virtio_blk::VIRTIO_BLK_T_IN; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; use virtio_blk::request::{Request, RequestType}; - use virtio_queue::{mock::MockSplitQueue, Descriptor}; + use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue}; use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryMmap}; // The same as the RequestHeader type in virtio_blk, with exposed fields @@ -91,7 +91,7 @@ pub mod tests { }, ]; - let q_descriptors: Vec = + let q_descriptors: Vec = descriptors.into_iter().map(|desc| desc.into()).collect(); let mut chain = vq.build_multiple_desc_chains(&q_descriptors).unwrap(); diff --git a/fuzz/common/src/lib.rs b/fuzz/common/src/lib.rs index e8b3ea2f..473108e3 100644 --- a/fuzz/common/src/lib.rs +++ b/fuzz/common/src/lib.rs @@ -1,4 +1,7 @@ -use ::virtio_queue::{Descriptor, Queue, QueueOwnedT, QueueT}; +use ::virtio_queue::{ + desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, + Queue, QueueOwnedT, QueueT, +}; use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; @@ -183,9 +186,11 @@ impl Into for LoadOrdering { } } -impl Into for FuzzingDescriptor { - fn into(self) -> Descriptor { - Descriptor::new(self.addr, self.len, self.flags, self.next) +impl Into for FuzzingDescriptor { + fn into(self) -> RawDescriptor { + RawDescriptor::from(SplitDescriptor::new( + self.addr, self.len, self.flags, self.next, + )) } } diff --git a/fuzz/common/src/virtio_queue.rs b/fuzz/common/src/virtio_queue.rs index f5bc140b..1f4eac3f 100644 --- a/fuzz/common/src/virtio_queue.rs +++ b/fuzz/common/src/virtio_queue.rs @@ -20,7 +20,7 @@ pub mod tests { use crate::create_corpus_file; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; - use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue, QueueT}; + use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue, Queue, QueueT}; use vm_memory::{GuestAddress, GuestMemoryMmap}; pub fn create_basic_virtio_queue_ops() -> VirtioQueueInput { @@ -51,7 +51,7 @@ pub mod tests { // To be able to call the functions we actually need to create the environment for running. let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(&mem, DEFAULT_QUEUE_SIZE); - let q_descriptors: Vec = + let q_descriptors: Vec = descriptors.iter().map(|desc| (*desc).into()).collect(); vq.build_multiple_desc_chains(&q_descriptors).unwrap(); let mut q: Queue = vq.create_queue().unwrap(); diff --git a/fuzz/common/src/vsock.rs b/fuzz/common/src/vsock.rs index a9518874..9d5e4883 100644 --- a/fuzz/common/src/vsock.rs +++ b/fuzz/common/src/vsock.rs @@ -174,8 +174,8 @@ mod tests { use crate::virtio_queue::DEFAULT_QUEUE_SIZE; use std::io::Write; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; + use virtio_queue::desc::RawDescriptor; use virtio_queue::mock::MockSplitQueue; - use virtio_queue::Descriptor; use virtio_vsock::packet::VsockPacket; use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryMmap}; @@ -332,7 +332,7 @@ mod tests { next: 0, }, ]; - let q_descriptors: Vec = + let q_descriptors: Vec = descriptors.iter().map(|desc| (*desc).into()).collect(); let mut chain = vq.build_multiple_desc_chains(&q_descriptors).unwrap(); @@ -382,7 +382,7 @@ mod tests { next: 0, }, ]; - let q_descriptors: Vec = + let q_descriptors: Vec = descriptors.iter().map(|desc| (*desc).into()).collect(); let mut chain = vq.build_multiple_desc_chains(&q_descriptors).unwrap(); diff --git a/fuzz/fuzz_targets/blk.rs b/fuzz/fuzz_targets/blk.rs index 91e08e94..650df14e 100644 --- a/fuzz/fuzz_targets/blk.rs +++ b/fuzz/fuzz_targets/blk.rs @@ -6,7 +6,7 @@ use libfuzzer_sys::fuzz_target; use std::hint::black_box; use virtio_blk::request::Request; use virtio_blk::stdio_executor::StdIoBackend; -use virtio_queue::{mock::MockSplitQueue, Descriptor}; +use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue}; use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; fuzz_target!(|data: &[u8]| { @@ -23,7 +23,7 @@ fuzz_target!(|data: &[u8]| { let vq = MockSplitQueue::create(&m, start_addr, DEFAULT_QUEUE_SIZE); - let descriptors: Vec = fuzz_input + let descriptors: Vec = fuzz_input .descriptors .iter() .map(|desc| (*desc).into()) diff --git a/fuzz/fuzz_targets/virtio_queue.rs b/fuzz/fuzz_targets/virtio_queue.rs index dd07a2eb..3a7a2931 100644 --- a/fuzz/fuzz_targets/virtio_queue.rs +++ b/fuzz/fuzz_targets/virtio_queue.rs @@ -4,7 +4,7 @@ use common::{ virtio_queue::{VirtioQueueInput, DEFAULT_QUEUE_SIZE}, }; use libfuzzer_sys::fuzz_target; -use virtio_queue::{mock::MockSplitQueue, Descriptor}; +use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue}; use vm_memory::{GuestAddress, GuestMemoryMmap}; fuzz_target!(|data: &[u8]| { @@ -20,7 +20,7 @@ fuzz_target!(|data: &[u8]| { let start_addr = GuestAddress(0x1000); let m = GuestMemoryMmap::<()>::from_ranges(&[(start_addr, 0x11000)]).unwrap(); let vq = MockSplitQueue::create(&m, start_addr, DEFAULT_QUEUE_SIZE); - let descriptors: Vec = fuzz_input + let descriptors: Vec = fuzz_input .descriptors .iter() .map(|desc| (*desc).into()) diff --git a/fuzz/fuzz_targets/virtio_queue_ser.rs b/fuzz/fuzz_targets/virtio_queue_ser.rs index 7fdeb4ff..e464ee7e 100644 --- a/fuzz/fuzz_targets/virtio_queue_ser.rs +++ b/fuzz/fuzz_targets/virtio_queue_ser.rs @@ -5,7 +5,7 @@ use common::{ }; use libfuzzer_sys::fuzz_target; use std::convert::{Into, TryFrom}; -use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue, QueueState}; +use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue, Queue, QueueState}; use vm_memory::{GuestAddress, GuestMemoryMmap}; fuzz_target!(|data: &[u8]| { @@ -22,7 +22,7 @@ fuzz_target!(|data: &[u8]| { let m = GuestMemoryMmap::<()>::from_ranges(&[(start_addr, 0x11000)]).unwrap(); let vq = MockSplitQueue::create(&m, start_addr, DEFAULT_QUEUE_SIZE); - let descriptors: Vec = fuzz_input + let descriptors: Vec = fuzz_input .descriptors .iter() .map(|desc| (*desc).into()) diff --git a/fuzz/fuzz_targets/vsock.rs b/fuzz/fuzz_targets/vsock.rs index 0d0977e9..519f7ab1 100644 --- a/fuzz/fuzz_targets/vsock.rs +++ b/fuzz/fuzz_targets/vsock.rs @@ -2,7 +2,7 @@ use common::virtio_queue::DEFAULT_QUEUE_SIZE; use common::vsock::{InitFunction, VsockInput}; use libfuzzer_sys::fuzz_target; -use virtio_queue::{mock::MockSplitQueue, Descriptor}; +use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue}; use virtio_vsock::packet::VsockPacket; use vm_memory::{GuestAddress, GuestMemoryMmap}; @@ -19,7 +19,7 @@ fuzz_target!(|data: &[u8]| { let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x1000), 0x11000)]).unwrap(); let vq = MockSplitQueue::create(&m, start_addr, DEFAULT_QUEUE_SIZE); - let descriptors: Vec = fuzz_input + let descriptors: Vec = fuzz_input .descriptors .iter() .map(|desc| (*desc).into()) diff --git a/virtio-blk/src/request.rs b/virtio-blk/src/request.rs index 61f31601..cee57323 100644 --- a/virtio-blk/src/request.rs +++ b/virtio-blk/src/request.rs @@ -32,7 +32,10 @@ use virtio_bindings::bindings::virtio_blk::{ VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, }; -use virtio_queue::{Descriptor, DescriptorChain}; +use virtio_queue::{ + desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, + DescriptorChain, +}; use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError}; /// Block request parsing errors. @@ -158,10 +161,11 @@ impl Request { } // Checks that a descriptor meets the minimal requirements for a valid status descriptor. - fn check_status_desc(mem: &M, desc: Descriptor) -> Result<()> + fn check_status_desc(mem: &M, desc: RawDescriptor) -> Result<()> where M: GuestMemory + ?Sized, { + let desc = SplitDescriptor::from(desc); // The status MUST always be writable. if !desc.is_write_only() { return Err(Error::UnexpectedReadOnlyDescriptor); @@ -182,7 +186,8 @@ impl Request { } // Checks that a descriptor meets the minimal requirements for a valid data descriptor. - fn check_data_desc(desc: Descriptor, request_type: RequestType) -> Result<()> { + fn check_data_desc(desc: RawDescriptor, request_type: RequestType) -> Result<()> { + let desc = SplitDescriptor::from(desc); // We do this check only for the device-readable buffers, as opposed to // also check that the device doesn't want to read a device-writable buffer // because this one is not a MUST (the device MAY do that for debugging or @@ -234,14 +239,14 @@ impl Request { let mut desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; while desc.has_next() { - Request::check_data_desc(desc, request.request_type)?; + Request::check_data_desc(RawDescriptor::from(desc), request.request_type)?; request.data.push((desc.addr(), desc.len())); desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; } let status_desc = desc; - Request::check_status_desc(desc_chain.memory(), status_desc)?; + Request::check_status_desc(desc_chain.memory(), RawDescriptor::from(status_desc))?; request.status_addr = status_desc.addr(); Ok(request) @@ -298,9 +303,24 @@ mod tests { // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = [ // A device-writable request header descriptor. - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x30_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; // Create a queue of max 16 descriptors and a descriptor chain based on the array above. let queue = MockSplitQueue::new(&mem, 16); @@ -320,10 +340,15 @@ mod tests { ); let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), // A device-readable request status descriptor. - Descriptor::new(0x30_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x30_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v[..3]).unwrap(); @@ -334,10 +359,20 @@ mod tests { ); let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), // Status descriptor with len = 0. - Descriptor::new(0x30_0000, 0x0, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x0, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v[..3]).unwrap(); assert_eq!( @@ -346,9 +381,14 @@ mod tests { ); let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, 0, 0), - Descriptor::new(0x30_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v[..3]).unwrap(); @@ -378,10 +418,25 @@ mod tests { // Invalid status address. let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x30_0000, 0x200, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x1100_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x200, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x1100_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let req_header = RequestHeader { request_type: VIRTIO_BLK_T_OUT, @@ -403,10 +458,25 @@ mod tests { // Valid descriptor chain for OUT. let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x30_0000, 0x200, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x40_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x200, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x40_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let req_header = RequestHeader { request_type: VIRTIO_BLK_T_OUT, @@ -448,8 +518,13 @@ mod tests { // Valid descriptor chain for FLUSH. let v = [ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x40_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x40_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let req_header = RequestHeader { request_type: VIRTIO_BLK_T_FLUSH, diff --git a/virtio-console/src/console.rs b/virtio-console/src/console.rs index 05e3ef7f..46e20cf2 100644 --- a/virtio-console/src/console.rs +++ b/virtio-console/src/console.rs @@ -282,8 +282,8 @@ where mod tests { use super::*; use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE; + use virtio_queue::desc::{split::Descriptor as SplitDescriptor, RawDescriptor}; use virtio_queue::mock::MockSplitQueue; - use virtio_queue::Descriptor; use vm_memory::{GuestAddress, GuestMemoryMmap}; impl PartialEq for Error { @@ -352,8 +352,13 @@ mod tests { // One descriptor is write only let v = [ - Descriptor::new(0x1000, INPUT_SIZE, 0, 0), - Descriptor::new(0x2000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x1000, INPUT_SIZE, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x2000, + INPUT_SIZE, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); @@ -366,8 +371,8 @@ mod tests { // Descriptor is outside of the memory bounds let v = [ - Descriptor::new(0x0001_0000, INPUT_SIZE, 0, 0), - Descriptor::new(0x0002_0000, INPUT_SIZE, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x0001_0000, INPUT_SIZE, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x0002_0000, INPUT_SIZE, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v[..2]).unwrap(); assert_eq!( @@ -379,8 +384,8 @@ mod tests { // Test normal functionality. let v = [ - Descriptor::new(0x3000, INPUT_SIZE, 0, 0), - Descriptor::new(0x4000, INPUT_SIZE, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x3000, INPUT_SIZE, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x4000, INPUT_SIZE, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v[..2]).unwrap(); mem.write_slice( @@ -419,8 +424,13 @@ mod tests { // One descriptor is read only let v = [ - Descriptor::new(0x1000, 0x10, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x2000, INPUT_SIZE, 0, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0x10, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new(0x2000, INPUT_SIZE, 0, 0)), ]; let queue = MockSplitQueue::new(&mem, 16); @@ -433,8 +443,18 @@ mod tests { // Descriptor is out of memory bounds let v = [ - Descriptor::new(0x0001_0000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x0002_0000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x0001_0000, + INPUT_SIZE, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x0002_0000, + INPUT_SIZE, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); @@ -455,8 +475,18 @@ mod tests { .enqueue_data(&mut vec![INPUT_VALUE * 2; INPUT_SIZE as usize]) .unwrap(); let v = [ - Descriptor::new(0x3000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x4000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x3000, + INPUT_SIZE, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x4000, + INPUT_SIZE, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); @@ -477,12 +507,12 @@ mod tests { console .enqueue_data(&mut vec![INPUT_VALUE; 2 * INPUT_SIZE as usize]) .unwrap(); - let v = [Descriptor::new( + let v = [RawDescriptor::from(SplitDescriptor::new( 0x5000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0, - )]; + ))]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v[..1]).unwrap(); @@ -497,12 +527,12 @@ mod tests { assert!(!console.is_input_buffer_empty()); - let v = [Descriptor::new( + let v = [RawDescriptor::from(SplitDescriptor::new( 0x6000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0, - )]; + ))]; let mut chain = queue.build_desc_chain(&v[..1]).unwrap(); assert_eq!( @@ -516,12 +546,12 @@ mod tests { assert!(console.is_input_buffer_empty()); // Input buffer is empty. - let v = [Descriptor::new( + let v = [RawDescriptor::from(SplitDescriptor::new( 0x7000, INPUT_SIZE, VRING_DESC_F_WRITE as u16, 0, - )]; + ))]; let mut chain = queue.build_desc_chain(&v[..1]).unwrap(); assert_eq!(console.process_receiveq_chain(&mut chain).unwrap(), 0); diff --git a/virtio-queue/src/chain.rs b/virtio-queue/src/chain.rs index 19598e14..993ddcde 100644 --- a/virtio-queue/src/chain.rs +++ b/virtio-queue/src/chain.rs @@ -17,7 +17,7 @@ use std::ops::Deref; use vm_memory::bitmap::{BitmapSlice, WithBitmapSlice}; use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryRegion}; -use crate::{Descriptor, Error, Reader, Writer}; +use crate::{desc::split::Descriptor, Error, Reader, Writer}; use virtio_bindings::bindings::virtio_ring::VRING_DESC_ALIGN_SIZE; /// A virtio descriptor chain. @@ -253,6 +253,7 @@ where #[cfg(test)] mod tests { use super::*; + use crate::desc::{split::Descriptor as SplitDescriptor, RawDescriptor}; use crate::mock::{DescriptorTable, MockSplitQueue}; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT}; use vm_memory::GuestMemoryMmap; @@ -281,7 +282,12 @@ mod tests { { // the first desc has a normal len, and the next_descriptor flag is set // but the the index of the next descriptor is too large - let desc = Descriptor::new(0x1000, 0x1000, VRING_DESC_F_NEXT as u16, 16); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0x1000, + VRING_DESC_F_NEXT as u16, + 16, + )); vq.desc_table().store(0, desc).unwrap(); let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0); @@ -291,10 +297,15 @@ mod tests { // finally, let's test an ok chain { - let desc = Descriptor::new(0x1000, 0x1000, VRING_DESC_F_NEXT as u16, 1); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0x1000, + VRING_DESC_F_NEXT as u16, + 1, + )); vq.desc_table().store(0, desc).unwrap(); - let desc = Descriptor::new(0x2000, 0x1000, 0, 0); + let desc = RawDescriptor::from(SplitDescriptor::new(0x2000, 0x1000, 0, 0)); vq.desc_table().store(1, desc).unwrap(); let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0); @@ -333,15 +344,15 @@ mod tests { // Populate the entire descriptor table with entries. Only the last one should not have the // VIRTQ_DESC_F_NEXT set. for i in 0..QUEUE_SIZE - 1 { - let desc = Descriptor::new( + let desc = RawDescriptor::from(SplitDescriptor::new( 0x1000 * (i + 1) as u64, 0x1000, VRING_DESC_F_NEXT as u16, i + 1, - ); + )); vq.desc_table().store(i, desc).unwrap(); } - let desc = Descriptor::new((0x1000 * 16) as u64, 0x1000, 0, 0); + let desc = RawDescriptor::from(SplitDescriptor::new((0x1000 * 16) as u64, 0x1000, 0, 0)); vq.desc_table().store(QUEUE_SIZE - 1, desc).unwrap(); let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), QUEUE_SIZE, 0); @@ -366,18 +377,23 @@ mod tests { let dtable = vq.desc_table(); // Create a chain with one normal descriptor and one pointing to an indirect table. - let desc = Descriptor::new(0x6000, 0x1000, VRING_DESC_F_NEXT as u16, 1); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x6000, + 0x1000, + VRING_DESC_F_NEXT as u16, + 1, + )); dtable.store(0, desc).unwrap(); // The spec forbids setting both VIRTQ_DESC_F_INDIRECT and VIRTQ_DESC_F_NEXT in flags. We do // not currently enforce this rule, we just ignore the VIRTQ_DESC_F_NEXT flag. - let desc = Descriptor::new( + let desc = RawDescriptor::from(SplitDescriptor::new( 0x7000, 0x1000, (VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT) as u16, 2, - ); + )); dtable.store(1, desc).unwrap(); - let desc = Descriptor::new(0x8000, 0x1000, 0, 0); + let desc = RawDescriptor::from(SplitDescriptor::new(0x8000, 0x1000, 0, 0)); dtable.store(2, desc).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = DescriptorChain::new(m, vq.start(), 16, 0); @@ -385,10 +401,15 @@ mod tests { // create an indirect table with 4 chained descriptors let idtable = DescriptorTable::new(m, GuestAddress(0x7000), 4); for i in 0..4u16 { - let desc: Descriptor = if i < 3 { - Descriptor::new(0x1000 * i as u64, 0x1000, VRING_DESC_F_NEXT as u16, i + 1) + let desc: RawDescriptor = if i < 3 { + RawDescriptor::from(SplitDescriptor::new( + 0x1000 * i as u64, + 0x1000, + VRING_DESC_F_NEXT as u16, + i + 1, + )) } else { - Descriptor::new(0x1000 * i as u64, 0x1000, 0, 0) + RawDescriptor::from(SplitDescriptor::new(0x1000 * i as u64, 0x1000, 0, 0)) }; idtable.store(i, desc).unwrap(); } @@ -423,12 +444,12 @@ mod tests { let dtable = vq.desc_table(); // Create a chain with a descriptor pointing to an indirect table with unaligned address. - let desc = Descriptor::new( + let desc = RawDescriptor::from(SplitDescriptor::new( 0x7001, 0x1000, (VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT) as u16, 2, - ); + )); dtable.store(0, desc).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = DescriptorChain::new(m, vq.start(), 16, 0); @@ -436,10 +457,15 @@ mod tests { // Create an indirect table with 4 chained descriptors. let idtable = DescriptorTable::new(m, GuestAddress(0x7001), 4); for i in 0..4u16 { - let desc: Descriptor = if i < 3 { - Descriptor::new(0x1000 * i as u64, 0x1000, VRING_DESC_F_NEXT as u16, i + 1) + let desc: RawDescriptor = if i < 3 { + RawDescriptor::from(SplitDescriptor::new( + 0x1000 * i as u64, + 0x1000, + VRING_DESC_F_NEXT as u16, + i + 1, + )) } else { - Descriptor::new(0x1000 * i as u64, 0x1000, 0, 0) + RawDescriptor::from(SplitDescriptor::new(0x1000 * i as u64, 0x1000, 0, 0)) }; idtable.store(i, desc).unwrap(); } @@ -465,7 +491,12 @@ mod tests { // Create a chain with a descriptor pointing to an invalid indirect table: len not a // multiple of descriptor size. - let desc = Descriptor::new(0x1000, 0x1001, VRING_DESC_F_INDIRECT as u16, 0); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0x1001, + VRING_DESC_F_INDIRECT as u16, + 0, + )); vq.desc_table().store(0, desc).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = @@ -480,12 +511,12 @@ mod tests { // Create a chain with a descriptor pointing to an invalid indirect table: table len > // u16::MAX. - let desc = Descriptor::new( + let desc = RawDescriptor::from(SplitDescriptor::new( 0x1000, (u16::MAX as u32 + 1) * VRING_DESC_ALIGN_SIZE, VRING_DESC_F_INDIRECT as u16, 0, - ); + )); vq.desc_table().store(0, desc).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = @@ -499,10 +530,15 @@ mod tests { let vq = MockSplitQueue::new(m, 16); // Create a chain with a descriptor pointing to an indirect table. - let desc = Descriptor::new(0x1000, 0x1000, VRING_DESC_F_INDIRECT as u16, 0); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0x1000, + VRING_DESC_F_INDIRECT as u16, + 0, + )); vq.desc_table().store(0, desc).unwrap(); // It's ok for an indirect descriptor to have flags = 0. - let desc = Descriptor::new(0x3000, 0x1000, 0, 0); + let desc = RawDescriptor::from(SplitDescriptor::new(0x3000, 0x1000, 0, 0)); m.write_obj(desc, GuestAddress(0x1000)).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = @@ -511,7 +547,12 @@ mod tests { // But it's not allowed to have an indirect descriptor that points to another indirect // table. - let desc = Descriptor::new(0x3000, 0x1000, VRING_DESC_F_INDIRECT as u16, 0); + let desc = RawDescriptor::from(SplitDescriptor::new( + 0x3000, + 0x1000, + VRING_DESC_F_INDIRECT as u16, + 0, + )); m.write_obj(desc, GuestAddress(0x1000)).unwrap(); let mut c: DescriptorChain<&GuestMemoryMmap> = diff --git a/virtio-queue/src/desc/split.rs b/virtio-queue/src/desc/split.rs index 9aa01e4b..2778f921 100644 --- a/virtio-queue/src/desc/split.rs +++ b/virtio-queue/src/desc/split.rs @@ -17,6 +17,46 @@ use virtio_bindings::bindings::virtio_ring::{ VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, }; +/// A virtio descriptor constraints with C representation. +/// +/// # Example +/// +/// ```rust +/// # use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; +/// # use virtio_queue::mock::MockSplitQueue; +/// use virtio_queue::{desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, Queue, QueueOwnedT}; +/// use vm_memory::{GuestAddress, GuestMemoryMmap}; +/// +/// # fn populate_queue(m: &GuestMemoryMmap) -> Queue { +/// # let vq = MockSplitQueue::new(m, 16); +/// # let mut q = vq.create_queue().unwrap(); +/// # +/// # // We have only one chain: (0, 1). +/// # let desc = RawDescriptor::from(SplitDescriptor::new(0x1000, 0x1000, VRING_DESC_F_NEXT as u16, 1)); +/// # vq.desc_table().store(0, desc); +/// # let desc = RawDescriptor::from(SplitDescriptor::new(0x2000, 0x1000, VRING_DESC_F_WRITE as u16, 0)); +/// # vq.desc_table().store(1, desc); +/// # +/// # vq.avail().ring().ref_at(0).unwrap().store(u16::to_le(0)); +/// # vq.avail().idx().store(u16::to_le(1)); +/// # q +/// # } +/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); +/// // Populate the queue with descriptor chains and update the available ring accordingly. +/// let mut queue = populate_queue(m); +/// let mut i = queue.iter(m).unwrap(); +/// let mut c = i.next().unwrap(); +/// +/// // Get the first descriptor and access its fields. +/// let desc = c.next().unwrap(); +/// let _addr = desc.addr(); +/// let _len = desc.len(); +/// let _flags = desc.flags(); +/// let _next = desc.next(); +/// let _is_write_only = desc.is_write_only(); +/// let _has_next = desc.has_next(); +/// let _refers_to_ind_table = desc.refers_to_indirect_table(); +/// ``` /// A virtio split descriptor constraints with C representation. #[repr(C)] #[derive(Default, Clone, Copy, Debug)] diff --git a/virtio-queue/src/descriptor.rs b/virtio-queue/src/descriptor.rs deleted file mode 100644 index 7f1564b5..00000000 --- a/virtio-queue/src/descriptor.rs +++ /dev/null @@ -1,276 +0,0 @@ -// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE-BSD-3-Clause file. -// -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// -// Copyright © 2019 Intel Corporation -// -// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved. -// -// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause - -use vm_memory::{ByteValued, GuestAddress, Le16, Le32, Le64}; - -use virtio_bindings::bindings::virtio_ring::{ - VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, -}; - -/// A virtio descriptor constraints with C representation. -/// -/// # Example -/// -/// ```rust -/// # use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; -/// # use virtio_queue::mock::MockSplitQueue; -/// use virtio_queue::{Descriptor, Queue, QueueOwnedT}; -/// use vm_memory::{GuestAddress, GuestMemoryMmap}; -/// -/// # fn populate_queue(m: &GuestMemoryMmap) -> Queue { -/// # let vq = MockSplitQueue::new(m, 16); -/// # let mut q = vq.create_queue().unwrap(); -/// # -/// # // We have only one chain: (0, 1). -/// # let desc = Descriptor::new(0x1000, 0x1000, VRING_DESC_F_NEXT as u16, 1); -/// # vq.desc_table().store(0, desc); -/// # let desc = Descriptor::new(0x2000, 0x1000, VRING_DESC_F_WRITE as u16, 0); -/// # vq.desc_table().store(1, desc); -/// # -/// # vq.avail().ring().ref_at(0).unwrap().store(u16::to_le(0)); -/// # vq.avail().idx().store(u16::to_le(1)); -/// # q -/// # } -/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); -/// // Populate the queue with descriptor chains and update the available ring accordingly. -/// let mut queue = populate_queue(m); -/// let mut i = queue.iter(m).unwrap(); -/// let mut c = i.next().unwrap(); -/// -/// // Get the first descriptor and access its fields. -/// let desc = c.next().unwrap(); -/// let _addr = desc.addr(); -/// let _len = desc.len(); -/// let _flags = desc.flags(); -/// let _next = desc.next(); -/// let _is_write_only = desc.is_write_only(); -/// let _has_next = desc.has_next(); -/// let _refers_to_ind_table = desc.refers_to_indirect_table(); -/// ``` -// Note that the `ByteValued` implementation of this structure expects the `Descriptor` to store -// only plain old data types. -#[repr(C)] -#[derive(Default, Clone, Copy, Debug)] -pub struct Descriptor { - /// Guest physical address of device specific data. - addr: Le64, - - /// Length of device specific data. - len: Le32, - - /// Includes next, write, and indirect bits. - flags: Le16, - - /// Index into the descriptor table of the next descriptor if flags has the `next` bit set. - next: Le16, -} - -#[allow(clippy::len_without_is_empty)] -impl Descriptor { - /// Return the guest physical address of the descriptor buffer. - pub fn addr(&self) -> GuestAddress { - GuestAddress(self.addr.into()) - } - - /// Return the length of the descriptor buffer. - pub fn len(&self) -> u32 { - self.len.into() - } - - /// Return the flags for this descriptor, including next, write and indirect bits. - pub fn flags(&self) -> u16 { - self.flags.into() - } - - /// Return the value stored in the `next` field of the descriptor. - pub fn next(&self) -> u16 { - self.next.into() - } - - /// Check whether this descriptor refers to a buffer containing an indirect descriptor table. - pub fn refers_to_indirect_table(&self) -> bool { - self.flags() & VRING_DESC_F_INDIRECT as u16 != 0 - } - - /// Check whether the `VIRTQ_DESC_F_NEXT` is set for the descriptor. - pub fn has_next(&self) -> bool { - self.flags() & VRING_DESC_F_NEXT as u16 != 0 - } - - /// Check if the driver designated this as a write only descriptor. - /// - /// If this is false, this descriptor is read only. - /// Write only means the the emulated device can write and the driver can read. - pub fn is_write_only(&self) -> bool { - self.flags() & VRING_DESC_F_WRITE as u16 != 0 - } -} - -#[cfg(any(test, feature = "test-utils"))] -impl Descriptor { - /// Create a new descriptor. - /// - /// # Arguments - /// * `addr` - the guest physical address of the descriptor buffer. - /// * `len` - the length of the descriptor buffer. - /// * `flags` - the `flags` for the descriptor. - /// * `next` - the `next` field of the descriptor. - pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self { - Descriptor { - addr: addr.into(), - len: len.into(), - flags: flags.into(), - next: next.into(), - } - } - - /// Set the guest physical address of the descriptor buffer. - pub fn set_addr(&mut self, addr: u64) { - self.addr = addr.into(); - } - - /// Set the length of the descriptor buffer. - pub fn set_len(&mut self, len: u32) { - self.len = len.into(); - } - - /// Set the flags for this descriptor. - pub fn set_flags(&mut self, flags: u16) { - self.flags = flags.into(); - } - - /// Set the value stored in the `next` field of the descriptor. - pub fn set_next(&mut self, next: u16) { - self.next = next.into(); - } -} - -// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and -// all accesses through safe `vm-memory` API will validate any garbage that could be -// included in there. -unsafe impl ByteValued for Descriptor {} - -/// Represents the contents of an element from the used virtqueue ring. -// Note that the `ByteValued` implementation of this structure expects the `VirtqUsedElem` to store -// only plain old data types. -#[repr(C)] -#[derive(Clone, Copy, Default, Debug)] -pub struct VirtqUsedElem { - id: Le32, - len: Le32, -} - -impl VirtqUsedElem { - /// Create a new `VirtqUsedElem` instance. - /// - /// # Arguments - /// * `id` - the index of the used descriptor chain. - /// * `len` - the total length of the descriptor chain which was used (written to). - pub(crate) fn new(id: u32, len: u32) -> Self { - VirtqUsedElem { - id: id.into(), - len: len.into(), - } - } -} - -#[cfg(any(test, feature = "test-utils"))] -#[allow(clippy::len_without_is_empty)] -impl VirtqUsedElem { - /// Get the index of the used descriptor chain. - pub fn id(&self) -> u32 { - self.id.into() - } - - /// Get `length` field of the used ring entry. - pub fn len(&self) -> u32 { - self.len.into() - } -} - -// SAFETY: This is safe because `VirtqUsedElem` contains only wrappers over POD types -// and all accesses through safe `vm-memory` API will validate any garbage that could be -// included in there. -unsafe impl ByteValued for VirtqUsedElem {} - -#[cfg(test)] -mod tests { - use super::*; - use memoffset::offset_of; - use std::mem::{align_of, size_of}; - - #[test] - fn test_descriptor_offset() { - assert_eq!(size_of::(), 16); - assert_eq!(offset_of!(Descriptor, addr), 0); - assert_eq!(offset_of!(Descriptor, len), 8); - assert_eq!(offset_of!(Descriptor, flags), 12); - assert_eq!(offset_of!(Descriptor, next), 14); - assert!(align_of::() <= 16); - } - - #[test] - fn test_descriptor_getter_setter() { - let mut desc = Descriptor::new(0, 0, 0, 0); - - desc.set_addr(0x1000); - assert_eq!(desc.addr(), GuestAddress(0x1000)); - desc.set_len(0x2000); - assert_eq!(desc.len(), 0x2000); - desc.set_flags(VRING_DESC_F_NEXT as u16); - assert_eq!(desc.flags(), VRING_DESC_F_NEXT as u16); - assert!(desc.has_next()); - assert!(!desc.is_write_only()); - assert!(!desc.refers_to_indirect_table()); - desc.set_flags(VRING_DESC_F_WRITE as u16); - assert_eq!(desc.flags(), VRING_DESC_F_WRITE as u16); - assert!(!desc.has_next()); - assert!(desc.is_write_only()); - assert!(!desc.refers_to_indirect_table()); - desc.set_flags(VRING_DESC_F_INDIRECT as u16); - assert_eq!(desc.flags(), VRING_DESC_F_INDIRECT as u16); - assert!(!desc.has_next()); - assert!(!desc.is_write_only()); - assert!(desc.refers_to_indirect_table()); - desc.set_next(3); - assert_eq!(desc.next(), 3); - } - - #[test] - fn test_descriptor_copy() { - let e1 = Descriptor::new(1, 2, VRING_DESC_F_NEXT as u16, 3); - let mut e2 = Descriptor::default(); - - e2.as_mut_slice().copy_from_slice(e1.as_slice()); - assert_eq!(e1.addr(), e2.addr()); - assert_eq!(e1.len(), e2.len()); - assert_eq!(e1.flags(), e2.flags()); - assert_eq!(e1.next(), e2.next()); - } - - #[test] - fn test_used_elem_offset() { - assert_eq!(offset_of!(VirtqUsedElem, id), 0); - assert_eq!(offset_of!(VirtqUsedElem, len), 4); - assert_eq!(size_of::(), 8); - } - - #[test] - fn test_used_elem_copy() { - let e1 = VirtqUsedElem::new(3, 15); - let mut e2 = VirtqUsedElem::new(0, 0); - - e2.as_mut_slice().copy_from_slice(e1.as_slice()); - assert_eq!(e1.id, e2.id); - assert_eq!(e1.len, e2.len); - } -} diff --git a/virtio-queue/src/descriptor_utils.rs b/virtio-queue/src/descriptor_utils.rs index 5c90637b..8048141e 100644 --- a/virtio-queue/src/descriptor_utils.rs +++ b/virtio-queue/src/descriptor_utils.rs @@ -365,7 +365,10 @@ impl io::Write for Writer<'_, B> { #[cfg(test)] mod tests { use super::*; - use crate::{Descriptor, Queue, QueueOwnedT, QueueT}; + use crate::{ + desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, + Queue, QueueOwnedT, QueueT, + }; use vm_memory::{GuestAddress, GuestMemoryMmap, Le32}; use crate::mock::MockSplitQueue; @@ -402,12 +405,12 @@ mod tests { flags |= VRING_DESC_F_NEXT; } - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( buffers_start_addr.raw_value(), size, flags as u16, (index + 1) as u16, - )); + ))); let offset = size + spaces_between_regions; buffers_start_addr = buffers_start_addr @@ -439,7 +442,7 @@ mod tests { let queue = MockSplitQueue::create(&memory, GuestAddress(0x0), MAX_QUEUE_SIZE); // set addr out of memory - let descriptor = Descriptor::new(0x1001, 1, 0, 1_u16); + let descriptor = RawDescriptor::from(SplitDescriptor::new(0x1001, 1, 0, 1_u16)); queue.build_desc_chain(&[descriptor]).unwrap(); let avail_ring = queue.avail_addr(); diff --git a/virtio-queue/src/lib.rs b/virtio-queue/src/lib.rs index 0bdae8af..27e3f86c 100644 --- a/virtio-queue/src/lib.rs +++ b/virtio-queue/src/lib.rs @@ -23,7 +23,6 @@ use log::error; use vm_memory::{GuestMemory, GuestMemoryError, VolatileMemoryError}; pub use self::chain::{DescriptorChain, DescriptorChainRwIter}; -pub use self::descriptor::{Descriptor, VirtqUsedElem}; pub use self::descriptor_utils::{Reader, Writer}; pub use self::queue::{AvailIter, Queue}; pub use self::queue_sync::QueueSync; @@ -35,7 +34,6 @@ pub mod desc; pub mod mock; mod chain; -mod descriptor; mod descriptor_utils; mod queue; mod queue_sync; diff --git a/virtio-queue/src/mock.rs b/virtio-queue/src/mock.rs index 1f435884..46169061 100644 --- a/virtio-queue/src/mock.rs +++ b/virtio-queue/src/mock.rs @@ -12,7 +12,13 @@ use vm_memory::{ }; use crate::defs::{VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE}; -use crate::{Descriptor, DescriptorChain, Error, Queue, QueueOwnedT, QueueT, VirtqUsedElem}; +use crate::{ + desc::{ + split::{Descriptor as SplitDescriptor, VirtqUsedElem}, + RawDescriptor, + }, + DescriptorChain, Error, Queue, QueueOwnedT, QueueT, +}; use std::fmt::{self, Debug, Display}; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT}; @@ -177,7 +183,7 @@ pub type UsedRing<'a, M> = SplitQueueRing<'a, M, VirtqUsedElem>; /// Refers to the buffers the driver is using for the device. pub struct DescriptorTable<'a, M> { - table: ArrayRef<'a, M, Descriptor>, + table: ArrayRef<'a, M, RawDescriptor>, len: u16, free_descriptors: Vec, } @@ -196,14 +202,14 @@ impl<'a, M: GuestMemory> DescriptorTable<'a, M> { } /// Read one descriptor from the specified index. - pub fn load(&self, index: u16) -> Result { + pub fn load(&self, index: u16) -> Result { self.table .ref_at(index as usize) .map(|load_ref| load_ref.load()) } /// Write one descriptor at the specified index. - pub fn store(&self, index: u16, value: Descriptor) -> Result<(), MockError> { + pub fn store(&self, index: u16, value: RawDescriptor) -> Result<(), MockError> { self.table .ref_at(index as usize) .map(|store_ref| store_ref.store(value)) @@ -211,7 +217,7 @@ impl<'a, M: GuestMemory> DescriptorTable<'a, M> { /// Return the total size of the DescriptorTable in bytes. pub fn total_size(&self) -> u64 { - (self.len as usize * size_of::()) as u64 + (self.len as usize * size_of::()) as u64 } /// Create a chain of descriptors. @@ -228,7 +234,7 @@ impl<'a, M: GuestMemory> DescriptorTable<'a, M> { for (pos, index_value) in indices.iter().copied().enumerate() { // Addresses and lens constant for now. - let mut desc = Descriptor::new(0x1000, 0x1000, 0, 0); + let mut desc = SplitDescriptor::new(0x1000, 0x1000, 0, 0); // It's not the last descriptor in the chain. if pos < indices.len() - 1 { @@ -237,6 +243,8 @@ impl<'a, M: GuestMemory> DescriptorTable<'a, M> { } else { desc.set_flags(0); } + + let desc = RawDescriptor::from(desc); self.store(index_value, desc)?; } @@ -389,12 +397,13 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { // We just allocate the indirect table and forget about it for now. let indirect_addr = self.alloc_indirect_chain(len)?; - let mut desc = self.desc_table.load(head_idx)?; + let desc = self.desc_table.load(head_idx)?; + let mut desc = SplitDescriptor::from(desc); desc.set_flags(VRING_DESC_F_INDIRECT as u16); desc.set_addr(indirect_addr.raw_value()); - desc.set_len(u32::from(len) * size_of::() as u32); + desc.set_len(u32::from(len) * size_of::() as u32); - self.desc_table.store(head_idx, desc)?; + self.desc_table.store(head_idx, RawDescriptor::from(desc))?; self.update_avail_idx(head_idx) } @@ -424,7 +433,7 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { /// the descriptor table, and returns the first `DescriptorChain` available. pub fn build_multiple_desc_chains( &self, - descs: &[Descriptor], + descs: &[RawDescriptor], ) -> Result, MockError> { self.add_desc_chains(descs, 0)?; self.create_queue::() @@ -442,9 +451,13 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { // TODO: make this function work with a generic queue. For now that's not possible because // we cannot create the descriptor chain from an iterator as iterator is not implemented for // a generic T, just for `Queue`. - pub fn build_desc_chain(&self, descs: &[Descriptor]) -> Result, MockError> { - let mut modified_descs: Vec = Vec::with_capacity(descs.len()); + pub fn build_desc_chain( + &self, + descs: &[RawDescriptor], + ) -> Result, MockError> { + let mut modified_descs: Vec = Vec::with_capacity(descs.len()); for (idx, desc) in descs.iter().enumerate() { + let desc = SplitDescriptor::from(*desc); let (flags, next) = if idx == descs.len() - 1 { // Clear the NEXT flag if it was set. The value of the next field of the // Descriptor doesn't matter at this point. @@ -454,7 +467,12 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { // descriptor. This ignores any value is actually present in `desc.next`. (desc.flags() | VRING_DESC_F_NEXT as u16, idx as u16 + 1) }; - modified_descs.push(Descriptor::new(desc.addr().0, desc.len(), flags, next)); + modified_descs.push(RawDescriptor::from(SplitDescriptor::new( + desc.addr().0, + desc.len(), + flags, + next, + ))); } self.build_multiple_desc_chains(&modified_descs[..]) } @@ -465,7 +483,7 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { // The descriptor chain related information is written in memory starting with address 0. // The `addr` fields of the input descriptors should start at a sufficiently // greater location (i.e. 1MiB, or `0x10_0000`). - pub fn add_desc_chains(&self, descs: &[Descriptor], offset: u16) -> Result<(), MockError> { + pub fn add_desc_chains(&self, descs: &[RawDescriptor], offset: u16) -> Result<(), MockError> { let mut new_entries = 0; let avail_idx: u16 = self .mem @@ -477,7 +495,9 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> { let i = idx as u16 + offset; self.desc_table().store(i, *desc)?; - if idx == 0 || descs[idx - 1].flags() & VRING_DESC_F_NEXT as u16 != 1 { + if idx == 0 + || SplitDescriptor::from(descs[idx - 1]).flags() & VRING_DESC_F_NEXT as u16 != 1 + { // Update the available ring position. self.mem .write_obj( diff --git a/virtio-queue/src/queue.rs b/virtio-queue/src/queue.rs index 7ab252ee..4965b082 100644 --- a/virtio-queue/src/queue.rs +++ b/virtio-queue/src/queue.rs @@ -19,10 +19,8 @@ use crate::defs::{ VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE, VIRTQ_AVAIL_RING_META_SIZE, VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_RING_HEADER_SIZE, VIRTQ_USED_RING_META_SIZE, }; -use crate::{ - error, Descriptor, DescriptorChain, Error, QueueGuard, QueueOwnedT, QueueState, QueueT, - VirtqUsedElem, -}; +use crate::desc::{split::VirtqUsedElem, RawDescriptor}; +use crate::{error, DescriptorChain, Error, QueueGuard, QueueOwnedT, QueueState, QueueT}; use virtio_bindings::bindings::virtio_ring::VRING_USED_F_NO_NOTIFY; /// The maximum queue size as defined in the Virtio Spec. @@ -301,7 +299,7 @@ impl QueueT for Queue { let desc_table = self.desc_table; // The multiplication can not overflow an u64 since we are multiplying an u16 with a // small number. - let desc_table_size = size_of::() as u64 * queue_size; + let desc_table_size = size_of::() as u64 * queue_size; let avail_ring = self.avail_ring; // The operations below can not overflow an u64 since they're working with relatively small // numbers compared to u64::MAX. @@ -614,7 +612,7 @@ impl QueueOwnedT for Queue { /// ```rust /// # use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; /// # use virtio_queue::mock::MockSplitQueue; -/// use virtio_queue::{Descriptor, Queue, QueueOwnedT}; +/// use virtio_queue::{desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, Queue, QueueOwnedT}; /// use vm_memory::{GuestAddress, GuestMemoryMmap}; /// /// # fn populate_queue(m: &GuestMemoryMmap) -> Queue { @@ -631,7 +629,7 @@ impl QueueOwnedT for Queue { /// # _ => VRING_DESC_F_NEXT, /// # }; /// # -/// # descs.push(Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1)); +/// # descs.push(RawDescriptor::from(SplitDescriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1))); /// # } /// # /// # vq.add_desc_chains(&descs, 0).unwrap(); @@ -780,8 +778,8 @@ impl PartialEq for Error { mod tests { use super::*; use crate::defs::{DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR}; + use crate::desc::{split::Descriptor as SplitDescriptor, RawDescriptor}; use crate::mock::MockSplitQueue; - use crate::Descriptor; use virtio_bindings::bindings::virtio_ring::{ VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, VRING_USED_F_NO_NOTIFY, }; @@ -1065,12 +1063,12 @@ mod tests { _ => VRING_DESC_F_NEXT, }; - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1, - )); + ))); } vq.add_desc_chains(&descs, 0).unwrap(); @@ -1191,12 +1189,12 @@ mod tests { _ => VRING_DESC_F_NEXT, }; - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1, - )); + ))); } vq.add_desc_chains(&descs, 0).unwrap(); @@ -1256,12 +1254,12 @@ mod tests { // Create descriptors to fill up the queue let mut descs = Vec::new(); for i in 0..queue_size { - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, 0_u16, i + 1, - )); + ))); } vq.add_desc_chains(&descs, 0).unwrap(); @@ -1304,12 +1302,12 @@ mod tests { _ => VRING_DESC_F_NEXT, }; - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( (0x1000 * (j + 1)) as u64, 0x1000, flags as u16, j + 1, - )); + ))); } vq.add_desc_chains(&descs, 0).unwrap(); @@ -1380,12 +1378,12 @@ mod tests { _ => VRING_DESC_F_NEXT, }; - descs.push(Descriptor::new( + descs.push(RawDescriptor::from(SplitDescriptor::new( (0x1000 * (j + 1)) as u64, 0x1000, flags as u16, j + 1, - )); + ))); } vq.add_desc_chains(&descs, 0).unwrap(); @@ -1430,8 +1428,13 @@ mod tests { // total) { let descs = vec![ - Descriptor::new(0x1000, 0xffff_ffff, VRING_DESC_F_NEXT as u16, 1), - Descriptor::new(0x1000, 0x1234_5678, 0, 2), + RawDescriptor::from(SplitDescriptor::new( + 0x1000, + 0xffff_ffff, + VRING_DESC_F_NEXT as u16, + 1, + )), + RawDescriptor::from(SplitDescriptor::new(0x1000, 0x1234_5678, 0, 2)), ]; vq.add_desc_chains(&descs, 0).unwrap(); let mut yielded_bytes_by_iteration = 0_u32; @@ -1444,12 +1447,12 @@ mod tests { // Same as above, but test with a descriptor which is self-referential { - let descs = vec![Descriptor::new( + let descs = vec![RawDescriptor::from(SplitDescriptor::new( 0x1000, 0xffff_ffff, VRING_DESC_F_NEXT as u16, 0, - )]; + ))]; vq.add_desc_chains(&descs, 0).unwrap(); let mut yielded_bytes_by_iteration = 0_u32; for d in q.iter(m).unwrap().next().unwrap() { @@ -1467,12 +1470,12 @@ mod tests { let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 1); // This input was generated by the fuzzer, both for the QueueS and the Descriptor - let descriptors: Vec = vec![Descriptor::new( + let descriptors: Vec = vec![RawDescriptor::from(SplitDescriptor::new( 14178673876262995140, 3301229764, 50372, 50372, - )]; + ))]; vq.build_desc_chain(&descriptors).unwrap(); let mut q = Queue { @@ -1534,14 +1537,19 @@ mod tests { let vq = MockSplitQueue::new(m, 1024); // This input below was generated by the fuzzer. - let descriptors: Vec = vec![ - Descriptor::new(21508325467, 0, 1, 4), - Descriptor::new(2097152, 4096, 3, 0), - Descriptor::new(18374686479672737792, 4294967295, 65535, 29), - Descriptor::new(76842670169653248, 1114115, 0, 0), - Descriptor::new(16, 983040, 126, 3), - Descriptor::new(897648164864, 0, 0, 0), - Descriptor::new(111669149722, 0, 0, 0), + let descriptors: Vec = vec![ + RawDescriptor::from(SplitDescriptor::new(21508325467, 0, 1, 4)), + RawDescriptor::from(SplitDescriptor::new(2097152, 4096, 3, 0)), + RawDescriptor::from(SplitDescriptor::new( + 18374686479672737792, + 4294967295, + 65535, + 29, + )), + RawDescriptor::from(SplitDescriptor::new(76842670169653248, 1114115, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(16, 983040, 126, 3)), + RawDescriptor::from(SplitDescriptor::new(897648164864, 0, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(111669149722, 0, 0, 0)), ]; vq.build_multiple_desc_chains(&descriptors).unwrap(); diff --git a/virtio-vsock/src/packet.rs b/virtio-vsock/src/packet.rs index a3aee4a3..7be82ccc 100644 --- a/virtio-vsock/src/packet.rs +++ b/virtio-vsock/src/packet.rs @@ -195,7 +195,7 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// ```rust /// # use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE; /// # use virtio_queue::mock::MockSplitQueue; - /// # use virtio_queue::{Descriptor, Queue, QueueT}; + /// # use virtio_queue::{desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, Queue, QueueT}; /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap}; /// @@ -206,8 +206,8 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// # let mut q = vq.create_queue().unwrap(); /// # /// # let v = vec![ - /// # Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - /// # Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + /// # RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0)), + /// # RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0)), /// # ]; /// # let mut chain = vq.build_desc_chain(&v); /// # q @@ -374,7 +374,7 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// /// ```rust /// # use virtio_queue::mock::MockSplitQueue; - /// # use virtio_queue::{Descriptor, Queue, QueueT}; + /// # use virtio_queue::{desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, Queue, QueueT}; /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap}; /// @@ -386,8 +386,8 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// # let mut q = vq.create_queue().unwrap(); /// # /// # let v = vec![ - /// # Descriptor::new(0x5_0000, 0x100, 0, 0), - /// # Descriptor::new(0x8_0000, 0x100, 0, 0), + /// # RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), + /// # RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, 0, 0)), /// # ]; /// # let mut chain = vq.build_desc_chain(&v); /// # q @@ -516,7 +516,7 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// ```rust /// # use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE; /// # use virtio_queue::mock::MockSplitQueue; - /// # use virtio_queue::{Descriptor, Queue, QueueT}; + /// # use virtio_queue::{desc::{split::Descriptor as SplitDescriptor, RawDescriptor}, Queue, QueueT}; /// use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; /// # use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap}; /// @@ -538,8 +538,8 @@ impl<'a, B: BitmapSlice> VsockPacket<'a, B> { /// # let mut q = vq.create_queue().unwrap(); /// # /// # let v = vec![ - /// # Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - /// # Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + /// # RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0)), + /// # RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0)), /// # ]; /// # let mut chain = vq.build_desc_chain(&v); /// # q @@ -684,8 +684,8 @@ mod tests { use vm_memory::{GuestAddress, GuestMemoryMmap}; use virtio_bindings::bindings::virtio_ring::VRING_DESC_F_WRITE; + use virtio_queue::desc::{split::Descriptor as SplitDescriptor, RawDescriptor}; use virtio_queue::mock::MockSplitQueue; - use virtio_queue::Descriptor; impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { @@ -734,8 +734,13 @@ mod tests { // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = vec![ // A device-readable packet header descriptor should be invalid. - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -746,13 +751,18 @@ mod tests { let v = vec![ // A header length < PKT_HEADER_SIZE is invalid. - Descriptor::new( + RawDescriptor::from(SplitDescriptor::new( 0x10_0000, PKT_HEADER_SIZE as u32 - 1, VRING_DESC_F_WRITE as u16, 0, - ), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -761,18 +771,18 @@ mod tests { ); let v = vec![ - Descriptor::new( + RawDescriptor::from(SplitDescriptor::new( 0x10_0000, PKT_HEADER_SIZE as u32, VRING_DESC_F_WRITE as u16, 0, - ), - Descriptor::new( + )), + RawDescriptor::from(SplitDescriptor::new( 0x20_0000, MAX_PKT_BUF_SIZE + 1, VRING_DESC_F_WRITE as u16, 0, - ), + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -782,12 +792,12 @@ mod tests { let v = vec![ // The data descriptor should always be present on the RX path. - Descriptor::new( + RawDescriptor::from(SplitDescriptor::new( 0x10_0000, PKT_HEADER_SIZE as u32, VRING_DESC_F_WRITE as u16, 0, - ), + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -796,8 +806,13 @@ mod tests { ); let v = vec![ - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -810,8 +825,18 @@ mod tests { let v = vec![ // The header doesn't fit entirely in the memory bounds. - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -822,8 +847,18 @@ mod tests { let v = vec![ // The header is outside the memory bounds. - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x30_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x30_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -834,9 +869,14 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x5_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), // A device-readable packet data descriptor should be invalid. - Descriptor::new(0x8_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -844,9 +884,19 @@ mod tests { Error::UnexpectedReadOnlyDescriptor ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x5_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), // The data array doesn't fit entirely in the memory bounds. - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -855,9 +905,19 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x5_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), // The data array is outside the memory bounds. - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -869,8 +929,18 @@ mod tests { // Let's also test a valid descriptor chain. let v = vec![ - Descriptor::new(0x5_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x5_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x8_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -899,12 +969,12 @@ mod tests { // Let's also test a valid descriptor chain, with both header and data on a single // descriptor. - let v = vec![Descriptor::new( + let v = vec![RawDescriptor::from(SplitDescriptor::new( 0x5_0000, PKT_HEADER_SIZE as u32 + 0x100, VRING_DESC_F_WRITE as u16, 0, - )]; + ))]; let mut chain = queue.build_desc_chain(&v).unwrap(); let packet = VsockPacket::from_rx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap(); @@ -933,8 +1003,13 @@ mod tests { // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = vec![ // A device-writable packet header descriptor should be invalid. - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -945,8 +1020,13 @@ mod tests { let v = vec![ // A header length < PKT_HEADER_SIZE is invalid. - Descriptor::new(0x10_0000, PKT_HEADER_SIZE as u32 - 1, 0, 0), - Descriptor::new(0x20_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + PKT_HEADER_SIZE as u32 - 1, + 0, + 0, + )), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -955,7 +1035,12 @@ mod tests { ); // On the TX path, it is allowed to not have a data descriptor. - let v = vec![Descriptor::new(0x10_0000, PKT_HEADER_SIZE as u32, 0, 0)]; + let v = vec![RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + PKT_HEADER_SIZE as u32, + 0, + 0, + ))]; let mut chain = queue.build_desc_chain(&v).unwrap(); let header = PacketHeader { @@ -987,8 +1072,8 @@ mod tests { let v = vec![ // The header doesn't fit entirely in the memory bounds. - Descriptor::new(0x10_0000, 0x100, 0, 0), - Descriptor::new(0x20_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -999,8 +1084,8 @@ mod tests { let v = vec![ // The header is outside the memory bounds. - Descriptor::new(0x20_0000, 0x100, 0, 0), - Descriptor::new(0x30_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x30_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1026,8 +1111,8 @@ mod tests { }; mem.write_obj(header, GuestAddress(0x5_0000)).unwrap(); let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), - Descriptor::new(0x8_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1051,7 +1136,7 @@ mod tests { mem.write_obj(header, GuestAddress(0x5_0000)).unwrap(); let v = vec![ // The data descriptor is missing. - Descriptor::new(0x5_0000, PKT_HEADER_SIZE as u32, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, PKT_HEADER_SIZE as u32, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1060,9 +1145,9 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), // The data array doesn't fit entirely in the memory bounds. - Descriptor::new(0x10_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x10_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1071,9 +1156,9 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), // The data array is outside the memory bounds. - Descriptor::new(0x20_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x20_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1084,9 +1169,14 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), // A device-writable packet data descriptor should be invalid. - Descriptor::new(0x8_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x8_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1095,9 +1185,9 @@ mod tests { ); let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), // A data length < the length of data as described by the header. - Descriptor::new(0x8_0000, LEN - 1, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x8_0000, LEN - 1, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); assert_eq!( @@ -1107,8 +1197,8 @@ mod tests { // Let's also test a valid descriptor chain, with both header and data. let v = vec![ - Descriptor::new(0x5_0000, 0x100, 0, 0), - Descriptor::new(0x8_0000, 0x100, 0, 0), + RawDescriptor::from(SplitDescriptor::new(0x5_0000, 0x100, 0, 0)), + RawDescriptor::from(SplitDescriptor::new(0x8_0000, 0x100, 0, 0)), ]; let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -1139,12 +1229,12 @@ mod tests { // Let's also test a valid descriptor chain, with both header and data on a single // descriptor. - let v = vec![Descriptor::new( + let v = vec![RawDescriptor::from(SplitDescriptor::new( 0x5_0000, PKT_HEADER_SIZE as u32 + 0x100, 0, 0, - )]; + ))]; let mut chain = queue.build_desc_chain(&v).unwrap(); let packet = VsockPacket::from_tx_virtq_chain(&mem, &mut chain, MAX_PKT_BUF_SIZE).unwrap(); @@ -1173,8 +1263,18 @@ mod tests { GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap(); // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = vec![ - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -1297,8 +1397,18 @@ mod tests { GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap(); // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = vec![ - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); @@ -1385,8 +1495,18 @@ mod tests { GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x30_0000)]).unwrap(); // The `build_desc_chain` function will populate the `NEXT` related flags and field. let v = vec![ - Descriptor::new(0x10_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), - Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + RawDescriptor::from(SplitDescriptor::new( + 0x10_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), + RawDescriptor::from(SplitDescriptor::new( + 0x20_0000, + 0x100, + VRING_DESC_F_WRITE as u16, + 0, + )), ]; let queue = MockSplitQueue::new(&mem, 16); let mut chain = queue.build_desc_chain(&v).unwrap(); From 2f4f6f525d6204f62e605020adac6e48672c0517 Mon Sep 17 00:00:00 2001 From: Wenyu Huang Date: Sun, 20 Oct 2024 10:51:32 -0400 Subject: [PATCH 3/4] Add packed descriptor Add the packed descriptor for support packed vring. Signed-off-by: Wenyu Huang --- virtio-queue/src/desc/mod.rs | 40 ++++++- virtio-queue/src/desc/packed.rs | 203 ++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 virtio-queue/src/desc/packed.rs diff --git a/virtio-queue/src/desc/mod.rs b/virtio-queue/src/desc/mod.rs index 317791bd..19beb550 100644 --- a/virtio-queue/src/desc/mod.rs +++ b/virtio-queue/src/desc/mod.rs @@ -2,6 +2,7 @@ use vm_memory::{ByteValued, Le16, Le32, Le64}; +pub mod packed; pub mod split; /// a virtio descriptor @@ -33,17 +34,34 @@ impl From for RawDescriptor { } } +impl From for RawDescriptor { + fn from(desc: packed::Descriptor) -> Self { + RawDescriptor( + Le64::from(desc.addr().0), + Le32::from(desc.len()), + Le16::from(desc.id()), + Le16::from(desc.flags()), + ) + } +} + impl From for split::Descriptor { fn from(desc: RawDescriptor) -> split::Descriptor { split::Descriptor::new(desc.0.into(), desc.1.into(), desc.2.into(), desc.3.into()) } } +impl From for packed::Descriptor { + fn from(desc: RawDescriptor) -> packed::Descriptor { + packed::Descriptor::new(desc.0.into(), desc.1.into(), desc.2.into(), desc.3.into()) + } +} + #[cfg(test)] mod tests { use vm_memory::{Le16, Le32, Le64}; - use super::{split, RawDescriptor}; + use super::{packed, split, RawDescriptor}; #[test] fn test_desc_from_split() { @@ -64,4 +82,24 @@ mod tests { assert_eq!(split_desc.flags(), desc.2); assert_eq!(split_desc.next(), desc.3); } + + #[test] + fn test_desc_from_packed() { + let packed_desc = packed::Descriptor::new(1, 2, 3, 4); + let desc = RawDescriptor::from(packed_desc); + assert_eq!(packed_desc.addr().0, desc.0); + assert_eq!(packed_desc.len(), desc.1); + assert_eq!(packed_desc.id(), desc.2); + assert_eq!(packed_desc.flags(), desc.3); + } + + #[test] + fn test_packed_from_desc() { + let desc = RawDescriptor(Le64::from(1), Le32::from(2), Le16::from(3), Le16::from(4)); + let packed_desc = packed::Descriptor::from(desc); + assert_eq!(packed_desc.addr().0, desc.0); + assert_eq!(packed_desc.len(), desc.1); + assert_eq!(packed_desc.id(), desc.2); + assert_eq!(packed_desc.flags(), desc.3); + } } diff --git a/virtio-queue/src/desc/packed.rs b/virtio-queue/src/desc/packed.rs new file mode 100644 index 00000000..7f0d60bf --- /dev/null +++ b/virtio-queue/src/desc/packed.rs @@ -0,0 +1,203 @@ +//! packed descriptor +use virtio_bindings::bindings::virtio_ring::{ + VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, +}; +use vm_memory::{ByteValued, GuestAddress, Le16, Le32, Le64}; + +/// A virtio packed descriptor constraints with C representation. +#[repr(C)] +#[derive(Default, Clone, Copy, Debug)] +pub struct Descriptor { + /// Guest physical address of device specific data. + addr: Le64, + /// Length of device specific data. + len: Le32, + /// Index of descriptor in the descriptor table. + id: Le16, + /// Includes next, write, and indirect bits. + flags: Le16, +} + +#[allow(clippy::len_without_is_empty)] +impl Descriptor { + /// Return the guest physical address of the descriptor buffer. + pub fn addr(&self) -> GuestAddress { + GuestAddress(self.addr.into()) + } + + /// Return the length of the descriptor buffer. + pub fn len(&self) -> u32 { + self.len.into() + } + + /// Return the flags for this descriptor, including next, write and indirect bits. + pub fn flags(&self) -> u16 { + self.flags.into() + } + + /// Return the index of the descriptor in the descriptor table. + pub fn id(&self) -> u16 { + self.id.into() + } + + /// Check whether this descriptor refers to a buffer containing an indirect descriptor table. + pub fn refers_to_indirect_table(&self) -> bool { + self.flags() & VRING_DESC_F_INDIRECT as u16 != 0 + } + + /// Check whether the `VIRTQ_DESC_F_NEXT` is set for the descriptor. + pub fn has_next(&self) -> bool { + self.flags() & VRING_DESC_F_NEXT as u16 != 0 + } + + /// Check if the driver designated this as a write only descriptor. + /// + /// If this is false, this descriptor is read only. + /// Write only means the the emulated device can write and the driver can read. + pub fn is_write_only(&self) -> bool { + self.flags() & VRING_DESC_F_WRITE as u16 != 0 + } +} + +impl Descriptor { + /// Create a new descriptor. + /// + /// # Arguments + /// * `addr` - the guest physical address of the descriptor buffer. + /// * `len` - the length of the descriptor buffer. + /// * `flags` - the `flags` for the descriptor. + /// * `next` - the `next` field of the descriptor. + pub fn new(addr: u64, len: u32, id: u16, flags: u16) -> Self { + Descriptor { + addr: addr.into(), + len: len.into(), + id: id.into(), + flags: flags.into(), + } + } + + /// Set the guest physical address of the descriptor buffer. + pub fn set_addr(&mut self, addr: u64) { + self.addr = addr.into(); + } + + /// Set the length of the descriptor buffer. + pub fn set_len(&mut self, len: u32) { + self.len = len.into(); + } + + /// Set the flags for this descriptor. + pub fn set_flags(&mut self, flags: u16) { + self.flags = flags.into(); + } + + /// Set the value stored in the `next` field of the descriptor. + pub fn set_id(&mut self, id: u16) { + self.id = id.into(); + } +} + +// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and +// all accesses through safe `vm-memory` API will validate any garbage that could be +// included in there. +unsafe impl ByteValued for Descriptor {} + +/// A packed descriptor event constraints with C representation. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct PackedDescEvent { + off_wrap: Le16, + flags: Le16, +} + +impl PackedDescEvent { + /// Create a new `VirtqUsedElem` instance. + /// + /// # Arguments + /// * `id` - the index of the used descriptor chain. + /// * `len` - the total length of the descriptor chain which was used (written to). + #[allow(unused)] + pub(crate) fn new(off_wrap: u16, flags: u16) -> Self { + PackedDescEvent { + off_wrap: off_wrap.into(), + flags: flags.into(), + } + } +} + +// SAFETY: This is safe because `PackedDescEvent` contains only wrappers over POD types and +// all accesses through safe `vm-memory` API will validate any garbage that could be +// included in there. +unsafe impl ByteValued for PackedDescEvent {} + +#[cfg(test)] +mod tests { + use super::*; + use memoffset::offset_of; + use std::mem::{align_of, size_of}; + + #[test] + fn test_descriptor_offset() { + assert_eq!(size_of::(), 16); + assert_eq!(offset_of!(Descriptor, addr), 0); + assert_eq!(offset_of!(Descriptor, len), 8); + assert_eq!(offset_of!(Descriptor, id), 12); + assert_eq!(offset_of!(Descriptor, flags), 14); + assert!(align_of::() <= 16); + } + + #[test] + fn test_descriptor_getter_setter() { + let mut desc = Descriptor::new(0, 0, 0, 0); + + desc.set_addr(0x1000); + assert_eq!(desc.addr(), GuestAddress(0x1000)); + desc.set_len(0x2000); + assert_eq!(desc.len(), 0x2000); + desc.set_flags(VRING_DESC_F_NEXT as u16); + assert_eq!(desc.flags(), VRING_DESC_F_NEXT as u16); + assert!(desc.has_next()); + assert!(!desc.is_write_only()); + assert!(!desc.refers_to_indirect_table()); + desc.set_flags(VRING_DESC_F_WRITE as u16); + assert_eq!(desc.flags(), VRING_DESC_F_WRITE as u16); + assert!(!desc.has_next()); + assert!(desc.is_write_only()); + assert!(!desc.refers_to_indirect_table()); + desc.set_flags(VRING_DESC_F_INDIRECT as u16); + assert_eq!(desc.flags(), VRING_DESC_F_INDIRECT as u16); + assert!(!desc.is_write_only()); + assert!(desc.refers_to_indirect_table()); + desc.set_id(1); + assert_eq!(desc.id(), 1); + } + + #[test] + fn test_descriptor_copy() { + let e1 = Descriptor::new(1, 2, 0, 3); + let mut e2 = Descriptor::default(); + + e2.as_mut_slice().copy_from_slice(e1.as_slice()); + assert_eq!(e1.addr(), e2.addr()); + assert_eq!(e1.len(), e2.len()); + assert_eq!(e1.id(), e2.id()); + assert_eq!(e1.flags(), e2.flags()); + } + + #[test] + fn test_packed_desc_event_offset() { + assert_eq!(offset_of!(PackedDescEvent, off_wrap), 0); + assert_eq!(offset_of!(PackedDescEvent, flags), 2); + assert_eq!(size_of::(), 4); + } + + #[test] + fn test_packed_desc_event_copy() { + let e1 = PackedDescEvent::new(1, 2); + let mut e2 = PackedDescEvent::new(0, 0); + + e2.as_mut_slice().copy_from_slice(e1.as_slice()); + assert_eq!(e1.off_wrap, e2.off_wrap); + assert_eq!(e1.flags, e2.flags); + } +} From 0a9cb3a41025f565a251260c8fa517e4046ce1ca Mon Sep 17 00:00:00 2001 From: Wenyu Huang Date: Sun, 3 Nov 2024 10:10:35 -0500 Subject: [PATCH 4/4] Update virtio-queue CHANGELOG Signed-off-by: Wenyu Huang --- virtio-queue/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/virtio-queue/CHANGELOG.md b/virtio-queue/CHANGELOG.md index 69280596..efdc3e00 100644 --- a/virtio-queue/CHANGELOG.md +++ b/virtio-queue/CHANGELOG.md @@ -7,6 +7,12 @@ ## Changed - Updated virtio-bindings from 0.2.4 to 0.2.5. +- Use `RawDescriptor` to represent the memory layout of the split and packed descriptor in virtio-queue/desc. +- Move the split descriptor to the virtio-queue/desc. + +## Added + +- Add packed descriptor in virtio-queue # v0.14.0