summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2020-05-13 14:03:48 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-27 06:03:49 +0000
commit247134fe68f32f45f7a92a7f3181c0a74f713dec (patch)
tree385eb74095d7282ece8c32471c2bdd1a702d4665
parent1cc1d5e6eea56c24261380fee6549b4958735d9d (diff)
downloadcrosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar.gz
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar.bz2
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar.lz
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar.xz
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.tar.zst
crosvm-247134fe68f32f45f7a92a7f3181c0a74f713dec.zip
devices: usb: add unit test for ring buffer cycle
Validate that the toggle_cycle code works as expected.

I initially misunderstood the behavior of toggle_cycle in the Link TRB,
but it appears to work correctly as written after writing a unit test to
verify my understanding.  Add the unit test anyway to be sure the
behavior doesn't regress in the future.

BUG=None
TEST=cargo test -p devices

Change-Id: I9dbc34b26225945fa6d31c34261f53d5b64ba259
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2199956
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
-rw-r--r--devices/src/usb/xhci/ring_buffer.rs85
1 files changed, 85 insertions, 0 deletions
diff --git a/devices/src/usb/xhci/ring_buffer.rs b/devices/src/usb/xhci/ring_buffer.rs
index 3033b0e..91806c6 100644
--- a/devices/src/usb/xhci/ring_buffer.rs
+++ b/devices/src/usb/xhci/ring_buffer.rs
@@ -264,4 +264,89 @@ mod test {
         let descriptor = transfer_ring.dequeue_transfer_descriptor().unwrap();
         assert_eq!(descriptor.is_none(), true);
     }
+
+    #[test]
+    fn ring_test_toggle_cycle() {
+        let trb_size = size_of::<Trb>() as u64;
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+        let mut transfer_ring = RingBuffer::new(String::new(), gm.clone());
+
+        let mut trb = NormalTrb::new();
+        trb.set_trb_type(TrbType::Normal);
+        trb.set_data_buffer(1);
+        trb.set_chain(false);
+        trb.set_cycle(false);
+        gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+            .unwrap();
+
+        let mut ltrb = LinkTrb::new();
+        ltrb.set_trb_type(TrbType::Link);
+        ltrb.set_ring_segment_pointer(0x100);
+        ltrb.set_toggle_cycle(true);
+        ltrb.set_cycle(false);
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + trb_size))
+            .unwrap();
+
+        // Initial state: consumer cycle = false
+        transfer_ring.set_dequeue_pointer(GuestAddress(0x100));
+        transfer_ring.set_consumer_cycle_state(false);
+
+        // Read first transfer descriptor.
+        let descriptor = transfer_ring
+            .dequeue_transfer_descriptor()
+            .unwrap()
+            .unwrap();
+        assert_eq!(descriptor.len(), 1);
+        assert_eq!(descriptor[0].trb.get_parameter(), 1);
+
+        // Cycle bit should be unchanged since we haven't advanced past the Link TRB yet.
+        assert_eq!(transfer_ring.consumer_cycle_state, false);
+
+        // Overwrite the first TRB with a new one (data = 2)
+        // with the new producer cycle bit state (true).
+        let mut trb = NormalTrb::new();
+        trb.set_trb_type(TrbType::Normal);
+        trb.set_data_buffer(2);
+        trb.set_cycle(true); // Link TRB toggled the cycle.
+        gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+            .unwrap();
+
+        // Read new transfer descriptor.
+        let descriptor = transfer_ring
+            .dequeue_transfer_descriptor()
+            .unwrap()
+            .unwrap();
+        assert_eq!(descriptor.len(), 1);
+        assert_eq!(descriptor[0].trb.get_parameter(), 2);
+
+        assert_eq!(transfer_ring.consumer_cycle_state, true);
+
+        // Update the Link TRB with the new cycle bit.
+        let mut ltrb = LinkTrb::new();
+        ltrb.set_trb_type(TrbType::Link);
+        ltrb.set_ring_segment_pointer(0x100);
+        ltrb.set_toggle_cycle(true);
+        ltrb.set_cycle(true); // Producer cycle state is now 1.
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + trb_size))
+            .unwrap();
+
+        // Overwrite the first TRB again with a new one (data = 3)
+        // with the new producer cycle bit state (false).
+        let mut trb = NormalTrb::new();
+        trb.set_trb_type(TrbType::Normal);
+        trb.set_data_buffer(3);
+        trb.set_cycle(false); // Link TRB toggled the cycle.
+        gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+            .unwrap();
+
+        // Read new transfer descriptor.
+        let descriptor = transfer_ring
+            .dequeue_transfer_descriptor()
+            .unwrap()
+            .unwrap();
+        assert_eq!(descriptor.len(), 1);
+        assert_eq!(descriptor[0].trb.get_parameter(), 3);
+
+        assert_eq!(transfer_ring.consumer_cycle_state, false);
+    }
 }