mod errors;
use self::errors::ChipsetBuilderError;
use self::errors::ErrorListExt;
use self::errors::FinalChipsetBuilderError;
use super::backing::arc_mutex::device::ArcMutexChipsetDeviceBuilder;
use super::backing::arc_mutex::pci::BusResolverWeakMutexPci;
use super::backing::arc_mutex::pci::RegisterWeakMutexPci;
use super::backing::arc_mutex::pci::WeakMutexPciEntry;
use super::backing::arc_mutex::services::ArcMutexChipsetServices;
use super::backing::arc_mutex::state_unit::ArcMutexChipsetDeviceUnit;
use crate::chipset::io_ranges::IoRanges;
use crate::chipset::Chipset;
use crate::BusIdPci;
use crate::DebugEventHandler;
use crate::VmmChipsetDevice;
use chipset_device::ChipsetDevice;
use chipset_device_resources::LineSetId;
use closeable_mutex::CloseableMutex;
use pal_async::task::Spawn;
use pal_async::task::Task;
use state_unit::SpawnedUnit;
use state_unit::StateUnits;
use state_unit::UnitHandle;
use std::ops::RangeInclusive;
use std::sync::Arc;
use std::sync::Weak;
use vmcore::line_interrupt::LineSetTarget;
use vmcore::vm_task::VmTaskDriverSource;
use vmcore::vmtime::VmTimeSource;
pub struct ChipsetDevices {
chipset_unit: UnitHandle,
_chipset_task: Task<()>,
_arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
_line_set_units: Vec<SpawnedUnit<()>>,
}
impl ChipsetDevices {
pub fn chipset_unit(&self) -> &UnitHandle {
&self.chipset_unit
}
}
#[derive(Default)]
pub(crate) struct BusResolver {
pci: BusResolverWeakMutexPci,
}
pub struct ChipsetBuilder<'a> {
pub(crate) vm_chipset: Chipset,
pub(crate) bus_resolver: BusResolver,
pub(crate) units: &'a StateUnits,
pub(crate) driver_source: &'a VmTaskDriverSource,
pub(crate) vmtime: &'a VmTimeSource,
pub(crate) vmtime_unit: &'a UnitHandle,
pub(crate) chipset_unit: UnitHandle,
chipset_recv: mesh::Receiver<state_unit::StateRequest>,
line_sets: super::line_sets::LineSets,
arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
}
impl<'a> ChipsetBuilder<'a> {
pub(crate) fn new(
driver_source: &'a VmTaskDriverSource,
units: &'a StateUnits,
debug_event_handler: Arc<dyn DebugEventHandler>,
vmtime: &'a VmTimeSource,
vmtime_unit: &'a UnitHandle,
trace_unknown_pio: bool,
trace_unknown_mmio: bool,
fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
) -> Self {
let (send, chipset_recv) = mesh::channel();
let chipset_unit = units.add("chipset").build(send).unwrap();
Self {
vm_chipset: Chipset {
mmio_ranges: IoRanges::new(trace_unknown_mmio, fallback_mmio_device),
pio_ranges: IoRanges::new(trace_unknown_pio, None),
pic: None,
eoi_handler: None,
debug_event_handler,
},
bus_resolver: BusResolver::default(),
units,
driver_source,
vmtime,
vmtime_unit,
chipset_unit,
chipset_recv,
line_sets: super::line_sets::LineSets::new(),
arc_mutex_device_units: Vec::new(),
}
}
pub(crate) fn register_arc_mutex_device_unit(
&mut self,
unit: SpawnedUnit<ArcMutexChipsetDeviceUnit>,
) {
self.arc_mutex_device_units.push(unit)
}
pub(crate) fn register_weak_mutex_pci_bus(
&mut self,
bus_id: BusIdPci,
bus: Box<dyn RegisterWeakMutexPci>,
) {
let existing = self.bus_resolver.pci.buses.insert(bus_id.clone(), bus);
assert!(
existing.is_none(),
"shouldn't be possible to have duplicate bus IDs: {:?}",
bus_id
)
}
pub(crate) fn register_weak_mutex_pci_device(
&mut self,
bus_id: BusIdPci,
bdf: (u8, u8, u8),
name: Arc<str>,
dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
) {
self.bus_resolver
.pci
.devices
.entry(bus_id)
.or_default()
.push(WeakMutexPciEntry { bdf, name, dev });
}
pub(crate) fn line_set(
&mut self,
id: LineSetId,
) -> (&vmcore::line_interrupt::LineSet, &UnitHandle) {
self.line_sets.line_set(self.driver_source, self.units, id)
}
#[must_use]
pub(crate) fn try_set_pic(
&mut self,
pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
) -> bool {
if self.vm_chipset.pic.is_some() {
return false;
}
self.vm_chipset.pic = pic;
true
}
#[must_use]
pub(crate) fn try_set_eoi_handler(
&mut self,
eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
) -> bool {
if self.vm_chipset.eoi_handler.is_some() {
return false;
}
self.vm_chipset.eoi_handler = eoi_handler;
true
}
pub fn arc_mutex_device<'b, T: VmmChipsetDevice>(
&'b mut self,
dev_name: impl Into<Arc<str>>,
) -> ArcMutexChipsetDeviceBuilder<'b, 'a, T> {
ArcMutexChipsetDeviceBuilder::new(dev_name.into(), |dev, name| {
ArcMutexChipsetServices::new(self, dev.clone(), name)
})
}
pub fn build(mut self) -> Result<(Arc<Chipset>, ChipsetDevices), FinalChipsetBuilderError> {
let mut errs = None;
for conflict in (self.vm_chipset.mmio_ranges).take_static_registration_conflicts() {
errs.append(ChipsetBuilderError::MmioConflict(conflict));
}
for conflict in (self.vm_chipset.pio_ranges).take_static_registration_conflicts() {
errs.append(ChipsetBuilderError::PioConflict(conflict));
}
{
let BusResolver { pci } = self.bus_resolver;
match pci.resolve() {
Ok(()) => {}
Err(conflicts) => {
for conflict in conflicts {
errs.append(ChipsetBuilderError::PciConflict(conflict));
}
}
}
}
if let Some(err) = errs {
return Err(FinalChipsetBuilderError(err));
}
let vm_chipset = Arc::new(self.vm_chipset);
let chipset_task = self.driver_source.simple().spawn("chipset-unit", {
let vm_chipset = vm_chipset.clone();
let mut recv = self.chipset_recv;
async move {
while let Ok(req) = recv.recv().await {
req.apply(&mut chipset_unit::ChipsetUnit(&vm_chipset)).await;
}
}
});
let devices = ChipsetDevices {
chipset_unit: self.chipset_unit,
_chipset_task: chipset_task,
_arc_mutex_device_units: self.arc_mutex_device_units,
_line_set_units: self.line_sets.units,
};
Ok((vm_chipset, devices))
}
pub fn add_external_line_target(
&mut self,
id: LineSetId,
source_range: RangeInclusive<u32>,
target_start: u32,
debug_label: &str,
target: Arc<dyn LineSetTarget>,
) {
self.line_set(id)
.0
.add_target(source_range, target_start, debug_label, target)
}
}
mod chipset_unit {
use crate::Chipset;
use inspect::InspectMut;
use state_unit::StateUnit;
use vmcore::save_restore::RestoreError;
use vmcore::save_restore::SaveError;
use vmcore::save_restore::SavedStateBlob;
#[derive(InspectMut)]
#[inspect(transparent)]
pub struct ChipsetUnit<'a>(pub &'a Chipset);
impl StateUnit for ChipsetUnit<'_> {
async fn start(&mut self) {}
async fn stop(&mut self) {}
async fn reset(&mut self) -> anyhow::Result<()> {
Ok(())
}
async fn save(&mut self) -> Result<Option<SavedStateBlob>, SaveError> {
Ok(None)
}
async fn restore(&mut self, _buffer: SavedStateBlob) -> Result<(), RestoreError> {
Err(RestoreError::SavedStateNotSupported)
}
}
}