summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2020-01-28 15:13:21 -0800
committerCommit Bot <commit-bot@chromium.org>2020-02-03 18:12:01 +0000
commitdf2bfe30f3c1712427efb1ceab2841cfaafa64fd (patch)
tree42f32e4f0a372eb5cef44996f176ff61ef858416
parent392b73cdbc3c862cf4ffaa0cace19d22077c5648 (diff)
downloadcrosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar.gz
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar.bz2
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar.lz
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar.xz
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.tar.zst
crosvm-df2bfe30f3c1712427efb1ceab2841cfaafa64fd.zip
devices: xhci: support TRB Immediate Data bit
Transfer TRBs have a flag that indicates that data is transferred within
the TRB itself instead of as a separate buffer.  Add support for this
type of transfer in the ScatterGatherBuffer implementation.

This fixes USB support when using Linux 5.1+ as the guest kernel, since
it now uses immediate data transfers.

BUG=chromium:1046564
TEST=`adb root` to connected phone on Linux 5.4 guest kernel

Change-Id: I6c37db422ac8e65d10e1a91807b15e903ad614de
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2026262
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
-rw-r--r--devices/src/usb/xhci/scatter_gather_buffer.rs58
1 files changed, 51 insertions, 7 deletions
diff --git a/devices/src/usb/xhci/scatter_gather_buffer.rs b/devices/src/usb/xhci/scatter_gather_buffer.rs
index 4974bc1..f64f778 100644
--- a/devices/src/usb/xhci/scatter_gather_buffer.rs
+++ b/devices/src/usb/xhci/scatter_gather_buffer.rs
@@ -2,7 +2,9 @@
 // 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 super::xhci_abi::{
+    AddressedTrb, Error as TrbError, NormalTrb, TransferDescriptor, TrbCast, TrbType,
+};
 use bit_field::Error as BitFieldError;
 use std::fmt::{self, Display};
 use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
@@ -14,6 +16,7 @@ pub enum Error {
     UnknownTrbType(BitFieldError),
     CastTrb(TrbError),
     BadTrbType(TrbType),
+    ImmediateDataTooLong(usize),
 }
 
 type Result<T> = std::result::Result<T, Error>;
@@ -28,6 +31,7 @@ impl Display for Error {
             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),
+            ImmediateDataTooLong(l) => write!(f, "immediate data longer than allowed: {}", l),
         }
     }
 }
@@ -67,13 +71,30 @@ impl ScatterGatherBuffer {
         Ok(total_len)
     }
 
+    /// Get the guest address and length of the TRB's data buffer.
+    /// This is usually a separate buffer pointed to by the TRB,
+    /// but it can also be within the TRB itself in the case of immediate data.
+    fn get_trb_data(&self, atrb: &AddressedTrb) -> Result<(GuestAddress, usize)> {
+        let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
+        let len = normal_trb.get_trb_transfer_length() as usize;
+        let addr = if normal_trb.get_immediate_data() == 1 {
+            // If the Immediate Data flag is set, the first <= 8 bytes of the TRB hold the data.
+            if len > 8 {
+                return Err(Error::ImmediateDataTooLong(len));
+            }
+            atrb.gpa
+        } else {
+            normal_trb.get_data_buffer()
+        };
+        Ok((GuestAddress(addr), 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 (guest_address, len) = self.get_trb_data(&atrb)?;
             let buffer_len = {
                 if offset == buffer.len() {
                     return Ok(total_size);
@@ -89,7 +110,7 @@ impl ScatterGatherBuffer {
             offset = buffer_end;
             total_size += self
                 .mem
-                .read_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+                .read_at_addr(cur_buffer, guest_address)
                 .map_err(Error::ReadGuestMemory)?;
         }
         Ok(total_size)
@@ -100,8 +121,7 @@ impl ScatterGatherBuffer {
         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 (guest_address, len) = self.get_trb_data(&atrb)?;
             let buffer_len = {
                 if offset == buffer.len() {
                     return Ok(total_size);
@@ -117,7 +137,7 @@ impl ScatterGatherBuffer {
             offset = buffer_end;
             total_size += self
                 .mem
-                .write_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
+                .write_at_addr(cur_buffer, guest_address)
                 .map_err(Error::WriteGuestMemory)?;
         }
         Ok(total_size)
@@ -176,4 +196,28 @@ mod test {
         buffer.read(&mut data_read).unwrap();
         assert_eq!(data_to_write, data_read);
     }
+
+    #[test]
+    fn immediate_data_test() {
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+        let mut td = TransferDescriptor::new();
+
+        let expected_immediate_data: [u8; 8] = [0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D, 0xCA, 0xFE];
+
+        let mut trb = Trb::new();
+        let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
+        ntrb.set_trb_type(TrbType::Normal);
+        ntrb.set_data_buffer(u64::from_le_bytes(expected_immediate_data));
+        ntrb.set_trb_transfer_length(8);
+        ntrb.set_immediate_data(1);
+        td.push(AddressedTrb { trb, gpa: 0xC00 });
+
+        gm.write_obj_at_addr(trb, GuestAddress(0xc00)).unwrap();
+
+        let buffer = ScatterGatherBuffer::new(gm.clone(), td).unwrap();
+
+        let mut data_read = [0; 8];
+        buffer.read(&mut data_read).unwrap();
+        assert_eq!(data_read, expected_immediate_data);
+    }
 }