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