Skip to content

Commit 0028736

Browse files
committed
Add desc crate and split.rs
Add desc crate for split and packed descriptor. The Descriptor struct in mod.rs represents memory layout. Signed-off-by: Wenyu Huang <[email protected]>
1 parent a318603 commit 0028736

File tree

3 files changed

+272
-0
lines changed

3 files changed

+272
-0
lines changed

virtio-queue/src/desc/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! Descriptor types for virtio queue.
2+
3+
use vm_memory::{ByteValued, Le16, Le32, Le64};
4+
5+
pub mod split;
6+
7+
/// A virtio descriptor's layout constraints with C representation.
8+
/// This is a unified representation of the memory layout order
9+
/// for packed descriptors and split descriptors.
10+
#[repr(C)]
11+
#[derive(Clone, Copy, Debug, Default)]
12+
pub struct Descriptor(Le64, Le32, Le16, Le16);
13+
14+
// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and
15+
// all accesses through safe `vm-memory` API will validate any garbage that could be
16+
// included in there.
17+
unsafe impl ByteValued for Descriptor {}
18+
19+
impl From<split::Descriptor> for Descriptor {
20+
fn from(desc: split::Descriptor) -> Self {
21+
Descriptor(
22+
Le64::from(desc.addr().0),
23+
Le32::from(desc.len()),
24+
Le16::from(desc.flags()),
25+
Le16::from(desc.next()),
26+
)
27+
}
28+
}
29+
30+
impl From<Descriptor> for split::Descriptor {
31+
fn from(desc: Descriptor) -> split::Descriptor {
32+
split::Descriptor::new(desc.0.into(), desc.1.into(), desc.2.into(), desc.3.into())
33+
}
34+
}

virtio-queue/src/desc/split.rs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE-BSD-3-Clause file.
4+
//
5+
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6+
//
7+
// Copyright © 2019 Intel Corporation
8+
//
9+
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
10+
//
11+
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
12+
//! split descriptor
13+
14+
use vm_memory::{ByteValued, GuestAddress, Le16, Le32, Le64};
15+
16+
use virtio_bindings::bindings::virtio_ring::{
17+
VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE,
18+
};
19+
20+
/// A virtio split descriptor constraints with C representation.
21+
#[repr(C)]
22+
#[derive(Default, Clone, Copy, Debug)]
23+
pub struct Descriptor {
24+
/// Guest physical address of device specific data.
25+
addr: Le64,
26+
27+
/// Length of device specific data.
28+
len: Le32,
29+
30+
/// Includes next, write, and indirect bits.
31+
flags: Le16,
32+
33+
/// Index into the descriptor table of the next descriptor if flags has the `next` bit set.
34+
next: Le16,
35+
}
36+
37+
#[allow(clippy::len_without_is_empty)]
38+
impl Descriptor {
39+
/// Create a new descriptor.
40+
///
41+
/// # Arguments
42+
/// * `addr` - the guest physical address of the descriptor buffer.
43+
/// * `len` - the length of the descriptor buffer.
44+
/// * `flags` - the `flags` for the descriptor.
45+
/// * `next` - the `next` field of the descriptor.
46+
pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self {
47+
Descriptor {
48+
addr: addr.into(),
49+
len: len.into(),
50+
flags: flags.into(),
51+
next: next.into(),
52+
}
53+
}
54+
55+
/// Return the guest physical address of the descriptor buffer.
56+
pub fn addr(&self) -> GuestAddress {
57+
GuestAddress(self.addr.into())
58+
}
59+
60+
/// Return the length of the descriptor buffer.
61+
pub fn len(&self) -> u32 {
62+
self.len.into()
63+
}
64+
65+
/// Return the flags for this descriptor, including next, write and indirect bits.
66+
pub fn flags(&self) -> u16 {
67+
self.flags.into()
68+
}
69+
70+
/// Return the value stored in the `next` field of the descriptor.
71+
pub fn next(&self) -> u16 {
72+
self.next.into()
73+
}
74+
75+
/// Check whether this descriptor refers to a buffer containing an indirect descriptor table.
76+
pub fn refers_to_indirect_table(&self) -> bool {
77+
self.flags() & VRING_DESC_F_INDIRECT as u16 != 0
78+
}
79+
80+
/// Check whether the `VIRTQ_DESC_F_NEXT` is set for the descriptor.
81+
pub fn has_next(&self) -> bool {
82+
self.flags() & VRING_DESC_F_NEXT as u16 != 0
83+
}
84+
85+
/// Check if the driver designated this as a write only descriptor.
86+
///
87+
/// If this is false, this descriptor is read only.
88+
/// Write only means the the emulated device can write and the driver can read.
89+
pub fn is_write_only(&self) -> bool {
90+
self.flags() & VRING_DESC_F_WRITE as u16 != 0
91+
}
92+
}
93+
94+
#[cfg(any(test, feature = "test-utils"))]
95+
impl Descriptor {
96+
/// Set the guest physical address of the descriptor buffer.
97+
pub fn set_addr(&mut self, addr: u64) {
98+
self.addr = addr.into();
99+
}
100+
101+
/// Set the length of the descriptor buffer.
102+
pub fn set_len(&mut self, len: u32) {
103+
self.len = len.into();
104+
}
105+
106+
/// Set the flags for this descriptor.
107+
pub fn set_flags(&mut self, flags: u16) {
108+
self.flags = flags.into();
109+
}
110+
111+
/// Set the value stored in the `next` field of the descriptor.
112+
pub fn set_next(&mut self, next: u16) {
113+
self.next = next.into();
114+
}
115+
}
116+
117+
// SAFETY: This is safe because `Descriptor` contains only wrappers over POD types and
118+
// all accesses through safe `vm-memory` API will validate any garbage that could be
119+
// included in there.
120+
unsafe impl ByteValued for Descriptor {}
121+
122+
/// Represents the contents of an element from the used virtqueue ring.
123+
// Note that the `ByteValued` implementation of this structure expects the `VirtqUsedElem` to store
124+
// only plain old data types.
125+
#[repr(C)]
126+
#[derive(Clone, Copy, Default, Debug)]
127+
pub struct VirtqUsedElem {
128+
id: Le32,
129+
len: Le32,
130+
}
131+
132+
impl VirtqUsedElem {
133+
/// Create a new `VirtqUsedElem` instance.
134+
///
135+
/// # Arguments
136+
/// * `id` - the index of the used descriptor chain.
137+
/// * `len` - the total length of the descriptor chain which was used (written to).
138+
#[allow(unused)]
139+
pub(crate) fn new(id: u32, len: u32) -> Self {
140+
VirtqUsedElem {
141+
id: id.into(),
142+
len: len.into(),
143+
}
144+
}
145+
}
146+
147+
#[cfg(any(test, feature = "test-utils"))]
148+
#[allow(clippy::len_without_is_empty)]
149+
impl VirtqUsedElem {
150+
/// Get the index of the used descriptor chain.
151+
pub fn id(&self) -> u32 {
152+
self.id.into()
153+
}
154+
155+
/// Get `length` field of the used ring entry.
156+
pub fn len(&self) -> u32 {
157+
self.len.into()
158+
}
159+
}
160+
161+
// SAFETY: This is safe because `VirtqUsedElem` contains only wrappers over POD types
162+
// and all accesses through safe `vm-memory` API will validate any garbage that could be
163+
// included in there.
164+
unsafe impl ByteValued for VirtqUsedElem {}
165+
166+
#[cfg(test)]
167+
mod tests {
168+
use super::*;
169+
use memoffset::offset_of;
170+
use std::mem::{align_of, size_of};
171+
172+
#[test]
173+
fn test_descriptor_offset() {
174+
assert_eq!(size_of::<Descriptor>(), 16);
175+
assert_eq!(offset_of!(Descriptor, addr), 0);
176+
assert_eq!(offset_of!(Descriptor, len), 8);
177+
assert_eq!(offset_of!(Descriptor, flags), 12);
178+
assert_eq!(offset_of!(Descriptor, next), 14);
179+
assert!(align_of::<Descriptor>() <= 16);
180+
}
181+
182+
#[test]
183+
fn test_descriptor_getter_setter() {
184+
let mut desc = Descriptor::new(0, 0, 0, 0);
185+
186+
desc.set_addr(0x1000);
187+
assert_eq!(desc.addr(), GuestAddress(0x1000));
188+
desc.set_len(0x2000);
189+
assert_eq!(desc.len(), 0x2000);
190+
desc.set_flags(VRING_DESC_F_NEXT as u16);
191+
assert_eq!(desc.flags(), VRING_DESC_F_NEXT as u16);
192+
assert!(desc.has_next());
193+
assert!(!desc.is_write_only());
194+
assert!(!desc.refers_to_indirect_table());
195+
desc.set_flags(VRING_DESC_F_WRITE as u16);
196+
assert_eq!(desc.flags(), VRING_DESC_F_WRITE as u16);
197+
assert!(!desc.has_next());
198+
assert!(desc.is_write_only());
199+
assert!(!desc.refers_to_indirect_table());
200+
desc.set_flags(VRING_DESC_F_INDIRECT as u16);
201+
assert_eq!(desc.flags(), VRING_DESC_F_INDIRECT as u16);
202+
assert!(!desc.has_next());
203+
assert!(!desc.is_write_only());
204+
assert!(desc.refers_to_indirect_table());
205+
desc.set_next(3);
206+
assert_eq!(desc.next(), 3);
207+
}
208+
209+
#[test]
210+
fn test_descriptor_copy() {
211+
let e1 = Descriptor::new(1, 2, VRING_DESC_F_NEXT as u16, 3);
212+
let mut e2 = Descriptor::default();
213+
214+
e2.as_mut_slice().copy_from_slice(e1.as_slice());
215+
assert_eq!(e1.addr(), e2.addr());
216+
assert_eq!(e1.len(), e2.len());
217+
assert_eq!(e1.flags(), e2.flags());
218+
assert_eq!(e1.next(), e2.next());
219+
}
220+
221+
#[test]
222+
fn test_used_elem_offset() {
223+
assert_eq!(offset_of!(VirtqUsedElem, id), 0);
224+
assert_eq!(offset_of!(VirtqUsedElem, len), 4);
225+
assert_eq!(size_of::<VirtqUsedElem>(), 8);
226+
}
227+
228+
#[test]
229+
fn test_used_elem_copy() {
230+
let e1 = VirtqUsedElem::new(3, 15);
231+
let mut e2 = VirtqUsedElem::new(0, 0);
232+
233+
e2.as_mut_slice().copy_from_slice(e1.as_slice());
234+
assert_eq!(e1.id, e2.id);
235+
assert_eq!(e1.len, e2.len);
236+
}
237+
}

virtio-queue/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use self::queue_sync::QueueSync;
3030
pub use self::state::QueueState;
3131

3232
pub mod defs;
33+
pub mod desc;
3334
#[cfg(any(test, feature = "test-utils"))]
3435
pub mod mock;
3536

0 commit comments

Comments
 (0)