1use 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
23pub struct NvmeFaultControllerResolver;
25
26declare_static_async_resolver! {
27 NvmeFaultControllerResolver,
28 (PciDeviceHandleKind, NvmeFaultControllerHandle),
29}
30
31#[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 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}