devices/pci/pcie/
pcie_rp.rs

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
5use std::collections::BTreeMap;
6
7use anyhow::bail;
8use anyhow::Context;
9use anyhow::Result;
10use base::Event;
11use vm_control::GpeNotify;
12use vm_control::PmeNotify;
13
14use crate::bus::HotPlugBus;
15use crate::bus::HotPlugKey;
16use crate::pci::pcie::pcie_host::PcieHostPort;
17use crate::pci::pcie::pcie_port::PciePort;
18use crate::pci::pcie::pcie_port::PciePortVariant;
19use crate::pci::pcie::*;
20use crate::pci::PciAddress;
21
22const PCIE_RP_DID: u16 = 0x3420;
23pub struct PcieRootPort {
24    pcie_port: PciePort,
25    downstream_devices: BTreeMap<PciAddress, HotPlugKey>,
26    hotplug_out_begin: bool,
27    removed_downstream: Vec<PciAddress>,
28}
29
30impl 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
64impl 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
90impl 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
210impl 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
227impl PmeNotify for PcieRootPort {
228    fn notify(&mut self, requester_id: u16) {
229        self.pcie_port.inject_pme(requester_id);
230    }
231}