flowey_hvlite/pipelines/
vmm_tests.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use flowey::node::prelude::ReadVar;
5use flowey::pipeline::prelude::*;
6use flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::VmmTestSelectionFlags;
7use flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::VmmTestSelections;
8use flowey_lib_hvlite::install_vmm_tests_deps::VmmTestsDepSelections;
9use flowey_lib_hvlite::run_cargo_build::common::CommonArch;
10use flowey_lib_hvlite::run_cargo_build::common::CommonTriple;
11use std::path::PathBuf;
12use vmm_test_images::KnownTestArtifacts;
13
14#[derive(clap::ValueEnum, Copy, Clone)]
15pub enum VmmTestTargetCli {
16    /// Windows Aarch64
17    WindowsAarch64,
18    /// Windows X64
19    WindowsX64,
20    /// Linux X64
21    LinuxX64,
22}
23
24/// Build everything needed and run the VMM tests
25#[derive(clap::Args)]
26pub struct VmmTestsCli {
27    /// Specify what target to build the VMM tests for
28    ///
29    /// If not specified, defaults to the current host target.
30    #[clap(long)]
31    target: Option<VmmTestTargetCli>,
32
33    /// Directory for the output artifacts
34    #[clap(long)]
35    dir: PathBuf,
36
37    /// Custom test filter
38    #[clap(long, conflicts_with("flags"))]
39    filter: Option<String>,
40    /// Custom list of artifacts to download
41    #[clap(long, conflicts_with("flags"), requires("filter"))]
42    artifacts: Vec<KnownTestArtifacts>,
43    /// Flags used to generate the VMM test filter
44    ///
45    /// Syntax: `--flags=<+|-><flag>,..`
46    ///
47    /// Available flags with default values:
48    ///
49    /// `-tdx,-snp,-hyperv_vbs,+windows,+ubuntu,+freebsd,+linux,+openhcl,+openvmm,+hyperv,+uefi,+pcat,+tmk,+guest_test_uefi`
50    // TODO: Automatically generate the list of possible flags
51    #[clap(long)]
52    flags: Option<VmmTestSelectionFlags>,
53
54    /// pass `--verbose` to cargo
55    #[clap(long)]
56    verbose: bool,
57    /// Automatically install any missing required dependencies.
58    #[clap(long)]
59    install_missing_deps: bool,
60
61    /// Use unstable WHP interfaces
62    #[clap(long)]
63    unstable_whp: bool,
64    /// Release build instead of debug build
65    #[clap(long)]
66    release: bool,
67
68    /// Build only, do not run
69    #[clap(long)]
70    build_only: bool,
71    /// Copy extras to output dir (symbols, etc)
72    #[clap(long)]
73    copy_extras: bool,
74
75    /// Optional: custom kernel modules
76    #[clap(long)]
77    custom_kernel_modules: Option<PathBuf>,
78    /// Optional: custom kernel image
79    #[clap(long)]
80    custom_kernel: Option<PathBuf>,
81}
82
83impl IntoPipeline for VmmTestsCli {
84    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
85        if !matches!(backend_hint, PipelineBackendHint::Local) {
86            anyhow::bail!("vmm-tests is for local use only")
87        }
88
89        let Self {
90            target,
91            dir,
92            filter,
93            artifacts,
94            flags,
95            verbose,
96            install_missing_deps,
97            unstable_whp,
98            release,
99            build_only,
100            copy_extras,
101            custom_kernel_modules,
102            custom_kernel,
103        } = self;
104
105        let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
106            ReadVar::from_static(crate::repo_root()),
107        );
108
109        let mut pipeline = Pipeline::new();
110
111        let target = if let Some(t) = target {
112            t
113        } else {
114            match (
115                FlowArch::host(backend_hint),
116                FlowPlatform::host(backend_hint),
117            ) {
118                (FlowArch::Aarch64, FlowPlatform::Windows) => VmmTestTargetCli::WindowsAarch64,
119                (FlowArch::X86_64, FlowPlatform::Windows) => VmmTestTargetCli::WindowsX64,
120                (FlowArch::X86_64, FlowPlatform::Linux(_)) => VmmTestTargetCli::LinuxX64,
121                _ => anyhow::bail!("unsupported host"),
122            }
123        };
124
125        let target = match target {
126            VmmTestTargetCli::WindowsAarch64 => CommonTriple::AARCH64_WINDOWS_MSVC,
127            VmmTestTargetCli::WindowsX64 => CommonTriple::X86_64_WINDOWS_MSVC,
128            VmmTestTargetCli::LinuxX64 => CommonTriple::X86_64_LINUX_GNU,
129        };
130        let target_os = target.as_triple().operating_system;
131        let target_architecture = target.as_triple().architecture;
132
133        let recipe_arch = match target_architecture {
134            target_lexicon::Architecture::X86_64 => CommonArch::X86_64,
135            target_lexicon::Architecture::Aarch64(_) => CommonArch::Aarch64,
136            _ => anyhow::bail!("Unsupported architecture: {:?}", target_architecture),
137        };
138
139        let mut job = pipeline.new_job(
140            FlowPlatform::host(backend_hint),
141            FlowArch::host(backend_hint),
142            "build vmm test dependencies",
143        );
144
145        job = job.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init);
146
147        // Override kernel with local paths if both kernel and modules are specified
148        if let (Some(kernel_path), Some(modules_path)) =
149            (custom_kernel.clone(), custom_kernel_modules.clone())
150        {
151            job =
152                job.dep_on(
153                    move |_| flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalKernel {
154                        arch: recipe_arch,
155                        kernel: ReadVar::from_static(kernel_path),
156                        modules: ReadVar::from_static(modules_path),
157                    },
158                );
159        }
160
161        job = job
162            .dep_on(
163                |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
164                    hvlite_repo_source: openvmm_repo.clone(),
165                },
166            )
167            .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
168                local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
169                    interactive: true,
170                    auto_install: install_missing_deps,
171                    force_nuget_mono: false,
172                    external_nuget_auth: false,
173                    ignore_rust_version: true,
174                }),
175                verbose: ReadVar::from_static(verbose),
176                locked: false,
177                deny_warnings: false,
178            })
179            .dep_on(|ctx| {
180                flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::Params {
181                    target,
182                    test_content_dir: dir,
183                    selections: if let Some(filter) = filter {
184                        VmmTestSelections::Custom {
185                            filter,
186                            artifacts,
187                            // TODO: add a way to manually specify these
188                            // For now, just build and install everything.
189                            build: Default::default(),
190                            deps: match target_os {
191                                target_lexicon::OperatingSystem::Windows => {
192                                    VmmTestsDepSelections::Windows {
193                                        hyperv: true,
194                                        whp: true,
195                                        // No hardware isolation support on Aarch64, so don't default to needing it when the
196                                        // user specifies a custom filter.
197                                        hardware_isolation: match target_architecture {
198                                            target_lexicon::Architecture::Aarch64(_) => false,
199                                            target_lexicon::Architecture::X86_64 => true,
200                                            _ => panic!(
201                                                "Unhandled architecture: {:?}",
202                                                target_architecture
203                                            ),
204                                        },
205                                    }
206                                }
207                                target_lexicon::OperatingSystem::Linux => {
208                                    VmmTestsDepSelections::Linux
209                                }
210                                _ => unreachable!(),
211                            },
212                        }
213                    } else {
214                        VmmTestSelections::Flags(flags.unwrap_or_default())
215                    },
216                    unstable_whp,
217                    release,
218                    build_only,
219                    copy_extras,
220                    custom_kernel_modules,
221                    custom_kernel,
222                    done: ctx.new_done_handle(),
223                }
224            });
225
226        job.finish();
227
228        Ok(pipeline)
229    }
230}