1 use crate::{Error, Queue, QueueT};
2 use 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)]
15 pub 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 
36 impl TryFrom<QueueState> for Queue {
37     type Error = Error;
38 
try_from(q_state: QueueState) -> Result<Self, Self::Error>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)]
56 mod tests {
57     use super::*;
58 
create_valid_queue_state() -> QueueState59     fn create_valid_queue_state() -> QueueState {
60         let queue = Queue::new(16).unwrap();
61         queue.state()
62     }
63 
64     #[test]
test_empty_queue_state()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]
test_invalid_queue_state()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 }
120