From 2ec87968d59126b1267a611a2925fa6e325bc76e Mon Sep 17 00:00:00 2001 From: Xiong Zhang Date: Fri, 21 May 2021 15:01:45 +0800 Subject: [PATCH] devices:pcie: Implement HotPlugBus trait on PcieRootPort PcieRootPort is used to notify hotplug event into guest, so implement HotPlugBus trait on it. BUG=b:185084350 TEST=Boot a guest with pcie root port and check its status Change-Id: Ide110d107422fa784bd8de0aaa87b319c786ef28 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2954677 Tested-by: kokoro Reviewed-by: Daniel Verkamp Commit-Queue: Daniel Verkamp --- devices/src/pci/pcie.rs | 95 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/devices/src/pci/pcie.rs b/devices/src/pci/pcie.rs index f2ee5fa389..2fa4c3143f 100644 --- a/devices/src/pci/pcie.rs +++ b/devices/src/pci/pcie.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use sync::Mutex; +use crate::bus::{HostHotPlugKey, HotPlugBus}; use crate::pci::msix::{MsixCap, MsixConfig}; use crate::pci::pci_configuration::{ PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciBridgeSubclass, PciCapability, @@ -291,12 +292,17 @@ const PCIE_SLTCAP_HPC: u32 = 0x40; // Hot-Plug Capable const PCIE_SLTCTL_OFFSET: usize = 0x18; const PCIE_SLTCTL_PIC_OFF: u16 = 0x300; const PCIE_SLTCTL_AIC_OFF: u16 = 0xC0; +const PCIE_SLTCTL_ABPE: u16 = 0x01; +const PCIE_SLTCTL_PDCE: u16 = 0x08; +const PCIE_SLTCTL_CCIE: u16 = 0x10; +const PCIE_SLTCTL_HPIE: u16 = 0x20; const PCIE_SLTSTA_OFFSET: usize = 0x1A; const PCIE_SLTSTA_ABP: u16 = 0x0001; const PCIE_SLTSTA_PFD: u16 = 0x0002; const PCIE_SLTSTA_PDC: u16 = 0x0008; const PCIE_SLTSTA_CC: u16 = 0x0010; +const PCIE_SLTSTA_PDS: u16 = 0x0040; const PCIE_SLTSTA_DLLSC: u16 = 0x0100; #[repr(C)] @@ -428,6 +434,7 @@ pub struct PcieRootPort { msix_config: Option>>, slot_control: u16, slot_status: u16, + downstream_device: Option<(PciAddress, Option)>, } impl PcieRootPort { @@ -438,6 +445,7 @@ impl PcieRootPort { msix_config: None, slot_control: PCIE_SLTCTL_PIC_OFF | PCIE_SLTCTL_AIC_OFF, slot_status: 0, + downstream_device: None, } } @@ -450,7 +458,15 @@ impl PcieRootPort { fn write_pcie_cap(&mut self, offset: usize, data: &[u8]) { match offset { PCIE_SLTCTL_OFFSET => match get_word(data) { - Some(v) => self.slot_control = v, + Some(v) => { + let old_control = self.slot_control; + self.slot_control = v; + if old_control != v { + // send Command completed events + self.slot_status |= PCIE_SLTSTA_CC; + self.trigger_cc_interrupt(); + } + } None => warn!("write SLTCTL isn't word, len: {}", data.len()), }, PCIE_SLTSTA_OFFSET => { @@ -480,6 +496,29 @@ impl PcieRootPort { _ => (), } } + + fn trigger_interrupt(&self) { + if let Some(msix_config) = &self.msix_config { + let mut msix_config = msix_config.lock(); + if msix_config.enabled() { + msix_config.trigger(0) + } + } + } + + fn trigger_cc_interrupt(&self) { + if (self.slot_control & PCIE_SLTCTL_CCIE) != 0 && (self.slot_status & PCIE_SLTSTA_CC) != 0 { + self.trigger_interrupt() + } + } + + fn trigger_hp_interrupt(&self) { + if (self.slot_control & PCIE_SLTCTL_HPIE) != 0 + && (self.slot_status & self.slot_control & (PCIE_SLTCTL_ABPE | PCIE_SLTCTL_PDCE)) != 0 + { + self.trigger_interrupt() + } + } } impl PcieDevice for PcieRootPort { @@ -526,3 +565,57 @@ impl PcieDevice for PcieRootPort { } } } + +impl HotPlugBus for PcieRootPort { + fn hot_plug(&mut self, addr: PciAddress) { + match self.downstream_device { + Some((guest_addr, _)) => { + if guest_addr != addr { + return; + } + } + None => return, + } + + self.slot_status = self.slot_status | PCIE_SLTSTA_PDS | PCIE_SLTSTA_PDC | PCIE_SLTSTA_ABP; + self.trigger_hp_interrupt(); + } + + fn hot_unplug(&mut self, addr: PciAddress) { + match self.downstream_device { + Some((guest_addr, _)) => { + if guest_addr != addr { + return; + } + } + None => return, + } + + self.slot_status &= !PCIE_SLTSTA_PDS; + self.slot_status = self.slot_status | PCIE_SLTSTA_PDC | PCIE_SLTSTA_ABP; + self.trigger_hp_interrupt(); + } + + fn add_hotplug_device(&mut self, host_key: HostHotPlugKey, guest_addr: PciAddress) { + self.downstream_device = Some((guest_addr, Some(host_key))) + } + + fn get_hotplug_device(&self, host_key: HostHotPlugKey) -> Option { + if let Some((guest_address, Some(host_info))) = &self.downstream_device { + match host_info { + HostHotPlugKey::Vfio { host_addr } => { + let saved_addr = *host_addr; + match host_key { + HostHotPlugKey::Vfio { host_addr } => { + if host_addr == saved_addr { + return Some(*guest_address); + } + } + } + } + } + } + + None + } +}