devices/usb/xhci/
xhci_controller.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::mem;
6use std::sync::atomic::AtomicBool;
7use std::sync::atomic::Ordering;
8use std::sync::Arc;
9
10use base::error;
11use base::AsRawDescriptor;
12use base::RawDescriptor;
13use base::SharedMemory;
14use resources::Alloc;
15use resources::AllocOptions;
16use resources::SystemAllocator;
17use vm_memory::GuestMemory;
18
19use crate::pci::BarRange;
20use crate::pci::PciAddress;
21use crate::pci::PciBarConfiguration;
22use crate::pci::PciBarPrefetchable;
23use crate::pci::PciBarRegionType;
24use crate::pci::PciClassCode;
25use crate::pci::PciConfiguration;
26use crate::pci::PciDevice;
27use crate::pci::PciDeviceError;
28use crate::pci::PciHeaderType;
29use crate::pci::PciInterruptPin;
30use crate::pci::PciProgrammingInterface;
31use crate::pci::PciSerialBusSubClass;
32use crate::register_space::Register;
33use crate::register_space::RegisterSpace;
34use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
35use crate::usb::xhci::xhci_regs::init_xhci_mmio_space_and_regs;
36use crate::usb::xhci::xhci_regs::XhciRegs;
37use crate::usb::xhci::Xhci;
38use crate::utils::FailHandle;
39use crate::IrqLevelEvent;
40use crate::Suspendable;
41
42const XHCI_BAR0_SIZE: u64 = 0x10000;
43
44#[derive(Clone, Copy)]
45enum UsbControllerProgrammingInterface {
46    Usb3HostController = 0x30,
47}
48
49impl PciProgrammingInterface for UsbControllerProgrammingInterface {
50    fn get_register_value(&self) -> u8 {
51        *self as u8
52    }
53}
54
55/// Use this handle to fail xhci controller.
56pub struct XhciFailHandle {
57    usbcmd: Register<u32>,
58    usbsts: Register<u32>,
59    xhci_failed: AtomicBool,
60}
61
62impl XhciFailHandle {
63    pub fn new(regs: &XhciRegs) -> XhciFailHandle {
64        XhciFailHandle {
65            usbcmd: regs.usbcmd.clone(),
66            usbsts: regs.usbsts.clone(),
67            xhci_failed: AtomicBool::new(false),
68        }
69    }
70}
71
72impl FailHandle for XhciFailHandle {
73    /// Fail this controller. Will set related registers and flip failed bool.
74    fn fail(&self) {
75        // set run/stop to stop.
76        const USBCMD_STOPPED: u32 = 0;
77        // Set host system error bit.
78        const USBSTS_HSE: u32 = 1 << 2;
79        self.usbcmd.set_value(USBCMD_STOPPED);
80        self.usbsts.set_value(USBSTS_HSE);
81
82        self.xhci_failed.store(true, Ordering::SeqCst);
83        error!("xhci controller stopped working");
84    }
85
86    /// Returns true if xhci is already failed.
87    fn failed(&self) -> bool {
88        self.xhci_failed.load(Ordering::SeqCst)
89    }
90}
91
92// Xhci controller should be created with backend device provider. Then irq should be assigned
93// before initialized. We are not making `failed` as a state here to optimize performance. Cause we
94// need to set failed in other threads.
95enum XhciControllerState {
96    Unknown,
97    Created {
98        device_provider: Box<dyn XhciBackendDeviceProvider>,
99    },
100    IrqAssigned {
101        device_provider: Box<dyn XhciBackendDeviceProvider>,
102        irq_evt: IrqLevelEvent,
103    },
104    Initialized {
105        mmio: RegisterSpace,
106        // Xhci init could fail.
107        #[allow(dead_code)]
108        xhci: Option<Arc<Xhci>>,
109        fail_handle: Arc<dyn FailHandle>,
110    },
111}
112
113/// xHCI PCI interface implementation.
114pub struct XhciController {
115    config_regs: PciConfiguration,
116    pci_address: Option<PciAddress>,
117    mem: GuestMemory,
118    state: XhciControllerState,
119    initial_usb_devices: Vec<std::fs::File>,
120}
121
122impl XhciController {
123    /// Create new xhci controller.
124    pub fn new(mem: GuestMemory, usb_provider: Box<dyn XhciBackendDeviceProvider>) -> Self {
125        let config_regs = PciConfiguration::new(
126            0x01b73, // fresco logic, (google = 0x1ae0)
127            0x1400,  // fresco logic fl1400. This chip has broken msi. See kernel xhci-pci.c
128            PciClassCode::SerialBusController,
129            &PciSerialBusSubClass::Usb,
130            Some(&UsbControllerProgrammingInterface::Usb3HostController),
131            PciHeaderType::Device,
132            0,
133            0,
134            0,
135        );
136        XhciController {
137            config_regs,
138            pci_address: None,
139            mem,
140            state: XhciControllerState::Created {
141                device_provider: usb_provider,
142            },
143            initial_usb_devices: Vec::new(),
144        }
145    }
146
147    /// Set USB device files to attach at startup (before VM boots).
148    pub fn set_initial_usb_devices(&mut self, devices: Vec<std::fs::File>) {
149        self.initial_usb_devices = devices;
150    }
151
152    /// Init xhci controller when it's forked.
153    pub fn init_when_forked(&mut self) {
154        match mem::replace(&mut self.state, XhciControllerState::Unknown) {
155            XhciControllerState::IrqAssigned {
156                device_provider,
157                irq_evt,
158            } => {
159                let (mmio, regs) = init_xhci_mmio_space_and_regs();
160                let fail_handle: Arc<dyn FailHandle> = Arc::new(XhciFailHandle::new(&regs));
161                let initial_devices = mem::take(&mut self.initial_usb_devices);
162                let xhci = match Xhci::new_with_devices(
163                    fail_handle.clone(),
164                    self.mem.clone(),
165                    device_provider,
166                    irq_evt,
167                    regs,
168                    initial_devices,
169                ) {
170                    Ok(xhci) => Some(xhci),
171                    Err(_) => {
172                        error!("fail to init xhci");
173                        fail_handle.fail();
174                        return;
175                    }
176                };
177
178                self.state = XhciControllerState::Initialized {
179                    mmio,
180                    xhci,
181                    fail_handle,
182                }
183            }
184            _ => {
185                error!("xhci controller is in a wrong state");
186            }
187        }
188    }
189}
190
191impl PciDevice for XhciController {
192    fn debug_label(&self) -> String {
193        "xhci controller".to_owned()
194    }
195
196    fn allocate_address(
197        &mut self,
198        resources: &mut SystemAllocator,
199    ) -> Result<PciAddress, PciDeviceError> {
200        if self.pci_address.is_none() {
201            self.pci_address = resources.allocate_pci(0, self.debug_label());
202        }
203        self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
204    }
205
206    fn keep_rds(&self) -> Vec<RawDescriptor> {
207        match &self.state {
208            XhciControllerState::Created { device_provider } => device_provider.keep_rds(),
209            XhciControllerState::IrqAssigned {
210                device_provider,
211                irq_evt,
212            } => {
213                let mut keep_rds = device_provider.keep_rds();
214                keep_rds.push(irq_evt.get_trigger().as_raw_descriptor());
215                keep_rds.push(irq_evt.get_resample().as_raw_descriptor());
216                keep_rds
217            }
218            _ => {
219                error!("xhci controller is in a wrong state");
220                vec![]
221            }
222        }
223    }
224
225    fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) {
226        match mem::replace(&mut self.state, XhciControllerState::Unknown) {
227            XhciControllerState::Created { device_provider } => {
228                self.config_regs.set_irq(irq_num as u8, pin);
229                self.state = XhciControllerState::IrqAssigned {
230                    device_provider,
231                    irq_evt,
232                }
233            }
234            _ => {
235                error!("xhci controller is in a wrong state");
236            }
237        }
238    }
239
240    fn allocate_io_bars(
241        &mut self,
242        resources: &mut SystemAllocator,
243    ) -> std::result::Result<Vec<BarRange>, PciDeviceError> {
244        let address = self
245            .pci_address
246            .expect("assign_address must be called prior to allocate_io_bars");
247        // xHCI spec 5.2.1.
248        let bar0_addr = resources
249            .allocate_mmio(
250                XHCI_BAR0_SIZE,
251                Alloc::PciBar {
252                    bus: address.bus,
253                    dev: address.dev,
254                    func: address.func,
255                    bar: 0,
256                },
257                "xhci_bar0".to_string(),
258                AllocOptions::new()
259                    .max_address(u32::MAX.into())
260                    .align(XHCI_BAR0_SIZE),
261            )
262            .map_err(|e| PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE, e))?;
263        let bar0_config = PciBarConfiguration::new(
264            0,
265            XHCI_BAR0_SIZE,
266            PciBarRegionType::Memory32BitRegion,
267            PciBarPrefetchable::NotPrefetchable,
268        )
269        .set_address(bar0_addr);
270        self.config_regs
271            .add_pci_bar(bar0_config)
272            .map_err(|e| PciDeviceError::IoRegistrationFailed(bar0_addr, e))?;
273        Ok(vec![BarRange {
274            addr: bar0_addr,
275            size: XHCI_BAR0_SIZE,
276            prefetchable: false,
277        }])
278    }
279
280    fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
281        self.config_regs.get_bar_configuration(bar_num)
282    }
283
284    fn read_config_register(&self, reg_idx: usize) -> u32 {
285        self.config_regs.read_reg(reg_idx)
286    }
287
288    fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
289        self.config_regs.write_reg(reg_idx, offset, data);
290    }
291
292    fn setup_pci_config_mapping(
293        &mut self,
294        shmem: &SharedMemory,
295        base: usize,
296        len: usize,
297    ) -> Result<bool, PciDeviceError> {
298        self.config_regs
299            .setup_mapping(shmem, base, len)
300            .map(|_| true)
301            .map_err(PciDeviceError::MmioSetup)
302    }
303
304    fn read_bar(&mut self, bar_index: usize, offset: u64, data: &mut [u8]) {
305        if bar_index != 0 {
306            return;
307        }
308
309        match &self.state {
310            XhciControllerState::Initialized { mmio, .. } => {
311                // Read bar would still work even if it's already failed.
312                mmio.read(offset, data);
313            }
314            _ => {
315                error!("xhci controller is in a wrong state");
316            }
317        }
318    }
319
320    fn write_bar(&mut self, bar_index: usize, offset: u64, data: &[u8]) {
321        if bar_index != 0 {
322            return;
323        }
324
325        match &self.state {
326            XhciControllerState::Initialized {
327                mmio, fail_handle, ..
328            } => {
329                if !fail_handle.failed() {
330                    mmio.write(offset, data);
331                }
332            }
333            _ => {
334                error!("xhci controller is in a wrong state");
335            }
336        }
337    }
338
339    fn on_device_sandboxed(&mut self) {
340        self.init_when_forked();
341    }
342}
343
344impl Suspendable for XhciController {}