1 // Copyright 2022 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::str::FromStr; 7 use std::sync::mpsc; 8 9 use anyhow::bail; 10 use base::error; 11 12 use crate::bus::HotPlugBus; 13 use crate::bus::HotPlugKey; 14 use crate::pci::pcie::pcie_port::PciePort; 15 use crate::pci::pcie::pcie_port::PciePortVariant; 16 use crate::pci::pcie::*; 17 use crate::pci::PciAddress; 18 use crate::pci::PciDeviceError; 19 20 const PCIE_UP_DID: u16 = 0x3500; 21 const PCIE_DP_DID: u16 = 0x3510; 22 23 pub struct PcieUpstreamPort { 24 pcie_port: PciePort, 25 hotplugged: bool, 26 downstream_devices: BTreeMap<PciAddress, HotPlugKey>, 27 } 28 29 impl PcieUpstreamPort { 30 /// Constructs a new PCIE upstream port new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self31 pub fn new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self { 32 PcieUpstreamPort { 33 pcie_port: PciePort::new( 34 PCIE_UP_DID, 35 "PcieUpstreamPort".to_string(), 36 primary_bus_num, 37 secondary_bus_num, 38 false, 39 PcieDevicePortType::UpstreamPort, 40 ), 41 hotplugged, 42 downstream_devices: BTreeMap::new(), 43 } 44 } 45 new_from_host( pcie_host: PcieHostPort, hotplugged: bool, ) -> std::result::Result<Self, PciDeviceError>46 pub fn new_from_host( 47 pcie_host: PcieHostPort, 48 hotplugged: bool, 49 ) -> std::result::Result<Self, PciDeviceError> { 50 let pcie_port = 51 PciePort::new_from_host(pcie_host, false, PcieDevicePortType::UpstreamPort)?; 52 Ok(PcieUpstreamPort { 53 pcie_port, 54 hotplugged, 55 downstream_devices: BTreeMap::new(), 56 }) 57 } 58 } 59 60 impl PciePortVariant for PcieUpstreamPort { get_pcie_port(&self) -> &PciePort61 fn get_pcie_port(&self) -> &PciePort { 62 &self.pcie_port 63 } 64 get_pcie_port_mut(&mut self) -> &mut PciePort65 fn get_pcie_port_mut(&mut self) -> &mut PciePort { 66 &mut self.pcie_port 67 } 68 get_removed_devices_impl(&self) -> Vec<PciAddress>69 fn get_removed_devices_impl(&self) -> Vec<PciAddress> { 70 Vec::new() 71 } 72 hotplug_implemented_impl(&self) -> bool73 fn hotplug_implemented_impl(&self) -> bool { 74 false 75 } 76 hotplugged_impl(&self) -> bool77 fn hotplugged_impl(&self) -> bool { 78 self.hotplugged 79 } 80 } 81 82 // Even if upstream port do not have a slot present, we still implement hotplug 83 // bus trait for it. Our purpose is simple. We want to store information of 84 // downstream devices in this upstream port, so that they could be used during 85 // hotplug out. 86 impl HotPlugBus for PcieUpstreamPort { 87 // Do nothing. We are not a real hotplug bus. hot_plug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>>88 fn hot_plug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>> { 89 bail!("hot plug not supported on upstream port.") 90 } 91 92 // Just remove the downstream device. hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>>93 fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>> { 94 self.downstream_devices.remove(&addr); 95 Ok(None) 96 } 97 get_secondary_bus_number(&self) -> Option<u8>98 fn get_secondary_bus_number(&self) -> Option<u8> { 99 Some(self.pcie_port.get_bus_range()?.secondary) 100 } 101 is_match(&self, host_addr: PciAddress) -> Option<u8>102 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 103 self.pcie_port.is_match(host_addr) 104 } 105 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)106 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 107 self.downstream_devices.insert(guest_addr, hotplug_key); 108 } 109 get_address(&self) -> Option<PciAddress>110 fn get_address(&self) -> Option<PciAddress> { 111 self.pcie_port.get_address() 112 } 113 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>114 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 115 for (guest_address, host_info) in self.downstream_devices.iter() { 116 if hotplug_key == *host_info { 117 return Some(*guest_address); 118 } 119 } 120 None 121 } 122 is_empty(&self) -> bool123 fn is_empty(&self) -> bool { 124 self.downstream_devices.is_empty() 125 } 126 get_hotplug_key(&self) -> Option<HotPlugKey>127 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 128 if self.pcie_port.is_host() { 129 match PciAddress::from_str(&self.pcie_port.debug_label()) { 130 Ok(host_addr) => Some(HotPlugKey::HostUpstreamPort { host_addr }), 131 Err(e) => { 132 error!( 133 "failed to get hotplug key for {}: {}", 134 self.pcie_port.debug_label(), 135 e 136 ); 137 None 138 } 139 } 140 } else { 141 None 142 } 143 } 144 } 145 146 pub struct PcieDownstreamPort { 147 pcie_port: PciePort, 148 hotplugged: bool, 149 downstream_devices: BTreeMap<PciAddress, HotPlugKey>, 150 hotplug_out_begin: bool, 151 removed_downstream: Vec<PciAddress>, 152 } 153 154 impl PcieDownstreamPort { 155 /// Constructs a new PCIE downstream port new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self156 pub fn new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self { 157 PcieDownstreamPort { 158 pcie_port: PciePort::new( 159 PCIE_DP_DID, 160 "PcieDownstreamPort".to_string(), 161 primary_bus_num, 162 secondary_bus_num, 163 false, 164 PcieDevicePortType::DownstreamPort, 165 ), 166 hotplugged, 167 downstream_devices: BTreeMap::new(), 168 hotplug_out_begin: false, 169 removed_downstream: Vec::new(), 170 } 171 } 172 new_from_host( pcie_host: PcieHostPort, hotplugged: bool, ) -> std::result::Result<Self, PciDeviceError>173 pub fn new_from_host( 174 pcie_host: PcieHostPort, 175 hotplugged: bool, 176 ) -> std::result::Result<Self, PciDeviceError> { 177 let pcie_port = 178 PciePort::new_from_host(pcie_host, true, PcieDevicePortType::DownstreamPort)?; 179 Ok(PcieDownstreamPort { 180 pcie_port, 181 hotplugged, 182 downstream_devices: BTreeMap::new(), 183 hotplug_out_begin: false, 184 removed_downstream: Vec::new(), 185 }) 186 } 187 } 188 189 impl PciePortVariant for PcieDownstreamPort { get_pcie_port(&self) -> &PciePort190 fn get_pcie_port(&self) -> &PciePort { 191 &self.pcie_port 192 } 193 get_pcie_port_mut(&mut self) -> &mut PciePort194 fn get_pcie_port_mut(&mut self) -> &mut PciePort { 195 &mut self.pcie_port 196 } 197 get_removed_devices_impl(&self) -> Vec<PciAddress>198 fn get_removed_devices_impl(&self) -> Vec<PciAddress> { 199 if self.pcie_port.removed_downstream_valid() { 200 self.removed_downstream.clone() 201 } else { 202 Vec::new() 203 } 204 } 205 hotplug_implemented_impl(&self) -> bool206 fn hotplug_implemented_impl(&self) -> bool { 207 false 208 } 209 hotplugged_impl(&self) -> bool210 fn hotplugged_impl(&self) -> bool { 211 self.hotplugged 212 } 213 } 214 215 impl HotPlugBus for PcieDownstreamPort { hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>>216 fn hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>> { 217 if !self.pcie_port.hotplug_implemented() { 218 bail!("hotplug not implemented."); 219 } 220 if self.downstream_devices.get(&addr).is_none() { 221 bail!("no downstream devices."); 222 } 223 self.pcie_port 224 .set_slot_status(PCIE_SLTSTA_PDS | PCIE_SLTSTA_ABP); 225 self.pcie_port.trigger_hp_or_pme_interrupt(); 226 Ok(None) 227 } 228 hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>>229 fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<mpsc::Receiver<()>>> { 230 if !self.pcie_port.hotplug_implemented() { 231 bail!("hotplug not implemented."); 232 } 233 if self.downstream_devices.remove(&addr).is_none() { 234 bail!("no downstream devices."); 235 } 236 237 if !self.hotplug_out_begin { 238 self.removed_downstream.clear(); 239 self.removed_downstream.push(addr); 240 // All the remaine devices will be removed also in this hotplug out interrupt 241 for (guest_pci_addr, _) in self.downstream_devices.iter() { 242 self.removed_downstream.push(*guest_pci_addr); 243 } 244 self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP); 245 self.pcie_port.trigger_hp_or_pme_interrupt(); 246 247 if self.pcie_port.is_host() { 248 self.pcie_port.hot_unplug() 249 } 250 } 251 252 self.hotplug_out_begin = true; 253 Ok(None) 254 } 255 get_address(&self) -> Option<PciAddress>256 fn get_address(&self) -> Option<PciAddress> { 257 self.pcie_port.get_address() 258 } 259 get_secondary_bus_number(&self) -> Option<u8>260 fn get_secondary_bus_number(&self) -> Option<u8> { 261 Some(self.pcie_port.get_bus_range()?.secondary) 262 } 263 is_match(&self, host_addr: PciAddress) -> Option<u8>264 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 265 self.pcie_port.is_match(host_addr) 266 } 267 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)268 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 269 if self.hotplug_out_begin { 270 self.hotplug_out_begin = false; 271 self.downstream_devices.clear(); 272 self.removed_downstream.clear(); 273 } 274 275 self.downstream_devices.insert(guest_addr, hotplug_key); 276 } 277 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>278 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 279 for (guest_address, host_info) in self.downstream_devices.iter() { 280 if hotplug_key == *host_info { 281 return Some(*guest_address); 282 } 283 } 284 None 285 } 286 is_empty(&self) -> bool287 fn is_empty(&self) -> bool { 288 self.downstream_devices.is_empty() 289 } 290 get_hotplug_key(&self) -> Option<HotPlugKey>291 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 292 if self.pcie_port.is_host() { 293 match PciAddress::from_str(&self.pcie_port.debug_label()) { 294 Ok(host_addr) => Some(HotPlugKey::HostDownstreamPort { host_addr }), 295 Err(e) => { 296 error!( 297 "failed to get hotplug key for {}: {}", 298 self.pcie_port.debug_label(), 299 e 300 ); 301 None 302 } 303 } 304 } else { 305 None 306 } 307 } 308 } 309