Skip to main content

vmotherboard/chipset/builder/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Exports [`ChipsetBuilder`].
5
6mod 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::pci::WeakMutexPcieRciepEntry;
19use super::backing::arc_mutex::services::ArcMutexChipsetServices;
20use super::backing::arc_mutex::state_unit::ArcMutexChipsetDeviceUnit;
21use crate::BusId;
22use crate::BusIdPci;
23use crate::BusIdPcieDownstreamPort;
24use crate::BusIdPcieEnumerator;
25use crate::DebugEventHandler;
26use crate::VmmChipsetDevice;
27use crate::chipset::Chipset;
28use crate::chipset::io_ranges::IoRanges;
29use anyhow::Context as _;
30use arc_cyclic_builder::ArcCyclicBuilderExt as _;
31use chipset_device::ChipsetDevice;
32use chipset_device::mmio::RegisterMmioIntercept;
33use chipset_device_resources::LineSetId;
34use closeable_mutex::CloseableMutex;
35use pal_async::task::Spawn;
36use pal_async::task::Task;
37use parking_lot::Mutex;
38use state_unit::SpawnedUnit;
39use state_unit::StateUnits;
40use state_unit::UnitHandle;
41use std::ops::RangeInclusive;
42use std::sync::Arc;
43use std::sync::Weak;
44use vmcore::line_interrupt::LineSetTarget;
45use vmcore::vm_task::VmTaskDriverSource;
46use vmcore::vmtime::VmTimeSource;
47
48/// A (type erased) bundle of state unit handles for added devices.
49pub struct ChipsetDevices {
50    chipset_unit: UnitHandle,
51    _chipset_task: Task<()>,
52    _arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
53    _line_set_units: Vec<SpawnedUnit<()>>,
54    mmio_ranges: IoRanges<u64>,
55}
56
57impl ChipsetDevices {
58    /// The root chipset unit handle.
59    ///
60    /// All devices that have MMIO, PIO, or PCI callbacks have a "stop after"
61    /// dependency on this handle.
62    pub fn chipset_unit(&self) -> &UnitHandle {
63        &self.chipset_unit
64    }
65
66    /// Adds a dynamically managed device to the chipset at runtime.
67    pub async fn add_dyn_device<T: VmmChipsetDevice>(
68        &self,
69        driver_source: &VmTaskDriverSource,
70        units: &StateUnits,
71        name: impl Into<Arc<str>>,
72        f: impl AsyncFnOnce(&mut (dyn RegisterMmioIntercept + Send)) -> anyhow::Result<T>,
73    ) -> anyhow::Result<(DynamicDeviceUnit, Arc<CloseableMutex<T>>)> {
74        let name = name.into();
75        let arc_builder = Arc::<CloseableMutex<T>>::new_cyclic_builder();
76        let device = f(
77            &mut super::backing::arc_mutex::services::register_mmio_for_device(
78                name.clone(),
79                arc_builder.weak(),
80                self.mmio_ranges.clone(),
81            ),
82        )
83        .await?;
84        let device = arc_builder.build(CloseableMutex::new(device));
85        let device_unit = ArcMutexChipsetDeviceUnit::new(device.clone(), false);
86        let builder = units.add(name).dependency_of(self.chipset_unit());
87        let unit = builder
88            .spawn(driver_source.simple(), |recv| device_unit.run(recv))
89            .context("name in use")?;
90
91        Ok((DynamicDeviceUnit(unit), device))
92    }
93}
94
95/// A unit handle for a dynamically managed device.
96pub struct DynamicDeviceUnit(SpawnedUnit<ArcMutexChipsetDeviceUnit>);
97
98impl DynamicDeviceUnit {
99    /// Removes and drops the dynamically managed device.
100    pub async fn remove(self) {
101        self.0.remove().await;
102    }
103}
104
105#[derive(Default)]
106pub(crate) struct BusResolver {
107    pci: BusResolverWeakMutexPci,
108    pcie: BusResolverWeakMutexPcie,
109}
110
111/// Mutable state behind the builder's interior mutex.
112pub(crate) struct ChipsetBuilderInner {
113    pub(crate) vm_chipset: Chipset,
114    pub(crate) bus_resolver: BusResolver,
115    pub(crate) line_sets: super::line_sets::LineSets,
116    pub(crate) arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
117    chipset_recv: mesh::Receiver<state_unit::StateRequest>,
118}
119
120/// A builder for [`Chipset`].
121///
122/// Methods on this type use interior mutability so that multiple devices
123/// can be constructed concurrently via `arc_mutex_device().try_add_async()`.
124pub struct ChipsetBuilder<'a> {
125    pub(crate) inner: Mutex<ChipsetBuilderInner>,
126
127    // External runtime dependencies (shared, read-only)
128    pub(crate) units: &'a StateUnits,
129    pub(crate) driver_source: &'a VmTaskDriverSource,
130    pub(crate) vmtime: &'a VmTimeSource,
131    pub(crate) vmtime_unit: &'a UnitHandle,
132
133    // Root chipset state-unit
134    pub(crate) chipset_unit: UnitHandle,
135}
136
137impl<'a> ChipsetBuilder<'a> {
138    pub(crate) fn new(
139        driver_source: &'a VmTaskDriverSource,
140        units: &'a StateUnits,
141        debug_event_handler: Arc<dyn DebugEventHandler>,
142        vmtime: &'a VmTimeSource,
143        vmtime_unit: &'a UnitHandle,
144        trace_unknown_pio: bool,
145        trace_unknown_mmio: bool,
146        fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
147    ) -> Self {
148        let (send, chipset_recv) = mesh::channel();
149        let chipset_unit = units.add("chipset").build(send).unwrap();
150
151        Self {
152            inner: Mutex::new(ChipsetBuilderInner {
153                vm_chipset: Chipset {
154                    mmio_ranges: IoRanges::new(trace_unknown_mmio, fallback_mmio_device),
155                    pio_ranges: IoRanges::new(trace_unknown_pio, None),
156
157                    pic: None,
158                    eoi_handler: None,
159                    debug_event_handler,
160                },
161
162                bus_resolver: BusResolver::default(),
163                line_sets: super::line_sets::LineSets::new(),
164                arc_mutex_device_units: Vec::new(),
165                chipset_recv,
166            }),
167
168            units,
169            driver_source,
170            vmtime,
171            vmtime_unit,
172
173            chipset_unit,
174        }
175    }
176
177    pub(crate) fn register_weak_mutex_pci_bus(
178        &self,
179        bus_id: BusIdPci,
180        bus: Box<dyn RegisterWeakMutexPci>,
181    ) {
182        let mut inner = self.inner.lock();
183        let existing = inner.bus_resolver.pci.buses.insert(bus_id.clone(), bus);
184        assert!(
185            existing.is_none(),
186            "shouldn't be possible to have duplicate bus IDs: {:?}",
187            bus_id
188        )
189    }
190
191    pub(crate) fn register_weak_mutex_pci_device(
192        &self,
193        bus_id: BusIdPci,
194        bdf: (u8, u8, u8),
195        name: Arc<str>,
196        dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
197    ) {
198        self.inner
199            .lock()
200            .bus_resolver
201            .pci
202            .devices
203            .entry(bus_id)
204            .or_default()
205            .push(WeakMutexPciEntry { bdf, name, dev });
206    }
207
208    /// Register a PCIe enumerator (ex. root complex or switch), and all of
209    /// it's downstream ports.
210    pub fn register_weak_mutex_pcie_enumerator(
211        &self,
212        bus_id: BusIdPcieEnumerator,
213        enumerator: Box<dyn RegisterWeakMutexPcie>,
214    ) {
215        let downstream_ports = enumerator.downstream_ports();
216        let mut inner = self.inner.lock();
217        let existing = inner
218            .bus_resolver
219            .pcie
220            .enumerators
221            .insert(bus_id.clone(), enumerator);
222        assert!(
223            existing.is_none(),
224            "duplicate pcie enumerator ID: {:?}",
225            bus_id
226        );
227
228        for port_info in downstream_ports {
229            let existing = inner.bus_resolver.pcie.ports.insert(
230                BusId::new(&port_info.name),
231                (port_info.devfn, bus_id.clone()),
232            );
233            assert!(
234                existing.is_none(),
235                "duplicate pcie port ID: {:?}",
236                port_info.name
237            );
238        }
239    }
240
241    pub(crate) fn register_weak_mutex_pcie_device(
242        &self,
243        bus_id_port: BusIdPcieDownstreamPort,
244        name: Arc<str>,
245        dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
246    ) {
247        self.inner
248            .lock()
249            .bus_resolver
250            .pcie
251            .devices
252            .push(WeakMutexPcieDeviceEntry {
253                bus_id_port,
254                name,
255                dev,
256            });
257    }
258
259    pub(crate) fn register_weak_mutex_pcie_rciep(
260        &self,
261        bus_id_enumerator: BusIdPcieEnumerator,
262        devfn: u8,
263        name: Arc<str>,
264        dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
265    ) {
266        self.inner
267            .lock()
268            .bus_resolver
269            .pcie
270            .rcieps
271            .push(WeakMutexPcieRciepEntry {
272                bus_id_enumerator,
273                devfn,
274                name,
275                dev,
276            });
277    }
278
279    /// Add a new [`ChipsetDevice`](chipset_device::ChipsetDevice) to the
280    /// chipset. **`dev_name` must be unique!**
281    pub fn arc_mutex_device<'b, T: VmmChipsetDevice>(
282        &'b self,
283        dev_name: impl Into<Arc<str>>,
284    ) -> ArcMutexChipsetDeviceBuilder<'b, 'a, T> {
285        ArcMutexChipsetDeviceBuilder::new(dev_name.into(), |dev, name| {
286            ArcMutexChipsetServices::new(self, dev.clone(), name)
287        })
288    }
289
290    /// Wrap up device construction, returning the completed chipset and devices
291    pub fn build(self) -> Result<(Arc<Chipset>, ChipsetDevices), FinalChipsetBuilderError> {
292        let mut inner = self.inner.into_inner();
293        let mut errs = None;
294
295        for conflict in (inner.vm_chipset.mmio_ranges).take_static_registration_conflicts() {
296            errs.append(ChipsetBuilderError::MmioConflict(conflict));
297        }
298
299        for conflict in (inner.vm_chipset.pio_ranges).take_static_registration_conflicts() {
300            errs.append(ChipsetBuilderError::PioConflict(conflict));
301        }
302
303        {
304            let BusResolver { pci, pcie } = inner.bus_resolver;
305
306            match pci.resolve() {
307                Ok(()) => {}
308                Err(conflicts) => {
309                    for conflict in conflicts {
310                        errs.append(ChipsetBuilderError::PciConflict(conflict));
311                    }
312                }
313            }
314
315            match pcie.resolve() {
316                Ok(()) => {}
317                Err(conflicts) => {
318                    for conflict in conflicts {
319                        errs.append(ChipsetBuilderError::PcieConflict(conflict));
320                    }
321                }
322            }
323        }
324
325        if let Some(err) = errs {
326            return Err(FinalChipsetBuilderError(err));
327        }
328
329        let mmio_ranges = inner.vm_chipset.mmio_ranges.clone();
330
331        // Spawn a task for the chipset unit.
332        let vm_chipset = Arc::new(inner.vm_chipset);
333        let chipset_task = self.driver_source.simple().spawn("chipset-unit", {
334            let vm_chipset = vm_chipset.clone();
335            let mut recv = inner.chipset_recv;
336            async move {
337                while let Ok(req) = recv.recv().await {
338                    req.apply(&mut chipset_unit::ChipsetUnit(&vm_chipset)).await;
339                }
340            }
341        });
342
343        let devices = ChipsetDevices {
344            chipset_unit: self.chipset_unit,
345            _chipset_task: chipset_task,
346            _arc_mutex_device_units: inner.arc_mutex_device_units,
347            _line_set_units: inner.line_sets.units,
348            mmio_ranges,
349        };
350
351        Ok((vm_chipset, devices))
352    }
353
354    /// Add a new line set target from an external source.
355    pub fn add_external_line_target(
356        &self,
357        id: LineSetId,
358        source_range: RangeInclusive<u32>,
359        target_start: u32,
360        debug_label: &str,
361        target: Arc<dyn LineSetTarget>,
362    ) {
363        let mut inner = self.inner.lock();
364        let (line_set, _) = inner.line_sets.line_set(self.driver_source, self.units, id);
365        line_set.add_target(source_range, target_start, debug_label, target)
366    }
367}
368
369mod chipset_unit {
370    use crate::Chipset;
371    use inspect::InspectMut;
372    use state_unit::StateUnit;
373    use vmcore::save_restore::RestoreError;
374    use vmcore::save_restore::SaveError;
375    use vmcore::save_restore::SavedStateBlob;
376
377    #[derive(InspectMut)]
378    #[inspect(transparent)]
379    pub struct ChipsetUnit<'a>(pub &'a Chipset);
380
381    impl StateUnit for ChipsetUnit<'_> {
382        async fn start(&mut self) {}
383
384        async fn stop(&mut self) {}
385
386        async fn reset(&mut self) -> anyhow::Result<()> {
387            Ok(())
388        }
389
390        async fn save(&mut self) -> Result<Option<SavedStateBlob>, SaveError> {
391            Ok(None)
392        }
393
394        async fn restore(&mut self, _buffer: SavedStateBlob) -> Result<(), RestoreError> {
395            Err(RestoreError::SavedStateNotSupported)
396        }
397    }
398}