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 8 use anyhow::bail; 9 use base::error; 10 use base::Event; 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<Event>>88 fn hot_plug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<Event>> { 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<Event>>93 fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>> { 94 self.downstream_devices.remove(&addr); 95 Ok(None) 96 } 97 get_ready_notification(&mut self) -> anyhow::Result<Event>98 fn get_ready_notification(&mut self) -> anyhow::Result<Event> { 99 bail!("hot plug not supported on upstream port.") 100 } 101 get_secondary_bus_number(&self) -> Option<u8>102 fn get_secondary_bus_number(&self) -> Option<u8> { 103 Some(self.pcie_port.get_bus_range()?.secondary) 104 } 105 is_match(&self, host_addr: PciAddress) -> Option<u8>106 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 107 self.pcie_port.is_match(host_addr) 108 } 109 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)110 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 111 self.downstream_devices.insert(guest_addr, hotplug_key); 112 } 113 get_address(&self) -> Option<PciAddress>114 fn get_address(&self) -> Option<PciAddress> { 115 self.pcie_port.get_address() 116 } 117 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>118 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 119 for (guest_address, host_info) in self.downstream_devices.iter() { 120 if hotplug_key == *host_info { 121 return Some(*guest_address); 122 } 123 } 124 None 125 } 126 is_empty(&self) -> bool127 fn is_empty(&self) -> bool { 128 self.downstream_devices.is_empty() 129 } 130 get_hotplug_key(&self) -> Option<HotPlugKey>131 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 132 if self.pcie_port.is_host() { 133 match PciAddress::from_str(&self.pcie_port.debug_label()) { 134 Ok(host_addr) => Some(HotPlugKey::HostUpstreamPort { host_addr }), 135 Err(e) => { 136 error!( 137 "failed to get hotplug key for {}: {}", 138 self.pcie_port.debug_label(), 139 e 140 ); 141 None 142 } 143 } 144 } else { 145 None 146 } 147 } 148 } 149 150 pub struct PcieDownstreamPort { 151 pcie_port: PciePort, 152 hotplugged: bool, 153 downstream_devices: BTreeMap<PciAddress, HotPlugKey>, 154 hotplug_out_begin: bool, 155 removed_downstream: Vec<PciAddress>, 156 } 157 158 impl PcieDownstreamPort { 159 /// Constructs a new PCIE downstream port new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self160 pub fn new(primary_bus_num: u8, secondary_bus_num: u8, hotplugged: bool) -> Self { 161 PcieDownstreamPort { 162 pcie_port: PciePort::new( 163 PCIE_DP_DID, 164 "PcieDownstreamPort".to_string(), 165 primary_bus_num, 166 secondary_bus_num, 167 false, 168 PcieDevicePortType::DownstreamPort, 169 ), 170 hotplugged, 171 downstream_devices: BTreeMap::new(), 172 hotplug_out_begin: false, 173 removed_downstream: Vec::new(), 174 } 175 } 176 new_from_host( pcie_host: PcieHostPort, hotplugged: bool, ) -> std::result::Result<Self, PciDeviceError>177 pub fn new_from_host( 178 pcie_host: PcieHostPort, 179 hotplugged: bool, 180 ) -> std::result::Result<Self, PciDeviceError> { 181 let pcie_port = 182 PciePort::new_from_host(pcie_host, true, PcieDevicePortType::DownstreamPort)?; 183 Ok(PcieDownstreamPort { 184 pcie_port, 185 hotplugged, 186 downstream_devices: BTreeMap::new(), 187 hotplug_out_begin: false, 188 removed_downstream: Vec::new(), 189 }) 190 } 191 } 192 193 impl PciePortVariant for PcieDownstreamPort { get_pcie_port(&self) -> &PciePort194 fn get_pcie_port(&self) -> &PciePort { 195 &self.pcie_port 196 } 197 get_pcie_port_mut(&mut self) -> &mut PciePort198 fn get_pcie_port_mut(&mut self) -> &mut PciePort { 199 &mut self.pcie_port 200 } 201 get_removed_devices_impl(&self) -> Vec<PciAddress>202 fn get_removed_devices_impl(&self) -> Vec<PciAddress> { 203 if self.pcie_port.removed_downstream_valid() { 204 self.removed_downstream.clone() 205 } else { 206 Vec::new() 207 } 208 } 209 hotplug_implemented_impl(&self) -> bool210 fn hotplug_implemented_impl(&self) -> bool { 211 false 212 } 213 hotplugged_impl(&self) -> bool214 fn hotplugged_impl(&self) -> bool { 215 self.hotplugged 216 } 217 } 218 219 impl HotPlugBus for PcieDownstreamPort { hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>220 fn hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>> { 221 if !self.pcie_port.hotplug_implemented() { 222 bail!("hotplug not implemented."); 223 } 224 if !self.downstream_devices.contains_key(&addr) { 225 bail!("no downstream devices."); 226 } 227 if !self.pcie_port.is_hotplug_ready() { 228 bail!("Hot unplug fail: slot is not enabled by the guest yet."); 229 } 230 self.pcie_port 231 .set_slot_status(PCIE_SLTSTA_PDS | PCIE_SLTSTA_ABP); 232 self.pcie_port.trigger_hp_or_pme_interrupt(); 233 Ok(None) 234 } 235 hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>236 fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>> { 237 if !self.pcie_port.hotplug_implemented() { 238 bail!("hotplug not implemented."); 239 } 240 if self.downstream_devices.remove(&addr).is_none() { 241 bail!("no downstream devices."); 242 } 243 if !self.pcie_port.is_hotplug_ready() { 244 bail!("Hot unplug fail: slot is not enabled by the guest yet."); 245 } 246 247 if !self.hotplug_out_begin { 248 self.removed_downstream.clear(); 249 self.removed_downstream.push(addr); 250 // All the remaine devices will be removed also in this hotplug out interrupt 251 for (guest_pci_addr, _) in self.downstream_devices.iter() { 252 self.removed_downstream.push(*guest_pci_addr); 253 } 254 self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP); 255 self.pcie_port.trigger_hp_or_pme_interrupt(); 256 let slot_control = self.pcie_port.get_slot_control(); 257 match slot_control & PCIE_SLTCTL_PIC { 258 PCIE_SLTCTL_PIC_ON => { 259 self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP); 260 self.pcie_port.trigger_hp_or_pme_interrupt(); 261 } 262 PCIE_SLTCTL_PIC_OFF => { 263 // Do not press attention button, as the slot is already off. Likely caused by 264 // previous hot plug failed. 265 self.pcie_port.mask_slot_status(!PCIE_SLTSTA_PDS); 266 } 267 _ => { 268 // Power indicator in blinking state. 269 bail!("Hot unplug fail: Power indicator is blinking."); 270 } 271 } 272 273 if self.pcie_port.is_host() { 274 self.pcie_port.hot_unplug() 275 } 276 } 277 278 self.hotplug_out_begin = true; 279 Ok(None) 280 } 281 get_ready_notification(&mut self) -> anyhow::Result<Event>282 fn get_ready_notification(&mut self) -> anyhow::Result<Event> { 283 Ok(self.pcie_port.get_ready_notification()?) 284 } 285 get_address(&self) -> Option<PciAddress>286 fn get_address(&self) -> Option<PciAddress> { 287 self.pcie_port.get_address() 288 } 289 get_secondary_bus_number(&self) -> Option<u8>290 fn get_secondary_bus_number(&self) -> Option<u8> { 291 Some(self.pcie_port.get_bus_range()?.secondary) 292 } 293 is_match(&self, host_addr: PciAddress) -> Option<u8>294 fn is_match(&self, host_addr: PciAddress) -> Option<u8> { 295 self.pcie_port.is_match(host_addr) 296 } 297 add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)298 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) { 299 if self.hotplug_out_begin { 300 self.hotplug_out_begin = false; 301 self.downstream_devices.clear(); 302 self.removed_downstream.clear(); 303 } 304 305 self.downstream_devices.insert(guest_addr, hotplug_key); 306 } 307 get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>308 fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> { 309 for (guest_address, host_info) in self.downstream_devices.iter() { 310 if hotplug_key == *host_info { 311 return Some(*guest_address); 312 } 313 } 314 None 315 } 316 is_empty(&self) -> bool317 fn is_empty(&self) -> bool { 318 self.downstream_devices.is_empty() 319 } 320 get_hotplug_key(&self) -> Option<HotPlugKey>321 fn get_hotplug_key(&self) -> Option<HotPlugKey> { 322 if self.pcie_port.is_host() { 323 match PciAddress::from_str(&self.pcie_port.debug_label()) { 324 Ok(host_addr) => Some(HotPlugKey::HostDownstreamPort { host_addr }), 325 Err(e) => { 326 error!( 327 "failed to get hotplug key for {}: {}", 328 self.pcie_port.debug_label(), 329 e 330 ); 331 None 332 } 333 } 334 } else { 335 None 336 } 337 } 338 } 339