Skip to main content

flowey_lib_hvlite/
init_vmm_tests_content_dir.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Setup directory structure that the VMM tests require to run.
5
6use crate::build_flowey_hvlite::FloweyHvliteOutput;
7use crate::build_guest_test_uefi::GuestTestUefiOutput;
8use crate::build_nextest_vmm_tests::NextestVmmTestsArchive;
9use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmOutput;
10use crate::build_openvmm::OpenvmmOutput;
11use crate::build_openvmm_vhost::OpenvmmVhostOutput;
12use crate::build_pipette::PipetteOutput;
13use crate::build_prep_steps::PrepStepsOutput;
14use crate::build_snp_linux_direct_igvm::SnpLinuxDirectIgvmOutput;
15use crate::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput;
16use crate::build_tmk_vmm::TmkVmmOutput;
17use crate::build_tmks::TmksOutput;
18use crate::build_tpm_guest_tests::TpmGuestTestsOutput;
19use crate::build_vmfirmwareigvm_dll::VmfirmwareigvmDllOutput;
20use crate::build_vmgstool::VmgstoolOutput;
21use crate::common::CommonArch;
22use crate::download_release_igvm_files_from_gh::OpenhclReleaseVersion;
23use flowey::node::prelude::*;
24use petri_artifacts_common::artifacts::*;
25use petri_artifacts_core::ArtifactId;
26use petri_artifacts_core::ArtifactTarget;
27use petri_artifacts_vmm_test::artifacts::*;
28
29macro_rules! define_vmm_tests_built_artifacts {
30    (
31        $name:ident,
32        $($artifact:ident(
33            $($variant:ident(
34                ($output_let_expr:expr, $member:ident $(, $none_member:ident)*),
35                $artifact_ty:ty
36            )),* $(,)?
37        ) => $output:ty),* $(,)?
38    ) => {
39        ::paste::paste! {
40            #[derive(Serialize, Deserialize)]
41            pub struct $name<C = VarNotClaimed> {$($(
42                pub [<$artifact _ $variant>]: Option<::flowey::node::prelude::ReadVar<$output, C>>,
43            )*)*}
44
45            impl Default for $name<VarNotClaimed> {
46                fn default() -> Self {
47                    Self {$($(
48                        [<$artifact _ $variant>]: None,
49                    )*)*}
50                }
51            }
52
53            impl $name<VarNotClaimed> {
54                pub fn claim(self, ctx: &mut StepCtx<'_>) -> $name<VarClaimed> {
55                    let Self {$($(
56                        [<$artifact _ $variant>],
57                    )*)*} = self;
58                    $name {$($(
59                        [<$artifact _ $variant>]: [<$artifact _ $variant>].claim(ctx),
60                    )*)*}
61                }
62
63                $(pub fn $artifact(&mut self, target: ArtifactTarget) -> ::anyhow::Result<&mut Option<::flowey::node::prelude::ReadVar<$output>>> {
64                    match &target {
65                        $($artifact_ty::TARGET => Ok(&mut self.[<$artifact _ $variant>]),)*
66                        _ if let Some(target) = retry_as_musl(target.clone()) => {
67                            return self.$artifact(target);
68                        }
69                        _ => Err(anyhow::anyhow!("{} {}", target, concat!("does not exist for ", stringify!($artifact)))),
70                    }
71                })*
72
73                $($(pub fn [<$artifact _ $variant _target>]() -> ::target_lexicon::Triple {
74                    $artifact_ty::TARGET.target_triple().expect("no target triple for artifact")
75                })*)*
76            }
77
78            impl $name<VarClaimed> {
79                pub fn write(self, rt: &mut RustRuntimeServices<'_>, test_content_dir: impl AsRef<Path>) -> ::anyhow::Result<()> {
80                    let Self {$($(
81                        [<$artifact _ $variant>],
82                    )*)*} = self;
83
84
85                    $($(if let Some(artifact) = [<$artifact _ $variant>] {
86                        let dst = test_content_dir
87                            .as_ref()
88                            .join($artifact_ty::relative_path());
89
90                        #[expect(clippy::allow_attributes)]
91                        #[allow(irrefutable_let_patterns, unused_variables)]
92                        let $output_let_expr = rt.read(artifact) else {
93                            ::anyhow::bail!(concat!(
94                                "unexpected variant of ",
95                                stringify!($output),
96                                " for ",
97                                stringify!([<$artifact _ $variant>])
98                            ));
99                        };
100
101                        fs_err::create_dir_all(dst.parent().unwrap())?;
102                        fs_err::copy($member, &dst)?;
103                        dst.make_executable()?;
104                    })*)*
105
106                    Ok(())
107                }
108            }
109
110            #[derive(Serialize, Deserialize)]
111            pub struct [<$name Write>]<C = VarNotClaimed> {$($(
112                pub [<$artifact _ $variant>]: Option<::flowey::node::prelude::WriteVar<$output, C>>,
113            )*)*}
114
115            impl Default for [<$name Write>]<VarNotClaimed> {
116                fn default() -> Self {
117                    Self {$($(
118                        [<$artifact _ $variant>]: None,
119                    )*)*}
120                }
121            }
122
123            impl [<$name Write>]<VarNotClaimed> {
124                pub fn claim(self, ctx: &mut StepCtx<'_>) -> [<$name Write>]<VarClaimed> {
125                    let Self {$($(
126                        [<$artifact _ $variant>],
127                    )*)*} = self;
128                    [<$name Write>] {$($(
129                        [<$artifact _ $variant>]: [<$artifact _ $variant>].claim(ctx),
130                    )*)*}
131                }
132            }
133
134            impl [<$name Write>]<VarClaimed> {
135                pub fn resolve(self, rt: &mut RustRuntimeServices<'_>, test_content_dir: impl AsRef<Path>) -> ::anyhow::Result<()> {
136                    let Self {$($(
137                        [<$artifact _ $variant>],
138                    )*)*} = self;
139
140
141                    $($(if let Some(artifact) = [<$artifact _ $variant>] {
142                        let $member = test_content_dir
143                            .as_ref()
144                            .join($artifact_ty::relative_path());
145
146                        if $member.exists() {
147                            $(let $none_member = None;)*
148                            rt.write(
149                                artifact,
150                                &$output_let_expr,
151                            )
152                        } else {
153                            anyhow::bail!("{} not found", $member.display());
154                        }
155                    })*)*
156
157                    Ok(())
158                }
159            }
160
161            #[derive(Serialize, Deserialize, Default, Debug, Clone)]
162            pub struct [<$name Selections>] {$($(
163                pub [<$artifact _ $variant>]: bool,
164            )*)*}
165
166            impl [<$name Selections>] {
167                pub fn resolve_artifact(&mut self, id: &str) -> bool {
168                    match id {
169                        $($($artifact_ty::GLOBAL_UNIQUE_ID => {
170                            self.[<$artifact _ $variant>] = true;
171                            true
172                        })*)*
173                        _ => false
174                    }
175                }
176
177                $(pub fn [<$artifact _for>](&self, target: ArtifactTarget) -> ::anyhow::Result<bool>{
178                    match &target {
179                        $($artifact_ty::TARGET => {
180                            Ok(self.[<$artifact _ $variant>])
181                        })*
182                        _ => Err(anyhow::anyhow!("{} {}", target, concat!("does not exist for ", stringify!($artifact)))),
183                    }
184                }
185
186                pub fn [<require_ $artifact _for>](&mut self, target: ArtifactTarget) -> ::anyhow::Result<PathBuf>{
187                    match &target {
188                        $($artifact_ty::TARGET => {
189                            self.[<$artifact _ $variant>] = true;
190                            Ok($artifact_ty::relative_path())
191                        })*
192                        _ if let Some(target) = retry_as_musl(target.clone()) => {
193                            self.[<require_ $artifact _for>](target)
194                        }
195                        _ => Err(anyhow::anyhow!("{} {}", target, concat!("does not exist for ", stringify!($artifact)))),
196                    }
197                })*
198
199                pub fn new_var_set(&self, ctx: &mut ::flowey::node::prelude::NodeCtx<'_>) -> (
200                    $name,
201                    [<$name Write>],
202                ) {
203                    let mut built_artifacts_read = $name::default();
204                    let mut built_artifacts_write = [<$name Write>]::default();
205
206                    $($(if self.[<$artifact _ $variant>] {
207                        let (read_var, write_var) = ctx.new_var();
208                        built_artifacts_read.[<$artifact _ $variant>] = Some(read_var);
209                        built_artifacts_write.[<$artifact _ $variant>] = Some(write_var);
210                    })*)*
211
212                    (built_artifacts_read, built_artifacts_write)
213                }
214
215                pub fn handles(&self) -> Vec<::petri_artifacts_core::ErasedArtifactHandle> {
216                    let mut handles = Vec::new();
217
218                    $($(if self.[<$artifact _ $variant>] {
219                        handles.push($artifact_ty.erase())
220                    })*)*
221
222                    handles
223                }
224            }
225
226        }
227    };
228}
229
230// Try again with musl target, since some artifacts may only be built for musl
231// to save time. Our musl binaries can also run in gnu environments since they
232// are statically linked.
233fn retry_as_musl(target: ArtifactTarget) -> Option<ArtifactTarget> {
234    let ArtifactTarget::Triple(mut triple) = target else {
235        return None;
236    };
237
238    if matches!(
239        triple.operating_system,
240        target_lexicon::OperatingSystem::Linux
241    ) && !matches!(triple.environment, target_lexicon::Environment::Musl)
242    {
243        triple.environment = target_lexicon::Environment::Musl;
244        Some(ArtifactTarget::Triple(triple))
245    } else {
246        None
247    }
248}
249
250define_vmm_tests_built_artifacts!(
251    VmmTestsBuiltArtifacts,
252    // Artifacts used at the pipeline level.
253    flowey_hvlite(
254        windows_x64(
255            (FloweyHvliteOutput::WindowsBin { exe, pdb }, exe, pdb),
256            host_tools::FLOWEY_HVLITE_WINDOWS_X64
257        ),
258        windows_aarch64(
259            (FloweyHvliteOutput::WindowsBin { exe, pdb }, exe, pdb),
260            host_tools::FLOWEY_HVLITE_WINDOWS_AARCH64
261        ),
262        linux_x64(
263            (FloweyHvliteOutput::LinuxBin { bin, dbg }, bin, dbg),
264            host_tools::FLOWEY_HVLITE_LINUX_X64
265        ),
266        linux_musl_x64(
267            (FloweyHvliteOutput::LinuxBin { bin, dbg }, bin, dbg),
268            host_tools::FLOWEY_HVLITE_LINUX_X64_MUSL
269        ),
270    ) => FloweyHvliteOutput,
271    nextest_vmm_tests_archive(
272        windows_x64(
273            (NextestVmmTestsArchive { archive_file }, archive_file),
274            host_tools::NEXTEST_VMM_TESTS_ARCHIVE_WINDOWS_X64
275        ),
276        windows_aarch64(
277            (NextestVmmTestsArchive { archive_file }, archive_file),
278            host_tools::NEXTEST_VMM_TESTS_ARCHIVE_WINDOWS_AARCH64
279        ),
280        linux_x64(
281            (NextestVmmTestsArchive { archive_file }, archive_file),
282            host_tools::NEXTEST_VMM_TESTS_ARCHIVE_LINUX_X64
283        ),
284        linux_musl_x64(
285            (NextestVmmTestsArchive { archive_file }, archive_file),
286            host_tools::NEXTEST_VMM_TESTS_ARCHIVE_LINUX_X64_MUSL
287        ),
288        linux_musl_aarch64(
289            (NextestVmmTestsArchive { archive_file }, archive_file),
290            host_tools::NEXTEST_VMM_TESTS_ARCHIVE_LINUX_AARCH64_MUSL
291        ),
292    ) => NextestVmmTestsArchive,
293    prep_steps(
294        windows_x64(
295            (PrepStepsOutput::WindowsBin { exe, pdb }, exe, pdb),
296            host_tools::PREP_STEPS_WINDOWS_X64
297        ),
298        linux_musl_x64(
299            (PrepStepsOutput::LinuxBin { bin, dbg}, bin, dbg),
300            host_tools::PREP_STEPS_LINUX_X64_MUSL
301        ),
302    ) => PrepStepsOutput,
303    test_igvm_agent_rpc_server(
304        windows_x64(
305            (TestIgvmAgentRpcServerOutput { exe, pdb }, exe, pdb),
306            host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64
307        ),
308    ) => TestIgvmAgentRpcServerOutput,
309
310    // Artifacts used internally by petri.
311    openvmm(
312        windows_x64(
313            (OpenvmmOutput::WindowsBin { exe, pdb }, exe, pdb),
314            OPENVMM_WINDOWS_X64
315        ),
316        windows_aarch64(
317            (OpenvmmOutput::WindowsBin { exe, pdb }, exe, pdb),
318            OPENVMM_WINDOWS_AARCH64
319        ),
320        linux_x64(
321            (OpenvmmOutput::LinuxBin { bin, dbg}, bin, dbg),
322            OPENVMM_LINUX_X64
323        ),
324        linux_aarch64(
325            (OpenvmmOutput::LinuxBin { bin, dbg}, bin, dbg),
326            OPENVMM_LINUX_AARCH64
327        ),
328        linux_musl_x64(
329            (OpenvmmOutput::LinuxBin { bin, dbg}, bin, dbg),
330            OPENVMM_LINUX_X64_MUSL
331        ),
332        linux_musl_aarch64(
333            (OpenvmmOutput::LinuxBin { bin, dbg}, bin, dbg),
334            OPENVMM_LINUX_AARCH64_MUSL
335        ),
336    ) => OpenvmmOutput,
337    openvmm_vhost(
338        linux_x64(
339            (OpenvmmVhostOutput { bin, dbg}, bin, dbg),
340            OPENVMM_VHOST_LINUX_X64
341        ),
342        linux_aarch64(
343            (OpenvmmVhostOutput { bin, dbg}, bin, dbg),
344            OPENVMM_VHOST_LINUX_AARCH64
345        ),
346        linux_musl_x64(
347            (OpenvmmVhostOutput { bin, dbg}, bin, dbg),
348            OPENVMM_VHOST_LINUX_X64_MUSL
349        ),
350        linux_musl_aarch64(
351            (OpenvmmVhostOutput { bin, dbg}, bin, dbg),
352            OPENVMM_VHOST_LINUX_AARCH64_MUSL
353        ),
354    ) => OpenvmmVhostOutput,
355    pipette(
356        windows_x64(
357            (PipetteOutput::WindowsBin { exe, pdb }, exe, pdb),
358            PIPETTE_WINDOWS_X64
359        ),
360        windows_aarch64(
361            (PipetteOutput::WindowsBin { exe, pdb }, exe, pdb),
362            PIPETTE_WINDOWS_AARCH64
363        ),
364        linux_musl_x64(
365            (PipetteOutput::LinuxBin { bin, dbg}, bin, dbg),
366            PIPETTE_LINUX_X64_MUSL
367        ),
368        linux_musl_aarch64(
369            (PipetteOutput::LinuxBin { bin, dbg}, bin, dbg),
370            PIPETTE_LINUX_AARCH64_MUSL
371        ),
372    ) => PipetteOutput,
373    guest_test_uefi(
374        x64(
375            (GuestTestUefiOutput { img, efi, pdb }, img, efi, pdb),
376            test_vhd::GUEST_TEST_UEFI_X64
377        ),
378        aarch64(
379            (GuestTestUefiOutput { img, efi, pdb }, img, efi, pdb),
380            test_vhd::GUEST_TEST_UEFI_AARCH64
381        ),
382    ) => GuestTestUefiOutput,
383    openhcl_standard(
384        x64(
385            (OpenhclIgvmOutput::X64 { igvm_bin }, igvm_bin),
386            openhcl_igvm::LATEST_STANDARD_X64
387        ),
388        aarch64(
389            (OpenhclIgvmOutput::Aarch64 { igvm_bin }, igvm_bin),
390            openhcl_igvm::LATEST_STANDARD_AARCH64
391        ),
392    ) => OpenhclIgvmOutput,
393    openhcl_standard_dev(
394        x64(
395            (OpenhclIgvmOutput::X64Devkern { igvm_bin }, igvm_bin),
396            openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64
397        ),
398        aarch64(
399            (OpenhclIgvmOutput::Aarch64Devkern { igvm_bin }, igvm_bin),
400            openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64
401        ),
402    ) => OpenhclIgvmOutput,
403    openhcl_cvm(
404        x64(
405            (OpenhclIgvmOutput::X64Cvm { igvm_bin, endorsements }, igvm_bin, endorsements),
406            openhcl_igvm::LATEST_CVM_X64
407        ),
408    ) => OpenhclIgvmOutput,
409    vmfirmwareigvm_cvm(
410        x64(
411            (VmfirmwareigvmDllOutput { dll }, dll),
412            vmfw_dll::LATEST_CVM_X64
413        ),
414    ) => VmfirmwareigvmDllOutput,
415    openhcl_linux_direct(
416        x64(
417            (OpenhclIgvmOutput::X64TestLinuxDirect { igvm_bin }, igvm_bin),
418            openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64
419        ),
420    ) => OpenhclIgvmOutput,
421    openhcl_sivm(
422        x64(
423            (OpenhclIgvmOutput::X64TestSivm { igvm_bin }, igvm_bin),
424            openhcl_igvm::LATEST_SIVM_TEST_X64
425        ),
426    ) => OpenhclIgvmOutput,
427    snp_linux_direct_igvm(
428        x64(
429            (SnpLinuxDirectIgvmOutput { igvm_bin, igvm_map }, igvm_bin, igvm_map),
430            snp_igvm::SNP_LINUX_DIRECT_IGVM_X64
431        ),
432    ) => SnpLinuxDirectIgvmOutput,
433    tmks(
434        x64(
435            (TmksOutput { bin, dbg}, bin, dbg),
436            tmks::SIMPLE_TMK_X64
437        ),
438        aarch64(
439            (TmksOutput { bin, dbg}, bin, dbg),
440            tmks::SIMPLE_TMK_AARCH64
441        ),
442    ) => TmksOutput,
443    tmk_vmm(
444        windows_x64(
445            (TmkVmmOutput::WindowsBin { exe, pdb }, exe, pdb),
446            tmks::TMK_VMM_WINDOWS_X64
447        ),
448        windows_aarch64(
449            (TmkVmmOutput::WindowsBin { exe, pdb }, exe, pdb),
450            tmks::TMK_VMM_WINDOWS_AARCH64
451        ),
452        linux_musl_x64(
453            (TmkVmmOutput::LinuxBin { bin, dbg}, bin, dbg),
454            tmks::TMK_VMM_LINUX_X64_MUSL
455        ),
456        linux_musl_aarch64(
457            (TmkVmmOutput::LinuxBin { bin, dbg}, bin, dbg),
458            tmks::TMK_VMM_LINUX_AARCH64_MUSL
459        ),
460    ) => TmkVmmOutput,
461    vmgstool(
462        windows_x64(
463            (VmgstoolOutput::WindowsBin { exe, pdb }, exe, pdb),
464            vmgstool::VMGSTOOL_WINDOWS_X64
465        ),
466        windows_aarch64(
467            (VmgstoolOutput::WindowsBin { exe, pdb }, exe, pdb),
468            vmgstool::VMGSTOOL_WINDOWS_AARCH64
469        ),
470        linux_x64(
471            (VmgstoolOutput::LinuxBin { bin, dbg}, bin, dbg),
472            vmgstool::VMGSTOOL_LINUX_X64
473        ),
474    ) => VmgstoolOutput,
475    vmgstool_dev(
476        windows_x64(
477            (VmgstoolOutput::WindowsBin { exe, pdb }, exe, pdb),
478            vmgstool::VMGSTOOL_DEV_WINDOWS_X64
479        ),
480        windows_aarch64(
481            (VmgstoolOutput::WindowsBin { exe, pdb }, exe, pdb),
482            vmgstool::VMGSTOOL_DEV_WINDOWS_AARCH64
483        ),
484        linux_x64(
485            (VmgstoolOutput::LinuxBin { bin, dbg}, bin, dbg),
486            vmgstool::VMGSTOOL_DEV_LINUX_X64
487        ),
488    ) => VmgstoolOutput,
489    tpm_guest_tests(
490        windows_x64(
491            (TpmGuestTestsOutput::WindowsBin { exe, pdb }, exe, pdb),
492            guest_tools::TPM_GUEST_TESTS_WINDOWS_X64
493        ),
494        linux_x64(
495            (TpmGuestTestsOutput::LinuxBin { bin, dbg}, bin, dbg),
496            guest_tools::TPM_GUEST_TESTS_LINUX_X64
497        ),
498    ) => TpmGuestTestsOutput,
499);
500
501pub type ResolveVmmTestsBuiltArtifacts =
502    Box<dyn Fn(&mut flowey::pipeline::prelude::PipelineJobCtx<'_>) -> VmmTestsBuiltArtifacts>;
503
504#[macro_export]
505macro_rules! vmm_tests_built_artifacts_builder {
506    (
507        $name:ty,
508        (
509            $($artifact:ident => $output:ty),* $(,)?
510        )
511    ) => {
512        ::paste::paste! {
513            #[derive(Default, Clone)]
514            pub struct $name {
515                $(pub [<use_ $artifact>]: Option<::flowey::pipeline::prelude::UseTypedArtifact<$output>>,)*
516            }
517
518            impl $name {
519                pub fn finish(self) -> Result<$crate::init_vmm_tests_content_dir::ResolveVmmTestsBuiltArtifacts, &'static str> {
520                    let $name {
521                        $([<use_ $artifact>],)*
522                    } = self;
523
524                    $(let [<use_ $artifact>] = [<use_ $artifact>].ok_or(stringify!($artifact))?;)*
525
526                    Ok(Box::new(move |ctx| $crate::init_vmm_tests_content_dir::VmmTestsBuiltArtifacts {
527                        $($artifact: Some(ctx.use_typed_artifact(&[<use_ $artifact>])),)*
528                        .. Default::default()
529                    }))
530                }
531
532                pub fn selections() -> $crate::init_vmm_tests_content_dir::VmmTestsBuiltArtifactsSelections {
533                    let mut selections = $crate::init_vmm_tests_content_dir::VmmTestsBuiltArtifactsSelections::default();
534                    $(selections.$artifact = true;)*
535                    selections
536                }
537            }
538        }
539    };
540}
541
542define_vmm_tests_built_artifacts!(
543    VmmTestsPreBuiltArtifacts,
544    test_linux_kernel(
545        x64(
546            (src, src),
547            loadable::LINUX_DIRECT_TEST_KERNEL_X64
548        ),
549        aarch64(
550            (src, src),
551            loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64
552        ),
553    ) => PathBuf,
554    test_linux_kernel_cca(
555        aarch64(
556            (src, src),
557            loadable::LINUX_DIRECT_TEST_KERNEL_CCA_AARCH64
558        ),
559    ) => PathBuf,
560    test_linux_initrd(
561        x64(
562            (src, src),
563            loadable::LINUX_DIRECT_TEST_INITRD_X64
564        ),
565        aarch64(
566            (src, src),
567            loadable::LINUX_DIRECT_TEST_INITRD_AARCH64
568        ),
569    ) => PathBuf,
570    test_linux_bzimage(
571        x64(
572            (src, src),
573            loadable::LINUX_DIRECT_TEST_BZIMAGE_X64
574        ),
575    ) => PathBuf,
576    uefi(
577        x64(
578            (src, src),
579            loadable::UEFI_FIRMWARE_X64
580        ),
581        aarch64(
582            (src, src),
583            loadable::UEFI_FIRMWARE_AARCH64
584        ),
585    ) => PathBuf,
586    qemu_system_aarch64(
587        linux_x64(
588            (src, src),
589            QEMU_SYSTEM_AARCH64_LINUX_X64
590        ),
591    ) => PathBuf,
592);
593
594flowey_request! {
595    pub struct Request {
596        /// Directory to symlink / copy test contents into. Does not need to be
597        /// empty.
598        pub test_content_dir: ReadVar<PathBuf>,
599        /// What triple VMM tests are built for.
600        ///
601        /// Used to detect cases of running Windows VMM tests via WSL2, and adjusting
602        /// reported paths appropriately.
603        pub vmm_tests_target: target_lexicon::Triple,
604        /// Artifacts used by the tests that are built in the openvmm repo
605        pub built_artifacts: VmmTestsBuiltArtifacts,
606        /// Artifacts to download that are pre-built as part of OpenVMM deps
607        pub prebuilt_artifacts: VmmTestsPreBuiltArtifactsSelections,
608        /// Override the UEFI firmware flavor used by the tests
609        pub uefi_firmware_flavor: Option<crate::download_uefi_mu_msvm::FirmwareFlavor>,
610        /// Copy files necessary to use the test content dir as a minimal repo root.
611        ///
612        /// This is useful for running tests on machines without a local clone.
613        pub is_repo_root: bool,
614
615        // TODO: refactor these last two to use one artifact per arch so that
616        // they can be part of `VmmTestsPreBuiltArtifactsSelections`.
617        pub needs_virtio_win_drivers: bool,
618        pub needs_release_igvm: bool,
619
620        pub done: WriteVar<SideEffect>
621    }
622}
623
624new_simple_flow_node!(struct Node);
625
626impl SimpleFlowNode for Node {
627    type Request = Request;
628
629    fn imports(ctx: &mut ImportCtx<'_>) {
630        ctx.import::<crate::resolve_openvmm_test_initrd::Node>();
631        ctx.import::<crate::resolve_openvmm_test_linux_kernel::Node>();
632        ctx.import::<crate::resolve_openvmm_test_virtio_win::Node>();
633        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
634        ctx.import::<crate::download_uefi_mu_msvm::Node>();
635        ctx.import::<crate::download_release_igvm_files_from_gh::resolve::Node>();
636        ctx.import::<crate::resolve_openvmm_qemu::Node>();
637    }
638
639    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
640        let Request {
641            test_content_dir,
642            vmm_tests_target,
643            built_artifacts,
644            prebuilt_artifacts,
645            uefi_firmware_flavor,
646            is_repo_root,
647            needs_virtio_win_drivers,
648            needs_release_igvm,
649            done,
650        } = request;
651
652        let openvmm_repo_path =
653            is_repo_root.then(|| ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir));
654
655        let arch = CommonArch::from_architecture(vmm_tests_target.architecture)?;
656
657        let test_linux_initrd_x64 = prebuilt_artifacts.test_linux_initrd_x64.then(|| {
658            ctx.reqv(|v| crate::resolve_openvmm_test_initrd::Request::Get(CommonArch::X86_64, v))
659        });
660        let test_linux_initrd_aarch64 = prebuilt_artifacts.test_linux_initrd_aarch64.then(|| {
661            ctx.reqv(|v| crate::resolve_openvmm_test_initrd::Request::Get(CommonArch::Aarch64, v))
662        });
663        let test_linux_kernel_x64 = prebuilt_artifacts.test_linux_kernel_x64.then(|| {
664            ctx.reqv(|v| {
665                crate::resolve_openvmm_test_linux_kernel::Request::Get(
666                    crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel,
667                    CommonArch::X86_64,
668                    crate::resolve_openvmm_test_linux_kernel::DEFAULT_LINUX_TEST_KERNEL_VERSION,
669                    v,
670                )
671            })
672        });
673        let test_linux_kernel_aarch64 = prebuilt_artifacts.test_linux_kernel_aarch64.then(|| {
674            ctx.reqv(|v| {
675                crate::resolve_openvmm_test_linux_kernel::Request::Get(
676                    crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel,
677                    CommonArch::Aarch64,
678                    crate::resolve_openvmm_test_linux_kernel::DEFAULT_LINUX_TEST_KERNEL_VERSION,
679                    v,
680                )
681            })
682        });
683        let test_linux_kernel_cca_aarch64 =
684            prebuilt_artifacts.test_linux_kernel_cca_aarch64.then(|| {
685                ctx.reqv(|v| {
686                    crate::resolve_openvmm_test_linux_kernel::Request::Get(
687                        crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel,
688                        CommonArch::Aarch64,
689                        crate::resolve_openvmm_test_linux_kernel::CCA_LINUX_TEST_KERNEL_VERSION,
690                        v,
691                    )
692                })
693            });
694        let test_linux_bzimage_x64 = prebuilt_artifacts.test_linux_bzimage_x64.then(|| {
695            ctx.reqv(|v| {
696                crate::resolve_openvmm_test_linux_kernel::Request::Get(
697                    crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::BzImage,
698                    CommonArch::X86_64,
699                    crate::resolve_openvmm_test_linux_kernel::DEFAULT_LINUX_TEST_KERNEL_VERSION,
700                    v,
701                )
702            })
703        });
704
705        let uefi_x64 = prebuilt_artifacts.uefi_x64.then(|| {
706            ctx.reqv(|msvm_fd| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
707                arch: CommonArch::X86_64,
708                flavor: uefi_firmware_flavor,
709                msvm_fd,
710            })
711        });
712        let uefi_aarch64 = prebuilt_artifacts.uefi_aarch64.then(|| {
713            ctx.reqv(|msvm_fd| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
714                arch: CommonArch::Aarch64,
715                flavor: uefi_firmware_flavor,
716                msvm_fd,
717            })
718        });
719
720        let qemu_system_aarch64_linux_x64 =
721            prebuilt_artifacts.qemu_system_aarch64_linux_x64.then(|| {
722                ctx.reqv(|v| {
723                    crate::resolve_openvmm_qemu::Request::Get(
724                        crate::resolve_openvmm_qemu::QemuFile::SystemAarch64,
725                        CommonArch::X86_64,
726                        v,
727                    )
728                })
729            });
730
731        let prebuilt_artifacts = VmmTestsPreBuiltArtifacts {
732            test_linux_kernel_x64,
733            test_linux_kernel_aarch64,
734            test_linux_kernel_cca_aarch64,
735            test_linux_initrd_x64,
736            test_linux_initrd_aarch64,
737            test_linux_bzimage_x64,
738            uefi_x64,
739            uefi_aarch64,
740            qemu_system_aarch64_linux_x64,
741        };
742
743        let virtio_win_dir = needs_virtio_win_drivers
744            .then(|| ctx.reqv(crate::resolve_openvmm_test_virtio_win::Request::Get));
745
746        let release_igvm_files = needs_release_igvm.then(|| {
747            ctx.reqv(
748                |v| crate::download_release_igvm_files_from_gh::resolve::Request {
749                    arch,
750                    release_igvm_files: v,
751                    release_version: OpenhclReleaseVersion::latest(),
752                },
753            )
754        });
755
756        ctx.emit_rust_step("setting up vmm_tests content dir", |ctx| {
757            claim_vars!(
758                ctx,
759                (
760                    test_content_dir,
761                    openvmm_repo_path,
762                    // built artifacts
763                    built_artifacts,
764                    // downloaded artifacts
765                    prebuilt_artifacts,
766                    virtio_win_dir,
767                    release_igvm_files,
768                )
769            );
770
771            done.claim(ctx);
772
773            move |rt| {
774                read_vars!(rt, (release_igvm_files, test_content_dir));
775
776                if !test_content_dir.exists() {
777                    fs_err::create_dir_all(&test_content_dir)?
778                };
779
780                if let Some(openvmm_repo_path) = openvmm_repo_path {
781                    let openvmm_repo_path = rt.read(openvmm_repo_path);
782
783                    let nextest_config_file = PathBuf::new().join(".config").join("nextest.toml");
784                    fs_err::create_dir_all(
785                        test_content_dir
786                            .join(&nextest_config_file)
787                            .parent()
788                            .context("no parent")?,
789                    )?;
790                    fs_err::copy(
791                        openvmm_repo_path.join(&nextest_config_file),
792                        test_content_dir.join(&nextest_config_file),
793                    )?;
794
795                    let repo_cargo_toml_file = Path::new("Cargo.toml");
796                    fs_err::copy(
797                        openvmm_repo_path.join(repo_cargo_toml_file),
798                        test_content_dir.join(repo_cargo_toml_file),
799                    )?;
800
801                    let crate_cargo_toml_file = PathBuf::new()
802                        .join("vmm_tests")
803                        .join("vmm_tests")
804                        .join("Cargo.toml");
805                    fs_err::create_dir_all(
806                        test_content_dir
807                            .join(&crate_cargo_toml_file)
808                            .parent()
809                            .context("no parent")?,
810                    )?;
811                    fs_err::copy(
812                        openvmm_repo_path.join(&crate_cargo_toml_file),
813                        test_content_dir.join(&crate_cargo_toml_file),
814                    )?;
815                }
816
817                built_artifacts.write(rt, &test_content_dir)?;
818
819                prebuilt_artifacts.write(rt, &test_content_dir)?;
820
821                if let Some(release_igvm_files) = release_igvm_files {
822                    if let Some(src) = &release_igvm_files.openhcl {
823                        fs_err::copy(
824                            src,
825                            test_content_dir
826                                .join(openhcl_igvm::LATEST_RELEASE_STANDARD_X64::relative_path()),
827                        )?;
828                    }
829
830                    if let Some(src) = &release_igvm_files.openhcl_aarch64 {
831                        fs_err::copy(
832                            src,
833                            test_content_dir.join(
834                                openhcl_igvm::LATEST_RELEASE_STANDARD_AARCH64::relative_path(),
835                            ),
836                        )?;
837                    }
838
839                    if let Some(src) = &release_igvm_files.openhcl_direct {
840                        fs_err::copy(
841                            src,
842                            test_content_dir.join(
843                                openhcl_igvm::LATEST_RELEASE_LINUX_DIRECT_X64::relative_path(),
844                            ),
845                        )?;
846                    }
847                }
848
849                if let Some(virtio_win_dir) = virtio_win_dir {
850                    let src = rt.read(virtio_win_dir);
851                    let dst =
852                        test_content_dir.join(virtio_win::VIRTIO_WINDOWS_DRIVERS::relative_path());
853                    let _ = fs_err::remove_dir_all(&dst);
854                    flowey_lib_common::_util::copy_dir_all(&src, &dst)?;
855                }
856
857                // log the current contents of the dir
858                log::info!("final folder content: {}", test_content_dir.display());
859                fn list_dir(dir: PathBuf, layer: usize) -> anyhow::Result<()> {
860                    if layer > 2
861                        || dir
862                            .file_name()
863                            .and_then(|n| n.to_str())
864                            .is_some_and(|n| ["temp", "test_results"].contains(&n))
865                    {
866                        log::info!("{}- ...", " ".repeat(layer * 2));
867                        return Ok(());
868                    }
869                    for entry in dir.read_dir()? {
870                        let entry = entry?;
871                        log::info!("{}- {}", " ".repeat(layer * 2), entry.file_name().display());
872                        let subdir = entry.path();
873                        if subdir.is_dir() {
874                            list_dir(subdir, layer + 1)?;
875                        }
876                    }
877                    Ok(())
878                }
879                list_dir(test_content_dir, 0)?;
880
881                Ok(())
882            }
883        });
884
885        Ok(())
886    }
887}
888
889/// Utility builders which make it easy to "skim off" artifacts required by VMM
890/// test execution from other pipeline jobs.
891//
892// DEVNOTE: this is pub so internal tests can reuse the same builders
893pub mod vmm_tests_artifact_builders {
894    use super::*;
895
896    vmm_tests_built_artifacts_builder!(
897        VmmTestsArtifactsBuilderLinuxX86,
898        (
899            // windows build machine
900            pipette_windows_x64 => PipetteOutput,
901            // linux build machine
902            nextest_vmm_tests_archive_linux_x64 => NextestVmmTestsArchive,
903            nextest_vmm_tests_archive_linux_musl_x64 => NextestVmmTestsArchive,
904            openvmm_linux_x64 => OpenvmmOutput,
905            openvmm_linux_musl_x64 => OpenvmmOutput,
906            openvmm_vhost_linux_x64 => OpenvmmVhostOutput,
907            pipette_linux_musl_x64 => PipetteOutput,
908            prep_steps_linux_musl_x64 => PrepStepsOutput,
909            snp_linux_direct_igvm_x64 => SnpLinuxDirectIgvmOutput,
910            tmk_vmm_linux_musl_x64 => TmkVmmOutput,
911            // any machine
912            guest_test_uefi_x64 => GuestTestUefiOutput,
913            tmks_x64 => TmksOutput,
914            // aarch64 emulated binaries
915            nextest_vmm_tests_archive_linux_musl_aarch64 => NextestVmmTestsArchive,
916            openvmm_linux_musl_aarch64 => OpenvmmOutput,
917            pipette_linux_musl_aarch64 => PipetteOutput,
918        )
919    );
920
921    vmm_tests_built_artifacts_builder!(
922        VmmTestsArtifactsBuilderLinuxMuslX86,
923        (
924            // windows build machine
925            pipette_windows_x64 => PipetteOutput,
926            // linux build machine
927            nextest_vmm_tests_archive_linux_musl_x64 => NextestVmmTestsArchive,
928            openvmm_linux_musl_x64 => OpenvmmOutput,
929            openvmm_vhost_linux_musl_x64 => OpenvmmVhostOutput,
930            pipette_linux_musl_x64 => PipetteOutput,
931            prep_steps_linux_musl_x64 => PrepStepsOutput,
932            snp_linux_direct_igvm_x64 => SnpLinuxDirectIgvmOutput,
933            tmk_vmm_linux_musl_x64 => TmkVmmOutput,
934            // any machine
935            guest_test_uefi_x64 => GuestTestUefiOutput,
936            tmks_x64 => TmksOutput,
937        )
938    );
939
940    vmm_tests_built_artifacts_builder!(
941        VmmTestsArtifactsBuilderWindowsX86,
942        (
943            // windows build machine
944            nextest_vmm_tests_archive_windows_x64 => NextestVmmTestsArchive,
945            openvmm_windows_x64 => OpenvmmOutput,
946            pipette_windows_x64 => PipetteOutput,
947            tmk_vmm_windows_x64 => TmkVmmOutput,
948            prep_steps_windows_x64 => PrepStepsOutput,
949            vmgstool_windows_x64 => VmgstoolOutput,
950            vmgstool_dev_windows_x64 => VmgstoolOutput,
951            tpm_guest_tests_windows_x64 => TpmGuestTestsOutput,
952            test_igvm_agent_rpc_server_windows_x64 => TestIgvmAgentRpcServerOutput,
953            vmfirmwareigvm_cvm_x64 => VmfirmwareigvmDllOutput,
954            // linux build machine
955            openhcl_standard_x64 => OpenhclIgvmOutput,
956            openhcl_cvm_x64 => OpenhclIgvmOutput,
957            openhcl_linux_direct_x64 => OpenhclIgvmOutput,
958            openhcl_sivm_x64 => OpenhclIgvmOutput,
959            pipette_linux_musl_x64 => PipetteOutput,
960            tmk_vmm_linux_musl_x64 => TmkVmmOutput,
961            tpm_guest_tests_linux_x64 => TpmGuestTestsOutput,
962            // any machine
963            guest_test_uefi_x64 => GuestTestUefiOutput,
964            tmks_x64 => TmksOutput,
965        )
966    );
967
968    vmm_tests_built_artifacts_builder!(
969        VmmTestsArtifactsBuilderWindowsAarch64,
970        (
971            // windows build machine
972            nextest_vmm_tests_archive_windows_aarch64 => NextestVmmTestsArchive,
973            openvmm_windows_aarch64 => OpenvmmOutput,
974            pipette_windows_aarch64 => PipetteOutput,
975            tmk_vmm_windows_aarch64 => TmkVmmOutput,
976            vmgstool_windows_aarch64 => VmgstoolOutput,
977            vmgstool_dev_windows_aarch64 => VmgstoolOutput,
978            // linux build machine
979            openhcl_standard_aarch64 => OpenhclIgvmOutput,
980            pipette_linux_musl_aarch64 => PipetteOutput,
981            tmk_vmm_linux_musl_aarch64 => TmkVmmOutput,
982            // any machine
983            guest_test_uefi_aarch64 => GuestTestUefiOutput,
984            tmks_aarch64 => TmksOutput,
985        )
986    );
987}