vmotherboard/chipset/builder/
mod.rs1mod errors;
7
8use self::errors::ChipsetBuilderError;
9use self::errors::ErrorListExt;
10use self::errors::FinalChipsetBuilderError;
11use super::backing::arc_mutex::device::ArcMutexChipsetDeviceBuilder;
12use super::backing::arc_mutex::pci::BusResolverWeakMutexPci;
13use super::backing::arc_mutex::pci::BusResolverWeakMutexPcie;
14use super::backing::arc_mutex::pci::RegisterWeakMutexPci;
15use super::backing::arc_mutex::pci::RegisterWeakMutexPcie;
16use super::backing::arc_mutex::pci::WeakMutexPciEntry;
17use super::backing::arc_mutex::pci::WeakMutexPcieDeviceEntry;
18use super::backing::arc_mutex::services::ArcMutexChipsetServices;
19use super::backing::arc_mutex::state_unit::ArcMutexChipsetDeviceUnit;
20use crate::BusId;
21use crate::BusIdPci;
22use crate::BusIdPcieDownstreamPort;
23use crate::BusIdPcieEnumerator;
24use crate::DebugEventHandler;
25use crate::VmmChipsetDevice;
26use crate::chipset::Chipset;
27use crate::chipset::io_ranges::IoRanges;
28use anyhow::Context as _;
29use arc_cyclic_builder::ArcCyclicBuilderExt as _;
30use chipset_device::ChipsetDevice;
31use chipset_device::mmio::RegisterMmioIntercept;
32use chipset_device_resources::LineSetId;
33use closeable_mutex::CloseableMutex;
34use pal_async::task::Spawn;
35use pal_async::task::Task;
36use state_unit::SpawnedUnit;
37use state_unit::StateUnits;
38use state_unit::UnitHandle;
39use std::ops::RangeInclusive;
40use std::sync::Arc;
41use std::sync::Weak;
42use vmcore::line_interrupt::LineSetTarget;
43use vmcore::vm_task::VmTaskDriverSource;
44use vmcore::vmtime::VmTimeSource;
45
46pub struct ChipsetDevices {
48 chipset_unit: UnitHandle,
49 _chipset_task: Task<()>,
50 _arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
51 _line_set_units: Vec<SpawnedUnit<()>>,
52 mmio_ranges: IoRanges<u64>,
53}
54
55impl ChipsetDevices {
56 pub fn chipset_unit(&self) -> &UnitHandle {
61 &self.chipset_unit
62 }
63
64 pub async fn add_dyn_device<T: VmmChipsetDevice>(
66 &self,
67 driver_source: &VmTaskDriverSource,
68 units: &StateUnits,
69 name: impl Into<Arc<str>>,
70 f: impl AsyncFnOnce(&mut dyn RegisterMmioIntercept) -> anyhow::Result<T>,
71 ) -> anyhow::Result<(DynamicDeviceUnit, Arc<CloseableMutex<T>>)> {
72 let name = name.into();
73 let arc_builder = Arc::<CloseableMutex<T>>::new_cyclic_builder();
74 let device = f(
75 &mut super::backing::arc_mutex::services::register_mmio_for_device(
76 name.clone(),
77 arc_builder.weak(),
78 self.mmio_ranges.clone(),
79 ),
80 )
81 .await?;
82 let device = arc_builder.build(CloseableMutex::new(device));
83 let device_unit = ArcMutexChipsetDeviceUnit::new(device.clone(), false);
84 let builder = units.add(name).dependency_of(self.chipset_unit());
85 let unit = builder
86 .spawn(driver_source.simple(), |recv| device_unit.run(recv))
87 .context("name in use")?;
88
89 Ok((DynamicDeviceUnit(unit), device))
90 }
91}
92
93pub struct DynamicDeviceUnit(SpawnedUnit<ArcMutexChipsetDeviceUnit>);
95
96impl DynamicDeviceUnit {
97 pub async fn remove(self) {
99 self.0.remove().await;
100 }
101}
102
103#[derive(Default)]
104pub(crate) struct BusResolver {
105 pci: BusResolverWeakMutexPci,
106 pcie: BusResolverWeakMutexPcie,
107}
108
109pub struct ChipsetBuilder<'a> {
111 pub(crate) vm_chipset: Chipset,
113
114 pub(crate) bus_resolver: BusResolver,
115
116 pub(crate) units: &'a StateUnits,
118 pub(crate) driver_source: &'a VmTaskDriverSource,
119 pub(crate) vmtime: &'a VmTimeSource,
120 pub(crate) vmtime_unit: &'a UnitHandle,
121
122 pub(crate) chipset_unit: UnitHandle,
125 chipset_recv: mesh::Receiver<state_unit::StateRequest>,
126
127 line_sets: super::line_sets::LineSets,
128
129 arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
131}
132
133impl<'a> ChipsetBuilder<'a> {
134 pub(crate) fn new(
135 driver_source: &'a VmTaskDriverSource,
136 units: &'a StateUnits,
137 debug_event_handler: Arc<dyn DebugEventHandler>,
138 vmtime: &'a VmTimeSource,
139 vmtime_unit: &'a UnitHandle,
140 trace_unknown_pio: bool,
141 trace_unknown_mmio: bool,
142 fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
143 ) -> Self {
144 let (send, chipset_recv) = mesh::channel();
145 let chipset_unit = units.add("chipset").build(send).unwrap();
146
147 Self {
148 vm_chipset: Chipset {
149 mmio_ranges: IoRanges::new(trace_unknown_mmio, fallback_mmio_device),
150 pio_ranges: IoRanges::new(trace_unknown_pio, None),
151
152 pic: None,
153 eoi_handler: None,
154 debug_event_handler,
155 },
156
157 bus_resolver: BusResolver::default(),
158
159 units,
160 driver_source,
161 vmtime,
162 vmtime_unit,
163
164 chipset_unit,
165 chipset_recv,
166
167 line_sets: super::line_sets::LineSets::new(),
168
169 arc_mutex_device_units: Vec::new(),
170 }
171 }
172
173 pub(crate) fn register_arc_mutex_device_unit(
174 &mut self,
175 unit: SpawnedUnit<ArcMutexChipsetDeviceUnit>,
176 ) {
177 self.arc_mutex_device_units.push(unit)
178 }
179
180 pub(crate) fn register_weak_mutex_pci_bus(
181 &mut self,
182 bus_id: BusIdPci,
183 bus: Box<dyn RegisterWeakMutexPci>,
184 ) {
185 let existing = self.bus_resolver.pci.buses.insert(bus_id.clone(), bus);
186 assert!(
187 existing.is_none(),
188 "shouldn't be possible to have duplicate bus IDs: {:?}",
189 bus_id
190 )
191 }
192
193 pub(crate) fn register_weak_mutex_pci_device(
194 &mut self,
195 bus_id: BusIdPci,
196 bdf: (u8, u8, u8),
197 name: Arc<str>,
198 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
199 ) {
200 self.bus_resolver
201 .pci
202 .devices
203 .entry(bus_id)
204 .or_default()
205 .push(WeakMutexPciEntry { bdf, name, dev });
206 }
207
208 pub fn register_weak_mutex_pcie_enumerator(
211 &mut self,
212 bus_id: BusIdPcieEnumerator,
213 enumerator: Box<dyn RegisterWeakMutexPcie>,
214 ) {
215 let downstream_ports = enumerator.downstream_ports();
216 let existing = self
217 .bus_resolver
218 .pcie
219 .enumerators
220 .insert(bus_id.clone(), enumerator);
221 assert!(
222 existing.is_none(),
223 "duplicate pcie enumerator ID: {:?}",
224 bus_id
225 );
226
227 for (port_number, port_name) in downstream_ports {
228 let existing = self
229 .bus_resolver
230 .pcie
231 .ports
232 .insert(BusId::new(&port_name), (port_number, bus_id.clone()));
233 assert!(
234 existing.is_none(),
235 "duplicate pcie port ID: {:?}",
236 port_name
237 );
238 }
239 }
240
241 pub(crate) fn register_weak_mutex_pcie_device(
242 &mut self,
243 bus_id_port: BusIdPcieDownstreamPort,
244 name: Arc<str>,
245 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
246 ) {
247 self.bus_resolver
248 .pcie
249 .devices
250 .push(WeakMutexPcieDeviceEntry {
251 bus_id_port,
252 name,
253 dev,
254 });
255 }
256
257 pub(crate) fn line_set(
258 &mut self,
259 id: LineSetId,
260 ) -> (&vmcore::line_interrupt::LineSet, &UnitHandle) {
261 self.line_sets.line_set(self.driver_source, self.units, id)
262 }
263
264 #[must_use]
265 pub(crate) fn try_set_pic(
266 &mut self,
267 pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
268 ) -> bool {
269 if self.vm_chipset.pic.is_some() {
270 return false;
271 }
272 self.vm_chipset.pic = pic;
273 true
274 }
275
276 #[must_use]
277 pub(crate) fn try_set_eoi_handler(
278 &mut self,
279 eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
280 ) -> bool {
281 if self.vm_chipset.eoi_handler.is_some() {
282 return false;
283 }
284 self.vm_chipset.eoi_handler = eoi_handler;
285 true
286 }
287
288 pub fn arc_mutex_device<'b, T: VmmChipsetDevice>(
291 &'b mut self,
292 dev_name: impl Into<Arc<str>>,
293 ) -> ArcMutexChipsetDeviceBuilder<'b, 'a, T> {
294 ArcMutexChipsetDeviceBuilder::new(dev_name.into(), |dev, name| {
295 ArcMutexChipsetServices::new(self, dev.clone(), name)
296 })
297 }
298
299 pub fn build(mut self) -> Result<(Arc<Chipset>, ChipsetDevices), FinalChipsetBuilderError> {
301 let mut errs = None;
302
303 for conflict in (self.vm_chipset.mmio_ranges).take_static_registration_conflicts() {
304 errs.append(ChipsetBuilderError::MmioConflict(conflict));
305 }
306
307 for conflict in (self.vm_chipset.pio_ranges).take_static_registration_conflicts() {
308 errs.append(ChipsetBuilderError::PioConflict(conflict));
309 }
310
311 {
312 let BusResolver { pci, pcie } = self.bus_resolver;
313
314 match pci.resolve() {
315 Ok(()) => {}
316 Err(conflicts) => {
317 for conflict in conflicts {
318 errs.append(ChipsetBuilderError::PciConflict(conflict));
319 }
320 }
321 }
322
323 match pcie.resolve() {
324 Ok(()) => {}
325 Err(conflicts) => {
326 for conflict in conflicts {
327 errs.append(ChipsetBuilderError::PcieConflict(conflict));
328 }
329 }
330 }
331 }
332
333 if let Some(err) = errs {
334 return Err(FinalChipsetBuilderError(err));
335 }
336
337 let mmio_ranges = self.vm_chipset.mmio_ranges.clone();
338
339 let vm_chipset = Arc::new(self.vm_chipset);
341 let chipset_task = self.driver_source.simple().spawn("chipset-unit", {
342 let vm_chipset = vm_chipset.clone();
343 let mut recv = self.chipset_recv;
344 async move {
345 while let Ok(req) = recv.recv().await {
346 req.apply(&mut chipset_unit::ChipsetUnit(&vm_chipset)).await;
347 }
348 }
349 });
350
351 let devices = ChipsetDevices {
352 chipset_unit: self.chipset_unit,
353 _chipset_task: chipset_task,
354 _arc_mutex_device_units: self.arc_mutex_device_units,
355 _line_set_units: self.line_sets.units,
356 mmio_ranges,
357 };
358
359 Ok((vm_chipset, devices))
360 }
361
362 pub fn add_external_line_target(
364 &mut self,
365 id: LineSetId,
366 source_range: RangeInclusive<u32>,
367 target_start: u32,
368 debug_label: &str,
369 target: Arc<dyn LineSetTarget>,
370 ) {
371 self.line_set(id)
372 .0
373 .add_target(source_range, target_start, debug_label, target)
374 }
375}
376
377mod chipset_unit {
378 use crate::Chipset;
379 use inspect::InspectMut;
380 use state_unit::StateUnit;
381 use vmcore::save_restore::RestoreError;
382 use vmcore::save_restore::SaveError;
383 use vmcore::save_restore::SavedStateBlob;
384
385 #[derive(InspectMut)]
386 #[inspect(transparent)]
387 pub struct ChipsetUnit<'a>(pub &'a Chipset);
388
389 impl StateUnit for ChipsetUnit<'_> {
390 async fn start(&mut self) {}
391
392 async fn stop(&mut self) {}
393
394 async fn reset(&mut self) -> anyhow::Result<()> {
395 Ok(())
396 }
397
398 async fn save(&mut self) -> Result<Option<SavedStateBlob>, SaveError> {
399 Ok(None)
400 }
401
402 async fn restore(&mut self, _buffer: SavedStateBlob) -> Result<(), RestoreError> {
403 Err(RestoreError::SavedStateNotSupported)
404 }
405 }
406}