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::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
46/// A (type erased) bundle of state unit handles for added devices.
47pub 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    /// The root chipset unit handle.
57    ///
58    /// All devices that have MMIO, PIO, or PCI callbacks have a "stop after"
59    /// dependency on this handle.
60    pub fn chipset_unit(&self) -> &UnitHandle {
61        &self.chipset_unit
62    }
63
64    /// Adds a dynamically managed device to the chipset at runtime.
65    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
93/// A unit handle for a dynamically managed device.
94pub struct DynamicDeviceUnit(SpawnedUnit<ArcMutexChipsetDeviceUnit>);
95
96impl DynamicDeviceUnit {
97    /// Removes and drops the dynamically managed device.
98    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
109/// A builder for [`Chipset`]
110pub struct ChipsetBuilder<'a> {
111    // The chipset that's getting built-up
112    pub(crate) vm_chipset: Chipset,
113
114    pub(crate) bus_resolver: BusResolver,
115
116    // External runtime dependencies
117    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    // Root chipset state-unit (which devices may need to take a dependency on,
123    // if they use any Chipset-specific services)
124    pub(crate) chipset_unit: UnitHandle,
125    chipset_recv: mesh::Receiver<state_unit::StateRequest>,
126
127    line_sets: super::line_sets::LineSets,
128
129    // Fields related to `Arc + Mutex`-backed `ChipsetDevice` construction
130    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    /// 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        &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    /// Add a new [`ChipsetDevice`](chipset_device::ChipsetDevice) to the
289    /// chipset. **`dev_name` must be unique!**
290    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    /// Wrap up device construction, returning the completed chipset and devices
300    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        // Spawn a task for the chipset unit.
340        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    /// Add a new line set target from an external source.
363    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}