diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2020-01-28 15:13:21 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-02-03 18:12:01 +0000 |
commit | df2bfe30f3c1712427efb1ceab2841cfaafa64fd (patch) | |
tree | 42f32e4f0a372eb5cef44996f176ff61ef858416 | |
parent | 392b73cdbc3c862cf4ffaa0cace19d22077c5648 (diff) | |
download | crosvm-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.rs | 58 |
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); + } } |