xref: /aosp_15_r20/external/crosvm/devices/src/pci/pcie/pcie_switch.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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