vmm_core/
device_builder.rs1use anyhow::Context as _;
7use guestmem::DoorbellRegistration;
8use guestmem::GuestMemory;
9use pci_core::msi::MsiInterruptSet;
10use pci_core::msi::MsiInterruptTarget;
11use std::sync::Arc;
12use vm_resource::Resource;
13use vm_resource::ResourceResolver;
14use vm_resource::kind::PciDeviceHandleKind;
15use vmbus_server::Guid;
16use vmbus_server::VmbusServerControl;
17use vmcore::vm_task::VmTaskDriverSource;
18use vmcore::vpci_msi::VpciInterruptMapper;
19use vmotherboard::ChipsetBuilder;
20
21pub async fn build_vpci_device(
24 driver_source: &VmTaskDriverSource,
25 resolver: &ResourceResolver,
26 guest_memory: &GuestMemory,
27 vmbus: &VmbusServerControl,
28 instance_id: Guid,
29 resource: Resource<PciDeviceHandleKind>,
30 chipset_builder: &mut ChipsetBuilder<'_>,
31 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
32 mapper: Option<&dyn guestmem::MemoryMapper>,
33 new_virtual_device: impl FnOnce(
34 u64,
35 ) -> anyhow::Result<(
36 Arc<dyn MsiInterruptTarget>,
37 VpciInterruptMapper,
38 )>,
39) -> anyhow::Result<()> {
40 let device_name = format!("{}:vpci-{instance_id}", resource.id());
41
42 let mut msi_set = MsiInterruptSet::new();
43
44 let device = {
45 chipset_builder
46 .arc_mutex_device(device_name)
47 .with_external_pci()
48 .try_add_async(async |services| {
49 resolver
50 .resolve(
51 resource,
52 pci_resources::ResolvePciDeviceHandleParams {
53 register_msi: &mut msi_set,
54 register_mmio: &mut services.register_mmio(),
55 driver_source,
56 guest_memory,
57 doorbell_registration,
58 shared_mem_mapper: mapper,
59 },
60 )
61 .await
62 .map(|r| r.0)
63 })
64 .await?
65 };
66
67 {
68 let device_id = (instance_id.data2 as u64) << 16 | (instance_id.data3 as u64 & 0xfff8);
69 let vpci_bus_name = format!("vpci:{instance_id}");
70 chipset_builder
71 .arc_mutex_device(vpci_bus_name)
72 .try_add_async(async |services| {
73 let (msi_controller, interrupt_mapper) =
74 new_virtual_device(device_id).context("failed to create virtual device")?;
75
76 msi_set.connect(msi_controller.as_ref());
77
78 let bus = vpci::bus::VpciBus::new(
79 driver_source,
80 instance_id,
81 device,
82 &mut services.register_mmio(),
83 vmbus,
84 interrupt_mapper,
85 )
86 .await?;
87
88 anyhow::Ok(bus)
89 })
90 .await?;
91 }
92
93 Ok(())
94}