xref: /aosp_15_r20/external/crosvm/devices/src/pci/pcie/pcie_rp.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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
new(secondary_bus_num: u8, slot_implemented: bool) -> Self32     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
new_from_host(pcie_host: PcieHostPort, slot_implemented: bool) -> Result<Self>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 {
get_pcie_port(&self) -> &PciePort65     fn get_pcie_port(&self) -> &PciePort {
66         &self.pcie_port
67     }
68 
get_pcie_port_mut(&mut self) -> &mut PciePort69     fn get_pcie_port_mut(&mut self) -> &mut PciePort {
70         &mut self.pcie_port
71     }
72 
get_removed_devices_impl(&self) -> Vec<PciAddress>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 
hotplug_implemented_impl(&self) -> bool81     fn hotplug_implemented_impl(&self) -> bool {
82         self.pcie_port.hotplug_implemented()
83     }
84 
hotplugged_impl(&self) -> bool85     fn hotplugged_impl(&self) -> bool {
86         false
87     }
88 }
89 
90 impl HotPlugBus for PcieRootPort {
hot_plug(&mut self, addr: PciAddress) -> Result<Option<Event>>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 
hot_unplug(&mut self, addr: PciAddress) -> Result<Option<Event>>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 
get_ready_notification(&mut self) -> anyhow::Result<Event>161     fn get_ready_notification(&mut self) -> anyhow::Result<Event> {
162         Ok(self.pcie_port.get_ready_notification()?)
163     }
164 
get_address(&self) -> Option<PciAddress>165     fn get_address(&self) -> Option<PciAddress> {
166         self.pcie_port.get_address()
167     }
168 
get_secondary_bus_number(&self) -> Option<u8>169     fn get_secondary_bus_number(&self) -> Option<u8> {
170         Some(self.pcie_port.get_bus_range()?.secondary)
171     }
172 
is_match(&self, host_addr: PciAddress) -> Option<u8>173     fn is_match(&self, host_addr: PciAddress) -> Option<u8> {
174         self.pcie_port.is_match(host_addr)
175     }
176 
add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress)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 
get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>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 
is_empty(&self) -> bool201     fn is_empty(&self) -> bool {
202         self.downstream_devices.is_empty()
203     }
204 
get_hotplug_key(&self) -> Option<HotPlugKey>205     fn get_hotplug_key(&self) -> Option<HotPlugKey> {
206         None
207     }
208 }
209 
210 impl GpeNotify for PcieRootPort {
notify(&mut self)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 {
notify(&mut self, requester_id: u16)228     fn notify(&mut self, requester_id: u16) {
229         self.pcie_port.inject_pme(requester_id);
230     }
231 }
232