Skip to content

Commit c450ebb

Browse files
vmm_core/vmotherboard: chipset bus resolution for pcie enumerators and downstream ports (#2122)
This change introduces `vmotherboard` support in OpenVMM for wiring PCIe topology components together. This PR builds on previous changes to introduce root complex and root port emulators, and in subsequent PRs will be used to wire endpoint devices up to their parent devices. For a view of how this fits together, see #1976 Specifially, this change: - Introduces two `vmotherboard::BusId` types. which are separate because they have distinct wiring requirements. - The first such type represents a multi-port upstream device such as a root complex or in the future a switch. These devices represent an internal bus, with multiple downstream ports - The second such type represents each downstream port of the multi-port devices, at most one downstream device can be attached to each downstream port. - Adds a bus resolver to the motherboard, with associated error handling types, to facilitate the motherboard connecting upstream and downstream devices based on port names - These are modeled off of the legacy PCI bus resolution architecture, though had to be separate. The legacy PCI bus infrastructure models a multi-device bus, where BDFs are statically defined at VM-start, whereas the PCIe bus infrastructure models both multi-port semantics for upstream devices and single-parent semantics for the PCIe links and resolution is done based on string identifiers not BDFs - When an enumerator is registered with the motherboard, the motherboard queries the downstream ports of the enumerator and tracks them internally for resolution - Updates the VM worker to register PCIe root complexes with this new resolution architecture.
1 parent 0c211f1 commit c450ebb

File tree

11 files changed

+291
-33
lines changed

11 files changed

+291
-33
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9360,6 +9360,7 @@ dependencies = [
93609360
"parking_lot",
93619361
"paste",
93629362
"pci_bus",
9363+
"pcie",
93639364
"range_map_vec",
93649365
"state_unit",
93659366
"thiserror 2.0.16",

openvmm/hvlite_core/src/worker/dispatch.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,9 @@ impl InitializedVm {
17961796
),
17971797
});
17981798

1799+
let bus_id = vmotherboard::BusId::new(&rc.name);
1800+
chipset_builder.register_weak_mutex_pcie_enumerator(bus_id, Box::new(root_complex));
1801+
17991802
ecam_address += ecam_size;
18001803
low_mmio_address -= low_mmio_size;
18011804
high_mmio_address += rc.high_mmio_size;

vmm_core/vmotherboard/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ guest_watchdog.workspace = true
3939
ide.workspace = true
4040
missing_dev.workspace = true
4141
pci_bus.workspace = true
42+
pcie.workspace = true
4243
vga_proxy = { optional = true, workspace = true }
4344
vga = { optional = true, workspace = true }
4445
watchdog_core.workspace = true

vmm_core/vmotherboard/src/base_chipset.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,10 @@ impl ConfigureChipsetDevice for ArcMutexChipsetServices<'_, '_> {
797797
mod weak_mutex_pci {
798798
use crate::chipset::PciConflict;
799799
use crate::chipset::PciConflictReason;
800+
use crate::chipset::PcieConflict;
801+
use crate::chipset::PcieConflictReason;
800802
use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPci;
803+
use crate::chipset::backing::arc_mutex::pci::RegisterWeakMutexPcie;
801804
use chipset_device::ChipsetDevice;
802805
use chipset_device::io::IoResult;
803806
use closeable_mutex::CloseableMutex;
@@ -885,6 +888,27 @@ mod weak_mutex_pci {
885888
})
886889
}
887890
}
891+
892+
// wiring to enable using the generic PCIe root port alongside the Arc+CloseableMutex device infra
893+
impl RegisterWeakMutexPcie for Arc<CloseableMutex<pcie::root::GenericPcieRootComplex>> {
894+
fn add_pcie_device(
895+
&mut self,
896+
port: u8,
897+
name: Arc<str>,
898+
dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
899+
) -> Result<(), PcieConflict> {
900+
self.lock()
901+
.add_pcie_device(port, name.clone(), WeakMutexPciDeviceWrapper(dev))
902+
.map_err(|existing_dev_name| PcieConflict {
903+
reason: PcieConflictReason::ExistingDev(existing_dev_name),
904+
conflict_dev: name,
905+
})
906+
}
907+
908+
fn downstream_ports(&self) -> Vec<(u8, Arc<str>)> {
909+
self.lock().downstream_ports()
910+
}
911+
}
888912
}
889913

890914
pub struct ArcMutexIsaDmaChannel {

vmm_core/vmotherboard/src/chipset/backing/arc_mutex/device.rs

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
use super::services::ArcMutexChipsetServices;
88
use crate::BusIdPci;
9+
use crate::BusIdPcieDownstreamPort;
910
use crate::VmmChipsetDevice;
1011
use arc_cyclic_builder::ArcCyclicBuilder;
1112
use arc_cyclic_builder::ArcCyclicBuilderExt;
@@ -71,6 +72,7 @@ pub struct ArcMutexChipsetDeviceBuilder<'a, 'b, T> {
7172

7273
pci_addr: Option<(u8, u8, u8)>,
7374
pci_bus_id: Option<BusIdPci>,
75+
pcie_port: Option<BusIdPcieDownstreamPort>,
7476
external_pci: bool,
7577
}
7678

@@ -97,6 +99,7 @@ where
9799

98100
pci_addr: None,
99101
pci_bus_id: None,
102+
pcie_port: None,
100103
external_pci: false,
101104
}
102105
}
@@ -120,6 +123,12 @@ where
120123
self
121124
}
122125

126+
/// For PCIe devices: place the device on the specified downstream port
127+
pub fn on_pcie_port(mut self, id: BusIdPcieDownstreamPort) -> Self {
128+
self.pcie_port = Some(id);
129+
self
130+
}
131+
123132
/// For PCI devices: do not register the device with any PCI bus. This is
124133
/// used when the device is hooked up to a bus (such as a VPCI bus) outside
125134
/// of the vmotherboard infrastructure.
@@ -156,39 +165,50 @@ where
156165

157166
if !self.external_pci {
158167
if let Some(dev) = typed_dev.supports_pci() {
159-
// static pci registration
160-
let bdf = match (self.pci_addr, dev.suggested_bdf()) {
161-
(Some(override_bdf), Some(suggested_bdf)) => {
162-
let (ob, od, of) = override_bdf;
163-
let (sb, sd, sf) = suggested_bdf;
164-
tracing::info!(
165-
"overriding suggested bdf: using {:02x}:{:02x}:{} instead of {:02x}:{:02x}:{}",
166-
ob,
167-
od,
168-
of,
169-
sb,
170-
sd,
171-
sf
172-
);
173-
override_bdf
174-
}
175-
(None, Some(bdf)) | (Some(bdf), None) => bdf,
176-
(None, None) => {
177-
return Err(
178-
AddDeviceErrorKind::NoPciBusAddress.with_dev_name(self.dev_name)
179-
);
180-
}
181-
};
182-
183-
let bus_id = match self.pci_bus_id.take() {
184-
Some(bus_id) => bus_id,
185-
None => panic!(
186-
"wiring error: did not invoke `on_pci_bus` for `{}`",
168+
if self.pci_bus_id.is_some() && self.pcie_port.is_some() {
169+
panic!(
170+
"wiring error: invoked both `on_pci_bus` and `on_pcie_port` for `{}`",
187171
self.dev_name
188-
),
189-
};
190-
191-
self.services.register_static_pci(bus_id, bdf);
172+
);
173+
}
174+
175+
if let Some(bus_id_port) = self.pcie_port {
176+
self.services.register_static_pcie(bus_id_port);
177+
} else {
178+
// static pci registration
179+
let bdf = match (self.pci_addr, dev.suggested_bdf()) {
180+
(Some(override_bdf), Some(suggested_bdf)) => {
181+
let (ob, od, of) = override_bdf;
182+
let (sb, sd, sf) = suggested_bdf;
183+
tracing::info!(
184+
"overriding suggested bdf: using {:02x}:{:02x}:{} instead of {:02x}:{:02x}:{}",
185+
ob,
186+
od,
187+
of,
188+
sb,
189+
sd,
190+
sf
191+
);
192+
override_bdf
193+
}
194+
(None, Some(bdf)) | (Some(bdf), None) => bdf,
195+
(None, None) => {
196+
return Err(
197+
AddDeviceErrorKind::NoPciBusAddress.with_dev_name(self.dev_name)
198+
);
199+
}
200+
};
201+
202+
let bus_id = match self.pci_bus_id.take() {
203+
Some(bus_id) => bus_id,
204+
None => panic!(
205+
"wiring error: did not invoke `on_pci_bus` for `{}`",
206+
self.dev_name
207+
),
208+
};
209+
210+
self.services.register_static_pci(bus_id, bdf);
211+
}
192212
}
193213
}
194214

vmm_core/vmotherboard/src/chipset/backing/arc_mutex/pci.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
// Licensed under the MIT License.
33

44
use crate::BusIdPci;
5+
use crate::BusIdPcieDownstreamPort;
6+
use crate::BusIdPcieEnumerator;
57
use crate::chipset::PciConflict;
68
use crate::chipset::PciConflictReason;
9+
use crate::chipset::PcieConflict;
10+
use crate::chipset::PcieConflictReason;
711
use chipset_device::ChipsetDevice;
812
use closeable_mutex::CloseableMutex;
913
use std::collections::HashMap;
@@ -68,3 +72,78 @@ impl BusResolverWeakMutexPci {
6872
if !errs.is_empty() { Err(errs) } else { Ok(()) }
6973
}
7074
}
75+
76+
/// An abstraction over an upstream PCIe enumerator implementation that
77+
/// is able to route accesses to `Weak<CloseableMutex<dyn ChipsetDevice>>`
78+
/// devices via downstream ports.
79+
pub trait RegisterWeakMutexPcie: Send {
80+
/// Try to add a PCIe device to the enumerator at the specified port,
81+
/// reporting any conflicts.
82+
fn add_pcie_device(
83+
&mut self,
84+
port: u8,
85+
name: Arc<str>,
86+
device: Weak<CloseableMutex<dyn ChipsetDevice>>,
87+
) -> Result<(), PcieConflict>;
88+
89+
/// Enumerate the downstream ports.
90+
fn downstream_ports(&self) -> Vec<(u8, Arc<str>)>;
91+
}
92+
93+
pub struct WeakMutexPcieDeviceEntry {
94+
pub bus_id_port: BusIdPcieDownstreamPort,
95+
pub name: Arc<str>,
96+
pub dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
97+
}
98+
99+
#[derive(Default)]
100+
pub struct BusResolverWeakMutexPcie {
101+
pub enumerators: HashMap<BusIdPcieEnumerator, Box<dyn RegisterWeakMutexPcie>>,
102+
pub ports: HashMap<BusIdPcieDownstreamPort, (u8, BusIdPcieEnumerator)>,
103+
pub devices: Vec<WeakMutexPcieDeviceEntry>,
104+
}
105+
106+
impl BusResolverWeakMutexPcie {
107+
pub fn resolve(mut self) -> Result<(), Vec<PcieConflict>> {
108+
let mut errs = Vec::new();
109+
110+
for WeakMutexPcieDeviceEntry {
111+
bus_id_port,
112+
name,
113+
dev,
114+
} in self.devices
115+
{
116+
let (port_number, bus_id_enumerator) = match self.ports.get(&bus_id_port) {
117+
Some(v) => v,
118+
None => {
119+
errs.push(PcieConflict {
120+
conflict_dev: name.clone(),
121+
reason: PcieConflictReason::MissingDownstreamPort,
122+
});
123+
continue;
124+
}
125+
};
126+
127+
let enumerator = match self.enumerators.get_mut(bus_id_enumerator) {
128+
Some(enumerator) => enumerator,
129+
None => {
130+
errs.push(PcieConflict {
131+
conflict_dev: name.clone(),
132+
reason: PcieConflictReason::MissingEnumerator,
133+
});
134+
continue;
135+
}
136+
};
137+
138+
match enumerator.add_pcie_device(*port_number, name, dev) {
139+
Ok(()) => {}
140+
Err(conflict) => {
141+
errs.push(conflict);
142+
continue;
143+
}
144+
};
145+
}
146+
147+
if !errs.is_empty() { Err(errs) } else { Ok(()) }
148+
}
149+
}

vmm_core/vmotherboard/src/chipset/backing/arc_mutex/services.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use self::device_range::DeviceRangeMapper;
77
use super::device::ArcMutexChipsetServicesFinalize;
88
use super::state_unit::ArcMutexChipsetDeviceUnit;
99
use crate::BusIdPci;
10+
use crate::BusIdPcieDownstreamPort;
1011
use crate::ChipsetBuilder;
1112
use crate::VmmChipsetDevice;
1213
use crate::chipset::io_ranges::IoRanges;
@@ -182,6 +183,14 @@ impl<'a, 'b> ArcMutexChipsetServices<'a, 'b> {
182183
);
183184
}
184185

186+
pub fn register_static_pcie(&mut self, bus_id: BusIdPcieDownstreamPort) {
187+
self.builder.register_weak_mutex_pcie_device(
188+
bus_id,
189+
self.dev_name.clone(),
190+
self.dev.clone(),
191+
);
192+
}
193+
185194
pub fn new_line(&mut self, id: LineSetId, name: &str, vector: u32) -> LineInterrupt {
186195
let (line_set, _) = self.builder.line_set(id.clone());
187196
match line_set.new_line(vector, format!("{}:{}", self.dev_name, name)) {

vmm_core/vmotherboard/src/chipset/builder/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
use crate::chipset::PciConflict;
5+
use crate::chipset::PcieConflict;
56
use crate::chipset::io_ranges::IoRangeConflict;
67
use std::fmt::Debug;
78
use thiserror::Error;
@@ -21,6 +22,9 @@ pub enum ChipsetBuilderError {
2122
/// detected static pci address conflict
2223
#[error("static pci conflict: {0}")]
2324
PciConflict(PciConflict),
25+
/// detected static pcie port conflict
26+
#[error("static pcie port conflict: {0}")]
27+
PcieConflict(PcieConflict),
2428
}
2529

2630
#[derive(Debug, Error)]

0 commit comments

Comments
 (0)