vmm_core/
device_builder.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Functions for resolving and building devices.
5
6use 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
21/// Resolves a PCI device resource, builds the corresponding device, and builds
22/// a VPCI bus to host it.
23pub 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}