瀏覽代碼

Test sending to console.

Andrew Walbran 2 年之前
父節點
當前提交
819f8ca470
共有 3 個文件被更改,包括 140 次插入13 次删除
  1. 48 1
      src/device/console.rs
  2. 66 6
      src/queue.rs
  3. 26 6
      src/transport/fake.rs

+ 48 - 1
src/device/console.rs

@@ -233,7 +233,7 @@ mod tests {
     };
     use alloc::{sync::Arc, vec};
     use core::ptr::NonNull;
-    use std::sync::Mutex;
+    use std::{sync::Mutex, thread, time::Duration};
 
     #[test]
     fn receive() {
@@ -282,4 +282,51 @@ mod tests {
         assert_eq!(console.recv(true).unwrap(), Some(42));
         assert_eq!(console.recv(true).unwrap(), None);
     }
+
+    #[test]
+    fn send() {
+        let mut config_space = Config {
+            cols: ReadOnly::new(0),
+            rows: ReadOnly::new(0),
+            max_nr_ports: ReadOnly::new(0),
+            emerg_wr: WriteOnly::default(),
+        };
+        let state = Arc::new(Mutex::new(State {
+            status: DeviceStatus::empty(),
+            driver_features: 0,
+            guest_page_size: 0,
+            interrupt_pending: false,
+            queues: vec![QueueStatus::default(); 2],
+        }));
+        let transport = FakeTransport {
+            device_type: DeviceType::Console,
+            max_queue_size: 2,
+            device_features: 0,
+            config_space: NonNull::from(&mut config_space),
+            state: state.clone(),
+        };
+        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
+
+        // Start a thread to simulate the device waiting for characters.
+        let handle = thread::spawn(move || {
+            println!("Device waiting for a character.");
+            while !state.lock().unwrap().queues[usize::from(QUEUE_TRANSMITQ_PORT_0)].notified {
+                thread::sleep(Duration::from_millis(10));
+            }
+            println!("Transmit queue was notified.");
+            // Allocate a bigger buffer than we expect to use, so we can check how much was actually
+            // used.
+            let mut data = [0; 2];
+            let length_read = state
+                .lock()
+                .unwrap()
+                .read_from_queue::<QUEUE_SIZE>(QUEUE_TRANSMITQ_PORT_0, &mut data);
+            assert_eq!(length_read, 1);
+            assert_eq!(data, [b'Q', 0]);
+        });
+
+        assert_eq!(console.send(b'Q'), Ok(()));
+
+        handle.join().unwrap();
+    }
 }

+ 66 - 6
src/queue.rs

@@ -528,14 +528,14 @@ struct UsedElem {
 /// The fake device always uses descriptors in order.
 #[cfg(test)]
 pub(crate) fn fake_write_to_queue<const QUEUE_SIZE: usize>(
-    receive_queue_descriptors: *const Descriptor,
-    receive_queue_driver_area: VirtAddr,
-    receive_queue_device_area: VirtAddr,
+    queue_descriptors: *const Descriptor,
+    queue_driver_area: VirtAddr,
+    queue_device_area: VirtAddr,
     data: &[u8],
 ) {
-    let descriptors = ptr::slice_from_raw_parts(receive_queue_descriptors, QUEUE_SIZE);
-    let available_ring = receive_queue_driver_area as *const AvailRing<QUEUE_SIZE>;
-    let used_ring = receive_queue_device_area as *mut UsedRing<QUEUE_SIZE>;
+    let descriptors = ptr::slice_from_raw_parts(queue_descriptors, QUEUE_SIZE);
+    let available_ring = queue_driver_area as *const AvailRing<QUEUE_SIZE>;
+    let used_ring = queue_device_area as *mut UsedRing<QUEUE_SIZE>;
     // Safe because the various pointers are properly aligned, dereferenceable, initialised, and
     // nothing else accesses them during this block.
     unsafe {
@@ -577,6 +577,66 @@ pub(crate) fn fake_write_to_queue<const QUEUE_SIZE: usize>(
     }
 }
 
+/// Simulates the device reading from a VirtIO queue, for use in tests.
+///
+/// Data is read into the `data` buffer passed in. Returns the number of bytes actually read.
+///
+/// The fake device always uses descriptors in order.
+#[cfg(test)]
+pub(crate) fn fake_read_from_queue<const QUEUE_SIZE: usize>(
+    queue_descriptors: *const Descriptor,
+    queue_driver_area: VirtAddr,
+    queue_device_area: VirtAddr,
+    data: &mut [u8],
+) -> usize {
+    let descriptors = ptr::slice_from_raw_parts(queue_descriptors, QUEUE_SIZE);
+    let available_ring = queue_driver_area as *const AvailRing<QUEUE_SIZE>;
+    let used_ring = queue_device_area as *mut UsedRing<QUEUE_SIZE>;
+
+    // Safe because the various pointers are properly aligned, dereferenceable, initialised, and
+    // nothing else accesses them during this block.
+    unsafe {
+        // Make sure there is actually at least one descriptor available to read from.
+        assert_ne!((*available_ring).idx, (*used_ring).idx);
+        // The fake device always uses descriptors in order, like VIRTIO_F_IN_ORDER, so
+        // `used_ring.idx` marks the next descriptor we should take from the available ring.
+        let next_slot = (*used_ring).idx & (QUEUE_SIZE as u16 - 1);
+        let head_descriptor_index = (*available_ring).ring[next_slot as usize];
+        let mut descriptor = &(*descriptors)[head_descriptor_index as usize];
+
+        // Loop through all descriptors in the chain, reading data from them.
+        let mut remaining_data = data;
+        let mut total_length_read = 0;
+        loop {
+            // Check the buffer and read from it.
+            let flags = descriptor.flags;
+            assert!(!flags.contains(DescFlags::WRITE));
+            let buffer_length = descriptor.len as usize;
+            let length_to_read = min(remaining_data.len(), buffer_length);
+            ptr::copy(
+                descriptor.addr as *const u8,
+                remaining_data.as_mut_ptr(),
+                length_to_read,
+            );
+            remaining_data = &mut remaining_data[length_to_read..];
+            total_length_read += length_to_read;
+
+            if let Some(next) = descriptor.next() {
+                descriptor = &(*descriptors)[next as usize];
+            } else {
+                break;
+            }
+        }
+
+        // Mark the buffer as used.
+        (*used_ring).ring[next_slot as usize].id = head_descriptor_index as u32;
+        (*used_ring).ring[next_slot as usize].len = total_length_read as u32;
+        (*used_ring).idx += 1;
+
+        total_length_read
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;

+ 26 - 6
src/transport/fake.rs

@@ -1,6 +1,6 @@
 use super::{DeviceStatus, DeviceType, Transport};
 use crate::{
-    queue::{fake_write_to_queue, Descriptor},
+    queue::{fake_read_from_queue, fake_write_to_queue, Descriptor},
     PhysAddr, Result,
 };
 use alloc::{sync::Arc, vec::Vec};
@@ -109,15 +109,35 @@ impl State {
     ///
     /// The fake device always uses descriptors in order.
     pub fn write_to_queue<const QUEUE_SIZE: usize>(&mut self, queue_index: u16, data: &[u8]) {
-        let receive_queue = &self.queues[queue_index as usize];
-        assert_ne!(receive_queue.descriptors, 0);
+        let queue = &self.queues[queue_index as usize];
+        assert_ne!(queue.descriptors, 0);
         fake_write_to_queue::<QUEUE_SIZE>(
-            receive_queue.descriptors as *const Descriptor,
-            receive_queue.driver_area,
-            receive_queue.device_area,
+            queue.descriptors as *const Descriptor,
+            queue.driver_area,
+            queue.device_area,
             data,
         );
     }
+
+    /// Simulates the device reading from the given queue.
+    ///
+    /// Data is read into the `data` buffer passed in. Returns the number of bytes actually read.
+    ///
+    /// The fake device always uses descriptors in order.
+    pub fn read_from_queue<const QUEUE_SIZE: usize>(
+        &mut self,
+        queue_index: u16,
+        data: &mut [u8],
+    ) -> usize {
+        let queue = &self.queues[queue_index as usize];
+        assert_ne!(queue.descriptors, 0);
+        fake_read_from_queue::<QUEUE_SIZE>(
+            queue.descriptors as *const Descriptor,
+            queue.driver_area,
+            queue.device_area,
+            data,
+        )
+    }
 }
 
 #[derive(Clone, Debug, Default, Eq, PartialEq)]