Skip to main content

flowey_hvlite/pipelines/
checkin_gates.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! See [`CheckinGatesCli`]
5
6use crate::pipelines_shared::ado_pools;
7use crate::pipelines_shared::gh_pools;
8use anyhow::Context as _;
9use flowey::node::prelude::AdoResourcesRepositoryId;
10use flowey::node::prelude::FlowPlatformLinuxDistro;
11use flowey::node::prelude::GhPermission;
12use flowey::node::prelude::GhPermissionValue;
13use flowey::node::prelude::ReadVar;
14use flowey::pipeline::prelude::*;
15use flowey_lib_common::git_checkout::RepoSource;
16use flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::OpenhclIgvmBuildParams;
17use flowey_lib_hvlite::_jobs::check_openvmm_hcl_size::artifact_name_openhcl_baseline;
18use flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::TestContentConfig;
19use flowey_lib_hvlite::build_incubator::IncubatorProfileNameOrPath;
20use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
21use flowey_lib_hvlite::build_openvmm_hcl::OpenvmmHclBuildProfile;
22use flowey_lib_hvlite::build_openvmm_hcl::OpenvmmHclFeature;
23use flowey_lib_hvlite::common::CommonArch;
24use flowey_lib_hvlite::common::CommonPlatform;
25use flowey_lib_hvlite::common::CommonProfile;
26use flowey_lib_hvlite::common::CommonTriple;
27use flowey_lib_hvlite::init_vmm_tests_content_dir::ResolveVmmTestsBuiltArtifacts;
28use flowey_lib_hvlite::init_vmm_tests_content_dir::vmm_tests_artifact_builders;
29use flowey_lib_hvlite::init_vmm_tests_env::PetriParams;
30use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDeps;
31use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsLinux;
32use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsWindows;
33use std::collections::BTreeMap;
34use std::collections::BTreeSet;
35use target_lexicon::Triple;
36use vmm_test_images::KnownTestArtifacts;
37
38// This is a cap for surplus 2 MiB hugetlb pages, not a reservation. Keep it
39// generous enough for VMM tests without tying CI provisioning to one test's RAM.
40const HUGETLB_2MB_OVERCOMMIT_PAGES: u64 = 4096;
41
42#[derive(Copy, Clone, clap::ValueEnum)]
43enum PipelineConfig {
44    /// Run on all PRs targeting the OpenVMM GitHub repo.
45    Pr,
46    /// Run on all commits that land in a branch.
47    ///
48    /// The key difference between the CI and PR pipelines is whether things are
49    /// being built in `release` mode.
50    Ci,
51    /// Release variant of the `Pr` pipeline.
52    PrRelease,
53}
54
55/// A unified pipeline defining all checkin gates required to land a commit in
56/// the OpenVMM repo.
57#[derive(clap::Args)]
58pub struct CheckinGatesCli {
59    /// Which pipeline configuration to use.
60    #[clap(long)]
61    config: PipelineConfig,
62
63    #[clap(flatten)]
64    local_run_args: Option<crate::pipelines_shared::cfg_common_params::LocalRunArgs>,
65}
66
67impl IntoPipeline for CheckinGatesCli {
68    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
69        let Self {
70            config,
71            local_run_args,
72        } = self;
73
74        let release = match config {
75            PipelineConfig::Ci | PipelineConfig::PrRelease => true,
76            PipelineConfig::Pr => false,
77        };
78
79        let mut pipeline = Pipeline::new();
80
81        let mut vmgstools = BTreeMap::new();
82
83        // configure pr/ci branch triggers and add gh pipeline name
84        {
85            let branches = vec!["main".into(), "release/*".into()];
86
87            // Paths that don't affect the Rust build or tests. Changes
88            // to only these paths will not trigger the CI pipeline on push.
89            //
90            // NOTE: The PR pipeline intentionally does NOT use paths-ignore,
91            // because the "openvmm checkin gates" job is a required status
92            // check. If the workflow is skipped due to path filters, the
93            // gate is never reported and the PR is blocked. The CI pipeline
94            // can still use paths-ignore since it has no required checks.
95            let ci_paths_ignore = vec!["Guide/**".into(), "petri/logview/**".into()];
96
97            match config {
98                PipelineConfig::Ci => {
99                    pipeline
100                        .gh_set_ci_triggers(GhCiTriggers {
101                            branches,
102                            paths_ignore: ci_paths_ignore.clone(),
103                            ..Default::default()
104                        })
105                        .gh_set_name("OpenVMM CI");
106                }
107                PipelineConfig::Pr => {
108                    pipeline
109                        .gh_set_pr_triggers(GhPrTriggers {
110                            branches,
111                            ..GhPrTriggers::new_draftable()
112                        })
113                        .gh_set_name("OpenVMM PR")
114                        .ado_set_pr_triggers(AdoPrTriggers {
115                            branches: vec!["main".into(), "release/*".into(), "embargo/*".into()],
116                            exclude_paths: ci_paths_ignore.clone(),
117                            ..Default::default()
118                        });
119                }
120                PipelineConfig::PrRelease => {
121                    // This workflow is triggered when a specific label is present on a PR.
122                    let mut triggers = GhPrTriggers::new_draftable();
123                    triggers.branches = branches;
124                    triggers.types.push("labeled".into());
125                    pipeline
126                        .gh_set_pr_triggers(triggers)
127                        .gh_set_name("[Optional] OpenVMM Release PR");
128                }
129            }
130        }
131
132        let openvmm_repo_source = match backend_hint {
133            PipelineBackendHint::Local => {
134                RepoSource::ExistingClone(ReadVar::from_static(crate::repo_root()))
135            }
136            PipelineBackendHint::Github => RepoSource::GithubSelf,
137            PipelineBackendHint::Ado => {
138                RepoSource::AdoResource(AdoResourcesRepositoryId::new_self())
139            }
140        };
141
142        if let RepoSource::GithubSelf = &openvmm_repo_source {
143            pipeline.gh_set_flowey_bootstrap_template(
144                crate::pipelines_shared::gh_flowey_bootstrap_template::get_template(),
145            );
146        }
147
148        if let RepoSource::AdoResource(source) = &openvmm_repo_source {
149            pipeline.ado_set_flowey_bootstrap_template(
150                crate::pipelines_shared::ado_flowey_bootstrap_template::get_template_ado(source),
151            );
152        }
153
154        let cfg_common_params = crate::pipelines_shared::cfg_common_params::get_cfg_common_params(
155            &mut pipeline,
156            backend_hint,
157            local_run_args,
158        )?;
159
160        pipeline.inject_all_jobs_with(move |job| {
161            let mut job = job
162                .dep_on(&cfg_common_params)
163                .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
164                .dep_on(
165                    |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
166                        hvlite_repo_source: openvmm_repo_source.clone(),
167                    },
168                )
169                .gh_grant_permissions::<flowey_lib_common::git_checkout::Node>([(
170                    GhPermission::Contents,
171                    GhPermissionValue::Read,
172                )])
173                .gh_grant_permissions::<flowey_lib_common::gh_task_azure_login::Node>([(
174                    GhPermission::IdToken,
175                    GhPermissionValue::Write,
176                )]);
177
178            // For the release pipeline, only run if the "release-ci-required" label is present and PR is not draft
179            if matches!(config, PipelineConfig::PrRelease) {
180                job = job.gh_dangerous_override_if(
181                    "contains(github.event.pull_request.labels.*.name, 'release-ci-required') && github.event.pull_request.draft == false",
182                );
183            }
184
185            job
186        });
187
188        let openhcl_musl_target = |arch: CommonArch| -> Triple {
189            CommonTriple::Common {
190                arch,
191                platform: CommonPlatform::LinuxMusl,
192            }
193            .as_triple()
194        };
195
196        // initialize the various "VmmTestsArtifactsBuilder" containers, which
197        // are used to "skim off" various artifacts that the VMM test jobs
198        // require.
199        let mut vmm_tests_artifacts_linux_x86 =
200            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderLinuxX86::default();
201        let mut vmm_tests_artifacts_linux_musl_x86 =
202            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderLinuxX86::default();
203        let mut vmm_tests_artifacts_windows_x86 =
204            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderWindowsX86::default();
205        let mut vmm_tests_artifacts_windows_aarch64 =
206            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderWindowsAarch64::default();
207        let mut vmm_tests_artifacts_linux_aarch64_tcg =
208            vmm_tests_artifact_builders::VmmTestsArtifactsBuilderLinuxAarch64Tcg::default();
209
210        // Run VMM.Perf after merge, or before merge through the opt-in release
211        // PR pipeline.
212        let enable_vmm_perf = matches!(
213            (backend_hint, config),
214            (
215                PipelineBackendHint::Github,
216                PipelineConfig::Ci | PipelineConfig::PrRelease
217            )
218        );
219        let mut use_vmm_perf_runner_gnu_x64 = None;
220        let mut use_vmm_perf_runner_musl_x64 = None;
221        let mut use_vmm_perf_openvmm_gnu_x64 = None;
222        let mut use_vmm_perf_openvmm_musl_x64 = None;
223        let mut use_vmm_perf_runner_windows_x64 = None;
224        let mut use_vmm_perf_openvmm_windows_x64 = None;
225
226        // We need to maintain a list of all jobs, so we can hang the "all good"
227        // job off of them. This is requires because github status checks only allow
228        // specifying jobs, and not workflows.
229        // There's more info in the following discussion:
230        // <https://github.com/orgs/community/discussions/12395>
231        let mut all_jobs = Vec::new();
232
233        // Quick check gate
234        //
235        // Combined fmt + clippy on one self-hosted linux machine.
236        // Catches the most common failures quickly before fanning out expensive jobs.
237        let quick_check_job = if matches!(config, PipelineConfig::Pr | PipelineConfig::PrRelease) {
238            let job = pipeline
239                .new_job(
240                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
241                    FlowArch::X86_64,
242                    "quick check [fmt, clippy x64-linux]",
243                )
244                .gh_set_pool(gh_pools::default_linux())
245                .ado_set_pool(ado_pools::default_linux())
246                // 1. xtask fmt (linux)
247                .side_effect(|done| flowey_lib_hvlite::_jobs::check_xtask_fmt::Request {
248                    target: CommonTriple::X86_64_LINUX_GNU,
249                    done,
250                })
251                // 2. clippy for x64-linux-gnu
252                .side_effect(|done| flowey_lib_hvlite::_jobs::check_clippy::Request {
253                    target: target_lexicon::triple!("x86_64-unknown-linux-gnu"),
254                    profile: CommonProfile::from_release(release),
255                    done,
256                    also_check_misc_nostd_crates: false,
257                })
258                .finish();
259
260            Some(job)
261        } else {
262            // skip in CI
263            None
264        };
265
266        // emit xtask fmt job
267        {
268            let windows_fmt_job = pipeline
269                .new_job(
270                    FlowPlatform::Windows,
271                    FlowArch::X86_64,
272                    "xtask fmt (windows)",
273                )
274                .gh_set_pool(gh_pools::windows_x64_gh())
275                .ado_set_pool(ado_pools::default_windows())
276                .side_effect(|done| flowey_lib_hvlite::_jobs::check_xtask_fmt::Request {
277                    target: CommonTriple::X86_64_WINDOWS_MSVC,
278                    done,
279                })
280                .finish();
281
282            let linux_fmt_job = if let Some(ref qc) = quick_check_job {
283                // PR/PrRelease: linux fmt is handled by the quick-check job
284                qc.clone()
285            } else {
286                // CI mode: keep standalone linux fmt job
287                let job = pipeline
288                    .new_job(
289                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
290                        FlowArch::X86_64,
291                        "xtask fmt (linux)",
292                    )
293                    .gh_set_pool(gh_pools::linux_x64_gh())
294                    .ado_set_pool(ado_pools::default_linux())
295                    .side_effect(|done| flowey_lib_hvlite::_jobs::check_xtask_fmt::Request {
296                        target: CommonTriple::X86_64_LINUX_GNU,
297                        done,
298                    })
299                    .finish();
300                all_jobs.push(job.clone());
301                job
302            };
303
304            // cut down on extra noise by having the linux check run first, and
305            // then if it passes, run the windows checks just in case there is a
306            // difference between the two.
307            pipeline.non_artifact_dep(&windows_fmt_job, &linux_fmt_job);
308
309            all_jobs.push(windows_fmt_job);
310        }
311
312        // emit shared dependencies jobs
313        //
314        // In order to ensure we start running VMM tests as soon as possible, we emit
315        // a job for windows and linux building dependencies used by VMM tests on all platforms.
316        // These jobs build dependencies for all architectures, as these dependencies are typically
317        // small and fast to build.
318        //
319        // We have to create the per-arch artifacts up front so that we don't try
320        // to mutably borrow `pipeline` while a job builder also holds a mutable borrow.
321        let mut shared_win_pipette_artifacts = Vec::new();
322        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
323            let arch_tag = match arch {
324                CommonArch::X86_64 => "x64",
325                CommonArch::Aarch64 => "aarch64",
326            };
327            let (pub_pipette_windows, use_pipette_windows) =
328                pipeline.new_typed_artifact(format!("{arch_tag}-windows-pipette"));
329            // filter off artifacts required by the VMM tests job
330            match arch {
331                CommonArch::X86_64 => {
332                    vmm_tests_artifacts_linux_x86.use_pipette_windows =
333                        Some(use_pipette_windows.clone());
334                    vmm_tests_artifacts_linux_musl_x86.use_pipette_windows =
335                        Some(use_pipette_windows.clone());
336                    vmm_tests_artifacts_windows_x86.use_pipette_windows =
337                        Some(use_pipette_windows.clone());
338                }
339                CommonArch::Aarch64 => {
340                    vmm_tests_artifacts_windows_aarch64.use_pipette_windows =
341                        Some(use_pipette_windows.clone());
342                }
343            }
344            shared_win_pipette_artifacts.push((arch, pub_pipette_windows));
345        }
346        let mut shared_win_job = pipeline
347            .new_job(
348                FlowPlatform::Windows,
349                FlowArch::X86_64,
350                "build artifacts (shared VMM tests) [windows]",
351            )
352            .gh_set_pool(gh_pools::default_windows())
353            .ado_set_pool(ado_pools::default_windows());
354        for (arch, pub_pipette_windows) in shared_win_pipette_artifacts {
355            shared_win_job = shared_win_job.publish(pub_pipette_windows, |pipette| {
356                flowey_lib_hvlite::build_pipette::Request {
357                    target: CommonTriple::Common {
358                        arch,
359                        platform: CommonPlatform::WindowsMsvc,
360                    },
361                    profile: CommonProfile::from_release(release),
362                    pipette,
363                }
364            });
365        }
366        all_jobs.push(shared_win_job.finish());
367
368        // Now do linux
369        //
370        // Create the per-arch artifacts up front so that we don't try to
371        // mutably borrow `pipeline` while the job builder also holds a
372        // mutable borrow.
373        let mut shared_linux_artifacts = Vec::new();
374        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
375            let arch_tag = match arch {
376                CommonArch::X86_64 => "x64",
377                CommonArch::Aarch64 => "aarch64",
378            };
379
380            let (pub_tpm_guest_tests, use_tpm_guest_tests) =
381                pipeline.new_typed_artifact(format!("{arch_tag}-linux-tpm_guest_tests"));
382            let (pub_guest_test_uefi, use_guest_test_uefi) =
383                pipeline.new_typed_artifact(format!("{arch_tag}-guest_test_uefi"));
384            let (pub_pipette_linux_musl, use_pipette_linux_musl) =
385                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-pipette"));
386            let (pub_tmk_vmm, use_tmk_vmm) =
387                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-tmk_vmm"));
388            let (pub_tmks, use_tmks) = pipeline.new_typed_artifact(format!("{arch_tag}-tmks"));
389
390            match arch {
391                CommonArch::X86_64 => {
392                    vmm_tests_artifacts_linux_x86.use_guest_test_uefi =
393                        Some(use_guest_test_uefi.clone());
394                    vmm_tests_artifacts_windows_x86.use_guest_test_uefi =
395                        Some(use_guest_test_uefi.clone());
396                    vmm_tests_artifacts_windows_x86.use_tmks = Some(use_tmks.clone());
397                    vmm_tests_artifacts_linux_x86.use_tmks = Some(use_tmks.clone());
398                    vmm_tests_artifacts_windows_x86.use_tpm_guest_tests_linux =
399                        Some(use_tpm_guest_tests.clone());
400                    vmm_tests_artifacts_linux_musl_x86.use_guest_test_uefi =
401                        Some(use_guest_test_uefi.clone());
402                    vmm_tests_artifacts_linux_musl_x86.use_tmks = Some(use_tmks.clone());
403                    vmm_tests_artifacts_windows_x86.use_pipette_linux_musl =
404                        Some(use_pipette_linux_musl.clone());
405                    vmm_tests_artifacts_linux_x86.use_pipette_linux_musl =
406                        Some(use_pipette_linux_musl.clone());
407                    vmm_tests_artifacts_linux_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
408                    vmm_tests_artifacts_windows_x86.use_tmk_vmm_linux_musl =
409                        Some(use_tmk_vmm.clone());
410                    vmm_tests_artifacts_linux_musl_x86.use_pipette_linux_musl =
411                        Some(use_pipette_linux_musl.clone());
412                    vmm_tests_artifacts_linux_musl_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
413                }
414                CommonArch::Aarch64 => {
415                    vmm_tests_artifacts_windows_aarch64.use_guest_test_uefi =
416                        Some(use_guest_test_uefi.clone());
417                    vmm_tests_artifacts_windows_aarch64.use_tmks = Some(use_tmks.clone());
418                    vmm_tests_artifacts_windows_aarch64.use_pipette_linux_musl =
419                        Some(use_pipette_linux_musl.clone());
420                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm_linux_musl =
421                        Some(use_tmk_vmm.clone());
422                    vmm_tests_artifacts_linux_aarch64_tcg.use_guest_test_uefi =
423                        Some(use_guest_test_uefi.clone());
424                    vmm_tests_artifacts_linux_aarch64_tcg.use_tmks = Some(use_tmks.clone());
425                    vmm_tests_artifacts_linux_aarch64_tcg.use_pipette_linux_musl =
426                        Some(use_pipette_linux_musl.clone());
427                    vmm_tests_artifacts_linux_aarch64_tcg.use_tmk_vmm = Some(use_tmk_vmm.clone());
428                }
429            }
430
431            shared_linux_artifacts.push((
432                arch,
433                pub_tpm_guest_tests,
434                pub_guest_test_uefi,
435                pub_pipette_linux_musl,
436                pub_tmk_vmm,
437                pub_tmks,
438            ));
439        }
440
441        // Create incubator artifact handle (for TCG tests).
442        // Must be created before the shared_linux_job builder to avoid
443        // borrowing `pipeline` while the job builder holds a mutable borrow.
444        let (pub_incubator, use_incubator) = pipeline.new_typed_artifact("x64-linux-incubator");
445        vmm_tests_artifacts_linux_aarch64_tcg.use_incubator = Some(use_incubator);
446
447        let mut shared_linux_job = pipeline
448            .new_job(
449                FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
450                FlowArch::X86_64,
451                "build artifacts (shared VMM tests) [linux]",
452            )
453            .gh_set_pool(gh_pools::linux_intel_v6_1es())
454            .ado_set_pool(ado_pools::default_linux());
455        for (
456            arch,
457            pub_tpm_guest_tests,
458            pub_guest_test_uefi,
459            pub_pipette_linux_musl,
460            pub_tmk_vmm,
461            pub_tmks,
462        ) in shared_linux_artifacts
463        {
464            shared_linux_job = shared_linux_job
465                .publish(pub_guest_test_uefi, |guest_test_uefi| {
466                    flowey_lib_hvlite::build_guest_test_uefi::Request {
467                        arch,
468                        profile: CommonProfile::from_release(release),
469                        guest_test_uefi,
470                    }
471                })
472                .publish(pub_tmks, |tmks| flowey_lib_hvlite::build_tmks::Request {
473                    arch,
474                    profile: CommonProfile::from_release(release),
475                    tmks,
476                })
477                .publish(pub_tpm_guest_tests, |tpm_guest_tests| {
478                    flowey_lib_hvlite::build_tpm_guest_tests::Request {
479                        target: CommonTriple::Common {
480                            arch,
481                            platform: CommonPlatform::LinuxGnu,
482                        },
483                        profile: CommonProfile::from_release(release),
484                        tpm_guest_tests,
485                    }
486                })
487                .publish(pub_pipette_linux_musl, |pipette| {
488                    flowey_lib_hvlite::build_pipette::Request {
489                        target: CommonTriple::Common {
490                            arch,
491                            platform: CommonPlatform::LinuxMusl,
492                        },
493                        profile: CommonProfile::from_release(release),
494                        pipette,
495                    }
496                })
497                .publish(pub_tmk_vmm, |tmk_vmm| {
498                    flowey_lib_hvlite::build_tmk_vmm::Request {
499                        target: CommonTriple::Common {
500                            arch,
501                            platform: CommonPlatform::LinuxMusl,
502                        },
503                        profile: CommonProfile::from_release(release),
504                        tmk_vmm,
505                    }
506                });
507        }
508
509        // Build incubator binary (x86_64 Linux, for running TCG tests on CI hosts)
510        shared_linux_job = shared_linux_job.publish(pub_incubator, |incubator| {
511            flowey_lib_hvlite::build_incubator::Request {
512                target: CommonTriple::X86_64_LINUX_GNU,
513                profile: CommonProfile::from_release(release),
514                incubator,
515            }
516        });
517
518        all_jobs.push(shared_linux_job.finish());
519
520        // emit windows build machine jobs
521        //
522        // In order to ensure we start running VMM tests as soon as possible, we emit
523        // two separate windows job per arch - one for artifacts in the VMM tests
524        // hotpath, and another for any auxiliary artifacts that aren't
525        // required by VMM tests.
526        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
527            let arch_tag = match arch {
528                CommonArch::X86_64 => "x64",
529                CommonArch::Aarch64 => "aarch64",
530            };
531
532            // artifacts which _are_ in the VMM tests "hot path"
533            let (pub_openvmm, use_openvmm) =
534                pipeline.new_typed_artifact(format!("{arch_tag}-windows-openvmm"));
535
536            let (pub_tmk_vmm, use_tmk_vmm) =
537                pipeline.new_typed_artifact(format!("{arch_tag}-windows-tmk_vmm"));
538
539            let (pub_prep_steps, use_prep_steps) =
540                pipeline.new_typed_artifact(format!("{arch_tag}-windows-prep_steps"));
541
542            let (pub_vmgstool, use_vmgstool) =
543                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgstool"));
544
545            let (pub_vmgstool_dev, use_vmgstool_dev) =
546                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgstool-dev"));
547
548            let (pub_tpm_guest_tests, use_tpm_guest_tests_windows) =
549                pipeline.new_typed_artifact(format!("{arch_tag}-windows-tpm_guest_tests"));
550
551            let (pub_test_igvm_agent_rpc_server, use_test_igvm_agent_rpc_server) = pipeline
552                .new_typed_artifact(format!("{arch_tag}-windows-test_igvm_agent_rpc_server"));
553
554            let (pub_vmm_tests_archive, use_vmm_tests_archive) =
555                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmm-tests-archive"));
556            let (pub_vmm_perf, use_vmm_perf) =
557                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmm-perf-runner"));
558
559            // filter off interesting artifacts required by the VMM tests job
560            match arch {
561                CommonArch::X86_64 => {
562                    vmm_tests_artifacts_windows_x86.use_openvmm = Some(use_openvmm.clone());
563                    vmm_tests_artifacts_windows_x86.use_tmk_vmm = Some(use_tmk_vmm.clone());
564                    vmm_tests_artifacts_windows_x86.use_prep_steps = Some(use_prep_steps.clone());
565                    vmm_tests_artifacts_windows_x86.use_vmgstool = Some(use_vmgstool.clone());
566                    vmm_tests_artifacts_windows_x86.use_vmgstool_dev =
567                        Some(use_vmgstool_dev.clone());
568                    vmm_tests_artifacts_windows_x86.use_tpm_guest_tests_windows =
569                        Some(use_tpm_guest_tests_windows.clone());
570                    vmm_tests_artifacts_windows_x86.use_test_igvm_agent_rpc_server =
571                        Some(use_test_igvm_agent_rpc_server.clone());
572                    vmm_tests_artifacts_windows_x86.use_nextest_vmm_tests_archive =
573                        Some(use_vmm_tests_archive.clone());
574                    use_vmm_perf_runner_windows_x64 = Some(use_vmm_perf);
575                    use_vmm_perf_openvmm_windows_x64 = Some(use_openvmm.clone());
576                }
577                CommonArch::Aarch64 => {
578                    vmm_tests_artifacts_windows_aarch64.use_openvmm = Some(use_openvmm.clone());
579                    vmm_tests_artifacts_windows_aarch64.use_tmk_vmm = Some(use_tmk_vmm.clone());
580                    vmm_tests_artifacts_windows_aarch64.use_vmgstool = Some(use_vmgstool.clone());
581                    vmm_tests_artifacts_windows_aarch64.use_vmgstool_dev =
582                        Some(use_vmgstool_dev.clone());
583                    vmm_tests_artifacts_windows_aarch64.use_nextest_vmm_tests_archive =
584                        Some(use_vmm_tests_archive.clone());
585                }
586            }
587            // emit a job for artifacts which _are not_ in the VMM tests "hot
588            // path"
589            // artifacts which _are not_ in the VMM tests "hot path"
590            let (pub_igvmfilegen, _use_igvmfilegen) =
591                pipeline.new_typed_artifact(format!("{arch_tag}-windows-igvmfilegen"));
592            let (pub_vmgs_lib, _use_vmgs_lib) =
593                pipeline.new_typed_artifact(format!("{arch_tag}-windows-vmgs_lib"));
594            let (pub_hypestv, _use_hypestv) =
595                pipeline.new_typed_artifact(format!("{arch_tag}-windows-hypestv"));
596            let (pub_ohcldiag_dev, _use_ohcldiag_dev) =
597                pipeline.new_typed_artifact(format!("{arch_tag}-windows-ohcldiag-dev"));
598
599            let job = pipeline
600                .new_job(
601                    FlowPlatform::Windows,
602                    FlowArch::X86_64,
603                    format!("build artifacts (not for VMM tests) [{arch_tag}-windows]"),
604                )
605                .gh_set_pool(gh_pools::default_windows())
606                .ado_set_pool(ado_pools::default_windows())
607                .publish(pub_hypestv, |hypestv| {
608                    flowey_lib_hvlite::build_hypestv::Request {
609                        target: CommonTriple::Common {
610                            arch,
611                            platform: CommonPlatform::WindowsMsvc,
612                        },
613                        profile: CommonProfile::from_release(release),
614                        hypestv,
615                    }
616                })
617                .publish(pub_vmgs_lib, |vmgs_lib| {
618                    flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
619                        target: CommonTriple::Common {
620                            arch,
621                            platform: CommonPlatform::WindowsMsvc,
622                        },
623                        profile: CommonProfile::from_release(release),
624                        vmgs_lib,
625                    }
626                })
627                .publish(pub_igvmfilegen, |igvmfilegen| {
628                    flowey_lib_hvlite::build_igvmfilegen::Request {
629                        build_params:
630                            flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
631                                target: CommonTriple::Common {
632                                    arch,
633                                    platform: CommonPlatform::WindowsMsvc,
634                                },
635                                profile: CommonProfile::from_release(release).into(),
636                            },
637                        igvmfilegen,
638                    }
639                })
640                .publish(pub_ohcldiag_dev, |ohcldiag_dev| {
641                    flowey_lib_hvlite::build_ohcldiag_dev::Request {
642                        target: CommonTriple::Common {
643                            arch,
644                            platform: CommonPlatform::WindowsMsvc,
645                        },
646                        profile: CommonProfile::from_release(release),
647                        ohcldiag_dev,
648                    }
649                })
650                .publish(pub_vmm_perf, |vmm_perf| {
651                    flowey_lib_hvlite::build_vmm_perf::Request {
652                        target: CommonTriple::Common {
653                            arch,
654                            platform: CommonPlatform::WindowsMsvc,
655                        },
656                        profile: CommonProfile::from_release(release),
657                        vmm_perf,
658                    }
659                });
660
661            all_jobs.push(job.finish());
662
663            let vmgstool_target = CommonTriple::Common {
664                arch,
665                platform: CommonPlatform::WindowsMsvc,
666            };
667            if vmgstools
668                .insert(vmgstool_target.to_string(), use_vmgstool.clone())
669                .is_some()
670            {
671                anyhow::bail!("multiple vmgstools for the same target");
672            }
673
674            // emit a job for artifacts which _are_ in the VMM tests "hot path"
675            let job = pipeline
676                .new_job(
677                    FlowPlatform::Windows,
678                    FlowArch::X86_64,
679                    format!("build artifacts (for VMM tests) [{arch_tag}-windows]"),
680                )
681                .gh_set_pool(gh_pools::default_windows())
682                .ado_set_pool(ado_pools::default_windows())
683                .publish(pub_openvmm, |openvmm| {
684                    flowey_lib_hvlite::build_openvmm::Request {
685                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
686                            target: CommonTriple::Common {
687                                arch,
688                                platform: CommonPlatform::WindowsMsvc,
689                            },
690                            profile: CommonProfile::from_release(release),
691                            // FIXME: this relies on openvmm default features
692                            features: [].into(),
693                        },
694                        openvmm,
695                    }
696                })
697                .publish(pub_tmk_vmm, |tmk_vmm| {
698                    flowey_lib_hvlite::build_tmk_vmm::Request {
699                        target: CommonTriple::Common {
700                            arch,
701                            platform: CommonPlatform::WindowsMsvc,
702                        },
703                        profile: CommonProfile::from_release(release),
704                        tmk_vmm,
705                    }
706                })
707                .publish(pub_prep_steps, |prep_steps| {
708                    flowey_lib_hvlite::build_prep_steps::Request {
709                        target: CommonTriple::Common {
710                            arch,
711                            platform: CommonPlatform::WindowsMsvc,
712                        },
713                        profile: CommonProfile::from_release(release),
714                        prep_steps,
715                    }
716                })
717                .publish(pub_vmgstool, |vmgstool| {
718                    flowey_lib_hvlite::build_vmgstool::Request {
719                        target: vmgstool_target.clone(),
720                        profile: CommonProfile::from_release(release),
721                        with_crypto: true,
722                        with_test_helpers: false,
723                        vmgstool,
724                    }
725                })
726                .publish(pub_vmgstool_dev, |vmgstool| {
727                    flowey_lib_hvlite::build_vmgstool::Request {
728                        target: vmgstool_target,
729                        profile: CommonProfile::from_release(release),
730                        with_crypto: true,
731                        with_test_helpers: true,
732                        vmgstool,
733                    }
734                })
735                .publish(pub_tpm_guest_tests, |tpm_guest_tests| {
736                    flowey_lib_hvlite::build_tpm_guest_tests::Request {
737                        target: CommonTriple::Common {
738                            arch,
739                            platform: CommonPlatform::WindowsMsvc,
740                        },
741                        profile: CommonProfile::from_release(release),
742                        tpm_guest_tests,
743                    }
744                })
745                .publish(
746                    pub_test_igvm_agent_rpc_server,
747                    |test_igvm_agent_rpc_server| {
748                        flowey_lib_hvlite::build_test_igvm_agent_rpc_server::Request {
749                            target: CommonTriple::Common {
750                                arch,
751                                platform: CommonPlatform::WindowsMsvc,
752                            },
753                            profile: CommonProfile::from_release(release),
754                            test_igvm_agent_rpc_server,
755                        }
756                    },
757                ).publish(pub_vmm_tests_archive, |archive| flowey_lib_hvlite::build_nextest_vmm_tests::Request {
758                        target: CommonTriple::Common {
759                                arch,
760
761                                platform: CommonPlatform::WindowsMsvc,
762                            }.as_triple(),
763                        profile: CommonProfile::
764                        from_release(release),
765                        build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
766                            archive,
767                        ),
768                    });
769
770            all_jobs.push(job.finish());
771        }
772
773        // emit linux build machine jobs (without openhcl)
774        for arch in [CommonArch::Aarch64, CommonArch::X86_64] {
775            let arch_tag = match arch {
776                CommonArch::X86_64 => "x64",
777                CommonArch::Aarch64 => "aarch64",
778            };
779
780            let (pub_openvmm, use_openvmm) =
781                pipeline.new_typed_artifact(format!("{arch_tag}-linux-openvmm"));
782            let (pub_openvmm_vhost, use_openvmm_vhost) =
783                pipeline.new_typed_artifact(format!("{arch_tag}-linux-openvmm_vhost"));
784            let (pub_igvmfilegen, _) =
785                pipeline.new_typed_artifact(format!("{arch_tag}-linux-igvmfilegen"));
786            let (pub_vmgs_lib, _) =
787                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgs_lib"));
788            let (pub_vmgstool, use_vmgstool) =
789                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgstool"));
790            let (pub_vmgstool_dev, _use_vmgstool_dev) =
791                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmgstool-dev"));
792            let (pub_ohcldiag_dev, _) =
793                pipeline.new_typed_artifact(format!("{arch_tag}-linux-ohcldiag-dev"));
794            // Also build openvmm and openvmm_vhost for musl on this job,
795            // alongside pipette and tmk_vmm. This enables running VMM tests
796            // on Azure Linux (MSHV) runners which have an older glibc.
797            let (pub_openvmm_musl, use_openvmm_musl) =
798                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-openvmm"));
799            let (pub_openvmm_vhost_musl, use_openvmm_vhost_musl) =
800                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-openvmm_vhost"));
801            let (pub_prep_steps, use_prep_steps) =
802                pipeline.new_typed_artifact(format!("{arch_tag}-linux-prep_steps"));
803            let (pub_vmm_tests_archive, use_vmm_tests_archive) =
804                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmm-tests-archive"));
805            let (pub_vmm_tests_archive_musl, use_vmm_tests_archive_musl) =
806                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-vmm-tests-archive"));
807            let (pub_vmm_perf_gnu, use_vmm_perf_gnu) =
808                pipeline.new_typed_artifact(format!("{arch_tag}-linux-vmm-perf-runner"));
809            let (pub_vmm_perf_musl, use_vmm_perf_musl) =
810                pipeline.new_typed_artifact(format!("{arch_tag}-linux-musl-vmm-perf-runner"));
811
812            // skim off interesting artifacts required by the VMM tests job
813            match arch {
814                CommonArch::X86_64 => {
815                    vmm_tests_artifacts_linux_x86.use_openvmm = Some(use_openvmm.clone());
816                    vmm_tests_artifacts_linux_x86.use_openvmm_vhost =
817                        Some(use_openvmm_vhost.clone());
818                    vmm_tests_artifacts_linux_x86.use_prep_steps = Some(use_prep_steps.clone());
819                    vmm_tests_artifacts_linux_musl_x86.use_openvmm = Some(use_openvmm_musl.clone());
820                    vmm_tests_artifacts_linux_musl_x86.use_openvmm_vhost =
821                        Some(use_openvmm_vhost_musl.clone());
822                    vmm_tests_artifacts_linux_musl_x86.use_prep_steps =
823                        Some(use_prep_steps.clone());
824                    vmm_tests_artifacts_linux_x86.use_nextest_vmm_tests_archive =
825                        Some(use_vmm_tests_archive.clone());
826                    vmm_tests_artifacts_linux_musl_x86.use_nextest_vmm_tests_archive =
827                        Some(use_vmm_tests_archive_musl.clone());
828                    use_vmm_perf_runner_gnu_x64 = Some(use_vmm_perf_gnu);
829                    use_vmm_perf_runner_musl_x64 = Some(use_vmm_perf_musl);
830                    use_vmm_perf_openvmm_gnu_x64 = Some(use_openvmm.clone());
831                    use_vmm_perf_openvmm_musl_x64 = Some(use_openvmm_musl.clone());
832                }
833                CommonArch::Aarch64 => {
834                    vmm_tests_artifacts_linux_aarch64_tcg.use_openvmm =
835                        Some(use_openvmm_musl.clone());
836                    vmm_tests_artifacts_linux_aarch64_tcg.use_nextest_vmm_tests_archive =
837                        Some(use_vmm_tests_archive_musl.clone());
838                }
839            }
840
841            let vmgstool_target = CommonTriple::Common {
842                arch,
843                platform: CommonPlatform::LinuxGnu,
844            };
845            if vmgstools
846                .insert(vmgstool_target.to_string(), use_vmgstool.clone())
847                .is_some()
848            {
849                anyhow::bail!("multiple vmgstools for the same target");
850            }
851
852            // Emit a job for building dependencies used by just linux vmm tests
853            let job = pipeline
854                .new_job(
855                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
856                    FlowArch::X86_64,
857                    format!("build artifacts (for VMM tests) [{arch_tag}-linux]"),
858                )
859                .gh_set_pool(gh_pools::default_linux())
860                .ado_set_pool(ado_pools::default_linux())
861                .publish(pub_openvmm, |openvmm| {
862                    flowey_lib_hvlite::build_openvmm::Request {
863                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
864                            target: CommonTriple::Common {
865                                arch,
866                                platform: CommonPlatform::LinuxGnu,
867                            },
868                            profile: CommonProfile::from_release(release),
869                            // FIXME: this relies on openvmm default features
870                            features: [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::Tpm]
871                                .into(),
872                        },
873                        openvmm,
874                    }
875                })
876                .publish(pub_openvmm_vhost, |openvmm_vhost| {
877                    flowey_lib_hvlite::build_openvmm_vhost::Request {
878                        params: flowey_lib_hvlite::build_openvmm_vhost::OpenvmmVhostBuildParams {
879                            target: CommonTriple::Common {
880                                arch,
881                                platform: CommonPlatform::LinuxGnu,
882                            },
883                            profile: CommonProfile::from_release(release),
884                        },
885                        openvmm_vhost,
886                    }
887                })
888                .publish(pub_vmgstool, |vmgstool| {
889                    flowey_lib_hvlite::build_vmgstool::Request {
890                        target: vmgstool_target.clone(),
891                        profile: CommonProfile::from_release(release),
892                        with_crypto: true,
893                        with_test_helpers: false,
894                        vmgstool,
895                    }
896                })
897                .publish(pub_vmgstool_dev, |vmgstool| {
898                    flowey_lib_hvlite::build_vmgstool::Request {
899                        target: vmgstool_target,
900                        profile: CommonProfile::from_release(release),
901                        with_crypto: true,
902                        with_test_helpers: true,
903                        vmgstool,
904                    }
905                })
906                .publish(pub_vmgs_lib, |vmgs_lib| {
907                    flowey_lib_hvlite::build_and_test_vmgs_lib::Request {
908                        target: CommonTriple::Common {
909                            arch,
910                            platform: CommonPlatform::LinuxGnu,
911                        },
912                        profile: CommonProfile::from_release(release),
913                        vmgs_lib,
914                    }
915                })
916                .publish(pub_igvmfilegen, |igvmfilegen| {
917                    flowey_lib_hvlite::build_igvmfilegen::Request {
918                        build_params:
919                            flowey_lib_hvlite::build_igvmfilegen::IgvmfilegenBuildParams {
920                                target: CommonTriple::Common {
921                                    arch,
922                                    platform: CommonPlatform::LinuxGnu,
923                                },
924                                profile: CommonProfile::from_release(release).into(),
925                            },
926                        igvmfilegen,
927                    }
928                })
929                .publish(pub_ohcldiag_dev, |ohcldiag_dev| {
930                    flowey_lib_hvlite::build_ohcldiag_dev::Request {
931                        target: CommonTriple::Common {
932                            arch,
933                            platform: CommonPlatform::LinuxGnu,
934                        },
935                        profile: CommonProfile::from_release(release),
936                        ohcldiag_dev,
937                    }
938                })
939                .publish(pub_openvmm_musl, |openvmm| {
940                    flowey_lib_hvlite::build_openvmm::Request {
941                        params: flowey_lib_hvlite::build_openvmm::OpenvmmBuildParams {
942                            target: CommonTriple::Common {
943                                arch,
944                                platform: CommonPlatform::LinuxMusl,
945                            },
946                            profile: CommonProfile::from_release(release),
947                            features: [flowey_lib_hvlite::build_openvmm::OpenvmmFeature::Tpm]
948                                .into(),
949                        },
950                        openvmm,
951                    }
952                })
953                .publish(pub_openvmm_vhost_musl, |openvmm_vhost| {
954                    flowey_lib_hvlite::build_openvmm_vhost::Request {
955                        params: flowey_lib_hvlite::build_openvmm_vhost::OpenvmmVhostBuildParams {
956                            target: CommonTriple::Common {
957                                arch,
958                                platform: CommonPlatform::LinuxMusl,
959                            },
960                            profile: CommonProfile::from_release(release),
961                        },
962                        openvmm_vhost,
963                    }
964                })
965                .publish(pub_prep_steps, |prep_steps| {
966                    flowey_lib_hvlite::build_prep_steps::Request {
967                        target: CommonTriple::Common {
968                            arch,
969                            platform: CommonPlatform::LinuxMusl,
970                        },
971                        profile: CommonProfile::from_release(release),
972                        prep_steps,
973                    }
974                }).publish(pub_vmm_tests_archive, |archive| {
975                        flowey_lib_hvlite::build_nextest_vmm_tests::Request {
976                            target: CommonTriple::Common {
977                                arch,
978                                platform: CommonPlatform::LinuxGnu,
979                            }.as_triple(),
980                            profile: CommonProfile::from_release(release),
981                            build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
982                                archive,
983                            ),
984                        }
985                    }).publish(pub_vmm_tests_archive_musl, |archive| {
986                        flowey_lib_hvlite::build_nextest_vmm_tests::Request {
987                            target: CommonTriple::Common {
988                                arch,
989                                platform: CommonPlatform::LinuxMusl,
990                            }.as_triple(),
991                            profile: CommonProfile::from_release(release),
992                            build_mode: flowey_lib_hvlite::build_nextest_vmm_tests::BuildNextestVmmTestsMode::Archive(
993                                archive,
994                            ),
995                        }
996                    }).publish(pub_vmm_perf_gnu, |vmm_perf| {
997                        flowey_lib_hvlite::build_vmm_perf::Request {
998                            target: CommonTriple::Common {
999                                arch,
1000                                platform: CommonPlatform::LinuxGnu,
1001                            },
1002                            profile: CommonProfile::from_release(release),
1003                            vmm_perf,
1004                        }
1005                    })
1006                    .publish(pub_vmm_perf_musl, |vmm_perf| {
1007                        flowey_lib_hvlite::build_vmm_perf::Request {
1008                            target: CommonTriple::Common {
1009                                arch,
1010                                platform: CommonPlatform::LinuxMusl,
1011                            },
1012                            profile: CommonProfile::from_release(release),
1013                            vmm_perf,
1014                        }
1015                    });
1016
1017            all_jobs.push(job.finish());
1018        }
1019
1020        let mut use_openhcl_igvm_files_mi_secure_x86 = BTreeMap::new();
1021
1022        // emit openhcl build job
1023        for (arch, mi_secure) in [
1024            (CommonArch::Aarch64, false),
1025            (CommonArch::X86_64, false),
1026            (CommonArch::X86_64, true),
1027        ] {
1028            let arch_tag = match arch {
1029                CommonArch::X86_64 => "x64",
1030                CommonArch::Aarch64 => "aarch64",
1031            };
1032
1033            let additional_tag = mi_secure.then_some("mi-secure");
1034
1035            let openvmm_hcl_profile = if release {
1036                OpenvmmHclBuildProfile::OpenvmmHclShip
1037            } else {
1038                OpenvmmHclBuildProfile::Debug
1039            };
1040
1041            let mut igvm_recipes = match (arch, mi_secure) {
1042                (CommonArch::X86_64, false) => vec![
1043                    OpenhclIgvmRecipe::X64,
1044                    OpenhclIgvmRecipe::X64Devkern,
1045                    OpenhclIgvmRecipe::X64TestLinuxDirect,
1046                    OpenhclIgvmRecipe::X64TestLinuxDirectDevkern,
1047                    OpenhclIgvmRecipe::X64Cvm,
1048                ],
1049                (CommonArch::X86_64, true) => vec![
1050                    OpenhclIgvmRecipe::X64,
1051                    OpenhclIgvmRecipe::X64TestLinuxDirect,
1052                    OpenhclIgvmRecipe::X64Cvm,
1053                ],
1054                (CommonArch::Aarch64, false) => {
1055                    vec![
1056                        OpenhclIgvmRecipe::Aarch64,
1057                        OpenhclIgvmRecipe::Aarch64Devkern,
1058                    ]
1059                }
1060                _ => unreachable!(),
1061            };
1062            if flowey_lib_hvlite::_jobs::cfg_versions::OPENHCL_KERNEL_DEV_VERSION.is_none() {
1063                igvm_recipes.retain(|recipe| !recipe.uses_dev_kernel());
1064            }
1065
1066            let (mut pub_openhcl_igvms, use_openhcl_igvms) =
1067                pipeline.new_typed_artifact_collection(igvm_recipes.clone(), additional_tag, None);
1068            let (mut pub_openhcl_igvms_extras, _use_openhcl_igvms_extras) = pipeline
1069                .new_typed_artifact_collection(
1070                    igvm_recipes.clone(),
1071                    additional_tag,
1072                    Some("extras"),
1073                );
1074            let (pub_openhcl_baseline, _use_openhcl_baseline) =
1075                (matches!(config, PipelineConfig::Ci) && !mi_secure)
1076                    .then(|| pipeline.new_typed_artifact(artifact_name_openhcl_baseline(arch)))
1077                    .unzip();
1078
1079            // skim off interesting artifacts required by the VMM tests job
1080            match (arch, mi_secure) {
1081                (CommonArch::X86_64, false) => {
1082                    vmm_tests_artifacts_windows_x86.use_openhcl_standard =
1083                        use_openhcl_igvms.get(&OpenhclIgvmRecipe::X64).cloned();
1084                    vmm_tests_artifacts_windows_x86.use_openhcl_cvm =
1085                        use_openhcl_igvms.get(&OpenhclIgvmRecipe::X64Cvm).cloned();
1086                    vmm_tests_artifacts_windows_x86.use_openhcl_linux_direct = use_openhcl_igvms
1087                        .get(&OpenhclIgvmRecipe::X64TestLinuxDirect)
1088                        .cloned();
1089                }
1090                (CommonArch::X86_64, true) => {
1091                    // we'll skim these off later so we can reuse most of the
1092                    // standard x64 builder
1093                    use_openhcl_igvm_files_mi_secure_x86 = use_openhcl_igvms;
1094                }
1095                (CommonArch::Aarch64, false) => {
1096                    vmm_tests_artifacts_windows_aarch64.use_openhcl_standard =
1097                        use_openhcl_igvms.get(&OpenhclIgvmRecipe::Aarch64).cloned();
1098                }
1099                _ => unreachable!(),
1100            }
1101
1102            let build_openhcl_job_tag = |arch_tag, mi_secure| {
1103                format!(
1104                    "build openhcl {}[{arch_tag}-linux]",
1105                    if mi_secure { "(mi-secure) " } else { "" }
1106                )
1107            };
1108            let job = pipeline
1109                .new_job(
1110                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1111                    FlowArch::X86_64,
1112                    build_openhcl_job_tag(arch_tag, mi_secure),
1113                )
1114                .gh_set_pool(gh_pools::default_linux())
1115                .ado_set_pool(ado_pools::default_linux())
1116                .dep_on(|ctx| {
1117                    let publish_baseline_artifact = pub_openhcl_baseline
1118                        .map(|baseline_artifact| ctx.publish_typed_artifact(baseline_artifact));
1119
1120                    flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::Params {
1121                        igvm_files: igvm_recipes
1122                            .into_iter()
1123                            .map(|recipe| {
1124                                let pub_openhcl_igvm = pub_openhcl_igvms.remove(&recipe).unwrap();
1125                                let pub_openhcl_igvm_extras =
1126                                    pub_openhcl_igvms_extras.remove(&recipe).unwrap();
1127                                (
1128                                    OpenhclIgvmBuildParams {
1129                                        profile: openvmm_hcl_profile,
1130                                        recipe,
1131                                        custom_target: Some(CommonTriple::Custom(
1132                                            openhcl_musl_target(arch),
1133                                        )),
1134                                        extra_features: if mi_secure {
1135                                            [OpenvmmHclFeature::MiSecure].into()
1136                                        } else {
1137                                            BTreeSet::new()
1138                                        },
1139                                        // mi secure uses release_cfg=false to select dev manifests (with larger
1140                                        // VTL2 memory) since mi-secure adds overhead that may not fit in
1141                                        // the tighter release memory budget.
1142                                        release_cfg: release && !mi_secure,
1143                                        // Enable confidential diagnostics on the CVM IGVM
1144                                        // consumed by the VMM tests.
1145                                        confidential_debug: true,
1146                                    },
1147                                    ctx.publish_typed_artifact(pub_openhcl_igvm),
1148                                    ctx.publish_typed_artifact(pub_openhcl_igvm_extras),
1149                                )
1150                            })
1151                            .collect(),
1152                        artifact_openhcl_verify_size_baseline: publish_baseline_artifact,
1153                    }
1154                });
1155
1156            all_jobs.push(job.finish());
1157
1158            // TODO: Once we have a few runs of the openvmm-mirror PR pipeline, this job can be re-worked to use ADO artifacts instead of GH artifacts.
1159            if matches!(config, PipelineConfig::Pr)
1160                && !matches!(backend_hint, PipelineBackendHint::Ado)
1161                && !mi_secure
1162            {
1163                let job = pipeline
1164                    .new_job(
1165                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1166                        FlowArch::X86_64,
1167                        format!("verify openhcl binary size [{}]", arch_tag),
1168                    )
1169                    .gh_set_pool(gh_pools::linux_x64_gh())
1170                    .side_effect(|done| {
1171                        flowey_lib_hvlite::_jobs::check_openvmm_hcl_size::Request {
1172                            target: CommonTriple::Common {
1173                                arch,
1174                                platform: CommonPlatform::LinuxMusl,
1175                            },
1176                            done,
1177                            pipeline_name: "openvmm-ci.yaml".into(),
1178                            job_name: build_openhcl_job_tag(arch_tag, mi_secure),
1179                        }
1180                    });
1181                all_jobs.push(job.finish());
1182            }
1183        }
1184
1185        // Emit clippy + unit-test jobs
1186        //
1187        // The only reason we bundle clippy and unit-tests together is to avoid
1188        // requiring another build agent.
1189        struct ClippyUnitTestJobParams<'a> {
1190            platform: FlowPlatform,
1191            arch: FlowArch,
1192            gh_pool: GhRunner,
1193            ado_pool: Option<AdoPool>,
1194            clippy_targets: Option<(&'a str, &'a [(Triple, bool)])>,
1195            unit_test_target: Option<(&'a str, Triple)>,
1196        }
1197
1198        let macos_clippy_targets = [(target_lexicon::triple!("aarch64-apple-darwin"), false)];
1199        let x64_linux_macos_clippy_targets = [
1200            (target_lexicon::triple!("x86_64-unknown-linux-gnu"), false),
1201            (target_lexicon::triple!("aarch64-apple-darwin"), false),
1202        ];
1203
1204        for ClippyUnitTestJobParams {
1205            platform,
1206            arch,
1207            gh_pool,
1208            ado_pool,
1209            clippy_targets,
1210            unit_test_target,
1211        } in [
1212            ClippyUnitTestJobParams {
1213                platform: FlowPlatform::Windows,
1214                arch: FlowArch::X86_64,
1215                gh_pool: gh_pools::windows_intel_v6_1es(),
1216                ado_pool: Some(ado_pools::windows_amd_v6_1es()),
1217                clippy_targets: Some((
1218                    "x64-windows",
1219                    &[(target_lexicon::triple!("x86_64-pc-windows-msvc"), false)],
1220                )),
1221                unit_test_target: Some((
1222                    "x64-windows",
1223                    target_lexicon::triple!("x86_64-pc-windows-msvc"),
1224                )),
1225            },
1226            ClippyUnitTestJobParams {
1227                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1228                arch: FlowArch::X86_64,
1229                gh_pool: gh_pools::linux_intel_v6_1es(),
1230                ado_pool: Some(ado_pools::linux_amd_v6_1es()),
1231                clippy_targets: if quick_check_job.is_some() {
1232                    // quick check already ran clippy for x64-linux;
1233                    // still need macos cross-clippy here.
1234                    Some(("macos", macos_clippy_targets.as_slice()))
1235                } else {
1236                    Some((
1237                        "x64-linux, macos",
1238                        x64_linux_macos_clippy_targets.as_slice(),
1239                    ))
1240                },
1241                unit_test_target: Some((
1242                    "x64-linux",
1243                    target_lexicon::triple!("x86_64-unknown-linux-gnu"),
1244                )),
1245            },
1246            ClippyUnitTestJobParams {
1247                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1248                arch: FlowArch::X86_64,
1249                gh_pool: gh_pools::linux_intel_v6_1es(),
1250                ado_pool: Some(ado_pools::linux_amd_v6_1es()),
1251                clippy_targets: Some((
1252                    "x64-linux-musl, misc nostd",
1253                    &[(openhcl_musl_target(CommonArch::X86_64), true)],
1254                )),
1255                unit_test_target: Some(("x64-linux-musl", openhcl_musl_target(CommonArch::X86_64))),
1256            },
1257            ClippyUnitTestJobParams {
1258                platform: FlowPlatform::Windows,
1259                arch: FlowArch::Aarch64,
1260                gh_pool: gh_pools::windows_arm_v6_1es(),
1261                ado_pool: None,
1262                clippy_targets: Some((
1263                    "aarch64-windows",
1264                    &[(target_lexicon::triple!("aarch64-pc-windows-msvc"), false)],
1265                )),
1266                unit_test_target: Some((
1267                    "aarch64-windows",
1268                    target_lexicon::triple!("aarch64-pc-windows-msvc"),
1269                )),
1270            },
1271            ClippyUnitTestJobParams {
1272                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1273                arch: FlowArch::Aarch64,
1274                gh_pool: gh_pools::linux_arm_v5_1es(),
1275                ado_pool: None,
1276                clippy_targets: Some((
1277                    "aarch64-linux",
1278                    &[(target_lexicon::triple!("aarch64-unknown-linux-gnu"), false)],
1279                )),
1280                unit_test_target: Some((
1281                    "aarch64-linux",
1282                    target_lexicon::triple!("aarch64-unknown-linux-gnu"),
1283                )),
1284            },
1285            ClippyUnitTestJobParams {
1286                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1287                arch: FlowArch::Aarch64,
1288                gh_pool: gh_pools::linux_arm_v5_1es(),
1289                ado_pool: None,
1290                clippy_targets: Some((
1291                    "aarch64-linux-musl, misc nostd",
1292                    &[(openhcl_musl_target(CommonArch::Aarch64), true)],
1293                )),
1294                unit_test_target: Some((
1295                    "aarch64-linux-musl",
1296                    openhcl_musl_target(CommonArch::Aarch64),
1297                )),
1298            },
1299        ] {
1300            // Skip unsupported jobs on ADO backend
1301            if matches!(backend_hint, PipelineBackendHint::Ado) && ado_pool.is_none() {
1302                continue;
1303            }
1304
1305            let mut job_name = Vec::new();
1306            if let Some((label, _)) = &clippy_targets {
1307                job_name.push(format!("clippy [{label}]"));
1308            }
1309            if let Some((label, _)) = &unit_test_target {
1310                job_name.push(format!("unit tests [{label}]"));
1311            }
1312            let job_name = job_name.join(", ");
1313
1314            let unit_test_target = unit_test_target.map(|(label, target)| {
1315                let test_label = format!("{label}-unit-tests");
1316                let pub_unit_test_junit_xml = if matches!(backend_hint, PipelineBackendHint::Local)
1317                {
1318                    Some(pipeline.new_artifact(&test_label).0)
1319                } else {
1320                    None
1321                };
1322                (test_label, target, pub_unit_test_junit_xml)
1323            });
1324
1325            let mut clippy_unit_test_job = pipeline
1326                .new_job(platform, arch, job_name)
1327                .gh_set_pool(gh_pool);
1328
1329            if let Some(pool) = ado_pool {
1330                clippy_unit_test_job = clippy_unit_test_job.ado_set_pool(pool);
1331            }
1332
1333            if let Some((_, targets)) = clippy_targets {
1334                for (target, also_check_misc_nostd_crates) in targets {
1335                    clippy_unit_test_job = clippy_unit_test_job.side_effect(|done| {
1336                        flowey_lib_hvlite::_jobs::check_clippy::Request {
1337                            target: target.clone(),
1338                            profile: CommonProfile::from_release(release),
1339                            done,
1340                            also_check_misc_nostd_crates: *also_check_misc_nostd_crates,
1341                        }
1342                    });
1343                }
1344            }
1345
1346            if let Some((test_label, target, pub_unit_test_junit_xml)) = unit_test_target {
1347                clippy_unit_test_job = clippy_unit_test_job
1348                    .dep_on(|ctx| {
1349                        flowey_lib_hvlite::_jobs::build_and_run_nextest_unit_tests::Params {
1350                            junit_test_label: test_label,
1351                            nextest_profile:
1352                                flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
1353                            fail_job_on_test_fail: true,
1354                            target: target.clone(),
1355                            profile: CommonProfile::from_release(release),
1356                            artifact_dir: pub_unit_test_junit_xml.map(|x| ctx.publish_artifact(x)),
1357                            done: ctx.new_done_handle(),
1358                        }
1359                    })
1360                    .side_effect(|done| {
1361                        flowey_lib_hvlite::_jobs::build_and_run_doc_tests::Params {
1362                            target,
1363                            profile: CommonProfile::from_release(release),
1364                            done,
1365                        }
1366                    });
1367            }
1368
1369            all_jobs.push(clippy_unit_test_job.finish());
1370        }
1371
1372        let vmm_tests_artifacts_windows_intel_x86 = vmm_tests_artifacts_windows_x86
1373            .clone()
1374            .finish()
1375            .map_err(|missing| {
1376                anyhow::anyhow!("missing required windows-intel vmm_tests artifact: {missing}")
1377            })?;
1378        let vmm_tests_artifacts_windows_intel_mi_secure_x86 = {
1379            let mut builder = vmm_tests_artifacts_windows_x86.clone();
1380            builder.use_openhcl_standard = use_openhcl_igvm_files_mi_secure_x86
1381                .get(&OpenhclIgvmRecipe::X64)
1382                .cloned();
1383            builder.use_openhcl_cvm = use_openhcl_igvm_files_mi_secure_x86
1384                .get(&OpenhclIgvmRecipe::X64Cvm)
1385                .cloned();
1386            builder.use_openhcl_linux_direct = use_openhcl_igvm_files_mi_secure_x86
1387                .get(&OpenhclIgvmRecipe::X64TestLinuxDirect)
1388                .cloned();
1389            builder
1390        }
1391        .finish()
1392        .map_err(|missing| {
1393            anyhow::anyhow!(
1394                "missing required windows-intel-mi-secure vmm_tests artifact: {missing}"
1395            )
1396        })?;
1397        let vmm_tests_artifacts_windows_intel_tdx_x86 = vmm_tests_artifacts_windows_x86
1398            .clone()
1399            .finish()
1400            .map_err(|missing| {
1401                anyhow::anyhow!("missing required windows-intel-tdx vmm_tests artifact: {missing}")
1402            })?;
1403        let vmm_tests_artifacts_windows_amd_x86 = vmm_tests_artifacts_windows_x86
1404            .clone()
1405            .finish()
1406            .map_err(|missing| {
1407                anyhow::anyhow!("missing required windows-amd vmm_tests artifact: {missing}")
1408            })?;
1409        let vmm_tests_artifacts_windows_amd_snp_x86 = vmm_tests_artifacts_windows_x86
1410            .clone()
1411            .finish()
1412            .map_err(|missing| {
1413                anyhow::anyhow!("missing required windows-amd-snp vmm_tests artifact: {missing}")
1414            })?;
1415        let vmm_tests_artifacts_linux_mshv_x86 = vmm_tests_artifacts_linux_musl_x86
1416            .finish()
1417            .map_err(|missing| {
1418                anyhow::anyhow!("missing required linux-mshv (musl) vmm_tests artifact: {missing}")
1419            })?;
1420        let vmm_tests_artifacts_linux_x86 =
1421            vmm_tests_artifacts_linux_x86.finish().map_err(|missing| {
1422                anyhow::anyhow!("missing required linux vmm_tests artifact: {missing}")
1423            })?;
1424        let vmm_tests_artifacts_windows_aarch64 = vmm_tests_artifacts_windows_aarch64
1425            .finish()
1426            .map_err(|missing| {
1427                anyhow::anyhow!("missing required windows-aarch64 vmm_tests artifact: {missing}")
1428            })?;
1429        let vmm_tests_artifacts_linux_aarch64_tcg = vmm_tests_artifacts_linux_aarch64_tcg
1430            .finish()
1431            .map_err(|missing| {
1432                anyhow::anyhow!("missing required linux-aarch64-tcg vmm_tests artifact: {missing}")
1433            })?;
1434
1435        // Emit VMM tests runner jobs
1436        struct VmmTestJobParams<'a> {
1437            platform: FlowPlatform,
1438            arch: FlowArch,
1439            gh_pool: GhRunner,
1440            ado_pool: Option<AdoPool>,
1441            label: &'a str,
1442            target: CommonTriple,
1443            resolve_vmm_tests_artifacts: ResolveVmmTestsBuiltArtifacts,
1444            incubator_profile: Option<&'a str>,
1445            nextest_filter_expr: String,
1446            downloaded_artifacts: Vec<KnownTestArtifacts>,
1447            prep_steps_variants: Vec<String>,
1448            external_deps: VmmTestsExternalDeps,
1449        }
1450
1451        let standard_filter = {
1452            // Standard VM-based CI machines should be able to run all tests except
1453            // those that require special hardware features (tdx/snp) or need to be
1454            // run on a baremetal host (hyper-v vbs doesn't seem to work nested).
1455            let mut filter = "all()".to_string();
1456
1457            // Run "very_heavy" tests that require lots of VPs on the self-hosted
1458            // CVM runners that have more cores.
1459            filter.push_str(" & !test(very_heavy)");
1460
1461            // Even though OpenVMM + VBS + Windows tests can run on standard CI
1462            // machines, we exclude them here to avoid needing to run prep_steps
1463            // on non-self-hosted runners. This saves several minutes of CI time
1464            // that would be used for very few tests. We need to run prep_steps
1465            // on CVM runners anyways, so we might as well run those tests there.
1466            filter.push_str(
1467                " & !test(openvmm_openhcl_uefi_x64_windows_datacenter_core_2025_x64_prepped_vbs)",
1468            );
1469
1470            // Our standard runners need to be updated to support Hyper-V OpenHCL
1471            // PCAT, so run those tests on the CVM runners for now.
1472            filter.push_str(" & !test(hyperv_openhcl_pcat)");
1473
1474            // Currently, we don't have a good way for ADO runners to authenticate in GitHub
1475            // (that don't involve PATs) which is a requirement to download GH Workflow Artifacts
1476            // required by the upgrade and downgrade servicing tests. For now,
1477            // we will exclude these tests from running in the internal mirror.
1478            // Our standard runners also need to be updated to run Hyper-V
1479            // servicing tests.
1480            match backend_hint {
1481                PipelineBackendHint::Ado => {
1482                    filter.push_str(
1483                        " & !(test(servicing) & (test(upgrade) + test(downgrade) + test(hyperv)))",
1484                    );
1485                }
1486                _ => {
1487                    filter.push_str(" & !(test(servicing) & test(hyperv))");
1488                }
1489            }
1490            filter
1491        };
1492
1493        let standard_x64_test_artifacts = vec![
1494            KnownTestArtifacts::Alpine323X64Vhd,
1495            KnownTestArtifacts::FreeBsd13_2X64Vhd,
1496            KnownTestArtifacts::FreeBsd13_2X64Iso,
1497            KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd,
1498            KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd,
1499            KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd,
1500            KnownTestArtifacts::Ubuntu2404ServerX64Vhd,
1501            KnownTestArtifacts::Ubuntu2504ServerX64Vhd,
1502            KnownTestArtifacts::VmgsWithBootEntry,
1503            KnownTestArtifacts::VmgsWith16kTpm,
1504        ];
1505
1506        // Prep variants needed by tests in the standard x64 filter
1507        // (e.g. boot_no_vmbus_windows needs the no-vmbus prepped VHD).
1508        let standard_x64_prep_variants: Vec<String> = vec!["no-vmbus".into()];
1509
1510        let cvm_filter = |isolation_type| {
1511            // arbitrarily breaking up this string to please rustfmt
1512            let mut filter =
1513                format!("test({isolation_type}) + (test(vbs) & test(hyperv)) + test(very_heavy)");
1514            filter.push_str(
1515                " + test(openvmm_openhcl_uefi_x64_windows_datacenter_core_2025_x64_prepped_vbs)",
1516            );
1517            // OpenHCL PCAT tests are flakey on AMD SNP runners, so only run on TDX for now
1518            if isolation_type == "tdx" {
1519                filter.push_str(" + test(hyperv_openhcl_pcat)");
1520            }
1521
1522            // See comment for standard filter. Run hyper-v servicing tests on CVM runners.
1523            match backend_hint {
1524                PipelineBackendHint::Ado => {
1525                    filter.push_str(
1526                        " + (test(servicing) & !(test(upgrade) + test(downgrade)) & test(hyperv))",
1527                    );
1528                }
1529                _ => {
1530                    filter.push_str(" + (test(servicing) & test(hyperv))");
1531                }
1532            }
1533
1534            // Exclude any PCAT tests that were picked up by other filters
1535            if isolation_type == "snp" {
1536                filter = format!("({filter}) & !test(pcat)")
1537            }
1538            filter
1539        };
1540
1541        // arbitrarily breaking up this string to please rustfmt
1542        let mut mi_secure_filter =
1543            "test(openhcl)& !test(servicing) & !test(cvm) & !test(memory_validation)".to_string();
1544        mi_secure_filter.push_str(
1545            "& !test(very_heavy) & !test(hyperv_openhcl_pcat) & !test(prepped_vbs) & !test(256mb)",
1546        );
1547
1548        let cvm_x64_test_artifacts = vec![
1549            KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd,
1550            KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd,
1551            KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd,
1552            KnownTestArtifacts::Ubuntu2504ServerX64Vhd,
1553            KnownTestArtifacts::VmgsWith16kTpm,
1554        ];
1555
1556        for VmmTestJobParams {
1557            platform,
1558            arch,
1559            gh_pool,
1560            ado_pool,
1561            label,
1562            target,
1563            resolve_vmm_tests_artifacts,
1564            incubator_profile,
1565            nextest_filter_expr,
1566            downloaded_artifacts,
1567            prep_steps_variants,
1568            external_deps,
1569        } in [
1570            VmmTestJobParams {
1571                platform: FlowPlatform::Windows,
1572                arch: FlowArch::X86_64,
1573                gh_pool: gh_pools::windows_intel_v6_1es(),
1574                ado_pool: Some(ado_pools::windows_intel_v6_1es()),
1575                label: "x64-windows-intel",
1576                target: CommonTriple::X86_64_WINDOWS_MSVC,
1577                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_x86,
1578                incubator_profile: None,
1579                nextest_filter_expr: standard_filter.clone(),
1580                downloaded_artifacts: standard_x64_test_artifacts.clone(),
1581                prep_steps_variants: standard_x64_prep_variants.clone(),
1582                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1583                    hyperv: true,
1584                    whp: true,
1585                    hardware_isolation: false,
1586                }),
1587            },
1588            VmmTestJobParams {
1589                platform: FlowPlatform::Windows,
1590                arch: FlowArch::X86_64,
1591                gh_pool: gh_pools::windows_intel_v6_1es(),
1592                ado_pool: Some(ado_pools::windows_intel_v6_1es()),
1593                label: "x64-windows-intel-mi-secure",
1594                target: CommonTriple::X86_64_WINDOWS_MSVC,
1595                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_mi_secure_x86,
1596                incubator_profile: None,
1597                nextest_filter_expr: mi_secure_filter,
1598                downloaded_artifacts: standard_x64_test_artifacts.clone(),
1599                prep_steps_variants: Vec::new(),
1600                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1601                    hyperv: true,
1602                    whp: true,
1603                    hardware_isolation: false,
1604                }),
1605            },
1606            VmmTestJobParams {
1607                platform: FlowPlatform::Windows,
1608                arch: FlowArch::X86_64,
1609                gh_pool: gh_pools::windows_tdx_gnr_self_hosted_baremetal(),
1610                ado_pool: None,
1611                label: "x64-windows-intel-tdx",
1612                target: CommonTriple::X86_64_WINDOWS_MSVC,
1613                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_intel_tdx_x86,
1614                incubator_profile: None,
1615                nextest_filter_expr: cvm_filter("tdx"),
1616                downloaded_artifacts: cvm_x64_test_artifacts.clone(),
1617                prep_steps_variants: vec!["standard".into()],
1618                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1619                    hyperv: true,
1620                    whp: true,
1621                    hardware_isolation: true,
1622                }),
1623            },
1624            VmmTestJobParams {
1625                platform: FlowPlatform::Windows,
1626                arch: FlowArch::X86_64,
1627                // a Windows hypervisor bug causes VMM tests to crash
1628                // when running on v7, so use v6
1629                gh_pool: gh_pools::windows_amd_v6_1es(),
1630                ado_pool: Some(ado_pools::windows_amd_v6_1es()),
1631                label: "x64-windows-amd",
1632                target: CommonTriple::X86_64_WINDOWS_MSVC,
1633                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_x86,
1634                incubator_profile: None,
1635                nextest_filter_expr: standard_filter.clone(),
1636                downloaded_artifacts: standard_x64_test_artifacts.clone(),
1637                prep_steps_variants: standard_x64_prep_variants.clone(),
1638                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1639                    hyperv: true,
1640                    whp: true,
1641                    hardware_isolation: false,
1642                }),
1643            },
1644            VmmTestJobParams {
1645                platform: FlowPlatform::Windows,
1646                arch: FlowArch::X86_64,
1647                gh_pool: gh_pools::windows_snp_self_hosted_baremetal(),
1648                ado_pool: None,
1649                label: "x64-windows-amd-snp",
1650                target: CommonTriple::X86_64_WINDOWS_MSVC,
1651                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_amd_snp_x86,
1652                incubator_profile: None,
1653                nextest_filter_expr: cvm_filter("snp"),
1654                downloaded_artifacts: cvm_x64_test_artifacts,
1655                prep_steps_variants: vec!["standard".into()],
1656                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1657                    hyperv: true,
1658                    whp: true,
1659                    hardware_isolation: true,
1660                }),
1661            },
1662            VmmTestJobParams {
1663                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1664                arch: FlowArch::X86_64,
1665                gh_pool: gh_pools::linux_amd_v7_1es(),
1666                ado_pool: Some(ado_pools::linux_amd_v6_1es()),
1667                label: "x64-linux-amd-kvm",
1668                target: CommonTriple::X86_64_LINUX_GNU,
1669                resolve_vmm_tests_artifacts: vmm_tests_artifacts_linux_x86,
1670                incubator_profile: None,
1671                // - No legal way to obtain gen1 pcat blobs on non-msft linux machines
1672                nextest_filter_expr: format!("{standard_filter} & !test(pcat_x64)"),
1673                downloaded_artifacts: standard_x64_test_artifacts.clone(),
1674                prep_steps_variants: standard_x64_prep_variants.clone(),
1675                external_deps: VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
1676                    hugetlb_2mb_overcommit_pages: Some(HUGETLB_2MB_OVERCOMMIT_PAGES),
1677                    prepare_vhost_vsock: true,
1678                }),
1679            },
1680            VmmTestJobParams {
1681                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::AzureLinux),
1682                arch: FlowArch::X86_64,
1683                // mshv image needs to be updated to use nvme for v6+ skus
1684                gh_pool: gh_pools::linux_mshv_intel_v5_1es(),
1685                ado_pool: None,
1686                label: "x64-linux-intel-mshv",
1687                target: CommonTriple::X86_64_LINUX_MUSL,
1688                resolve_vmm_tests_artifacts: vmm_tests_artifacts_linux_mshv_x86,
1689                incubator_profile: None,
1690                // - No legal way to obtain gen1 pcat blobs on non-msft linux machines
1691                nextest_filter_expr: format!("{standard_filter} & !test(pcat_x64)"),
1692                downloaded_artifacts: standard_x64_test_artifacts.clone(),
1693                prep_steps_variants: standard_x64_prep_variants.clone(),
1694                external_deps: VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
1695                    hugetlb_2mb_overcommit_pages: None,
1696                    prepare_vhost_vsock: true,
1697                }),
1698            },
1699            VmmTestJobParams {
1700                platform: FlowPlatform::Windows,
1701                arch: FlowArch::Aarch64,
1702                gh_pool: gh_pools::windows_arm_self_hosted_baremetal(),
1703                ado_pool: None,
1704                label: "aarch64-windows",
1705                target: CommonTriple::AARCH64_WINDOWS_MSVC,
1706                resolve_vmm_tests_artifacts: vmm_tests_artifacts_windows_aarch64,
1707                incubator_profile: None,
1708                nextest_filter_expr: "all()".to_string(),
1709                downloaded_artifacts: vec![
1710                    KnownTestArtifacts::Alpine323Aarch64Vhd,
1711                    KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd,
1712                    KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx,
1713                    KnownTestArtifacts::VmgsWithBootEntry,
1714                    KnownTestArtifacts::VmgsWith16kTpm,
1715                ],
1716                prep_steps_variants: Vec::new(),
1717                external_deps: VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
1718                    hyperv: true,
1719                    whp: true,
1720                    hardware_isolation: false,
1721                }),
1722            },
1723            VmmTestJobParams {
1724                platform: FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1725                arch: FlowArch::X86_64,
1726                gh_pool: gh_pools::default_linux(),
1727                ado_pool: Some(ado_pools::default_linux()),
1728                label: "aarch64-linux-tcg",
1729                target: CommonTriple::AARCH64_LINUX_MUSL,
1730                resolve_vmm_tests_artifacts: vmm_tests_artifacts_linux_aarch64_tcg,
1731                // aarch64-linux tests have no native CI hardware, so they run
1732                // inside the QEMU TCG incubator rather than directly on the host.
1733                incubator_profile: Some("aarch64-tcg-pcie"),
1734                nextest_filter_expr: "test(aarch64_tcg)".to_string(),
1735                downloaded_artifacts: vec![
1736                    KnownTestArtifacts::Alpine323Aarch64Vhd,
1737                    KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd,
1738                ],
1739                prep_steps_variants: Vec::new(),
1740                external_deps: VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
1741                    hugetlb_2mb_overcommit_pages: None,
1742                    prepare_vhost_vsock: false,
1743                }),
1744            },
1745        ] {
1746            // Skip unsupported jobs on ADO backend
1747            if matches!(backend_hint, PipelineBackendHint::Ado) && ado_pool.is_none() {
1748                continue;
1749            }
1750
1751            let test_label = format!("{label}-vmm-tests");
1752
1753            let mut vmm_tests_run_job = pipeline
1754                .new_job(platform, arch, format!("run vmm-tests [{label}]"))
1755                .gh_set_pool(gh_pool);
1756
1757            if let Some(pool) = ado_pool {
1758                vmm_tests_run_job = vmm_tests_run_job.ado_set_pool(pool);
1759            }
1760
1761            // TODO: maybe this should be inferred
1762            let require_2mb_hugetlb = matches!(
1763                external_deps,
1764                VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
1765                    hugetlb_2mb_overcommit_pages: Some(_),
1766                    ..
1767                })
1768            );
1769
1770            // TODO: figure out when this is actually needed
1771            let needs_release_igvm = !matches!(backend_hint, PipelineBackendHint::Ado);
1772
1773            vmm_tests_run_job = vmm_tests_run_job.dep_on(|ctx| {
1774                flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive::Params {
1775                    junit_test_label: test_label,
1776                    target: target.as_triple(),
1777                    nextest_profile: flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci,
1778                    nextest_filter_expr: Some(nextest_filter_expr),
1779                    test_content_config: TestContentConfig::Uninitialized {
1780                        test_content_dir: None,
1781                        built_artifacts: resolve_vmm_tests_artifacts(ctx),
1782                        needs_release_igvm,
1783                    },
1784                    downloaded_artifacts,
1785                    prep_steps_variants,
1786                    external_deps,
1787                    incubator_profile: incubator_profile
1788                        .map(|n| IncubatorProfileNameOrPath::Name(n.into())),
1789                    upload_logs_on_success: true,
1790                    fail_job_on_test_fail: true,
1791                    repetitions: std::num::NonZeroU64::new(1).unwrap(),
1792                    petri_params: PetriParams {
1793                        disable_remote_artifacts: true,
1794                        reuse_prepped_vhds: false,
1795                        require_2mb_hugetlb,
1796                    },
1797                    test_content_dir_as_repo_root: false,
1798                    done: ctx.new_done_handle(),
1799                }
1800            });
1801
1802            let vmm_tests_run_job = vmm_tests_run_job.finish();
1803            if !label.contains("snp") {
1804                all_jobs.push(vmm_tests_run_job);
1805            }
1806        }
1807
1808        if enable_vmm_perf {
1809            let runner_gnu =
1810                use_vmm_perf_runner_gnu_x64.context("missing x64 Linux GNU VMM.Perf runner")?;
1811            let runner_musl =
1812                use_vmm_perf_runner_musl_x64.context("missing x64 Linux MUSL VMM.Perf runner")?;
1813            let openvmm_gnu = use_vmm_perf_openvmm_gnu_x64
1814                .context("missing x64 Linux GNU OpenVMM artifact for VMM.Perf")?;
1815            let openvmm_musl = use_vmm_perf_openvmm_musl_x64
1816                .context("missing x64 Linux MUSL OpenVMM artifact for VMM.Perf")?;
1817            let runner_windows =
1818                use_vmm_perf_runner_windows_x64.context("missing x64 Windows VMM.Perf runner")?;
1819            let openvmm_windows = use_vmm_perf_openvmm_windows_x64
1820                .context("missing x64 Windows OpenVMM artifact for VMM.Perf")?;
1821            for (label, platform, pool, openvmm, runner, hugetlb_pages) in [
1822                (
1823                    "x64-linux-amd-kvm",
1824                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1825                    gh_pools::linux_amd_v7_1es(),
1826                    openvmm_gnu,
1827                    runner_gnu,
1828                    Some(HUGETLB_2MB_OVERCOMMIT_PAGES),
1829                ),
1830                (
1831                    "x64-linux-intel-mshv",
1832                    FlowPlatform::Linux(FlowPlatformLinuxDistro::AzureLinux),
1833                    gh_pools::linux_mshv_intel_v5_1es(),
1834                    openvmm_musl,
1835                    runner_musl,
1836                    None,
1837                ),
1838                (
1839                    "x64-windows-amd",
1840                    FlowPlatform::Windows,
1841                    gh_pools::windows_amd_v6_1es(),
1842                    openvmm_windows.clone(),
1843                    runner_windows.clone(),
1844                    None,
1845                ),
1846                (
1847                    "x64-windows-intel",
1848                    FlowPlatform::Windows,
1849                    gh_pools::windows_intel_v6_1es(),
1850                    openvmm_windows,
1851                    runner_windows,
1852                    None,
1853                ),
1854            ] {
1855                let job = pipeline
1856                    .new_job(
1857                        platform,
1858                        FlowArch::X86_64,
1859                        format!("run vmm-perf [{label}]"),
1860                    )
1861                    .gh_set_pool(pool)
1862                    .with_timeout_in_minutes(120)
1863                    .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init)
1864                    .dep_on(
1865                        |ctx| flowey_lib_hvlite::_jobs::setup_and_run_vmm_perf::Params {
1866                            label: format!("{label}-vmm-perf"),
1867                            runner: ctx.use_typed_artifact(&runner),
1868                            openvmm: ctx.use_typed_artifact(&openvmm),
1869                            profiles: flowey_lib_hvlite::run_vmm_perf::VmmPerfProfile::all(),
1870                            vm_sizes_json: None,
1871                            parameters_json: None,
1872                            runtime_archive: None,
1873                            root_dir: None,
1874                            hugetlb_2mb_overcommit_pages: hugetlb_pages,
1875                            done: ctx.new_done_handle(),
1876                        },
1877                    )
1878                    .finish();
1879                all_jobs.push(job);
1880            }
1881        }
1882
1883        // test the flowey local backend by running cargo xflowey build-igvm on x64
1884        {
1885            if matches!(backend_hint, PipelineBackendHint::Github) {
1886                let job = pipeline
1887                    .new_job(
1888                        FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1889                        FlowArch::X86_64,
1890                        "test flowey local backend",
1891                    )
1892                    .gh_set_pool(gh_pools::linux_x64_gh())
1893                    .side_effect(|done| {
1894                        flowey_lib_hvlite::_jobs::test_local_flowey_build_igvm::Request {
1895                            base_recipe: OpenhclIgvmRecipe::X64,
1896                            done,
1897                        }
1898                    });
1899                all_jobs.push(job.finish());
1900            }
1901        }
1902
1903        // Build the vendored source tree without the repository's
1904        // `.packages/` provisioning, as a Linux distribution would.
1905        {
1906            let distro_build_job = pipeline
1907                .new_job(
1908                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1909                    FlowArch::X86_64,
1910                    "build openvmm [distribution config, x64-linux-gnu]",
1911                )
1912                .gh_set_pool(gh_pools::linux_x64_gh())
1913                .ado_set_pool(ado_pools::default_linux())
1914                .side_effect(|done| {
1915                    flowey_lib_hvlite::_jobs::check_distro_build_from_checkout::Request { done }
1916                })
1917                .finish();
1918
1919            all_jobs.push(distro_build_job);
1920        }
1921
1922        // all jobs depend on the quick-check gate
1923        if let Some(ref quick_check) = quick_check_job {
1924            for job in all_jobs.iter() {
1925                pipeline.non_artifact_dep(job, quick_check);
1926            }
1927            all_jobs.push(quick_check.clone());
1928        }
1929
1930        if matches!(config, PipelineConfig::Pr)
1931            && matches!(backend_hint, PipelineBackendHint::Github)
1932        {
1933            // Add a job that depends on all others as a workaround for
1934            // https://github.com/orgs/community/discussions/12395.
1935            //
1936            // This workaround then itself requires _another_ workaround, requiring
1937            // the use of `gh_dangerous_override_if`, and some additional custom job
1938            // logic, to deal with https://github.com/actions/runner/issues/2566.
1939            //
1940            // TODO: Add a way for this job to skip flowey setup and become a true
1941            // no-op.
1942            let all_good_job = pipeline
1943                .new_job(
1944                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1945                    FlowArch::X86_64,
1946                    "openvmm checkin gates",
1947                )
1948                .gh_set_pool(gh_pools::linux_x64_gh())
1949                // always run this job, regardless whether or not any previous jobs failed
1950                .gh_dangerous_override_if("always() && github.event.pull_request.draft == false")
1951                .gh_dangerous_global_env_var("ANY_JOBS_FAILED", "${{ contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'failure') }}")
1952                .side_effect(|done| flowey_lib_hvlite::_jobs::all_good_job::Params {
1953                    did_fail_env_var: "ANY_JOBS_FAILED".into(),
1954                    done,
1955                })
1956                .finish();
1957
1958            for job in all_jobs.iter() {
1959                pipeline.non_artifact_dep(&all_good_job, job);
1960            }
1961        }
1962
1963        if matches!(config, PipelineConfig::Ci)
1964            && matches!(backend_hint, PipelineBackendHint::Github)
1965        {
1966            let publish_vmgstool_job = pipeline
1967                .new_job(
1968                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu),
1969                    FlowArch::X86_64,
1970                    "publish vmgstool",
1971                )
1972                .gh_grant_permissions::<flowey_lib_common::publish_gh_release::Node>([(
1973                    GhPermission::Contents,
1974                    GhPermissionValue::Write,
1975                )])
1976                .gh_set_pool(gh_pools::linux_x64_gh())
1977                .dep_on(
1978                    |ctx| flowey_lib_hvlite::_jobs::publish_vmgstool_gh_release::Request {
1979                        vmgstools: vmgstools
1980                            .into_iter()
1981                            .map(|(t, v)| (t, ctx.use_typed_artifact(&v)))
1982                            .collect(),
1983                        done: ctx.new_done_handle(),
1984                    },
1985                )
1986                .finish();
1987
1988            // All other jobs must succeed in order to publish
1989            for job in all_jobs.iter() {
1990                pipeline.non_artifact_dep(&publish_vmgstool_job, job);
1991            }
1992        }
1993
1994        Ok(pipeline)
1995    }
1996}