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::MsiInterruptSet;
12use pci_core::msi::MsiInterruptTarget;
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(
37 u64,
38 ) -> anyhow::Result<(
39 Arc<dyn MsiInterruptTarget>,
40 VpciInterruptMapper,
41 )>,
42 vtom: Option<u64>,
43) -> anyhow::Result<()> {
44 let device_name = format!("{}:vpci-{instance_id}", resource.id());
45
46 let device_builder = chipset_builder
47 .arc_mutex_device(device_name)
48 .with_external_pci();
49
50 let (device, msi_set) = resolve_and_add_pci_device(
51 device_builder,
52 driver_source,
53 resolver,
54 guest_memory,
55 resource,
56 doorbell_registration,
57 mapper,
58 )
59 .await?;
60
61 {
62 let device_id = (instance_id.data2 as u64) << 16 | (instance_id.data3 as u64 & 0xfff8);
63 let vpci_bus_name = format!("vpci:{instance_id}");
64 chipset_builder
65 .arc_mutex_device(vpci_bus_name)
66 .try_add_async(async |services| {
67 let (msi_controller, interrupt_mapper) =
68 new_virtual_device(device_id).context(format!(
69 "failed to create virtual device, device_id {device_id} = {} | {}",
70 instance_id.data2,
71 instance_id.data3 as u64 & 0xfff8
72 ))?;
73
74 msi_set.connect(msi_controller.as_ref());
75
76 let bus = vpci::bus::VpciBus::new(
77 driver_source,
78 instance_id,
79 device,
80 &mut services.register_mmio(),
81 vmbus,
82 interrupt_mapper,
83 vtom,
84 )
85 .await?;
86
87 anyhow::Ok(bus)
88 })
89 .await?;
90 }
91
92 Ok(())
93}
94
95pub async fn build_pcie_device(
98 chipset_builder: &mut ChipsetBuilder<'_>,
99 port_name: Arc<str>,
100 driver_source: &VmTaskDriverSource,
101 resolver: &ResourceResolver,
102 guest_memory: &GuestMemory,
103 resource: Resource<PciDeviceHandleKind>,
104 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
105 mapper: Option<&dyn guestmem::MemoryMapper>,
106 interrupt_target: Option<Arc<dyn MsiInterruptTarget>>,
107) -> anyhow::Result<()> {
108 let dev_name = format!("pcie:{}-{}", port_name, resource.id());
109 let device_builder = chipset_builder
110 .arc_mutex_device(dev_name)
111 .on_pcie_port(vmotherboard::BusId::new(&port_name));
112
113 let (_, msi_set) = resolve_and_add_pci_device(
114 device_builder,
115 driver_source,
116 resolver,
117 guest_memory,
118 resource,
119 doorbell_registration,
120 mapper,
121 )
122 .await?;
123
124 if let Some(target) = interrupt_target {
125 msi_set.connect(target.as_ref());
126 }
127
128 Ok(())
129}
130
131pub async fn resolve_and_add_pci_device(
133 device_builder: ArcMutexChipsetDeviceBuilder<'_, '_, ErasedChipsetDevice>,
134 driver_source: &VmTaskDriverSource,
135 resolver: &ResourceResolver,
136 guest_memory: &GuestMemory,
137 resource: Resource<PciDeviceHandleKind>,
138 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
139 mapper: Option<&dyn guestmem::MemoryMapper>,
140) -> anyhow::Result<(Arc<CloseableMutex<ErasedChipsetDevice>>, MsiInterruptSet)> {
141 let mut msi_set = MsiInterruptSet::new();
142
143 let device = {
144 device_builder
145 .try_add_async(async |services| {
146 resolver
147 .resolve(
148 resource,
149 pci_resources::ResolvePciDeviceHandleParams {
150 register_msi: &mut msi_set,
151 register_mmio: &mut services.register_mmio(),
152 driver_source,
153 guest_memory,
154 doorbell_registration,
155 shared_mem_mapper: mapper,
156 },
157 )
158 .await
159 .map(|r| r.0)
160 })
161 .await?
162 };
163
164 Ok((device, msi_set))
165}