summary refs log tree commit diff
path: root/devices/src/usb/xhci/ring_buffer.rs
diff options
context:
space:
mode:
authorJingkui Wang <jkwang@google.com>2019-03-07 23:54:09 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-03-16 15:25:22 -0700
commit0a5bf14261edd60641b75aeb60247001b5a3056f (patch)
tree9d93ee479ddd3be24fbd30e2193db37353e1e489 /devices/src/usb/xhci/ring_buffer.rs
parentc698769b42779a8f621b0df48583c4307c596cb6 (diff)
downloadcrosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar.gz
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar.bz2
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar.lz
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar.xz
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.tar.zst
crosvm-0a5bf14261edd60641b75aeb60247001b5a3056f.zip
usb: add ring buffer and ring buffer controller
for ring buffer, guest kernel is producer and crosvm is consumer

CQ-DEPEND=1510817
BUG=chromium:831850
TEST=cargo test

Change-Id: Ib62d2b42de1a77ff71ca0e2a0066feacc56dddc1
Reviewed-on: https://chromium-review.googlesource.com/1510818
Commit-Ready: Jingkui Wang <jkwang@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/usb/xhci/ring_buffer.rs')
-rw-r--r--devices/src/usb/xhci/ring_buffer.rs271
1 files changed, 271 insertions, 0 deletions
diff --git a/devices/src/usb/xhci/ring_buffer.rs b/devices/src/usb/xhci/ring_buffer.rs
new file mode 100644
index 0000000..37afe17
--- /dev/null
+++ b/devices/src/usb/xhci/ring_buffer.rs
@@ -0,0 +1,271 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::xhci_abi::{
+    AddressedTrb, Error as TrbError, LinkTrb, TransferDescriptor, Trb, TrbCast, TrbType,
+};
+use std::fmt::{self, Display};
+use std::mem::size_of;
+use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+#[derive(Debug)]
+pub enum Error {
+    ReadGuestMemory(GuestMemoryError),
+    BadDequeuePointer(GuestAddress),
+    CastTrb(TrbError),
+    TrbChain(TrbError),
+}
+
+type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            ReadGuestMemory(e) => write!(f, "cannot read guest memory: {}", e),
+            BadDequeuePointer(addr) => write!(f, "bad dequeue pointer: {}", addr),
+            CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+            TrbChain(e) => write!(f, "cannot get trb chain bit: {}", e),
+        }
+    }
+}
+
+/// Ring Buffer is segmented circular buffer in guest memory containing work items
+/// called transfer descriptors, each of which consists of one or more TRBs.
+/// Ring buffer logic is shared between transfer ring and command ring.
+/// Transfer Ring management is defined in xHCI spec 4.9.2.
+pub struct RingBuffer {
+    name: String,
+    mem: GuestMemory,
+    dequeue_pointer: GuestAddress,
+    // Used to check if the ring is empty. Toggled when looping back to the begining
+    // of the buffer.
+    consumer_cycle_state: bool,
+}
+
+impl Display for RingBuffer {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "RingBuffer `{}`", self.name)
+    }
+}
+
+// Public interfaces for Ring buffer.
+impl RingBuffer {
+    /// Create a new RingBuffer.
+    pub fn new(name: String, mem: GuestMemory) -> Self {
+        RingBuffer {
+            name,
+            mem,
+            dequeue_pointer: GuestAddress(0),
+            consumer_cycle_state: false,
+        }
+    }
+
+    /// Dequeue next transfer descriptor from the transfer ring.
+    pub fn dequeue_transfer_descriptor(&mut self) -> Result<Option<TransferDescriptor>> {
+        let mut td: TransferDescriptor = TransferDescriptor::new();
+        loop {
+            let addressed_trb = match self.get_current_trb()? {
+                Some(t) => t,
+                None => break,
+            };
+
+            match addressed_trb.trb.trb_type() {
+                Ok(TrbType::Link) => {
+                    let link_trb = addressed_trb
+                        .trb
+                        .cast::<LinkTrb>()
+                        .map_err(Error::CastTrb)?;
+                    self.dequeue_pointer = GuestAddress(link_trb.get_ring_segment_pointer());
+                    self.consumer_cycle_state =
+                        self.consumer_cycle_state != link_trb.get_toggle_cycle_bit();
+                    continue;
+                }
+                _ => {}
+            };
+
+            self.dequeue_pointer = match self.dequeue_pointer.checked_add(size_of::<Trb>() as u64) {
+                Some(addr) => addr,
+                None => {
+                    return Err(Error::BadDequeuePointer(self.dequeue_pointer.clone()));
+                }
+            };
+
+            td.push(addressed_trb);
+            if !addressed_trb.trb.get_chain_bit().map_err(Error::TrbChain)? {
+                usb_debug!("trb chain is false returning");
+                break;
+            }
+        }
+        // A valid transfer descriptor contains at least one addressed trb and the last trb has
+        // chain bit != 0.
+        match td.last() {
+            Some(t) => {
+                if t.trb.get_chain_bit().map_err(Error::TrbChain)? {
+                    return Ok(None);
+                }
+            }
+            None => return Ok(None),
+        }
+        Ok(Some(td))
+    }
+
+    /// Set dequeue pointer of the ring buffer.
+    pub fn set_dequeue_pointer(&mut self, addr: GuestAddress) {
+        usb_debug!("{}: set dequeue pointer {:x}", self.name.as_str(), addr.0);
+
+        self.dequeue_pointer = addr;
+    }
+
+    /// Set consumer cycle state of the ring buffer.
+    pub fn set_consumer_cycle_state(&mut self, state: bool) {
+        usb_debug!("{}: set consumer cycle state {}", self.name.as_str(), state);
+        self.consumer_cycle_state = state;
+    }
+
+    // Read trb pointed by dequeue pointer. Does not proceed dequeue pointer.
+    fn get_current_trb(&self) -> Result<Option<AddressedTrb>> {
+        let trb: Trb = self
+            .mem
+            .read_obj_from_addr(self.dequeue_pointer)
+            .map_err(Error::ReadGuestMemory)?;
+        usb_debug!("{}: trb read from memory {:?}", self.name.as_str(), trb);
+        // If cycle bit of trb does not equal consumer cycle state, the ring is empty.
+        // This trb is invalid.
+        if trb.get_cycle_bit() != self.consumer_cycle_state {
+            usb_debug!(
+                "cycle bit does not match, self cycle {}",
+                self.consumer_cycle_state
+            );
+            Ok(None)
+        } else {
+            Ok(Some(AddressedTrb {
+                trb,
+                gpa: self.dequeue_pointer.0,
+            }))
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use usb::xhci::xhci_abi::*;
+
+    #[test]
+    fn ring_test_dequeue() {
+        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());
+
+        // Structure of ring buffer:
+        //  0x100  --> 0x200  --> 0x300
+        //  trb 1  |   trb 3  |   trb 5
+        //  trb 2  |   trb 4  |   trb 6
+        //  l trb  -   l trb  -   l trb to 0x100
+        let mut trb = NormalTrb::new();
+        trb.set_trb_type(TrbType::Normal as u8);
+        trb.set_data_buffer(1);
+        trb.set_chain(1);
+        gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+            .unwrap();
+
+        trb.set_data_buffer(2);
+        gm.write_obj_at_addr(trb, GuestAddress(0x100 + trb_size))
+            .unwrap();
+
+        let mut ltrb = LinkTrb::new();
+        ltrb.set_trb_type(TrbType::Link as u8);
+        ltrb.set_ring_segment_pointer(0x200);
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + 2 * trb_size))
+            .unwrap();
+
+        trb.set_data_buffer(3);
+        gm.write_obj_at_addr(trb, GuestAddress(0x200)).unwrap();
+
+        // Chain bit is false.
+        trb.set_data_buffer(4);
+        trb.set_chain(0);
+        gm.write_obj_at_addr(trb, GuestAddress(0x200 + 1 * trb_size))
+            .unwrap();
+
+        ltrb.set_ring_segment_pointer(0x300);
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x200 + 2 * trb_size))
+            .unwrap();
+
+        trb.set_data_buffer(5);
+        trb.set_chain(1);
+        gm.write_obj_at_addr(trb, GuestAddress(0x300)).unwrap();
+
+        // Chain bit is false.
+        trb.set_data_buffer(6);
+        trb.set_chain(0);
+        gm.write_obj_at_addr(trb, GuestAddress(0x300 + 1 * trb_size))
+            .unwrap();
+
+        ltrb.set_ring_segment_pointer(0x100);
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x300 + 2 * trb_size))
+            .unwrap();
+
+        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(), 4);
+        assert_eq!(descriptor[0].trb.get_parameter(), 1);
+        assert_eq!(descriptor[1].trb.get_parameter(), 2);
+        assert_eq!(descriptor[2].trb.get_parameter(), 3);
+        assert_eq!(descriptor[3].trb.get_parameter(), 4);
+
+        // Read second transfer descriptor.
+        let descriptor = transfer_ring
+            .dequeue_transfer_descriptor()
+            .unwrap()
+            .unwrap();
+        assert_eq!(descriptor.len(), 2);
+        assert_eq!(descriptor[0].trb.get_parameter(), 5);
+        assert_eq!(descriptor[1].trb.get_parameter(), 6);
+    }
+
+    #[test]
+    fn transfer_ring_test_dequeue_failure() {
+        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 as u8);
+        trb.set_data_buffer(1);
+        trb.set_chain(1);
+        gm.write_obj_at_addr(trb.clone(), GuestAddress(0x100))
+            .unwrap();
+
+        trb.set_data_buffer(2);
+        gm.write_obj_at_addr(trb, GuestAddress(0x100 + trb_size))
+            .unwrap();
+
+        let mut ltrb = LinkTrb::new();
+        ltrb.set_trb_type(TrbType::Link as u8);
+        ltrb.set_ring_segment_pointer(0x200);
+        ltrb.set_toggle_cycle(1);
+        gm.write_obj_at_addr(ltrb, GuestAddress(0x100 + 2 * trb_size))
+            .unwrap();
+
+        trb.set_data_buffer(3);
+        gm.write_obj_at_addr(trb, GuestAddress(0x200)).unwrap();
+
+        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();
+        assert_eq!(descriptor.is_none(), true);
+    }
+
+}