flowey_lib_hvlite/_jobs/
local_build_igvm.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A local-only job that supports the `cargo xflowey build-igvm` CLI
5
6use flowey::node::prelude::*;
7
8use crate::build_openhcl_boot::OpenhclBootOutput;
9use crate::build_openhcl_igvm_from_recipe::IgvmManifestPath;
10use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
11use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetails;
12use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetailsLocalOnly;
13use crate::build_openhcl_igvm_from_recipe::OpenhclKernelPackage;
14use crate::build_openhcl_igvm_from_recipe::Vtl0KernelType;
15use crate::build_openhcl_initrd::OpenhclInitrdExtraParams;
16use crate::build_openvmm_hcl::OpenvmmHclBuildProfile;
17use crate::build_openvmm_hcl::OpenvmmHclFeature;
18use crate::build_openvmm_hcl::OpenvmmHclOutput;
19use crate::run_cargo_build::common::CommonArch;
20use crate::run_cargo_build::common::CommonTriple;
21use crate::run_igvmfilegen::IgvmOutput;
22
23#[derive(Default, Serialize, Deserialize, PartialEq, Eq)]
24pub struct Customizations {
25    pub build_label: Option<String>,
26    pub custom_directory: Vec<PathBuf>,
27    pub custom_kernel_modules: Option<PathBuf>,
28    pub custom_kernel: Option<PathBuf>,
29    pub custom_layer: Vec<PathBuf>,
30    pub custom_openhcl_boot: Option<PathBuf>,
31    pub custom_openvmm_hcl: Option<PathBuf>,
32    pub custom_sidecar: Option<PathBuf>,
33    pub custom_uefi: Option<PathBuf>,
34    pub custom_vtl0_kernel: Option<PathBuf>,
35    pub custom_extra_rootfs: Vec<PathBuf>,
36    pub override_arch: Option<CommonArch>,
37    pub override_kernel_pkg: Option<OpenhclKernelPackage>,
38    pub override_manifest: Option<PathBuf>,
39    pub override_openvmm_hcl_feature: Vec<String>,
40    pub with_debuginfo: bool,
41    pub with_perf_tools: bool,
42    pub with_sidecar: bool,
43}
44
45flowey_request! {
46    pub struct Params {
47        pub artifact_dir: ReadVar<PathBuf>,
48        pub done: WriteVar<SideEffect>,
49
50        pub base_recipe: OpenhclIgvmRecipe,
51        pub release: bool,
52
53        pub customizations: Customizations,
54    }
55}
56
57new_simple_flow_node!(struct Node);
58
59impl SimpleFlowNode for Node {
60    type Request = Params;
61
62    fn imports(ctx: &mut ImportCtx<'_>) {
63        ctx.import::<crate::build_openhcl_igvm_from_recipe::Node>();
64    }
65
66    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
67        let Params {
68            artifact_dir,
69            done,
70
71            base_recipe,
72            release,
73
74            customizations,
75        } = request;
76
77        let has_customizations = customizations != Customizations::default();
78
79        let Customizations {
80            build_label,
81            custom_directory,
82            custom_kernel_modules,
83            custom_kernel,
84            custom_layer,
85            override_manifest,
86            custom_openhcl_boot,
87            custom_openvmm_hcl,
88            custom_sidecar,
89            custom_uefi,
90            custom_vtl0_kernel,
91            override_arch,
92            override_kernel_pkg,
93            override_openvmm_hcl_feature,
94            with_debuginfo,
95            with_perf_tools,
96            with_sidecar,
97            custom_extra_rootfs,
98        } = customizations;
99
100        let profile = if release {
101            OpenvmmHclBuildProfile::OpenvmmHclShip
102        } else {
103            OpenvmmHclBuildProfile::Debug
104        };
105
106        let mut recipe_details = base_recipe.recipe_details(profile);
107        {
108            let OpenhclIgvmRecipeDetails {
109                local_only,
110                igvm_manifest,
111                openhcl_kernel_package,
112                openvmm_hcl_features,
113                target,
114                vtl0_kernel_type,
115                with_uefi,
116                with_interactive,
117                with_sidecar: with_sidecar_details,
118            } = &mut recipe_details;
119
120            if custom_kernel.is_some() {
121                *with_uefi = true
122            }
123
124            if with_sidecar || custom_sidecar.is_some() {
125                *with_sidecar_details = true;
126            }
127
128            // Debug builds include --interactive by default, for busybox, gdbserver, and perf.
129            *with_interactive = matches!(profile, OpenvmmHclBuildProfile::Debug) || with_perf_tools;
130
131            assert!(local_only.is_none());
132            *local_only = Some(OpenhclIgvmRecipeDetailsLocalOnly {
133                // ensure binary remains un-sripped if perf tooling was also
134                // requested
135                openvmm_hcl_no_strip: with_perf_tools || with_debuginfo,
136                openhcl_initrd_extra_params: Some(OpenhclInitrdExtraParams {
137                    extra_initrd_layers: custom_layer
138                        .into_iter()
139                        .map(|p| p.absolute())
140                        .collect::<Result<_, _>>()?,
141                    extra_initrd_directories: custom_directory
142                        .into_iter()
143                        .map(|p| p.absolute())
144                        .collect::<Result<_, _>>()?,
145                    custom_kernel_modules,
146                }),
147                custom_openvmm_hcl: custom_openvmm_hcl.map(|p| p.absolute()).transpose()?,
148                custom_openhcl_boot: custom_openhcl_boot.map(|p| p.absolute()).transpose()?,
149                custom_uefi: custom_uefi.map(|p| p.absolute()).transpose()?,
150                custom_kernel: custom_kernel.map(|p| p.absolute()).transpose()?,
151                custom_sidecar: custom_sidecar.map(|p| p.absolute()).transpose()?,
152                custom_extra_rootfs: custom_extra_rootfs
153                    .into_iter()
154                    .map(|p| p.absolute())
155                    .collect::<Result<_, _>>()?,
156            });
157
158            if let Some(p) = override_manifest {
159                *igvm_manifest = IgvmManifestPath::LocalOnlyCustom(p.absolute()?);
160            }
161
162            if let Some(override_kernel_pkg) = override_kernel_pkg {
163                *openhcl_kernel_package = override_kernel_pkg;
164            }
165
166            if !override_openvmm_hcl_feature.is_empty() {
167                *openvmm_hcl_features = override_openvmm_hcl_feature
168                    .into_iter()
169                    .map(OpenvmmHclFeature::LocalOnlyCustom)
170                    .collect()
171            }
172
173            if let Some(arch) = override_arch {
174                *target = match arch {
175                    CommonArch::X86_64 => CommonTriple::X86_64_LINUX_MUSL,
176                    CommonArch::Aarch64 => CommonTriple::AARCH64_LINUX_MUSL,
177                };
178            }
179
180            if let Some(p) = custom_vtl0_kernel {
181                *vtl0_kernel_type = Some(Vtl0KernelType::LocalOnlyCustom(p.absolute()?))
182            }
183        }
184
185        let build_label = if let Some(label) = build_label {
186            label
187        } else {
188            let base = match &recipe_details.igvm_manifest {
189                IgvmManifestPath::InTree(_) => {
190                    non_production_build_igvm_tool_out_name(&base_recipe).to_string()
191                }
192                IgvmManifestPath::LocalOnlyCustom(path) => path
193                    .file_name()
194                    .unwrap()
195                    .to_str()
196                    .unwrap()
197                    .strip_suffix(".json")
198                    .unwrap()
199                    .to_string(),
200            };
201
202            if has_customizations {
203                format!("{base}-custom")
204            } else {
205                base
206            }
207        };
208
209        let (built_openvmm_hcl, write_built_openvmm_hcl) = ctx.new_var();
210        let (built_openhcl_boot, write_built_openhcl_boot) = ctx.new_var();
211        let (built_openhcl_igvm, write_built_openhcl_igvm) = ctx.new_var();
212        let (built_sidecar, write_built_sidecar) = ctx.new_var();
213
214        ctx.req(crate::build_openhcl_igvm_from_recipe::Request {
215            profile,
216            recipe: OpenhclIgvmRecipe::LocalOnlyCustom(recipe_details.clone()),
217            custom_target: None,
218            built_openvmm_hcl: write_built_openvmm_hcl,
219            built_openhcl_boot: write_built_openhcl_boot,
220            built_openhcl_igvm: write_built_openhcl_igvm,
221            built_sidecar: write_built_sidecar,
222        });
223
224        ctx.emit_rust_step("copy to output directory", |ctx| {
225            done.claim(ctx);
226            let artifact_dir = artifact_dir.claim(ctx);
227            let built_openvmm_hcl = built_openvmm_hcl.claim(ctx);
228            let built_openhcl_boot = built_openhcl_boot.claim(ctx);
229            let built_openhcl_igvm = built_openhcl_igvm.claim(ctx);
230            let built_sidecar = built_sidecar.claim(ctx);
231            move |rt| {
232                let output_dir = rt
233                    .read(artifact_dir)
234                    .join(match profile {
235                        OpenvmmHclBuildProfile::Debug => "debug",
236                        OpenvmmHclBuildProfile::Release => "release",
237                        OpenvmmHclBuildProfile::OpenvmmHclShip => "ship",
238                    })
239                    .join(&build_label);
240                fs_err::create_dir_all(&output_dir)?;
241
242                let OpenvmmHclOutput { bin, dbg } = rt.read(built_openvmm_hcl);
243                fs_err::copy(bin, output_dir.join("openvmm_hcl"))?;
244                if let Some(dbg) = dbg {
245                    fs_err::copy(dbg, output_dir.join("openvmm_hcl.dbg"))?;
246                }
247
248                let OpenhclBootOutput { bin, dbg } = rt.read(built_openhcl_boot);
249                fs_err::copy(bin, output_dir.join("openhcl_boot"))?;
250                fs_err::copy(dbg, output_dir.join("openhcl_boot.dbg"))?;
251
252                if let Some(built_sidecar) = rt.read(built_sidecar) {
253                    let crate::build_sidecar::SidecarOutput { bin, dbg } = built_sidecar;
254                    fs_err::copy(bin, output_dir.join("sidecar"))?;
255                    fs_err::copy(dbg, output_dir.join("sidecar.dbg"))?;
256                }
257
258                let IgvmOutput {
259                    igvm_bin,
260                    igvm_map,
261                    igvm_tdx_json,
262                    igvm_snp_json,
263                    igvm_vbs_json,
264                } = rt.read(built_openhcl_igvm);
265                fs_err::copy(
266                    igvm_bin,
267                    output_dir.join(format!("openhcl-{build_label}.bin")),
268                )?;
269                if let Some(igvm_map) = igvm_map {
270                    fs_err::copy(
271                        igvm_map,
272                        output_dir.join(format!("openhcl-{build_label}.bin.map")),
273                    )?;
274                }
275                if let Some(igvm_tdx_json) = igvm_tdx_json {
276                    fs_err::copy(igvm_tdx_json, output_dir.join("openhcl-tdx.json"))?;
277                }
278                if let Some(igvm_snp_json) = igvm_snp_json {
279                    fs_err::copy(igvm_snp_json, output_dir.join("openhcl-snp.json"))?;
280                }
281                if let Some(igvm_vbs_json) = igvm_vbs_json {
282                    fs_err::copy(igvm_vbs_json, output_dir.join("openhcl-vbs.json"))?;
283                }
284                for e in fs_err::read_dir(output_dir)? {
285                    let e = e?;
286                    log::info!("{}", e.path().display());
287                }
288
289                Ok(())
290            }
291        });
292
293        Ok(())
294    }
295}
296
297pub fn non_production_build_igvm_tool_out_name(recipe: &OpenhclIgvmRecipe) -> &'static str {
298    match recipe {
299        OpenhclIgvmRecipe::X64 => "x64",
300        OpenhclIgvmRecipe::X64Devkern => "x64-devkern",
301        OpenhclIgvmRecipe::X64TestLinuxDirect => "x64-test-linux-direct",
302        OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => "x64-test-linux-direct-devkern",
303        OpenhclIgvmRecipe::X64Cvm => "x64-cvm",
304        OpenhclIgvmRecipe::X64CvmDevkern => "x64-cvm-devkern",
305        OpenhclIgvmRecipe::Aarch64 => "aarch64",
306        OpenhclIgvmRecipe::Aarch64Devkern => "aarch64-devkern",
307        OpenhclIgvmRecipe::LocalOnlyCustom(_) => unreachable!(),
308    }
309}