devices/pci/pcie/
pcie_switch.rs1use std::collections::BTreeMap;
6use std::str::FromStr;
7
8use anyhow::bail;
9use base::error;
10use base::Event;
11
12use crate::bus::HotPlugBus;
13use crate::bus::HotPlugKey;
14use crate::pci::pcie::pcie_port::PciePort;
15use crate::pci::pcie::pcie_port::PciePortVariant;
16use crate::pci::pcie::*;
17use crate::pci::PciAddress;
18use crate::pci::PciDeviceError;
19
20const PCIE_UP_DID: u16 = 0x3500;
21const PCIE_DP_DID: u16 = 0x3510;
22
23pub struct PcieUpstreamPort {
24 pcie_port: PciePort,
25 hotplugged: bool,
26 downstream_devices: BTreeMap<PciAddress, HotPlugKey>,
27}
28
29impl PcieUpstreamPort {
30 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
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
60impl PciePortVariant for PcieUpstreamPort {
61 fn get_pcie_port(&self) -> &PciePort {
62 &self.pcie_port
63 }
64
65 fn get_pcie_port_mut(&mut self) -> &mut PciePort {
66 &mut self.pcie_port
67 }
68
69 fn get_removed_devices_impl(&self) -> Vec<PciAddress> {
70 Vec::new()
71 }
72
73 fn hotplug_implemented_impl(&self) -> bool {
74 false
75 }
76
77 fn hotplugged_impl(&self) -> bool {
78 self.hotplugged
79 }
80}
81
82impl HotPlugBus for PcieUpstreamPort {
87 fn hot_plug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<Event>> {
89 bail!("hot plug not supported on upstream port.")
90 }
91
92 fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>> {
94 self.downstream_devices.remove(&addr);
95 Ok(None)
96 }
97
98 fn get_ready_notification(&mut self) -> anyhow::Result<Event> {
99 bail!("hot plug not supported on upstream port.")
100 }
101
102 fn get_secondary_bus_number(&self) -> Option<u8> {
103 Some(self.pcie_port.get_bus_range()?.secondary)
104 }
105
106 fn is_match(&self, host_addr: PciAddress) -> Option<u8> {
107 self.pcie_port.is_match(host_addr)
108 }
109
110 fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) {
111 self.downstream_devices.insert(guest_addr, hotplug_key);
112 }
113
114 fn get_address(&self) -> Option<PciAddress> {
115 self.pcie_port.get_address()
116 }
117
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
127 fn is_empty(&self) -> bool {
128 self.downstream_devices.is_empty()
129 }
130
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
150pub 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
158impl PcieDownstreamPort {
159 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
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
193impl PciePortVariant for PcieDownstreamPort {
194 fn get_pcie_port(&self) -> &PciePort {
195 &self.pcie_port
196 }
197
198 fn get_pcie_port_mut(&mut self) -> &mut PciePort {
199 &mut self.pcie_port
200 }
201
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
210 fn hotplug_implemented_impl(&self) -> bool {
211 false
212 }
213
214 fn hotplugged_impl(&self) -> bool {
215 self.hotplugged
216 }
217}
218
219impl HotPlugBus for PcieDownstreamPort {
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
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 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 self.pcie_port.mask_slot_status(!PCIE_SLTSTA_PDS);
266 }
267 _ => {
268 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
282 fn get_ready_notification(&mut self) -> anyhow::Result<Event> {
283 Ok(self.pcie_port.get_ready_notification()?)
284 }
285
286 fn get_address(&self) -> Option<PciAddress> {
287 self.pcie_port.get_address()
288 }
289
290 fn get_secondary_bus_number(&self) -> Option<u8> {
291 Some(self.pcie_port.get_bus_range()?.secondary)
292 }
293
294 fn is_match(&self, host_addr: PciAddress) -> Option<u8> {
295 self.pcie_port.is_match(host_addr)
296 }
297
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
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
317 fn is_empty(&self) -> bool {
318 self.downstream_devices.is_empty()
319 }
320
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}