• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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