use std::mem;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use base::error;
use base::AsRawDescriptor;
use base::RawDescriptor;
use base::SharedMemory;
use resources::Alloc;
use resources::AllocOptions;
use resources::SystemAllocator;
use vm_memory::GuestMemory;
use crate::pci::BarRange;
use crate::pci::PciAddress;
use crate::pci::PciBarConfiguration;
use crate::pci::PciBarPrefetchable;
use crate::pci::PciBarRegionType;
use crate::pci::PciClassCode;
use crate::pci::PciConfiguration;
use crate::pci::PciDevice;
use crate::pci::PciDeviceError;
use crate::pci::PciHeaderType;
use crate::pci::PciInterruptPin;
use crate::pci::PciProgrammingInterface;
use crate::pci::PciSerialBusSubClass;
use crate::register_space::Register;
use crate::register_space::RegisterSpace;
use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
use crate::usb::xhci::xhci_regs::init_xhci_mmio_space_and_regs;
use crate::usb::xhci::xhci_regs::XhciRegs;
use crate::usb::xhci::Xhci;
use crate::utils::FailHandle;
use crate::IrqLevelEvent;
use crate::Suspendable;
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
}
}
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 {
fn fail(&self) {
const USBCMD_STOPPED: u32 = 0;
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");
}
fn failed(&self) -> bool {
self.xhci_failed.load(Ordering::SeqCst)
}
}
enum XhciControllerState {
Unknown,
Created {
device_provider: Box<dyn XhciBackendDeviceProvider>,
},
IrqAssigned {
device_provider: Box<dyn XhciBackendDeviceProvider>,
irq_evt: IrqLevelEvent,
},
Initialized {
mmio: RegisterSpace,
#[allow(dead_code)]
xhci: Option<Arc<Xhci>>,
fail_handle: Arc<dyn FailHandle>,
},
}
pub struct XhciController {
config_regs: PciConfiguration,
pci_address: Option<PciAddress>,
mem: GuestMemory,
state: XhciControllerState,
}
impl XhciController {
pub fn new(mem: GuestMemory, usb_provider: Box<dyn XhciBackendDeviceProvider>) -> Self {
let config_regs = PciConfiguration::new(
0x01b73, 0x1400, PciClassCode::SerialBusController,
&PciSerialBusSubClass::Usb,
Some(&UsbControllerProgrammingInterface::Usb3HostController),
PciHeaderType::Device,
0,
0,
0,
);
XhciController {
config_regs,
pci_address: None,
mem,
state: XhciControllerState::Created {
device_provider: usb_provider,
},
}
}
pub fn init_when_forked(&mut self) {
match mem::replace(&mut self.state, XhciControllerState::Unknown) {
XhciControllerState::IrqAssigned {
device_provider,
irq_evt,
} => {
let (mmio, regs) = init_xhci_mmio_space_and_regs();
let fail_handle: Arc<dyn FailHandle> = Arc::new(XhciFailHandle::new(®s));
let xhci = match Xhci::new(
fail_handle.clone(),
self.mem.clone(),
device_provider,
irq_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");
}
}
}
}
impl PciDevice for XhciController {
fn debug_label(&self) -> String {
"xhci controller".to_owned()
}
fn allocate_address(
&mut self,
resources: &mut SystemAllocator,
) -> Result<PciAddress, PciDeviceError> {
if self.pci_address.is_none() {
self.pci_address = match resources.allocate_pci(0, self.debug_label()) {
Some(Alloc::PciBar {
bus,
dev,
func,
bar: _,
}) => Some(PciAddress { bus, dev, func }),
_ => None,
}
}
self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
}
fn keep_rds(&self) -> Vec<RawDescriptor> {
match &self.state {
XhciControllerState::Created { device_provider } => device_provider.keep_rds(),
XhciControllerState::IrqAssigned {
device_provider,
irq_evt,
} => {
let mut keep_rds = device_provider.keep_rds();
keep_rds.push(irq_evt.get_trigger().as_raw_descriptor());
keep_rds.push(irq_evt.get_resample().as_raw_descriptor());
keep_rds
}
_ => {
error!("xhci controller is in a wrong state");
vec![]
}
}
}
fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) {
match mem::replace(&mut self.state, XhciControllerState::Unknown) {
XhciControllerState::Created { device_provider } => {
self.config_regs.set_irq(irq_num as u8, pin);
self.state = XhciControllerState::IrqAssigned {
device_provider,
irq_evt,
}
}
_ => {
error!("xhci controller is in a wrong state");
}
}
}
fn allocate_io_bars(
&mut self,
resources: &mut SystemAllocator,
) -> std::result::Result<Vec<BarRange>, PciDeviceError> {
let address = self
.pci_address
.expect("assign_address must be called prior to allocate_io_bars");
let bar0_addr = resources
.allocate_mmio(
XHCI_BAR0_SIZE,
Alloc::PciBar {
bus: address.bus,
dev: address.dev,
func: address.func,
bar: 0,
},
"xhci_bar0".to_string(),
AllocOptions::new()
.max_address(u32::MAX.into())
.align(XHCI_BAR0_SIZE),
)
.map_err(|e| PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE, e))?;
let bar0_config = PciBarConfiguration::new(
0,
XHCI_BAR0_SIZE,
PciBarRegionType::Memory32BitRegion,
PciBarPrefetchable::NotPrefetchable,
)
.set_address(bar0_addr);
self.config_regs
.add_pci_bar(bar0_config)
.map_err(|e| PciDeviceError::IoRegistrationFailed(bar0_addr, e))?;
Ok(vec![BarRange {
addr: bar0_addr,
size: XHCI_BAR0_SIZE,
prefetchable: false,
}])
}
fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
self.config_regs.get_bar_configuration(bar_num)
}
fn read_config_register(&self, reg_idx: usize) -> u32 {
self.config_regs.read_reg(reg_idx)
}
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
self.config_regs.write_reg(reg_idx, offset, data);
}
fn setup_pci_config_mapping(
&mut self,
shmem: &SharedMemory,
base: usize,
len: usize,
) -> Result<bool, PciDeviceError> {
self.config_regs
.setup_mapping(shmem, base, len)
.map(|_| true)
.map_err(PciDeviceError::MmioSetup)
}
fn read_bar(&mut self, bar_index: usize, offset: u64, data: &mut [u8]) {
if bar_index != 0 {
return;
}
match &self.state {
XhciControllerState::Initialized { mmio, .. } => {
mmio.read(offset, data);
}
_ => {
error!("xhci controller is in a wrong state");
}
}
}
fn write_bar(&mut self, bar_index: usize, offset: u64, data: &[u8]) {
if bar_index != 0 {
return;
}
match &self.state {
XhciControllerState::Initialized {
mmio, fail_handle, ..
} => {
if !fail_handle.failed() {
mmio.write(offset, data);
}
}
_ => {
error!("xhci controller is in a wrong state");
}
}
}
fn on_device_sandboxed(&mut self) {
self.init_when_forked();
}
}
impl Suspendable for XhciController {}