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 7 use anyhow::bail; 8 use anyhow::Context; 9 use anyhow::Result; 10 use base::Event; 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<Event>>91 fn hot_plug(&mut self, addr: PciAddress) -> Result<Option<Event>> { 92 if self.pcie_port.is_hpc_pending() { 93 bail!("Hot plug fail: previous slot event is pending."); 94 } 95 if !self.pcie_port.is_hotplug_ready() { 96 bail!("Hot unplug fail: slot is not enabled by the guest yet."); 97 } 98 self.downstream_devices 99 .get(&addr) 100 .context("No downstream devices.")?; 101 102 let hpc_sender = Event::new()?; 103 let hpc_recvr = hpc_sender.try_clone()?; 104 self.pcie_port.set_hpc_sender(hpc_sender); 105 self.pcie_port 106 .set_slot_status(PCIE_SLTSTA_PDS | PCIE_SLTSTA_ABP); 107 self.pcie_port.trigger_hp_or_pme_interrupt(); 108 Ok(Some(hpc_recvr)) 109 } 110 hot_unplug(&mut self, addr: PciAddress) -> Result<Option<Event>>111 fn hot_unplug(&mut self, addr: PciAddress) -> Result<Option<Event>> { 112 if self.pcie_port.is_hpc_pending() { 113 bail!("Hot unplug fail: previous slot event is pending."); 114 } 115 if !self.pcie_port.is_hotplug_ready() { 116 bail!("Hot unplug fail: slot is not enabled by the guest yet."); 117 } 118 self.downstream_devices 119 .remove(&addr) 120 .context("No downstream devices.")?; 121 if self.hotplug_out_begin { 122 bail!("Hot unplug is pending.") 123 } 124 self.hotplug_out_begin = true; 125 126 self.removed_downstream.clear(); 127 self.removed_downstream.push(addr); 128 // All the remaine devices will be removed also in this hotplug out interrupt 129 for (guest_pci_addr, _) in self.downstream_devices.iter() { 130 self.removed_downstream.push(*guest_pci_addr); 131 } 132 133 let hpc_sender = Event::new()?; 134 let hpc_recvr = hpc_sender.try_clone()?; 135 let slot_control = self.pcie_port.get_slot_control(); 136 match slot_control & PCIE_SLTCTL_PIC { 137 PCIE_SLTCTL_PIC_ON => { 138 self.pcie_port.set_hpc_sender(hpc_sender); 139 self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP); 140 self.pcie_port.trigger_hp_or_pme_interrupt(); 141 } 142 PCIE_SLTCTL_PIC_OFF => { 143 // Do not press attention button, as the slot is already off. Likely caused by 144 // previous hot plug failed. 145 self.pcie_port.mask_slot_status(!PCIE_SLTSTA_PDS); 146 hpc_sender.signal()?; 147 } 148 _ => { 149 // Power indicator in blinking state. 150 // Should not be possible, since the previous slot event is pending. 151 bail!("Hot unplug fail: Power indicator is blinking."); 152 } 153 } 154 155 if self.pcie_port.is_host() { 156 self.pcie_port.hot_unplug() 157 } 158 Ok(Some(hpc_recvr)) 159 } 160 get_ready_notification(&mut self) -> anyhow::Result<Event>161 fn get_ready_notification(&mut self) -> anyhow::Result<Event> { 162 Ok(self.pcie_port.get_ready_notification()?) 163 } 164 get_address(&self) -> Option<PciAddress>165 fn get_address(&self) -> Option<PciAddress> { 166 self.pcie_port.get_address() 167 } 168 get_secondary_bus_number(&self) -> Option<u8>169 fn get_secondary_bus_number(&self) -> Option<u8> { 170 Some(self.pcie_port.get_bus_range()?.secondary) 171 } 172 is_match(&self, host_addr: PciAddress) -> Option<u8>173 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 174 self.pcie_port.is_match(host_addr) 175 } 176 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)177 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 178 if !self.pcie_port.hotplug_implemented() { 179 return; 180 } 181 182 // Begin the next round hotplug in process 183 if self.hotplug_out_begin { 184 self.hotplug_out_begin = false; 185 self.downstream_devices.clear(); 186 self.removed_downstream.clear(); 187 } 188 189 self.downstream_devices.insert(guest_addr, hotplug_key); 190 } 191 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>192 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 193 for (guest_address, host_info) in self.downstream_devices.iter() { 194 if hotplug_key == *host_info { 195 return Some(*guest_address); 196 } 197 } 198 None 199 } 200 is_empty(&self) -> bool201 fn is_empty(&self) -> bool { 202 self.downstream_devices.is_empty() 203 } 204 get_hotplug_key(&self) -> Option<HotPlugKey>205 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 206 None 207 } 208 } 209 210 impl GpeNotify for PcieRootPort { notify(&mut self)211 fn notify(&mut self) { 212 if !self.pcie_port.hotplug_implemented() { 213 return; 214 } 215 216 if self.pcie_port.is_host() { 217 self.pcie_port.prepare_hotplug(); 218 } 219 220 if self.pcie_port.should_trigger_pme() { 221 self.pcie_port 222 .inject_pme(self.pcie_port.get_address().unwrap().pme_requester_id()); 223 } 224 } 225 } 226 227 impl PmeNotify for PcieRootPort { notify(&mut self, requester_id: u16)228 fn notify(&mut self, requester_id: u16) { 229 self.pcie_port.inject_pme(requester_id); 230 } 231 } 232