Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pcie/pcie_rp.rs
5394 views
1
// Copyright 2021 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
7
use anyhow::bail;
8
use anyhow::Context;
9
use anyhow::Result;
10
use base::Event;
11
use vm_control::GpeNotify;
12
use vm_control::PmeNotify;
13
14
use crate::bus::HotPlugBus;
15
use crate::bus::HotPlugKey;
16
use crate::pci::pcie::pcie_host::PcieHostPort;
17
use crate::pci::pcie::pcie_port::PciePort;
18
use crate::pci::pcie::pcie_port::PciePortVariant;
19
use crate::pci::pcie::*;
20
use crate::pci::PciAddress;
21
22
const PCIE_RP_DID: u16 = 0x3420;
23
pub struct PcieRootPort {
24
pcie_port: PciePort,
25
downstream_devices: BTreeMap<PciAddress, HotPlugKey>,
26
hotplug_out_begin: bool,
27
removed_downstream: Vec<PciAddress>,
28
}
29
30
impl PcieRootPort {
31
/// Constructs a new PCIE root port
32
pub fn new(secondary_bus_num: u8, slot_implemented: bool) -> Self {
33
PcieRootPort {
34
pcie_port: PciePort::new(
35
PCIE_RP_DID,
36
"PcieRootPort".to_string(),
37
0,
38
secondary_bus_num,
39
slot_implemented,
40
PcieDevicePortType::RootPort,
41
),
42
downstream_devices: BTreeMap::new(),
43
hotplug_out_begin: false,
44
removed_downstream: Vec::new(),
45
}
46
}
47
48
/// Constructs a new PCIE root port which associated with the host physical pcie RP
49
pub fn new_from_host(pcie_host: PcieHostPort, slot_implemented: bool) -> Result<Self> {
50
Ok(PcieRootPort {
51
pcie_port: PciePort::new_from_host(
52
pcie_host,
53
slot_implemented,
54
PcieDevicePortType::RootPort,
55
)
56
.context("PciePort::new_from_host failed")?,
57
downstream_devices: BTreeMap::new(),
58
hotplug_out_begin: false,
59
removed_downstream: Vec::new(),
60
})
61
}
62
}
63
64
impl PciePortVariant for PcieRootPort {
65
fn get_pcie_port(&self) -> &PciePort {
66
&self.pcie_port
67
}
68
69
fn get_pcie_port_mut(&mut self) -> &mut PciePort {
70
&mut self.pcie_port
71
}
72
73
fn get_removed_devices_impl(&self) -> Vec<PciAddress> {
74
if self.pcie_port.removed_downstream_valid() {
75
self.removed_downstream.clone()
76
} else {
77
Vec::new()
78
}
79
}
80
81
fn hotplug_implemented_impl(&self) -> bool {
82
self.pcie_port.hotplug_implemented()
83
}
84
85
fn hotplugged_impl(&self) -> bool {
86
false
87
}
88
}
89
90
impl HotPlugBus for PcieRootPort {
91
fn hot_plug(&mut self, addr: PciAddress) -> Result<Option<Event>> {
92
if self.pcie_port.is_hpc_pending() {
93
bail!("Hot plug fail: previous slot event is pending.");
94
}
95
if !self.pcie_port.is_hotplug_ready() {
96
bail!("Hot unplug fail: slot is not enabled by the guest yet.");
97
}
98
self.downstream_devices
99
.get(&addr)
100
.context("No downstream devices.")?;
101
102
let hpc_sender = Event::new()?;
103
let hpc_recvr = hpc_sender.try_clone()?;
104
self.pcie_port.set_hpc_sender(hpc_sender);
105
self.pcie_port
106
.set_slot_status(PCIE_SLTSTA_PDS | PCIE_SLTSTA_ABP);
107
self.pcie_port.trigger_hp_or_pme_interrupt();
108
Ok(Some(hpc_recvr))
109
}
110
111
fn hot_unplug(&mut self, addr: PciAddress) -> Result<Option<Event>> {
112
if self.pcie_port.is_hpc_pending() {
113
bail!("Hot unplug fail: previous slot event is pending.");
114
}
115
if !self.pcie_port.is_hotplug_ready() {
116
bail!("Hot unplug fail: slot is not enabled by the guest yet.");
117
}
118
self.downstream_devices
119
.remove(&addr)
120
.context("No downstream devices.")?;
121
if self.hotplug_out_begin {
122
bail!("Hot unplug is pending.")
123
}
124
self.hotplug_out_begin = true;
125
126
self.removed_downstream.clear();
127
self.removed_downstream.push(addr);
128
// All the remaine devices will be removed also in this hotplug out interrupt
129
for (guest_pci_addr, _) in self.downstream_devices.iter() {
130
self.removed_downstream.push(*guest_pci_addr);
131
}
132
133
let hpc_sender = Event::new()?;
134
let hpc_recvr = hpc_sender.try_clone()?;
135
let slot_control = self.pcie_port.get_slot_control();
136
match slot_control & PCIE_SLTCTL_PIC {
137
PCIE_SLTCTL_PIC_ON => {
138
self.pcie_port.set_hpc_sender(hpc_sender);
139
self.pcie_port.set_slot_status(PCIE_SLTSTA_ABP);
140
self.pcie_port.trigger_hp_or_pme_interrupt();
141
}
142
PCIE_SLTCTL_PIC_OFF => {
143
// Do not press attention button, as the slot is already off. Likely caused by
144
// previous hot plug failed.
145
self.pcie_port.mask_slot_status(!PCIE_SLTSTA_PDS);
146
hpc_sender.signal()?;
147
}
148
_ => {
149
// Power indicator in blinking state.
150
// Should not be possible, since the previous slot event is pending.
151
bail!("Hot unplug fail: Power indicator is blinking.");
152
}
153
}
154
155
if self.pcie_port.is_host() {
156
self.pcie_port.hot_unplug()
157
}
158
Ok(Some(hpc_recvr))
159
}
160
161
fn get_ready_notification(&mut self) -> anyhow::Result<Event> {
162
Ok(self.pcie_port.get_ready_notification()?)
163
}
164
165
fn get_address(&self) -> Option<PciAddress> {
166
self.pcie_port.get_address()
167
}
168
169
fn get_secondary_bus_number(&self) -> Option<u8> {
170
Some(self.pcie_port.get_bus_range()?.secondary)
171
}
172
173
fn is_match(&self, host_addr: PciAddress) -> Option<u8> {
174
self.pcie_port.is_match(host_addr)
175
}
176
177
fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress) {
178
if !self.pcie_port.hotplug_implemented() {
179
return;
180
}
181
182
// Begin the next round hotplug in process
183
if self.hotplug_out_begin {
184
self.hotplug_out_begin = false;
185
self.downstream_devices.clear();
186
self.removed_downstream.clear();
187
}
188
189
self.downstream_devices.insert(guest_addr, hotplug_key);
190
}
191
192
fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress> {
193
for (guest_address, host_info) in self.downstream_devices.iter() {
194
if hotplug_key == *host_info {
195
return Some(*guest_address);
196
}
197
}
198
None
199
}
200
201
fn is_empty(&self) -> bool {
202
self.downstream_devices.is_empty()
203
}
204
205
fn get_hotplug_key(&self) -> Option<HotPlugKey> {
206
None
207
}
208
}
209
210
impl GpeNotify for PcieRootPort {
211
fn notify(&mut self) {
212
if !self.pcie_port.hotplug_implemented() {
213
return;
214
}
215
216
if self.pcie_port.is_host() {
217
self.pcie_port.prepare_hotplug();
218
}
219
220
if self.pcie_port.should_trigger_pme() {
221
self.pcie_port
222
.inject_pme(self.pcie_port.get_address().unwrap().pme_requester_id());
223
}
224
}
225
}
226
227
impl PmeNotify for PcieRootPort {
228
fn notify(&mut self, requester_id: u16) {
229
self.pcie_port.inject_pme(requester_id);
230
}
231
}
232
233