nvme_test/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resource resolver for the nvme controller.
5
6use crate::NsidConflict;
7use crate::NvmeFaultController;
8use crate::NvmeFaultControllerCaps;
9use async_trait::async_trait;
10use disk_backend::resolve::ResolveDiskParameters;
11use nvme_resources::NamespaceDefinition;
12use nvme_resources::NvmeFaultControllerHandle;
13use pci_resources::ResolvePciDeviceHandleParams;
14use pci_resources::ResolvedPciDevice;
15use tdisp::test_helpers::new_null_tdisp_interface;
16use thiserror::Error;
17use vm_resource::AsyncResolveResource;
18use vm_resource::ResolveError;
19use vm_resource::ResourceResolver;
20use vm_resource::declare_static_async_resolver;
21use vm_resource::kind::PciDeviceHandleKind;
22
23/// Resource resolver for [`NvmeFaultControllerHandle`].
24pub struct NvmeFaultControllerResolver;
25
26declare_static_async_resolver! {
27    NvmeFaultControllerResolver,
28    (PciDeviceHandleKind, NvmeFaultControllerHandle),
29}
30
31/// Error returned by [`NvmeFaultControllerResolver`].
32#[derive(Debug, Error)]
33#[expect(missing_docs)]
34pub enum Error {
35    #[error("failed to resolve namespace {nsid}")]
36    NamespaceResolve {
37        nsid: u32,
38        #[source]
39        source: ResolveError,
40    },
41    #[error(transparent)]
42    NsidConflict(NsidConflict),
43}
44
45#[async_trait]
46impl AsyncResolveResource<PciDeviceHandleKind, NvmeFaultControllerHandle>
47    for NvmeFaultControllerResolver
48{
49    type Output = ResolvedPciDevice;
50    type Error = Error;
51
52    async fn resolve(
53        &self,
54        resolver: &ResourceResolver,
55        resource: NvmeFaultControllerHandle,
56        input: ResolvePciDeviceHandleParams<'_>,
57    ) -> Result<Self::Output, Self::Error> {
58        // If TDISP tests are enabled, create a mock TDISP interface to expose
59        // for the device from OpenVMM.
60        let tdisp_interface: Option<Box<dyn tdisp::TdispHostDeviceTarget>> =
61            if resource.enable_tdisp_tests {
62                Some(Box::new(new_null_tdisp_interface("fault-controller-test")))
63            } else {
64                None
65            };
66
67        let controller = NvmeFaultController::new(
68            input.driver_source,
69            input.guest_memory.clone(),
70            input.msi_target,
71            input.register_mmio,
72            NvmeFaultControllerCaps {
73                msix_count: resource.msix_count,
74                max_io_queues: resource.max_io_queues,
75                subsystem_id: resource.subsystem_id,
76            },
77            resource.fault_config,
78            tdisp_interface,
79        );
80        for NamespaceDefinition {
81            nsid,
82            read_only,
83            disk,
84        } in resource.namespaces
85        {
86            let disk = resolver
87                .resolve(
88                    disk,
89                    ResolveDiskParameters {
90                        read_only,
91                        driver_source: input.driver_source,
92                    },
93                )
94                .await
95                .map_err(|source| Error::NamespaceResolve { nsid, source })?;
96            controller
97                .client()
98                .add_namespace(nsid, disk.0)
99                .await
100                .map_err(Error::NsidConflict)?;
101        }
102        Ok(controller.into())
103    }
104}