| use crate::{Error, Queue, QueueT}; |
| use vm_memory::GuestAddress; |
| |
| /// Representation of the `Queue` state. |
| /// |
| /// The `QueueState` represents the pure state of the `queue` without tracking any implementation |
| /// details of the queue. The goal with this design is to minimize the changes required to the |
| /// state, and thus the required transitions between states when upgrading or downgrading. |
| /// |
| /// In practice this means that the `QueueState` consists solely of POD (Plain Old Data). |
| /// |
| /// As this structure has all the fields public it is consider to be untrusted. A validated |
| /// queue can be created from the state by calling the associated `try_from` function. |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |
| pub struct QueueState { |
| /// The maximum size in elements offered by the device. |
| pub max_size: u16, |
| /// Tail position of the available ring. |
| pub next_avail: u16, |
| /// Head position of the used ring. |
| pub next_used: u16, |
| /// VIRTIO_F_RING_EVENT_IDX negotiated. |
| pub event_idx_enabled: bool, |
| /// The queue size in elements the driver selected. |
| pub size: u16, |
| /// Indicates if the queue is finished with configuration. |
| pub ready: bool, |
| /// Guest physical address of the descriptor table. |
| pub desc_table: u64, |
| /// Guest physical address of the available ring. |
| pub avail_ring: u64, |
| /// Guest physical address of the used ring. |
| pub used_ring: u64, |
| } |
| |
| impl TryFrom<QueueState> for Queue { |
| type Error = Error; |
| |
| fn try_from(q_state: QueueState) -> Result<Self, Self::Error> { |
| let mut q = Queue::new(q_state.max_size)?; |
| |
| q.set_next_avail(q_state.next_avail); |
| q.set_next_used(q_state.next_used); |
| q.set_event_idx(q_state.event_idx_enabled); |
| q.try_set_size(q_state.size)?; |
| q.set_ready(q_state.ready); |
| q.try_set_desc_table_address(GuestAddress(q_state.desc_table))?; |
| q.try_set_avail_ring_address(GuestAddress(q_state.avail_ring))?; |
| q.try_set_used_ring_address(GuestAddress(q_state.used_ring))?; |
| |
| Ok(q) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| fn create_valid_queue_state() -> QueueState { |
| let queue = Queue::new(16).unwrap(); |
| queue.state() |
| } |
| |
| #[test] |
| fn test_empty_queue_state() { |
| let max_size = 16; |
| let queue = Queue::new(max_size).unwrap(); |
| |
| // Saving the state of a queue on which we didn't do any operation is ok. |
| // Same for restore. |
| let queue_state = queue.state(); |
| let restored_q = Queue::try_from(queue_state).unwrap(); |
| assert_eq!(queue, restored_q); |
| } |
| |
| #[test] |
| fn test_invalid_queue_state() { |
| // Let's generate a state that we know is valid so we can just alter one field at a time. |
| let mut q_state = create_valid_queue_state(); |
| |
| // Test invalid max_size. |
| // Size too small. |
| q_state.max_size = 0; |
| assert!(Queue::try_from(q_state).is_err()); |
| // Size too big. |
| q_state.max_size = u16::MAX; |
| assert!(Queue::try_from(q_state).is_err()); |
| // Size not a power of 2. |
| q_state.max_size = 15; |
| assert!(Queue::try_from(q_state).is_err()); |
| |
| // Test invalid size. |
| let mut q_state = create_valid_queue_state(); |
| // Size too small. |
| q_state.size = 0; |
| assert!(Queue::try_from(q_state).is_err()); |
| // Size too big. |
| q_state.size = u16::MAX; |
| assert!(Queue::try_from(q_state).is_err()); |
| // Size not a power of 2. |
| q_state.size = 15; |
| assert!(Queue::try_from(q_state).is_err()); |
| |
| // Test invalid desc_table. |
| let mut q_state = create_valid_queue_state(); |
| q_state.desc_table = 0xf; |
| assert!(Queue::try_from(q_state).is_err()); |
| |
| // Test invalid avail_ring. |
| let mut q_state = create_valid_queue_state(); |
| q_state.avail_ring = 0x1; |
| assert!(Queue::try_from(q_state).is_err()); |
| |
| // Test invalid used_ring. |
| let mut q_state = create_valid_queue_state(); |
| q_state.used_ring = 0x3; |
| assert!(Queue::try_from(q_state).is_err()); |
| } |
| } |