summary refs log tree commit diff
diff options
context:
space:
mode:
authorJingkui Wang <jkwang@google.com>2019-03-07 23:14:06 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-03-13 21:04:58 -0700
commitfa132759424c5019dc9086e53d8db14500fee64f (patch)
tree368521d87a5fed79f8b52262bf018c50b843edeb
parent115fe4e233cd9263c0152bd37db1d7537a109400 (diff)
downloadcrosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar.gz
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar.bz2
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar.lz
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar.xz
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.tar.zst
crosvm-fa132759424c5019dc9086e53d8db14500fee64f.zip
add scatter gather buffer
scatter gather buffer is a buffer with segments.

CQ-DEPEND=CL:1510816
BUG=chromium:831850
TEST=cargo test

Change-Id: Iea54d5b3d6f9194113f6237eb14aafab71ca2ff9
Reviewed-on: https://chromium-review.googlesource.com/1510817
Commit-Ready: Jingkui Wang <jkwang@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Jingkui Wang <jkwang@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--devices/src/usb/xhci/mod.rs1
-rw-r--r--devices/src/usb/xhci/scatter_gather_buffer.rs180
2 files changed, 181 insertions, 0 deletions
diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs
index 5af9be4..57d9523 100644
--- a/devices/src/usb/xhci/mod.rs
+++ b/devices/src/usb/xhci/mod.rs
@@ -5,6 +5,7 @@
 mod event_ring;
 mod interrupter;
 mod intr_resample_handler;
+mod scatter_gather_buffer;
 mod xhci_abi;
 mod xhci_abi_schema;
 mod xhci_regs;
diff --git a/devices/src/usb/xhci/scatter_gather_buffer.rs b/devices/src/usb/xhci/scatter_gather_buffer.rs
new file mode 100644
index 0000000..1dc3ee2
--- /dev/null
+++ b/devices/src/usb/xhci/scatter_gather_buffer.rs
@@ -0,0 +1,180 @@
+// 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::{Error as TrbError, NormalTrb, TransferDescriptor, TrbCast, TrbType};
+use std::fmt::{self, Display};
+use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
+
+#[derive(Debug)]
+pub enum Error {
+    ReadGuestMemory(GuestMemoryError),
+    WriteGuestMemory(GuestMemoryError),
+    UnknownTrbType(TrbError),
+    CastTrb(TrbError),
+    BadTrbType(TrbType),
+}
+
+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),
+            WriteGuestMemory(e) => write!(f, "cannot write guest memory: {}", e),
+            UnknownTrbType(e) => write!(f, "unknown trb type: {}", e),
+            CastTrb(e) => write!(f, "cannot cast trb: {}", e),
+            BadTrbType(t) => write!(f, "should not build buffer from trb type: {:?}", t),
+        }
+    }
+}
+
+/// See xHCI spec 3.2.8 for scatter/gather transfer. It's used in bulk/interrupt transfers. See
+/// 3.2.10 for details.
+pub struct ScatterGatherBuffer {
+    mem: GuestMemory,
+    td: TransferDescriptor,
+}
+
+impl ScatterGatherBuffer {
+    /// Create a new buffer from transfer descriptor.
+    pub fn new(mem: GuestMemory, td: TransferDescriptor) -> Result<ScatterGatherBuffer> {
+        for atrb in &td {
+            let trb_type = atrb.trb.trb_type().map_err(Error::UnknownTrbType)?;
+            if trb_type != TrbType::Normal
+                && trb_type != TrbType::DataStage
+                && trb_type != TrbType::Isoch
+            {
+                return Err(Error::BadTrbType(trb_type));
+            }
+        }
+        Ok(ScatterGatherBuffer { mem, td })
+    }
+
+    /// Total len of this buffer.
+    pub fn len(&self) -> Result<usize> {
+        let mut total_len = 0usize;
+        for atrb in &self.td {
+            total_len += atrb
+                .trb
+                .cast::<NormalTrb>()
+                .map_err(Error::CastTrb)?
+                .get_trb_transfer_length() as usize;
+        }
+        Ok(total_len)
+    }
+
+    /// Read content to buffer, return number of bytes read.
+    pub fn read(&self, buffer: &mut [u8]) -> Result<usize> {
+        let mut total_size = 0usize;
+        let mut offset = 0;
+        for atrb in &self.td {
+            let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
+            let len = normal_trb.get_trb_transfer_length() as usize;
+            let buffer_len = {
+                if offset == buffer.len() {
+                    return Ok(total_size);
+                }
+                if buffer.len() > offset + len {
+                    len
+                } else {
+                    buffer.len() - offset
+                }
+            };
+            let buffer_end = offset + buffer_len;
+            let cur_buffer = &mut buffer[offset..buffer_end];
+            offset = buffer_end;
+            total_size += self
+                .mem
+                .read_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+                .map_err(Error::ReadGuestMemory)?;
+        }
+        Ok(total_size)
+    }
+
+    /// Write content from buffer, return number of bytes written.
+    pub fn write(&self, buffer: &[u8]) -> Result<usize> {
+        let mut total_size = 0usize;
+        let mut offset = 0;
+        for atrb in &self.td {
+            let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
+            let len = normal_trb.get_trb_transfer_length() as usize;
+            let buffer_len = {
+                if offset == buffer.len() {
+                    return Ok(total_size);
+                }
+                if buffer.len() > offset + len {
+                    len
+                } else {
+                    buffer.len() - offset
+                }
+            };
+            let buffer_end = offset + buffer_len;
+            let cur_buffer = &buffer[offset..buffer_end];
+            offset = buffer_end;
+            total_size += self
+                .mem
+                .write_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+                .map_err(Error::WriteGuestMemory)?;
+        }
+        Ok(total_size)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use usb::xhci::xhci_abi::{AddressedTrb, Trb};
+
+    #[test]
+    fn scatter_gather_buffer_test() {
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+        let mut td = TransferDescriptor::new();
+        // In this td, we are going to have scatter buffer at 0x100, length 4, 0x200 length 2 and
+        // 0x300 length 1.
+        let mut trb = Trb::new();
+        {
+            let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+            ntrb.set_trb_type(TrbType::Normal as u8);
+            ntrb.set_data_buffer(0x100);
+            ntrb.set_trb_transfer_length(4);
+        }
+        td.push(AddressedTrb { trb, gpa: 0 });
+        let mut trb = Trb::new();
+        {
+            let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+            ntrb.set_trb_type(TrbType::Normal as u8);
+            ntrb.set_data_buffer(0x200);
+            ntrb.set_trb_transfer_length(2);
+        }
+        td.push(AddressedTrb { trb, gpa: 0 });
+        let mut trb = Trb::new();
+        {
+            let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+            ntrb.set_trb_type(TrbType::Normal as u8);
+            ntrb.set_data_buffer(0x300);
+            ntrb.set_trb_transfer_length(1);
+        }
+        td.push(AddressedTrb { trb, gpa: 0 });
+
+        let buffer = ScatterGatherBuffer::new(gm.clone(), td).unwrap();
+
+        assert_eq!(buffer.len().unwrap(), 7);
+        let data_to_write: [u8; 7] = [7, 6, 5, 4, 3, 2, 1];
+        buffer.write(&data_to_write).unwrap();
+
+        let mut d = [0; 4];
+        gm.read_exact_at_addr(&mut d, GuestAddress(0x100)).unwrap();
+        assert_eq!(d, [7, 6, 5, 4]);;
+        gm.read_exact_at_addr(&mut d, GuestAddress(0x200)).unwrap();
+        assert_eq!(d, [3, 2, 0, 0]);;
+        gm.read_exact_at_addr(&mut d, GuestAddress(0x300)).unwrap();
+        assert_eq!(d, [1, 0, 0, 0]);;
+
+        let mut data_read = [0; 7];
+        buffer.read(&mut data_read).unwrap();
+        assert_eq!(data_to_write, data_read);
+    }
+}