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