Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pcie/pcie_switch.rs
5394 views
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
31
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
60
impl 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
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.
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.
93
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
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
160
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
193
impl 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
219
impl 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
// 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
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
}
339
340