1 // Copyright 2021 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 5 use std::collections::BTreeMap; 6 use std::sync::mpsc; 7 8 use anyhow::bail; 9 use anyhow::Context; 10 use anyhow::Result; 11 use vm_control::GpeNotify; 12 use vm_control::PmeNotify; 13 14 use crate::bus::HotPlugBus; 15 use crate::bus::HotPlugKey; 16 use crate::pci::pcie::pcie_host::PcieHostPort; 17 use crate::pci::pcie::pcie_port::PciePort; 18 use crate::pci::pcie::pcie_port::PciePortVariant; 19 use crate::pci::pcie::*; 20 use crate::pci::PciAddress; 21 22 const PCIE_RP_DID: u16 = 0x3420; 23 pub struct PcieRootPort { 24 pcie_port: PciePort, 25 downstream_devices: BTreeMap<PciAddress, HotPlugKey>, 26 hotplug_out_begin: bool, 27 removed_downstream: Vec<PciAddress>, 28 } 29 30 impl PcieRootPort { 31 /// Constructs a new PCIE root port new(secondary_bus_num: u8, slot_implemented: bool) -> Self32 pub fn new(secondary_bus_num: u8, slot_implemented: bool) -> Self { 33 PcieRootPort { 34 pcie_port: PciePort::new( 35 PCIE_RP_DID, 36 "PcieRootPort".to_string(), 37 0, 38 secondary_bus_num, 39 slot_implemented, 40 PcieDevicePortType::RootPort, 41 ), 42 downstream_devices: BTreeMap::new(), 43 hotplug_out_begin: false, 44 removed_downstream: Vec::new(), 45 } 46 } 47 48 /// Constructs a new PCIE root port which associated with the host physical pcie RP new_from_host(pcie_host: PcieHostPort, slot_implemented: bool) -> Result<Self>49 pub fn new_from_host(pcie_host: PcieHostPort, slot_implemented: bool) -> Result<Self> { 50 Ok(PcieRootPort { 51 pcie_port: PciePort::new_from_host( 52 pcie_host, 53 slot_implemented, 54 PcieDevicePortType::RootPort, 55 ) 56 .context("PciePort::new_from_host failed")?, 57 downstream_devices: BTreeMap::new(), 58 hotplug_out_begin: false, 59 removed_downstream: Vec::new(), 60 }) 61 } 62 } 63 64 impl PciePortVariant for PcieRootPort { get_pcie_port(&self) -> &PciePort65 fn get_pcie_port(&self) -> &PciePort { 66 &self.pcie_port 67 } 68 get_pcie_port_mut(&mut self) -> &mut PciePort69 fn get_pcie_port_mut(&mut self) -> &mut PciePort { 70 &mut self.pcie_port 71 } 72 get_removed_devices_impl(&self) -> Vec<PciAddress>73 fn get_removed_devices_impl(&self) -> Vec<PciAddress> { 74 if self.pcie_port.removed_downstream_valid() { 75 self.removed_downstream.clone() 76 } else { 77 Vec::new() 78 } 79 } 80 hotplug_implemented_impl(&self) -> bool81 fn hotplug_implemented_impl(&self) -> bool { 82 self.pcie_port.hotplug_implemented() 83 } 84 hotplugged_impl(&self) -> bool85 fn hotplugged_impl(&self) -> bool { 86 false 87 } 88 } 89 90 impl HotPlugBus for PcieRootPort { hot_plug(&mut self, addr: PciAddress) -> Result<Option<mpsc::Receiver<()>>>91 fn hot_plug(&mut self, addr: PciAddress) -> Result<Option<mpsc::Receiver<()>>> { 92 if self.pcie_port.is_cc_pending() { 93 bail!("Slot busy: plugging too early or guest does not support PCIe hotplug."); 94 } 95 self.downstream_devices 96 .get(&addr) 97 .context("No downstream devices.")?; 98 99 let (cc_sender, cc_recvr) = mpsc::channel(); 100 self.pcie_port.set_cc_sender(cc_sender); 101 self.pcie_port 102 .set_slot_status(PCIE_SLTSTA_PDS | PCIE_SLTSTA_ABP); 103 self.pcie_port.trigger_hp_or_pme_interrupt(); 104 Ok(Some(cc_recvr)) 105 } 106 hot_unplug(&mut self, addr: PciAddress) -> Result<Option<mpsc::Receiver<()>>>107 fn hot_unplug(&mut self, addr: PciAddress) -> Result<Option<mpsc::Receiver<()>>> { 108 self.downstream_devices 109 .remove(&addr) 110 .context("No downstream devices.")?; 111 if self.hotplug_out_begin { 112 bail!("Hot unplug is pending.") 113 } 114 self.hotplug_out_begin = true; 115 116 self.removed_downstream.clear(); 117 self.removed_downstream.push(addr); 118 // All the remaine devices will be removed also in this hotplug out interrupt 119 for (guest_pci_addr, _) in self.downstream_devices.iter() { 120 self.removed_downstream.push(*guest_pci_addr); 121 } 122 123 let (cc_sender, cc_recvr) = mpsc::channel(); 124 if self.pcie_port.set_cc_sender(cc_sender).is_some() { 125 bail!("Slot busy: unplugging too early or guest does not support PCIe hotplug."); 126 } 127 self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP); 128 self.pcie_port.trigger_hp_or_pme_interrupt(); 129 130 if self.pcie_port.is_host() { 131 self.pcie_port.hot_unplug() 132 } 133 Ok(Some(cc_recvr)) 134 } 135 get_address(&self) -> Option<PciAddress>136 fn get_address(&self) -> Option<PciAddress> { 137 self.pcie_port.get_address() 138 } 139 get_secondary_bus_number(&self) -> Option<u8>140 fn get_secondary_bus_number(&self) -> Option<u8> { 141 Some(self.pcie_port.get_bus_range()?.secondary) 142 } 143 is_match(&self, host_addr: PciAddress) -> Option<u8>144 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 145 self.pcie_port.is_match(host_addr) 146 } 147 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)148 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 149 if !self.pcie_port.hotplug_implemented() { 150 return; 151 } 152 153 // Begin the next round hotplug in process 154 if self.hotplug_out_begin { 155 self.hotplug_out_begin = false; 156 self.downstream_devices.clear(); 157 self.removed_downstream.clear(); 158 } 159 160 self.downstream_devices.insert(guest_addr, hotplug_key); 161 } 162 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>163 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 164 for (guest_address, host_info) in self.downstream_devices.iter() { 165 if hotplug_key == *host_info { 166 return Some(*guest_address); 167 } 168 } 169 None 170 } 171 is_empty(&self) -> bool172 fn is_empty(&self) -> bool { 173 self.downstream_devices.is_empty() 174 } 175 get_hotplug_key(&self) -> Option<HotPlugKey>176 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 177 None 178 } 179 } 180 181 impl GpeNotify for PcieRootPort { notify(&mut self)182 fn notify(&mut self) { 183 if !self.pcie_port.hotplug_implemented() { 184 return; 185 } 186 187 if self.pcie_port.is_host() { 188 self.pcie_port.prepare_hotplug(); 189 } 190 191 if self.pcie_port.should_trigger_pme() { 192 self.pcie_port 193 .inject_pme(self.pcie_port.get_address().unwrap().pme_requester_id()); 194 } 195 } 196 } 197 198 impl PmeNotify for PcieRootPort { notify(&mut self, requester_id: u16)199 fn notify(&mut self, requester_id: u16) { 200 self.pcie_port.inject_pme(requester_id); 201 } 202 } 203