vmm_core/
device_builder.rs1use anyhow::Context as _;
7use chipset_device_resources::ErasedChipsetDevice;
8use closeable_mutex::CloseableMutex;
9use guestmem::DoorbellRegistration;
10use guestmem::GuestMemory;
11use pci_core::msi::MsiConnection;
12use pci_core::msi::SignalMsi;
13use std::sync::Arc;
14use vm_resource::Resource;
15use vm_resource::ResourceResolver;
16use vm_resource::kind::PciDeviceHandleKind;
17use vmbus_server::Guid;
18use vmbus_server::VmbusServerControl;
19use vmcore::vm_task::VmTaskDriverSource;
20use vmcore::vpci_msi::VpciInterruptMapper;
21use vmotherboard::ArcMutexChipsetDeviceBuilder;
22use vmotherboard::ChipsetBuilder;
23
24pub async fn build_vpci_device(
27 driver_source: &VmTaskDriverSource,
28 resolver: &ResourceResolver,
29 guest_memory: &GuestMemory,
30 vmbus: &VmbusServerControl,
31 instance_id: Guid,
32 resource: Resource<PciDeviceHandleKind>,
33 chipset_builder: &mut ChipsetBuilder<'_>,
34 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
35 mapper: Option<&dyn guestmem::MemoryMapper>,
36 new_virtual_device: impl FnOnce(u64) -> anyhow::Result<(Arc<dyn SignalMsi>, VpciInterruptMapper)>,
37 vtom: Option<u64>,
38) -> anyhow::Result<()> {
39 let device_name = format!("{}:vpci-{instance_id}", resource.id());
40
41 let device_builder = chipset_builder
42 .arc_mutex_device(device_name)
43 .with_external_pci();
44
45 let (device, msi_conn) = resolve_and_add_pci_device(
46 device_builder,
47 driver_source,
48 resolver,
49 guest_memory,
50 resource,
51 doorbell_registration,
52 mapper,
53 )
54 .await?;
55
56 {
57 let device_id = (instance_id.data2 as u64) << 16 | (instance_id.data3 as u64 & 0xfff8);
58 let vpci_bus_name = format!("vpci:{instance_id}");
59 chipset_builder
60 .arc_mutex_device(vpci_bus_name)
61 .try_add_async(async |services| {
62 let (msi_controller, interrupt_mapper) =
63 new_virtual_device(device_id).context(format!(
64 "failed to create virtual device, device_id {device_id} = {} | {}",
65 instance_id.data2,
66 instance_id.data3 as u64 & 0xfff8
67 ))?;
68
69 msi_conn.connect(msi_controller);
70
71 let bus = vpci::bus::VpciBus::new(
72 driver_source,
73 instance_id,
74 device,
75 &mut services.register_mmio(),
76 vmbus,
77 interrupt_mapper,
78 vtom,
79 )
80 .await?;
81
82 anyhow::Ok(bus)
83 })
84 .await?;
85 }
86
87 Ok(())
88}
89
90pub async fn build_pcie_device(
93 chipset_builder: &mut ChipsetBuilder<'_>,
94 port_name: Arc<str>,
95 driver_source: &VmTaskDriverSource,
96 resolver: &ResourceResolver,
97 guest_memory: &GuestMemory,
98 resource: Resource<PciDeviceHandleKind>,
99 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
100 mapper: Option<&dyn guestmem::MemoryMapper>,
101 interrupt_target: Option<Arc<dyn SignalMsi>>,
102) -> anyhow::Result<()> {
103 let dev_name = format!("pcie:{}-{}", port_name, resource.id());
104 let device_builder = chipset_builder
105 .arc_mutex_device(dev_name)
106 .on_pcie_port(vmotherboard::BusId::new(&port_name));
107
108 let (_, msi_conn) = resolve_and_add_pci_device(
109 device_builder,
110 driver_source,
111 resolver,
112 guest_memory,
113 resource,
114 doorbell_registration,
115 mapper,
116 )
117 .await?;
118
119 if let Some(target) = interrupt_target {
120 msi_conn.connect(target);
121 }
122
123 Ok(())
124}
125
126pub async fn resolve_and_add_pci_device(
128 device_builder: ArcMutexChipsetDeviceBuilder<'_, '_, ErasedChipsetDevice>,
129 driver_source: &VmTaskDriverSource,
130 resolver: &ResourceResolver,
131 guest_memory: &GuestMemory,
132 resource: Resource<PciDeviceHandleKind>,
133 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
134 mapper: Option<&dyn guestmem::MemoryMapper>,
135) -> anyhow::Result<(Arc<CloseableMutex<ErasedChipsetDevice>>, MsiConnection)> {
136 let msi_conn = MsiConnection::new();
137
138 let device = {
139 device_builder
140 .try_add_async(async |services| {
141 resolver
142 .resolve(
143 resource,
144 pci_resources::ResolvePciDeviceHandleParams {
145 msi_target: msi_conn.target(),
146 register_mmio: &mut services.register_mmio(),
147 driver_source,
148 guest_memory,
149 doorbell_registration,
150 shared_mem_mapper: mapper,
151 },
152 )
153 .await
154 .map(|r| r.0)
155 })
156 .await?
157 };
158
159 Ok((device, msi_conn))
160}