diff options
author | Jingkui Wang <jkwang@google.com> | 2019-03-08 00:59:44 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-03-16 15:25:23 -0700 |
commit | 874f2e83ed9ad5eaafa0d9b434e2083bdda86e62 (patch) | |
tree | ffa4c38ecc2d17329c7e58233430812534024884 /devices/src/usb | |
parent | bbd77ff220bebdd296420909ba2c0fc38620973b (diff) | |
download | crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar.gz crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar.bz2 crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar.lz crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar.xz crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.tar.zst crosvm-874f2e83ed9ad5eaafa0d9b434e2083bdda86e62.zip |
usb: Add device slot, command ring and transfer ring
Device can be assigned to slot. Command ring handles usb commands, transfer ring handles usb transfers. CQ-DEPEND=CL:1510819 BUG=chromium:831850 TEST=cargo test Change-Id: Ib0836ee518d1c7a3e902630c7ea04e29b9496c80 Reviewed-on: https://chromium-review.googlesource.com/1510820 Commit-Ready: Jingkui Wang <jkwang@google.com> Tested-by: kokoro <noreply+kokoro@google.com> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/usb')
-rw-r--r-- | devices/src/usb/xhci/command_ring_controller.rs | 396 | ||||
-rw-r--r-- | devices/src/usb/xhci/device_slot.rs | 785 | ||||
-rw-r--r-- | devices/src/usb/xhci/mod.rs | 3 | ||||
-rw-r--r-- | devices/src/usb/xhci/ring_buffer_stop_cb.rs | 16 | ||||
-rw-r--r-- | devices/src/usb/xhci/transfer_ring_controller.rs | 88 |
5 files changed, 1288 insertions, 0 deletions
diff --git a/devices/src/usb/xhci/command_ring_controller.rs b/devices/src/usb/xhci/command_ring_controller.rs new file mode 100644 index 0000000..0ad5d82 --- /dev/null +++ b/devices/src/usb/xhci/command_ring_controller.rs @@ -0,0 +1,396 @@ +// 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::device_slot::{DeviceSlot, DeviceSlots, Error as DeviceSlotError}; +use super::interrupter::{Error as InterrupterError, Interrupter}; +use super::ring_buffer_controller::{ + Error as RingBufferControllerError, RingBufferController, TransferDescriptorHandler, +}; +use super::xhci_abi::{ + AddressDeviceCommandTrb, AddressedTrb, ConfigureEndpointCommandTrb, DisableSlotCommandTrb, + Error as TrbError, EvaluateContextCommandTrb, ResetDeviceCommandTrb, + SetTRDequeuePointerCommandTrb, StopEndpointCommandTrb, TransferDescriptor, TrbCast, + TrbCompletionCode, TrbType, +}; +use super::xhci_regs::{valid_slot_id, MAX_SLOTS}; +use std::fmt::{self, Display}; +use std::sync::Arc; +use sync::Mutex; +use sys_util::{Error as SysError, EventFd, GuestAddress, GuestMemory}; +use utils::EventLoop; + +#[derive(Debug)] +pub enum Error { + WriteEventFd(SysError), + SendInterrupt(InterrupterError), + CastTrb(TrbError), + BadSlotId(u8), + StopEndpoint(DeviceSlotError), + ConfigEndpoint(DeviceSlotError), + SetAddress(DeviceSlotError), + EvaluateContext(DeviceSlotError), + DisableSlot(DeviceSlotError), + ResetSlot(DeviceSlotError), +} + +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 { + WriteEventFd(e) => write!(f, "failed to write event fd: {}", e), + SendInterrupt(e) => write!(f, "failed to send interrupt: {}", e), + CastTrb(e) => write!(f, "failed to cast trb: {}", e), + BadSlotId(id) => write!(f, "bad slot id: {}", id), + StopEndpoint(e) => write!(f, "failed to stop endpoint: {}", e), + ConfigEndpoint(e) => write!(f, "failed to config endpoint: {}", e), + SetAddress(e) => write!(f, "failed to set address: {}", e), + EvaluateContext(e) => write!(f, "failed to evaluate context: {}", e), + DisableSlot(e) => write!(f, "failed to disable slot: {}", e), + ResetSlot(e) => write!(f, "failed to reset slot: {}", e), + } + } +} + +pub type CommandRingController = RingBufferController<CommandRingTrbHandler>; +pub type CommandRingControllerError = RingBufferControllerError; + +impl CommandRingController { + pub fn new( + mem: GuestMemory, + event_loop: Arc<EventLoop>, + slots: DeviceSlots, + interrupter: Arc<Mutex<Interrupter>>, + ) -> std::result::Result<Arc<CommandRingController>, RingBufferControllerError> { + RingBufferController::new_with_handler( + String::from("command ring"), + mem, + event_loop, + CommandRingTrbHandler::new(slots, interrupter), + ) + } +} + +pub struct CommandRingTrbHandler { + slots: DeviceSlots, + interrupter: Arc<Mutex<Interrupter>>, +} + +impl CommandRingTrbHandler { + fn new(slots: DeviceSlots, interrupter: Arc<Mutex<Interrupter>>) -> Self { + CommandRingTrbHandler { slots, interrupter } + } + + fn slot(&self, slot_id: u8) -> Result<Arc<DeviceSlot>> { + self.slots.slot(slot_id).ok_or(Error::BadSlotId(slot_id)) + } + + fn command_completion_callback( + interrupter: &Arc<Mutex<Interrupter>>, + completion_code: TrbCompletionCode, + slot_id: u8, + trb_addr: u64, + event_fd: &EventFd, + ) -> Result<()> { + interrupter + .lock() + .send_command_completion_trb(completion_code, slot_id, GuestAddress(trb_addr)) + .map_err(Error::SendInterrupt)?; + event_fd.write(1).map_err(Error::WriteEventFd) + } + + fn enable_slot(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + for slot_id in 1..=MAX_SLOTS { + if self.slot(slot_id)?.enable() { + return CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::Success, + slot_id, + atrb.gpa, + &event_fd, + ); + } + } + + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::NoSlotsAvailableError, + 0, + atrb.gpa, + &event_fd, + ) + } + + fn disable_slot(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<DisableSlotCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + if valid_slot_id(slot_id) { + let gpa = atrb.gpa; + let interrupter = self.interrupter.clone(); + self.slots + .disable_slot(slot_id, move |completion_code| { + CommandRingTrbHandler::command_completion_callback( + &interrupter, + completion_code, + slot_id, + gpa, + &event_fd, + ) + .map_err(|e| { + error!("failed to run command completion callback: {}", e); + () + }) + }) + .map_err(Error::DisableSlot) + } else { + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::TrbError, + slot_id, + atrb.gpa, + &event_fd, + ) + } + } + + fn address_device(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<AddressDeviceCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + let completion_code = { + if valid_slot_id(slot_id) { + self.slot(slot_id)? + .set_address(trb) + .map_err(Error::SetAddress)? + } else { + TrbCompletionCode::TrbError + } + }; + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + completion_code, + slot_id, + atrb.gpa, + &event_fd, + ) + } + + fn configure_endpoint(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<ConfigureEndpointCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + let completion_code = { + if valid_slot_id(slot_id) { + self.slot(slot_id)? + .configure_endpoint(trb) + .map_err(Error::ConfigEndpoint)? + } else { + TrbCompletionCode::TrbError + } + }; + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + completion_code, + slot_id, + atrb.gpa, + &event_fd, + ) + } + + fn evaluate_context(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<EvaluateContextCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + let completion_code = { + if valid_slot_id(slot_id) { + self.slot(slot_id)? + .evaluate_context(trb) + .map_err(Error::EvaluateContext)? + } else { + TrbCompletionCode::TrbError + } + }; + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + completion_code, + slot_id, + atrb.gpa, + &event_fd, + ) + } + + fn reset_device(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<ResetDeviceCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + if valid_slot_id(slot_id) { + let gpa = atrb.gpa; + let interrupter = self.interrupter.clone(); + self.slots + .reset_slot(slot_id, move |completion_code| { + CommandRingTrbHandler::command_completion_callback( + &interrupter, + completion_code, + slot_id, + gpa, + &event_fd, + ) + .map_err(|e| { + error!("command completion callback failed: {}", e); + () + }) + }) + .map_err(Error::ResetSlot) + } else { + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::TrbError, + slot_id, + atrb.gpa, + &event_fd, + ) + } + } + + fn stop_endpoint(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<StopEndpointCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + let endpoint_id = trb.get_endpoint_id(); + if valid_slot_id(slot_id) { + let gpa = atrb.gpa; + let interrupter = self.interrupter.clone(); + self.slots + .stop_endpoint(slot_id, endpoint_id, move |completion_code| { + CommandRingTrbHandler::command_completion_callback( + &interrupter, + completion_code, + slot_id, + gpa, + &event_fd, + ) + .map_err(|e| { + error!("command completion callback failed: {}", e); + () + }) + }) + .map_err(Error::StopEndpoint)?; + Ok(()) + } else { + error!("stop endpoint trb has invalid slot id {}", slot_id); + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::TrbError, + slot_id, + atrb.gpa, + &event_fd, + ) + } + } + + fn set_tr_dequeue_ptr(&self, atrb: &AddressedTrb, event_fd: EventFd) -> Result<()> { + let trb = atrb + .trb + .cast::<SetTRDequeuePointerCommandTrb>() + .map_err(Error::CastTrb)?; + let slot_id = trb.get_slot_id(); + let endpoint_id = trb.get_trb_type(); + // See Set TR Dequeue Pointer Trb in spec. + let dequeue_ptr = trb.get_dequeue_ptr() << 4; + let completion_code = { + if valid_slot_id(slot_id) { + self.slot(slot_id)? + .set_tr_dequeue_ptr(endpoint_id, dequeue_ptr) + } else { + error!("stop endpoint trb has invalid slot id {}", slot_id); + TrbCompletionCode::TrbError + } + }; + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + completion_code, + slot_id, + atrb.gpa, + &event_fd, + ) + } +} + +impl TransferDescriptorHandler for CommandRingTrbHandler { + fn handle_transfer_descriptor( + &self, + descriptor: TransferDescriptor, + complete_event: EventFd, + ) -> std::result::Result<(), ()> { + // Command descriptor always consist of a single TRB. + assert_eq!(descriptor.len(), 1); + let atrb = &descriptor[0]; + let command_result = match atrb.trb.trb_type() { + Ok(TrbType::EnableSlotCommand) => self.enable_slot(atrb, complete_event), + Ok(TrbType::DisableSlotCommand) => self.disable_slot(atrb, complete_event), + Ok(TrbType::AddressDeviceCommand) => self.address_device(atrb, complete_event), + Ok(TrbType::ConfigureEndpointCommand) => self.configure_endpoint(atrb, complete_event), + Ok(TrbType::EvaluateContextCommand) => self.evaluate_context(atrb, complete_event), + Ok(TrbType::ResetDeviceCommand) => self.reset_device(atrb, complete_event), + Ok(TrbType::NoopCommand) => CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::Success, + 0, + atrb.gpa, + &complete_event, + ), + Ok(TrbType::ResetEndpointCommand) => { + error!( + "Receiving reset endpoint command. \ + It should only happen when cmd ring stall" + ); + CommandRingTrbHandler::command_completion_callback( + &self.interrupter, + TrbCompletionCode::TrbError, + 0, + atrb.gpa, + &complete_event, + ) + } + Ok(TrbType::StopEndpointCommand) => self.stop_endpoint(atrb, complete_event), + Ok(TrbType::SetTRDequeuePointerCommand) => { + self.set_tr_dequeue_ptr(atrb, complete_event) + } + _ => { + warn!( + // We are not handling type 14,15,16. See table 6.4.6. + "Unexpected command ring trb type: {}", + atrb.trb.get_trb_type() + ); + match self.interrupter.lock().send_command_completion_trb( + TrbCompletionCode::TrbError, + 0, + GuestAddress(atrb.gpa), + ) { + Err(e) => Err(Error::SendInterrupt(e)), + Ok(_) => complete_event.write(1).map_err(Error::WriteEventFd), + } + } + }; + command_result.map_err(|e| { + error!("command failed: {}", e); + () + }) + } +} diff --git a/devices/src/usb/xhci/device_slot.rs b/devices/src/usb/xhci/device_slot.rs new file mode 100644 index 0000000..fcac44d --- /dev/null +++ b/devices/src/usb/xhci/device_slot.rs @@ -0,0 +1,785 @@ +// 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::interrupter::Interrupter; +use super::transfer_ring_controller::{TransferRingController, TransferRingControllerError}; +use super::usb_hub::{self, UsbHub}; +use super::xhci_abi::{ + AddressDeviceCommandTrb, ConfigureEndpointCommandTrb, DeviceContext, DeviceSlotState, + EndpointContext, EndpointState, Error as TrbError, EvaluateContextCommandTrb, + InputControlContext, SlotContext, TrbCompletionCode, DEVICE_CONTEXT_ENTRY_SIZE, +}; +use super::xhci_regs::{valid_slot_id, MAX_PORTS, MAX_SLOTS}; +use register_space::Register; +use std::fmt::{self, Display}; +use std::mem::size_of; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use sync::Mutex; +use sys_util::{GuestAddress, GuestMemory, GuestMemoryError}; +use usb::xhci::ring_buffer_stop_cb::{fallible_closure, RingBufferStopCallback}; +use utils::{EventLoop, FailHandle}; + +#[derive(Debug)] +pub enum Error { + BadPortId(u8), + ReadGuestMemory(GuestMemoryError), + WriteGuestMemory(GuestMemoryError), + WeakReferenceUpgrade, + CallbackFailed, + GetSlotContextState(TrbError), + GetPort(u8), + GetTrc(u8), + BadInputContextAddr(GuestAddress), + BadDeviceContextAddr(GuestAddress), + CreateTransferController(TransferRingControllerError), +} + +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 { + BadPortId(id) => write!(f, "device slot get a bad port id: {}", id), + ReadGuestMemory(e) => write!(f, "failed to read guest memory: {}", e), + WriteGuestMemory(e) => write!(f, "failed to write guest memory: {}", e), + WeakReferenceUpgrade => write!(f, "failed to upgrade weak reference"), + CallbackFailed => write!(f, "callback failed"), + GetSlotContextState(e) => write!(f, "failed to get slot context state: {}", e), + GetPort(v) => write!(f, "failed to get port: {}", v), + GetTrc(v) => write!(f, "failed to get trc: {}", v), + BadInputContextAddr(addr) => write!(f, "bad input context address: {}", addr), + BadDeviceContextAddr(addr) => write!(f, "bad device context: {}", addr), + CreateTransferController(e) => write!(f, "failed to create transfer controller: {}", e), + } + } +} + +/// See spec 4.5.1 for dci. +/// index 0: Control endpoint. Device Context Index: 1. +/// index 1: Endpoint 1 out. Device Context Index: 2 +/// index 2: Endpoint 1 in. Device Context Index: 3. +/// index 3: Endpoint 2 out. Device Context Index: 4 +/// ... +/// index 30: Endpoint 15 in. Device Context Index: 31 +pub const TRANSFER_RING_CONTROLLERS_INDEX_END: usize = 31; +/// End of device context index. +pub const DCI_INDEX_END: u8 = (TRANSFER_RING_CONTROLLERS_INDEX_END + 1) as u8; +/// Device context index of first transfer endpoint. +pub const FIRST_TRANSFER_ENDPOINT_DCI: u8 = 2; + +fn valid_endpoint_id(endpoint_id: u8) -> bool { + endpoint_id < DCI_INDEX_END && endpoint_id > 0 +} + +#[derive(Clone)] +pub struct DeviceSlots { + fail_handle: Arc<FailHandle>, + hub: Arc<UsbHub>, + slots: Vec<Arc<DeviceSlot>>, +} + +impl DeviceSlots { + pub fn new( + fail_handle: Arc<FailHandle>, + dcbaap: Register<u64>, + hub: Arc<UsbHub>, + interrupter: Arc<Mutex<Interrupter>>, + event_loop: Arc<EventLoop>, + mem: GuestMemory, + ) -> DeviceSlots { + let mut slots = Vec::new(); + for slot_id in 1..=MAX_SLOTS { + slots.push(Arc::new(DeviceSlot::new( + slot_id, + dcbaap.clone(), + hub.clone(), + interrupter.clone(), + event_loop.clone(), + mem.clone(), + ))); + } + DeviceSlots { + fail_handle, + hub, + slots, + } + } + + /// Note that slot id starts from 1. Slot index start from 0. + pub fn slot(&self, slot_id: u8) -> Option<Arc<DeviceSlot>> { + if valid_slot_id(slot_id) { + Some(self.slots[slot_id as usize - 1].clone()) + } else { + error!( + "trying to index a wrong slot id {}, max slot = {}", + slot_id, MAX_SLOTS + ); + None + } + } + + /// Stop all device slots and reset them. + pub fn stop_all_and_reset<C: FnMut() + 'static + Send>(&self, mut callback: C) { + usb_debug!("stopping all device slots and resetting host hub"); + let slots = self.slots.clone(); + let hub = self.hub.clone(); + let auto_callback = RingBufferStopCallback::new(fallible_closure( + self.fail_handle.clone(), + move || -> std::result::Result<(), usb_hub::Error> { + for slot in &slots { + slot.reset(); + } + hub.reset()?; + callback(); + Ok(()) + }, + )); + self.stop_all(auto_callback); + } + + /// Stop all devices. The auto callback will be executed when all trc is stopped. It could + /// happen asynchronously, if there are any pending transfers. + pub fn stop_all(&self, auto_callback: RingBufferStopCallback) { + for slot in &self.slots { + slot.stop_all_trc(auto_callback.clone()); + } + } + + /// Disable a slot. This might happen asynchronously, if there is any pending transfers. The + /// callback will be invoked when slot is actually disabled. + pub fn disable_slot< + C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send, + >( + &self, + slot_id: u8, + cb: C, + ) -> Result<()> { + usb_debug!("device slot {} is being disabled", slot_id); + DeviceSlot::disable( + self.fail_handle.clone(), + &self.slots[slot_id as usize - 1], + cb, + ) + } + + /// Reset a slot. This is a shortcut call for DeviceSlot::reset_slot. + pub fn reset_slot< + C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send, + >( + &self, + slot_id: u8, + cb: C, + ) -> Result<()> { + usb_debug!("device slot {} is resetting", slot_id); + DeviceSlot::reset_slot( + self.fail_handle.clone(), + &self.slots[slot_id as usize - 1], + cb, + ) + } + + pub fn stop_endpoint< + C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send, + >( + &self, + slot_id: u8, + endpoint_id: u8, + cb: C, + ) -> Result<()> { + self.slots[slot_id as usize - 1].stop_endpoint(self.fail_handle.clone(), endpoint_id, cb) + } +} + +// Usb port id. Valid ids starts from 1, to MAX_PORTS. +struct PortId(Mutex<u8>); + +impl PortId { + fn new() -> Self { + PortId(Mutex::new(0)) + } + + fn set(&self, value: u8) -> Result<()> { + if value < 1 || value > MAX_PORTS { + return Err(Error::BadPortId(value)); + } + *self.0.lock() = value; + Ok(()) + } + + fn reset(&self) { + *self.0.lock() = 0; + } + + fn get(&self) -> Result<(u8)> { + let val = *self.0.lock(); + if val == 0 { + return Err(Error::BadPortId(val)); + } + Ok(val) + } +} + +pub struct DeviceSlot { + slot_id: u8, + port_id: PortId, // Valid port id starts from 1, to MAX_PORTS. + dcbaap: Register<u64>, + hub: Arc<UsbHub>, + interrupter: Arc<Mutex<Interrupter>>, + event_loop: Arc<EventLoop>, + mem: GuestMemory, + enabled: AtomicBool, + transfer_ring_controllers: Mutex<Vec<Option<Arc<TransferRingController>>>>, +} + +impl DeviceSlot { + /// Create a new device slot. + pub fn new( + slot_id: u8, + dcbaap: Register<u64>, + hub: Arc<UsbHub>, + interrupter: Arc<Mutex<Interrupter>>, + event_loop: Arc<EventLoop>, + mem: GuestMemory, + ) -> Self { + let transfer_ring_controllers = vec![None; TRANSFER_RING_CONTROLLERS_INDEX_END]; + DeviceSlot { + slot_id, + port_id: PortId::new(), + dcbaap, + hub, + interrupter, + event_loop, + mem, + enabled: AtomicBool::new(false), + transfer_ring_controllers: Mutex::new(transfer_ring_controllers), + } + } + + fn get_trc(&self, i: usize) -> Option<Arc<TransferRingController>> { + let trcs = self.transfer_ring_controllers.lock(); + trcs[i].clone() + } + + fn set_trc(&self, i: usize, trc: Option<Arc<TransferRingController>>) { + let mut trcs = self.transfer_ring_controllers.lock(); + trcs[i] = trc; + } + + fn trc_len(&self) -> usize { + self.transfer_ring_controllers.lock().len() + } + + /// The arguments are identical to the fields in each doorbell register. The + /// target value: + /// 1: Reserved + /// 2: Control endpoint + /// 3: Endpoint 1 out + /// 4: Endpoint 1 in + /// 5: Endpoint 2 out + /// ... + /// 32: Endpoint 15 in + /// + /// Steam ID will be useful when host controller support streams. + /// The stream ID must be zero for endpoints that do not have streams + /// configured. + /// This function will return false if it fails to trigger transfer ring start. + pub fn ring_doorbell(&self, target: u8, _stream_id: u16) -> Result<bool> { + if !valid_endpoint_id(target) { + error!( + "device slot {}: Invalid target written to doorbell register. target: {}", + self.slot_id, target + ); + return Ok(false); + } + usb_debug!( + "device slot {}: ding-dong. who is that? target = {}", + self.slot_id, + target + ); + // See DCI in spec. + let endpoint_index = (target - 1) as usize; + let transfer_ring_controller = match self.get_trc(endpoint_index) { + Some(tr) => tr, + None => { + error!("Device endpoint is not inited"); + return Ok(false); + } + }; + let context = self.get_device_context()?; + // TODO(jkwang) Refactor the code to have bitfield return enum. + if context.endpoint_context[endpoint_index].get_endpoint_state() + == EndpointState::Running as u8 + { + usb_debug!("endpoint is started, start transfer ring"); + transfer_ring_controller.start(); + } else { + error!("doorbell rung when endpoint is not started"); + } + Ok(true) + } + + /// Enable the slot. This function returns false if it's already enabled. + pub fn enable(&self) -> bool { + let was_already_enabled = self.enabled.swap(true, Ordering::SeqCst); + if was_already_enabled { + error!("device slot is already enabled"); + } else { + usb_debug!("device slot {} enabled", self.slot_id); + } + !was_already_enabled + } + + /// Disable this device slot. If the slot is not enabled, callback will be invoked immediately + /// with error. Otherwise, callback will be invoked when all trc is stopped. + pub fn disable<C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send>( + fail_handle: Arc<FailHandle>, + slot: &Arc<DeviceSlot>, + mut callback: C, + ) -> Result<()> { + if slot.enabled.load(Ordering::SeqCst) { + let slot_weak = Arc::downgrade(slot); + let auto_callback = + RingBufferStopCallback::new(fallible_closure(fail_handle, move || { + // Slot should still be alive when the callback is invoked. If it's not, there + // must be a bug somewhere. + let slot = slot_weak.upgrade().ok_or(Error::WeakReferenceUpgrade)?; + let mut device_context = slot.get_device_context()?; + device_context + .slot_context + .set_state(DeviceSlotState::DisabledOrEnabled); + slot.set_device_context(device_context)?; + slot.reset(); + usb_debug!( + "device slot {}: all trc disabled, sending trb", + slot.slot_id + ); + callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed) + })); + slot.stop_all_trc(auto_callback); + Ok(()) + } else { + callback(TrbCompletionCode::SlotNotEnabledError).map_err(|_| Error::CallbackFailed) + } + } + + // Assigns the device address and initializes slot and endpoint 0 context. + pub fn set_address(&self, trb: &AddressDeviceCommandTrb) -> Result<TrbCompletionCode> { + if !self.enabled.load(Ordering::SeqCst) { + error!( + "trying to set address to a disabled device slot {}", + self.slot_id + ); + return Ok(TrbCompletionCode::SlotNotEnabledError); + } + let device_context = self.get_device_context()?; + let state = device_context + .slot_context + .state() + .map_err(Error::GetSlotContextState)?; + match state { + DeviceSlotState::DisabledOrEnabled => {} + DeviceSlotState::Default if trb.get_block_set_address_request() == 0 => {} + _ => { + error!("slot {} has unexpected slot state", self.slot_id); + return Ok(TrbCompletionCode::ContextStateError); + } + } + + // Copy all fields of the slot context and endpoint 0 context from the input context + // to the output context. + let input_context_ptr = GuestAddress(trb.get_input_context_pointer()); + // Copy slot context. + self.copy_context(input_context_ptr, 0)?; + // Copy control endpoint context. + self.copy_context(input_context_ptr, 1)?; + + // Read back device context. + let mut device_context = self.get_device_context()?; + let port_id = device_context.slot_context.get_root_hub_port_number(); + self.port_id.set(port_id)?; + usb_debug!( + "port id {} is assigned to slot id {}", + port_id, + self.slot_id + ); + + // Initialize the control endpoint. Endpoint id = 1. + self.set_trc( + 0, + Some( + TransferRingController::new( + self.mem.clone(), + self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?, + self.event_loop.clone(), + self.interrupter.clone(), + self.slot_id, + 1, + ) + .map_err(Error::CreateTransferController)?, + ), + ); + + // Assign slot ID as device address if block_set_address_request is not set. + if trb.get_block_set_address_request() > 0 { + device_context + .slot_context + .set_state(DeviceSlotState::Default); + } else { + let port = self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?; + match *port.get_backend_device() { + Some(ref mut backend) => { + backend.set_address(self.slot_id as u32); + } + None => { + return Ok(TrbCompletionCode::TransactionError); + } + } + + device_context + .slot_context + .set_usb_device_address(self.slot_id); + device_context + .slot_context + .set_state(DeviceSlotState::Addressed); + } + + // TODO(jkwang) trc should always exists. Fix this. + self.get_trc(0) + .ok_or(Error::GetTrc(0))? + .set_dequeue_pointer(GuestAddress( + device_context.endpoint_context[0].get_tr_dequeue_pointer() << 4, + )); + + self.get_trc(0) + .ok_or(Error::GetTrc(0))? + .set_consumer_cycle_state( + device_context.endpoint_context[0].get_dequeue_cycle_state() > 0, + ); + + usb_debug!("Setting endpoint 0 to running"); + device_context.endpoint_context[0].set_state(EndpointState::Running); + self.set_device_context(device_context)?; + Ok(TrbCompletionCode::Success) + } + + // Adds or drops multiple endpoints in the device slot. + pub fn configure_endpoint( + &self, + trb: &ConfigureEndpointCommandTrb, + ) -> Result<TrbCompletionCode> { + usb_debug!("configuring endpoint"); + let input_control_context = if trb.get_deconfigure() > 0 { + // From section 4.6.6 of the xHCI spec: + // Setting the deconfigure (DC) flag to '1' in the Configure Endpoint Command + // TRB is equivalent to setting Input Context Drop Context flags 2-31 to '1' + // and Add Context 2-31 flags to '0'. + let mut c = InputControlContext::new(); + c.set_add_context_flags(0); + c.set_drop_context_flags(0xfffffffc); + c + } else { + self.mem + .read_obj_from_addr(GuestAddress(trb.get_input_context_pointer())) + .map_err(Error::ReadGuestMemory)? + }; + + for device_context_index in 1..DCI_INDEX_END { + if input_control_context.drop_context_flag(device_context_index) { + self.drop_one_endpoint(device_context_index)?; + } + if input_control_context.add_context_flag(device_context_index) { + self.copy_context( + GuestAddress(trb.get_input_context_pointer()), + device_context_index, + )?; + self.add_one_endpoint(device_context_index)?; + } + } + + if trb.get_deconfigure() > 0 { + self.set_state(DeviceSlotState::Addressed)?; + } else { + self.set_state(DeviceSlotState::Configured)?; + } + Ok(TrbCompletionCode::Success) + } + + // Evaluates the device context by reading new values for certain fields of + // the slot context and/or control endpoint context. + pub fn evaluate_context(&self, trb: &EvaluateContextCommandTrb) -> Result<TrbCompletionCode> { + if !self.enabled.load(Ordering::SeqCst) { + return Ok(TrbCompletionCode::SlotNotEnabledError); + } + + let device_context = self.get_device_context()?; + let state = device_context + .slot_context + .state() + .map_err(Error::GetSlotContextState)?; + if state == DeviceSlotState::Default + || state == DeviceSlotState::Addressed + || state == DeviceSlotState::Configured + { + error!( + "wrong context state on evaluate context. state = {:?}", + state + ); + return Ok(TrbCompletionCode::ContextStateError); + } + + // TODO(jkwang) verify this + // The spec has multiple contradictions about validating context parameters in sections + // 4.6.7, 6.2.3.3. To keep things as simple as possible we do no further validation here. + let input_control_context: InputControlContext = self + .mem + .read_obj_from_addr(GuestAddress(trb.get_input_context_pointer())) + .map_err(Error::ReadGuestMemory)?; + + let mut device_context = self.get_device_context()?; + if input_control_context.add_context_flag(0) { + let input_slot_context: SlotContext = self + .mem + .read_obj_from_addr(GuestAddress( + trb.get_input_context_pointer() + DEVICE_CONTEXT_ENTRY_SIZE as u64, + )) + .map_err(Error::ReadGuestMemory)?; + device_context + .slot_context + .set_interrupter_target(input_slot_context.get_interrupter_target()); + + device_context + .slot_context + .set_max_exit_latency(input_slot_context.get_max_exit_latency()); + } + + // From 6.2.3.3: "Endpoint Contexts 2 throught 31 shall not be evaluated by the Evaluate + // Context Command". + if input_control_context.add_context_flag(1) { + let ep0_context: EndpointContext = self + .mem + .read_obj_from_addr(GuestAddress( + trb.get_input_context_pointer() + 2 * DEVICE_CONTEXT_ENTRY_SIZE as u64, + )) + .map_err(Error::ReadGuestMemory)?; + device_context.endpoint_context[0] + .set_max_packet_size(ep0_context.get_max_packet_size()); + } + self.set_device_context(device_context)?; + Ok(TrbCompletionCode::Success) + } + + /// Reset the device slot to default state and deconfigures all but the + /// control endpoint. + pub fn reset_slot< + C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send, + >( + fail_handle: Arc<FailHandle>, + slot: &Arc<DeviceSlot>, + mut callback: C, + ) -> Result<()> { + let state = slot.state()?; + if state != DeviceSlotState::Addressed && state != DeviceSlotState::Configured { + error!("reset slot failed due to context state error {:?}", state); + return callback(TrbCompletionCode::ContextStateError) + .map_err(|_| Error::CallbackFailed); + } + + let weak_s = Arc::downgrade(&slot); + let auto_callback = + RingBufferStopCallback::new(fallible_closure(fail_handle, move || -> Result<()> { + let s = weak_s.upgrade().ok_or(Error::WeakReferenceUpgrade)?; + for i in FIRST_TRANSFER_ENDPOINT_DCI..DCI_INDEX_END { + s.drop_one_endpoint(i)?; + } + let mut ctx = s.get_device_context()?; + ctx.slot_context.set_state(DeviceSlotState::Default); + ctx.slot_context.set_context_entries(1); + ctx.slot_context.set_root_hub_port_number(0); + s.set_device_context(ctx)?; + callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)?; + Ok(()) + })); + slot.stop_all_trc(auto_callback); + Ok(()) + } + + /// Stop all transfer ring controllers. + pub fn stop_all_trc(&self, auto_callback: RingBufferStopCallback) { + for i in 0..self.trc_len() { + if let Some(trc) = self.get_trc(i) { + trc.stop(auto_callback.clone()); + } + } + } + + /// Stop a endpoint. + pub fn stop_endpoint< + C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send, + >( + &self, + fail_handle: Arc<FailHandle>, + endpoint_id: u8, + mut cb: C, + ) -> Result<()> { + if !valid_endpoint_id(endpoint_id) { + error!("trb indexing wrong endpoint id"); + return cb(TrbCompletionCode::TrbError).map_err(|_| Error::CallbackFailed); + } + let index = endpoint_id - 1; + match self.get_trc(index as usize) { + Some(trc) => { + usb_debug!("stopping endpoint"); + let auto_cb = RingBufferStopCallback::new(fallible_closure( + fail_handle, + move || -> Result<()> { + cb(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed) + }, + )); + trc.stop(auto_cb); + } + None => { + error!("endpoint at index {} is not started", index); + cb(TrbCompletionCode::ContextStateError).map_err(|_| Error::CallbackFailed)?; + } + } + Ok(()) + } + + /// Set transfer ring dequeue pointer. + pub fn set_tr_dequeue_ptr(&self, endpoint_id: u8, ptr: u64) -> TrbCompletionCode { + if !valid_endpoint_id(endpoint_id) { + error!("trb indexing wrong endpoint id"); + return TrbCompletionCode::TrbError; + } + let index = endpoint_id - 1; + match self.get_trc(index as usize) { + Some(trc) => { + trc.set_dequeue_pointer(GuestAddress(ptr)); + TrbCompletionCode::Success + } + None => { + error!("set tr dequeue ptr failed due to no trc started"); + TrbCompletionCode::ContextStateError + } + } + } + + // Reset and reset_slot are different. + // Reset_slot handles command ring `reset slot` command. It will reset the slot state. + // Reset handles xhci reset. It will destroy everything. + fn reset(&self) { + for i in 0..self.trc_len() { + self.set_trc(i, None); + } + usb_debug!("reseting device slot {}!", self.slot_id); + self.enabled.store(false, Ordering::SeqCst); + self.port_id.reset(); + } + + fn add_one_endpoint(&self, device_context_index: u8) -> Result<()> { + usb_debug!( + "adding one endpoint, device context index {}", + device_context_index + ); + let mut device_context = self.get_device_context()?; + let transfer_ring_index = (device_context_index - 1) as usize; + let trc = TransferRingController::new( + self.mem.clone(), + self.hub + .get_port(self.port_id.get()?) + .ok_or(Error::GetPort(self.port_id.get()?))?, + self.event_loop.clone(), + self.interrupter.clone(), + self.slot_id, + device_context_index, + ) + .map_err(Error::CreateTransferController)?; + trc.set_dequeue_pointer(GuestAddress( + device_context.endpoint_context[transfer_ring_index].get_tr_dequeue_pointer() << 4, + )); + trc.set_consumer_cycle_state( + device_context.endpoint_context[transfer_ring_index].get_dequeue_cycle_state() > 0, + ); + self.set_trc(transfer_ring_index, Some(trc)); + device_context.endpoint_context[transfer_ring_index].set_state(EndpointState::Running); + self.set_device_context(device_context) + } + + fn drop_one_endpoint(&self, device_context_index: u8) -> Result<()> { + let endpoint_index = (device_context_index - 1) as usize; + self.set_trc(endpoint_index, None); + let mut ctx = self.get_device_context()?; + ctx.endpoint_context[endpoint_index].set_state(EndpointState::Disabled); + self.set_device_context(ctx) + } + + fn get_device_context(&self) -> Result<DeviceContext> { + self.mem + .read_obj_from_addr(self.get_device_context_addr()?) + .map_err(Error::ReadGuestMemory) + } + + fn set_device_context(&self, device_context: DeviceContext) -> Result<()> { + self.mem + .write_obj_at_addr(device_context, self.get_device_context_addr()?) + .map_err(Error::WriteGuestMemory) + } + + fn copy_context( + &self, + input_context_ptr: GuestAddress, + device_context_index: u8, + ) -> Result<()> { + // Note that it could be slot context or device context. They have the same size. Won't + // make a difference here. + let ctx: EndpointContext = self + .mem + .read_obj_from_addr( + input_context_ptr + .checked_add( + (device_context_index as u64 + 1) * DEVICE_CONTEXT_ENTRY_SIZE as u64, + ) + .ok_or(Error::BadInputContextAddr(input_context_ptr.clone()))?, + ) + .map_err(Error::ReadGuestMemory)?; + usb_debug!("context being copied {:?}", ctx); + let device_context_ptr = self.get_device_context_addr()?; + self.mem + .write_obj_at_addr( + ctx, + device_context_ptr + .checked_add(device_context_index as u64 * DEVICE_CONTEXT_ENTRY_SIZE as u64) + .ok_or(Error::BadDeviceContextAddr(device_context_ptr.clone()))?, + ) + .map_err(Error::WriteGuestMemory) + } + + fn get_device_context_addr(&self) -> Result<GuestAddress> { + let addr: u64 = self + .mem + .read_obj_from_addr(GuestAddress( + self.dcbaap.get_value() + size_of::<u64>() as u64 * self.slot_id as u64, + )) + .map_err(Error::ReadGuestMemory)?; + Ok(GuestAddress(addr)) + } + + // Returns the current state of the device slot. + fn state(&self) -> Result<DeviceSlotState> { + let context = self.get_device_context()?; + context + .slot_context + .state() + .map_err(Error::GetSlotContextState) + } + + fn set_state(&self, state: DeviceSlotState) -> Result<()> { + let mut ctx = self.get_device_context()?; + ctx.slot_context.set_state(state); + self.set_device_context(ctx) + } +} diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index d2b748c..f181529 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -4,6 +4,8 @@ extern crate usb_util; +mod command_ring_controller; +mod device_slot; mod event_ring; mod interrupter; mod intr_resample_handler; @@ -11,6 +13,7 @@ mod ring_buffer; mod ring_buffer_controller; mod ring_buffer_stop_cb; mod scatter_gather_buffer; +mod transfer_ring_controller; mod usb_hub; mod xhci_abi; mod xhci_abi_schema; diff --git a/devices/src/usb/xhci/ring_buffer_stop_cb.rs b/devices/src/usb/xhci/ring_buffer_stop_cb.rs index 29b3aa1..5a8eae7 100644 --- a/devices/src/usb/xhci/ring_buffer_stop_cb.rs +++ b/devices/src/usb/xhci/ring_buffer_stop_cb.rs @@ -3,6 +3,7 @@ // found in the LICENSE file. use std::sync::{Arc, Mutex}; +use utils::FailHandle; /// RingBufferStopCallback wraps a callback. The callback will be invoked when last instance of /// RingBufferStopCallback and its clones is dropped. @@ -34,6 +35,21 @@ impl Drop for RingBufferStopCallbackInner { } } +/// Helper function to wrap up a closure with fail handle. The fail handle will be triggered if the +/// closure returns an error. +pub fn fallible_closure<E: std::fmt::Display, C: FnMut() -> Result<(), E> + 'static + Send>( + fail_handle: Arc<FailHandle>, + mut callback: C, +) -> impl FnMut() + 'static + Send { + move || match callback() { + Ok(()) => {} + Err(e) => { + error!("callback failed {}", e); + fail_handle.fail(); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/devices/src/usb/xhci/transfer_ring_controller.rs b/devices/src/usb/xhci/transfer_ring_controller.rs new file mode 100644 index 0000000..f5eb4c0 --- /dev/null +++ b/devices/src/usb/xhci/transfer_ring_controller.rs @@ -0,0 +1,88 @@ +// 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 std::sync::Arc; +use sync::Mutex; +use sys_util::{EventFd, GuestMemory}; +use usb::xhci::ring_buffer_controller::{ + Error as RingBufferControllerError, RingBufferController, TransferDescriptorHandler, +}; +use utils::EventLoop; + +use super::interrupter::Interrupter; +use super::usb_hub::UsbPort; +use super::xhci_abi::TransferDescriptor; +use super::xhci_transfer::XhciTransferManager; + +/// Transfer ring controller manages transfer ring. +pub type TransferRingController = RingBufferController<TransferRingTrbHandler>; + +pub type TransferRingControllerError = RingBufferControllerError; + +/// TransferRingTrbHandler handles trbs on transfer ring. +pub struct TransferRingTrbHandler { + mem: GuestMemory, + port: Arc<UsbPort>, + interrupter: Arc<Mutex<Interrupter>>, + slot_id: u8, + endpoint_id: u8, + transfer_manager: XhciTransferManager, +} + +impl TransferDescriptorHandler for TransferRingTrbHandler { + fn handle_transfer_descriptor( + &self, + descriptor: TransferDescriptor, + completion_event: EventFd, + ) -> Result<(), ()> { + let xhci_transfer = self.transfer_manager.create_transfer( + self.mem.clone(), + self.port.clone(), + self.interrupter.clone(), + self.slot_id, + self.endpoint_id, + descriptor, + completion_event, + ); + xhci_transfer.send_to_backend_if_valid().map_err(|e| { + error!("failed to send transfer to backend: {}", e); + () + }) + } + + fn stop(&self) -> bool { + let backend = self.port.get_backend_device(); + if backend.is_some() { + self.transfer_manager.cancel_all(); + true + } else { + false + } + } +} + +impl TransferRingController { + pub fn new( + mem: GuestMemory, + port: Arc<UsbPort>, + event_loop: Arc<EventLoop>, + interrupter: Arc<Mutex<Interrupter>>, + slot_id: u8, + endpoint_id: u8, + ) -> Result<Arc<TransferRingController>, TransferRingControllerError> { + RingBufferController::new_with_handler( + format!("transfer ring slot_{} ep_{}", slot_id, endpoint_id), + mem.clone(), + event_loop, + TransferRingTrbHandler { + mem, + port, + interrupter, + slot_id, + endpoint_id, + transfer_manager: XhciTransferManager::new(), + }, + ) + } +} |