diff options
author | Jingkui Wang <jkwang@google.com> | 2019-03-08 20:41:57 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-03-17 21:33:08 -0700 |
commit | 100e6e48ad292406fb6f0a7eeb85465850bc28c7 (patch) | |
tree | 30f59fcb07b7cbee649a07390cd2397cfb65c463 /devices/src/usb | |
parent | c324429b467d530fbeadef1fc9b527bb23ce1632 (diff) | |
download | crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.gz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.bz2 crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.lz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.xz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.zst crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.zip |
implement xhci and add it to pci bus
Implement xhci controller, setup seccomp filters and add xhci to pci bus. CQ-DEPEND=CL:1512761 BUG=chromium:831850 TEST=local build Change-Id: I5c05452ece66e99d3a670e259e095fca616e835d Reviewed-on: https://chromium-review.googlesource.com/1512762 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Jingkui Wang <jkwang@google.com> Reviewed-by: Jingkui Wang <jkwang@google.com>
Diffstat (limited to 'devices/src/usb')
-rw-r--r-- | devices/src/usb/host_backend/context.rs | 7 | ||||
-rw-r--r-- | devices/src/usb/host_backend/mod.rs | 2 | ||||
-rw-r--r-- | devices/src/usb/mod.rs | 2 | ||||
-rw-r--r-- | devices/src/usb/xhci/mod.rs | 12 | ||||
-rw-r--r-- | devices/src/usb/xhci/xhci.rs | 387 | ||||
-rw-r--r-- | devices/src/usb/xhci/xhci_controller.rs | 277 |
6 files changed, 680 insertions, 7 deletions
diff --git a/devices/src/usb/host_backend/context.rs b/devices/src/usb/host_backend/context.rs index 3fd8d3d..260aad1 100644 --- a/devices/src/usb/host_backend/context.rs +++ b/devices/src/usb/host_backend/context.rs @@ -129,11 +129,14 @@ struct PollfdChangeHandler { impl LibUsbPollfdChangeHandler for PollfdChangeHandler { fn add_poll_fd(&self, fd: RawFd, events: c_short) { - self.event_loop.add_event( + match self.event_loop.add_event( &MaybeOwnedFd::Borrowed(fd), WatchingEvents::new(events as u32), self.event_handler.clone(), - ); + ) { + Err(e) => error!("cannot add event to event loop: {}", e), + Ok(_) => {} + } } fn remove_poll_fd(&self, fd: RawFd) { diff --git a/devices/src/usb/host_backend/mod.rs b/devices/src/usb/host_backend/mod.rs index 8951a08..ea0f44c 100644 --- a/devices/src/usb/host_backend/mod.rs +++ b/devices/src/usb/host_backend/mod.rs @@ -3,7 +3,7 @@ // found in the LICENSE file. pub mod context; -mod error; +pub mod error; pub mod host_backend_device_provider; pub mod host_device; mod hotplug; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 78d93bd..738c693 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -6,5 +6,5 @@ extern crate usb_util; #[macro_use] mod log; -mod host_backend; +pub mod host_backend; pub mod xhci; diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index 3aabdc6..c652e90 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -10,12 +10,18 @@ mod intr_resample_handler; mod ring_buffer; mod ring_buffer_controller; mod ring_buffer_stop_cb; -pub mod scatter_gather_buffer; mod transfer_ring_controller; -pub mod usb_hub; +mod xhci; +#[allow(dead_code)] mod xhci_abi; +#[allow(dead_code)] mod xhci_abi_schema; +#[allow(dead_code)] +mod xhci_regs; + +pub mod scatter_gather_buffer; +pub mod usb_hub; pub mod xhci_backend_device; pub mod xhci_backend_device_provider; -mod xhci_regs; +pub mod xhci_controller; pub mod xhci_transfer; diff --git a/devices/src/usb/xhci/xhci.rs b/devices/src/usb/xhci/xhci.rs new file mode 100644 index 0000000..b69397e --- /dev/null +++ b/devices/src/usb/xhci/xhci.rs @@ -0,0 +1,387 @@ +// 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::command_ring_controller::{CommandRingController, CommandRingControllerError}; +use super::device_slot::{DeviceSlots, Error as DeviceSlotError}; +use super::interrupter::{Error as InterrupterError, Interrupter}; +use super::intr_resample_handler::IntrResampleHandler; +use super::ring_buffer_stop_cb::RingBufferStopCallback; +use super::usb_hub::UsbHub; +use super::xhci_backend_device_provider::XhciBackendDeviceProvider; +use super::xhci_regs::*; +use std::fmt::{self, Display}; +use std::sync::Arc; +use sync::Mutex; +use sys_util::{EventFd, GuestAddress, GuestMemory}; +use usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider; +use utils::{Error as UtilsError, EventLoop, FailHandle}; + +#[derive(Debug)] +pub enum Error { + StartEventLoop(UtilsError), + GetDeviceSlot(u8), + StartResampleHandler, + SendInterrupt(InterrupterError), + EnableInterrupter(InterrupterError), + SetModeration(InterrupterError), + SetupEventRing(InterrupterError), + SetEventHandlerBusy(InterrupterError), + StartProvider, + RingDoorbell(DeviceSlotError), + CreateCommandRingController(CommandRingControllerError), +} + +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 { + StartEventLoop(e) => write!(f, "failed to start event loop: {}", e), + GetDeviceSlot(i) => write!(f, "failed to get device slot: {}", i), + StartResampleHandler => write!(f, "failed to start resample handler"), + SendInterrupt(e) => write!(f, "failed to send interrupter: {}", e), + EnableInterrupter(e) => write!(f, "failed to enable interrupter: {}", e), + SetModeration(e) => write!(f, "failed to set interrupter moderation: {}", e), + SetupEventRing(e) => write!(f, "failed to setup event ring: {}", e), + SetEventHandlerBusy(e) => write!(f, "failed to set event handler busy: {}", e), + StartProvider => write!(f, "failed to start backend provider"), + RingDoorbell(e) => write!(f, "failed to ring doorbell: {}", e), + CreateCommandRingController(e) => { + write!(f, "failed to create command ring controller: {}", e) + } + } + } +} + +/// xHCI controller implementation. +pub struct Xhci { + fail_handle: Arc<FailHandle>, + regs: XhciRegs, + interrupter: Arc<Mutex<Interrupter>>, + command_ring_controller: Arc<CommandRingController>, + device_slots: DeviceSlots, + // resample handler and device provider only lives on EventLoop to handle corresponding events. + // By design, event loop only hold weak reference. We need to keep a strong reference here to + // keep it alive. + #[allow(dead_code)] + intr_resample_handler: Arc<IntrResampleHandler>, + #[allow(dead_code)] + device_provider: HostBackendDeviceProvider, +} + +impl Xhci { + /// Create a new xHCI controller. + pub fn new( + fail_handle: Arc<FailHandle>, + mem: GuestMemory, + device_provider: HostBackendDeviceProvider, + irq_evt: EventFd, + irq_resample_evt: EventFd, + regs: XhciRegs, + ) -> Result<Arc<Self>> { + let (event_loop, _join_handle) = + EventLoop::start(Some(fail_handle.clone())).map_err(Error::StartEventLoop)?; + let interrupter = Arc::new(Mutex::new(Interrupter::new(mem.clone(), irq_evt, ®s))); + let event_loop = Arc::new(event_loop); + let intr_resample_handler = + IntrResampleHandler::start(&event_loop, interrupter.clone(), irq_resample_evt) + .ok_or(Error::StartResampleHandler)?; + let hub = Arc::new(UsbHub::new(®s, interrupter.clone())); + + let mut device_provider = device_provider; + device_provider + .start(fail_handle.clone(), event_loop.clone(), hub.clone()) + .map_err(|_| Error::StartProvider)?; + + let device_slots = DeviceSlots::new( + fail_handle.clone(), + regs.dcbaap.clone(), + hub.clone(), + interrupter.clone(), + event_loop.clone(), + mem.clone(), + ); + let command_ring_controller = CommandRingController::new( + mem.clone(), + event_loop.clone(), + device_slots.clone(), + interrupter.clone(), + ) + .map_err(Error::CreateCommandRingController)?; + let xhci = Arc::new(Xhci { + fail_handle, + regs, + intr_resample_handler, + interrupter, + command_ring_controller, + device_slots, + device_provider, + }); + Self::init_reg_callbacks(&xhci); + Ok(xhci) + } + + fn init_reg_callbacks(xhci: &Arc<Xhci>) { + // All the callbacks will hold a weak reference to avoid memory leak. Thos weak upgrade + // should never fail. + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.usbcmd.set_write_cb(move |val: u32| { + // All the weak reference upgrade should never fail. xhci hold reference to the + // registers, callback won't be invoked if xhci is gone. + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.usbcmd_callback(val); + xhci.handle_register_callback_result(r, 0) + }); + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.crcr.set_write_cb(move |val: u64| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.crcr_callback(val); + xhci.handle_register_callback_result(r, 0) + }); + + for i in 0..xhci.regs.portsc.len() { + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.portsc[i].set_write_cb(move |val: u32| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.portsc_callback(i as u32, val); + xhci.handle_register_callback_result(r, 0) + }); + } + + for i in 0..xhci.regs.doorbells.len() { + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.doorbells[i].set_write_cb(move |val: u32| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.doorbell_callback(i as u32, val); + xhci.handle_register_callback_result(r, ()); + val + }); + } + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.iman.set_write_cb(move |val: u32| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.iman_callback(val); + xhci.handle_register_callback_result(r, ()); + val + }); + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.imod.set_write_cb(move |val: u32| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.imod_callback(val); + xhci.handle_register_callback_result(r, ()); + val + }); + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.erstsz.set_write_cb(move |val: u32| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.erstsz_callback(val); + xhci.handle_register_callback_result(r, ()); + val + }); + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.erstba.set_write_cb(move |val: u64| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.erstba_callback(val); + xhci.handle_register_callback_result(r, ()); + val + }); + + let xhci_weak = Arc::downgrade(xhci); + xhci.regs.erdp.set_write_cb(move |val: u64| { + let xhci = xhci_weak.upgrade().unwrap(); + let r = xhci.erdp_callback(val); + xhci.handle_register_callback_result(r, ()); + val + }); + } + + fn handle_register_callback_result<T>(&self, r: Result<T>, t: T) -> T { + match r { + Ok(v) => v, + Err(e) => { + error!("xhci controller failed: {}", e); + self.fail_handle.fail(); + t + } + } + } + + // Callback for usbcmd register write. + fn usbcmd_callback(&self, value: u32) -> Result<u32> { + if (value & USB_CMD_RESET) > 0 { + usb_debug!("xhci_controller: reset controller"); + self.reset()?; + return Ok(value & (!USB_CMD_RESET)); + } + + if (value & USB_CMD_RUNSTOP) > 0 { + usb_debug!("xhci_controller: clear halt bits"); + self.regs.usbsts.clear_bits(USB_STS_HALTED); + } else { + usb_debug!("xhci_controller: halt device"); + self.halt()?; + self.regs.crcr.clear_bits(CRCR_COMMAND_RING_RUNNING); + } + + // Enable interrupter if needed. + let enabled = (value & USB_CMD_INTERRUPTER_ENABLE) > 0 + && (self.regs.iman.get_value() & IMAN_INTERRUPT_ENABLE) > 0; + usb_debug!("xhci_controller: interrupter enable?: {}", enabled); + self.interrupter + .lock() + .set_enabled(enabled) + .map_err(Error::EnableInterrupter)?; + Ok(value) + } + + // Callback for crcr register write. + fn crcr_callback(&self, value: u64) -> Result<u64> { + usb_debug!("xhci_controller: write to crcr {:x}", value); + let value = if (self.regs.crcr.get_value() & CRCR_COMMAND_RING_RUNNING) == 0 { + self.command_ring_controller + .set_dequeue_pointer(GuestAddress(value & CRCR_COMMAND_RING_POINTER)); + self.command_ring_controller + .set_consumer_cycle_state((value & CRCR_RING_CYCLE_STATE) > 0); + value + } else { + error!("Write to crcr while command ring is running"); + self.regs.crcr.get_value() + }; + Ok(value) + } + + // Callback for portsc register write. + fn portsc_callback(&self, index: u32, value: u32) -> Result<u32> { + let mut value = value; + usb_debug!( + "xhci_controller: write to portsc index {} value {:x}", + index, + value + ); + // xHCI spec 4.19.5. Note: we might want to change this logic if we support USB 3.0. + if (value & PORTSC_PORT_RESET) > 0 || (value & PORTSC_WARM_PORT_RESET) > 0 { + // Libusb onlys support blocking call to reset and "usually incurs a noticeable + // delay.". We are faking a reset now. + value &= !PORTSC_PORT_LINK_STATE_MASK; + value &= !PORTSC_PORT_RESET; + value |= PORTSC_PORT_ENABLED; + value |= PORTSC_PORT_RESET_CHANGE; + self.interrupter + .lock() + .send_port_status_change_trb((index + 1) as u8) + .map_err(Error::SendInterrupt)?; + } + Ok(value) + } + + // Callback for doorbell register write. + fn doorbell_callback(&self, index: u32, value: u32) -> Result<()> { + usb_debug!( + "xhci_controller: write to doorbell index {} value {:x}", + index, + value + ); + let target = (value & DOORBELL_TARGET) as u8; + let stream_id: u16 = (value >> DOORBELL_STREAM_ID_OFFSET) as u16; + if (self.regs.usbcmd.get_value() & USB_CMD_RUNSTOP) > 0 { + // First doorbell is for command ring. + if index == 0 { + if target != 0 || stream_id != 0 { + return Ok(()); + } + usb_debug!("doorbell to command ring"); + self.regs.crcr.set_bits(CRCR_COMMAND_RING_RUNNING); + self.command_ring_controller.start(); + } else { + usb_debug!("doorbell to device slot"); + self.device_slots + .slot(index as u8) + .ok_or(Error::GetDeviceSlot(index as u8))? + .ring_doorbell(target, stream_id) + .map_err(Error::RingDoorbell)?; + } + } + Ok(()) + } + + // Callback for iman register write. + fn iman_callback(&self, value: u32) -> Result<()> { + usb_debug!("xhci_controller: write to iman {:x}", value); + let enabled = ((value & IMAN_INTERRUPT_ENABLE) > 0) + && ((self.regs.usbcmd.get_value() & USB_CMD_INTERRUPTER_ENABLE) > 0); + self.interrupter + .lock() + .set_enabled(enabled) + .map_err(Error::EnableInterrupter) + } + + // Callback for imod register write. + fn imod_callback(&self, value: u32) -> Result<()> { + usb_debug!("xhci_controller: write to imod {:x}", value); + self.interrupter + .lock() + .set_moderation( + (value & IMOD_INTERRUPT_MODERATION_INTERVAL) as u16, + (value >> IMOD_INTERRUPT_MODERATION_COUNTER_OFFSET) as u16, + ) + .map_err(Error::SetModeration) + } + + // Callback for erstsz register write. + fn erstsz_callback(&self, value: u32) -> Result<()> { + usb_debug!("xhci_controller: write to erstz {:x}", value); + self.interrupter + .lock() + .set_event_ring_seg_table_size((value & ERSTSZ_SEGMENT_TABLE_SIZE) as u16) + .map_err(Error::SetupEventRing) + } + + // Callback for erstba register write. + fn erstba_callback(&self, value: u64) -> Result<()> { + usb_debug!("xhci_controller: write to erstba {:x}", value); + self.interrupter + .lock() + .set_event_ring_seg_table_base_addr(GuestAddress( + value & ERSTBA_SEGMENT_TABLE_BASE_ADDRESS, + )) + .map_err(Error::SetupEventRing) + } + + // Callback for erdp register write. + fn erdp_callback(&self, value: u64) -> Result<()> { + usb_debug!("xhci_controller: write to erdp {:x}", value); + let mut interrupter = self.interrupter.lock(); + interrupter + .set_event_ring_dequeue_pointer(GuestAddress(value & ERDP_EVENT_RING_DEQUEUE_POINTER)) + .map_err(Error::SetupEventRing)?; + interrupter + .set_event_handler_busy((value & ERDP_EVENT_HANDLER_BUSY) > 0) + .map_err(Error::SetEventHandlerBusy) + } + + fn reset(&self) -> Result<()> { + self.regs.usbsts.set_bits(USB_STS_CONTROLLER_NOT_READY); + let usbsts = self.regs.usbsts.clone(); + self.device_slots.stop_all_and_reset(move || { + usbsts.clear_bits(USB_STS_CONTROLLER_NOT_READY); + }); + Ok(()) + } + + fn halt(&self) -> Result<()> { + let usbsts = self.regs.usbsts.clone(); + self.device_slots + .stop_all(RingBufferStopCallback::new(move || { + usbsts.set_bits(USB_STS_HALTED); + })); + Ok(()) + } +} diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs new file mode 100644 index 0000000..f920704 --- /dev/null +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -0,0 +1,277 @@ +// 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 pci::{ + PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, + PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass, +}; +use register_space::{Register, RegisterSpace}; +use resources::SystemAllocator; +use std::mem; +use std::os::unix::io::RawFd; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use sys_util::{EventFd, GuestMemory}; +use usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider; +use usb::xhci::xhci::Xhci; +use usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider; +use usb::xhci::xhci_regs::{init_xhci_mmio_space_and_regs, XhciRegs}; +use utils::FailHandle; + +const XHCI_BAR0_SIZE: u64 = 0x10000; + +#[derive(Clone, Copy)] +enum UsbControllerProgrammingInterface { + Usb3HostController = 0x30, +} + +impl PciProgrammingInterface for UsbControllerProgrammingInterface { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +/// Use this handle to fail xhci controller. +pub struct XhciFailHandle { + usbcmd: Register<u32>, + usbsts: Register<u32>, + xhci_failed: AtomicBool, +} + +impl XhciFailHandle { + pub fn new(regs: &XhciRegs) -> XhciFailHandle { + XhciFailHandle { + usbcmd: regs.usbcmd.clone(), + usbsts: regs.usbsts.clone(), + xhci_failed: AtomicBool::new(false), + } + } +} + +impl FailHandle for XhciFailHandle { + /// Fail this controller. Will set related registers and flip failed bool. + fn fail(&self) { + // set run/stop to stop. + const USBCMD_STOPPED: u32 = 0; + // Set host system error bit. + const USBSTS_HSE: u32 = 1 << 2; + self.usbcmd.set_value(USBCMD_STOPPED); + self.usbsts.set_value(USBSTS_HSE); + + self.xhci_failed.store(true, Ordering::SeqCst); + error!("xhci controller stopped working"); + } + + /// Returns true if xhci is already failed. + fn failed(&self) -> bool { + self.xhci_failed.load(Ordering::SeqCst) + } +} + +// Xhci controller should be created with backend device provider. Then irq should be assigned +// before initialized. We are not making `failed` as a state here to optimize performance. Cause we +// need to set failed in other threads. +enum XhciControllerState { + Unknown, + Created { + device_provider: HostBackendDeviceProvider, + }, + IrqAssigned { + device_provider: HostBackendDeviceProvider, + irq_evt: EventFd, + irq_resample_evt: EventFd, + }, + Initialized { + mmio: RegisterSpace, + // Xhci init could fail. + #[allow(dead_code)] + xhci: Option<Arc<Xhci>>, + fail_handle: Arc<FailHandle>, + }, +} + +/// xHCI PCI interface implementation. +pub struct XhciController { + config_regs: PciConfiguration, + mem: GuestMemory, + bar0: u64, // bar0 in config_regs will be changed by guest. Not sure why. + state: XhciControllerState, +} + +impl XhciController { + /// Create new xhci controller. + pub fn new(mem: GuestMemory, usb_provider: HostBackendDeviceProvider) -> Self { + let config_regs = PciConfiguration::new( + 0x01b73, // fresco logic, (google = 0x1ae0) + 0x1000, // fresco logic pdk. This chip has broken msi. See kernel xhci-pci.c + PciClassCode::SerialBusController, + &PciSerialBusSubClass::USB, + Some(&UsbControllerProgrammingInterface::Usb3HostController), + PciHeaderType::Device, + 0, + 0, + ); + XhciController { + config_regs, + mem, + bar0: 0, + state: XhciControllerState::Created { + device_provider: usb_provider, + }, + } + } + + /// Init xhci controller when it's forked. + pub fn init_when_forked(&mut self) { + match mem::replace(&mut self.state, XhciControllerState::Unknown) { + XhciControllerState::IrqAssigned { + device_provider, + irq_evt, + irq_resample_evt, + } => { + let (mmio, regs) = init_xhci_mmio_space_and_regs(); + let fail_handle: Arc<FailHandle> = Arc::new(XhciFailHandle::new(®s)); + let xhci = match Xhci::new( + fail_handle.clone(), + self.mem.clone(), + device_provider, + irq_evt, + irq_resample_evt, + regs, + ) { + Ok(xhci) => Some(xhci), + Err(_) => { + error!("fail to init xhci"); + fail_handle.fail(); + return; + } + }; + + self.state = XhciControllerState::Initialized { + mmio, + xhci, + fail_handle, + } + } + _ => { + error!("xhci controller is in a wrong state"); + return; + } + } + } +} + +impl PciDevice for XhciController { + fn debug_label(&self) -> String { + "xhci controller".to_owned() + } + + fn keep_fds(&self) -> Vec<RawFd> { + match self.state { + XhciControllerState::Created { + ref device_provider, + } => device_provider.keep_fds(), + _ => { + error!("xhci controller is in a wrong state"); + vec![] + } + } + } + + fn assign_irq( + &mut self, + irq_evt: EventFd, + irq_resample_evt: EventFd, + irq_num: u32, + irq_pin: PciInterruptPin, + ) { + match mem::replace(&mut self.state, XhciControllerState::Unknown) { + XhciControllerState::Created { device_provider } => { + self.config_regs.set_irq(irq_num as u8, irq_pin); + self.state = XhciControllerState::IrqAssigned { + device_provider, + irq_evt, + irq_resample_evt, + } + } + _ => { + error!("xhci controller is in a wrong state"); + return; + } + } + } + + fn allocate_io_bars( + &mut self, + resources: &mut SystemAllocator, + ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { + // xHCI spec 5.2.1. + let bar0_addr = resources + .allocate_mmio_addresses(XHCI_BAR0_SIZE) + .ok_or(PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE))?; + let bar0_config = PciBarConfiguration::default() + .set_register_index(0) + .set_address(bar0_addr) + .set_size(XHCI_BAR0_SIZE); + self.config_regs + .add_pci_bar(&bar0_config) + .map_err(|e| PciDeviceError::IoRegistrationFailed(bar0_addr, e))?; + self.bar0 = bar0_addr; + Ok(vec![(bar0_addr, XHCI_BAR0_SIZE)]) + } + + fn config_registers(&self) -> &PciConfiguration { + &self.config_regs + } + + fn config_registers_mut(&mut self) -> &mut PciConfiguration { + &mut self.config_regs + } + + fn read_bar(&mut self, addr: u64, data: &mut [u8]) { + let bar0 = self.bar0; + if addr < bar0 || addr > bar0 + XHCI_BAR0_SIZE { + return; + } + match self.state { + XhciControllerState::Initialized { + ref mmio, + xhci: _, + fail_handle: _, + } => { + // Read bar would still work even if it's already failed. + mmio.read(addr - bar0, data); + } + _ => { + error!("xhci controller is in a wrong state"); + return; + } + } + } + + fn write_bar(&mut self, addr: u64, data: &[u8]) { + let bar0 = self.bar0; + if addr < bar0 || addr > bar0 + XHCI_BAR0_SIZE { + return; + } + match self.state { + XhciControllerState::Initialized { + ref mmio, + xhci: _, + ref fail_handle, + } => { + if !fail_handle.failed() { + mmio.write(addr - bar0, data); + } + } + _ => { + error!("xhci controller is in a wrong state"); + return; + } + } + } + fn on_device_sandboxed(&mut self) { + self.init_when_forked(); + } +} |