virtio_queue/
state.rs

1use crate::{Error, Queue, QueueT};
2use vm_memory::GuestAddress;
3
4/// Representation of the `Queue` state.
5///
6/// The `QueueState` represents the pure state of the `queue` without tracking any implementation
7/// details of the queue. The goal with this design is to minimize the changes required to the
8/// state, and thus the required transitions between states when upgrading or downgrading.
9///
10/// In practice this means that the `QueueState` consists solely of POD (Plain Old Data).
11///
12/// As this structure has all the fields public it is consider to be untrusted. A validated
13/// queue can be created from the state by calling the associated `try_from` function.
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
15pub struct QueueState {
16    /// The maximum size in elements offered by the device.
17    pub max_size: u16,
18    /// Tail position of the available ring.
19    pub next_avail: u16,
20    /// Head position of the used ring.
21    pub next_used: u16,
22    /// VIRTIO_F_RING_EVENT_IDX negotiated.
23    pub event_idx_enabled: bool,
24    /// The queue size in elements the driver selected.
25    pub size: u16,
26    /// Indicates if the queue is finished with configuration.
27    pub ready: bool,
28    /// Guest physical address of the descriptor table.
29    pub desc_table: u64,
30    /// Guest physical address of the available ring.
31    pub avail_ring: u64,
32    /// Guest physical address of the used ring.
33    pub used_ring: u64,
34}
35
36impl TryFrom<QueueState> for Queue {
37    type Error = Error;
38
39    fn try_from(q_state: QueueState) -> Result<Self, Self::Error> {
40        let mut q = Queue::new(q_state.max_size)?;
41
42        q.set_next_avail(q_state.next_avail);
43        q.set_next_used(q_state.next_used);
44        q.set_event_idx(q_state.event_idx_enabled);
45        q.try_set_size(q_state.size)?;
46        q.set_ready(q_state.ready);
47        q.try_set_desc_table_address(GuestAddress(q_state.desc_table))?;
48        q.try_set_avail_ring_address(GuestAddress(q_state.avail_ring))?;
49        q.try_set_used_ring_address(GuestAddress(q_state.used_ring))?;
50
51        Ok(q)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    fn create_valid_queue_state() -> QueueState {
60        let queue = Queue::new(16).unwrap();
61        queue.state()
62    }
63
64    #[test]
65    fn test_empty_queue_state() {
66        let max_size = 16;
67        let queue = Queue::new(max_size).unwrap();
68
69        // Saving the state of a queue on which we didn't do any operation is ok.
70        // Same for restore.
71        let queue_state = queue.state();
72        let restored_q = Queue::try_from(queue_state).unwrap();
73        assert_eq!(queue, restored_q);
74    }
75
76    #[test]
77    fn test_invalid_queue_state() {
78        // Let's generate a state that we know is valid so we can just alter one field at a time.
79        let mut q_state = create_valid_queue_state();
80
81        // Test invalid max_size.
82        // Size too small.
83        q_state.max_size = 0;
84        assert!(Queue::try_from(q_state).is_err());
85        // Size too big.
86        q_state.max_size = u16::MAX;
87        assert!(Queue::try_from(q_state).is_err());
88        // Size not a power of 2.
89        q_state.max_size = 15;
90        assert!(Queue::try_from(q_state).is_err());
91
92        // Test invalid size.
93        let mut q_state = create_valid_queue_state();
94        // Size too small.
95        q_state.size = 0;
96        assert!(Queue::try_from(q_state).is_err());
97        // Size too big.
98        q_state.size = u16::MAX;
99        assert!(Queue::try_from(q_state).is_err());
100        // Size not a power of 2.
101        q_state.size = 15;
102        assert!(Queue::try_from(q_state).is_err());
103
104        // Test invalid desc_table.
105        let mut q_state = create_valid_queue_state();
106        q_state.desc_table = 0xf;
107        assert!(Queue::try_from(q_state).is_err());
108
109        // Test invalid avail_ring.
110        let mut q_state = create_valid_queue_state();
111        q_state.avail_ring = 0x1;
112        assert!(Queue::try_from(q_state).is_err());
113
114        // Test invalid used_ring.
115        let mut q_state = create_valid_queue_state();
116        q_state.used_ring = 0x3;
117        assert!(Queue::try_from(q_state).is_err());
118    }
119}