blob: bd173958f5d2efd97c428d1db61fa50f6753478f [file] [log] [blame]
// 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 crate::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);
}
}