Skip to main content

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::*;
7use std::collections::BTreeSet;
8
9use crate::build_openhcl_boot::OpenhclBootOutput;
10use crate::build_openhcl_igvm_from_recipe::IgvmManifestPath;
11use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmEndorsements;
12use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
13use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetails;
14use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetailsLocalOnly;
15use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeType;
16use crate::build_openhcl_igvm_from_recipe::OpenhclKernelPackage;
17use crate::build_openhcl_igvm_from_recipe::Vtl0KernelType;
18use crate::build_openhcl_initrd::OpenhclInitrdExtraParams;
19use crate::build_openvmm_hcl::MaxTraceLevel;
20use crate::build_openvmm_hcl::OpenvmmHclBuildProfile;
21use crate::build_openvmm_hcl::OpenvmmHclFeature;
22use crate::build_openvmm_hcl::OpenvmmHclOutput;
23use crate::common::CommonArch;
24use crate::common::CommonTriple;
25
26#[derive(Default, Serialize, Deserialize, PartialEq, Eq)]
27pub struct Customizations {
28    pub build_label: Option<String>,
29    pub custom_directory: Vec<PathBuf>,
30    pub custom_kernel: Option<PathBuf>,
31    pub custom_layer: Vec<PathBuf>,
32    pub custom_openhcl_boot: Option<PathBuf>,
33    pub custom_openvmm_hcl: Option<PathBuf>,
34    pub custom_sidecar: Option<PathBuf>,
35    pub custom_vtl0_kernel: Option<PathBuf>,
36    pub custom_extra_rootfs: Vec<PathBuf>,
37    pub confidential_debug: bool,
38    pub disable_secure_avic: bool,
39    pub enable_product_policy: bool,
40    pub override_arch: Option<CommonArch>,
41    pub override_kernel_pkg: Option<OpenhclKernelPackage>,
42    pub override_manifest: Option<PathBuf>,
43    pub override_openvmm_hcl_feature: Vec<String>,
44    pub override_max_trace_level: Option<MaxTraceLevel>,
45    pub with_debuginfo: bool,
46    pub with_mi_secure: bool,
47    pub with_perf_tools: bool,
48    pub with_sidecar: bool,
49}
50
51flowey_request! {
52    pub struct Params {
53        pub artifact_dir: ReadVar<PathBuf>,
54        pub done: WriteVar<SideEffect>,
55
56        pub base_recipe: OpenhclIgvmRecipe,
57        pub release: bool,
58        pub release_cfg: bool,
59
60        pub customizations: Customizations,
61    }
62}
63
64new_simple_flow_node!(struct Node);
65
66impl SimpleFlowNode for Node {
67    type Request = Params;
68
69    fn imports(ctx: &mut ImportCtx<'_>) {
70        ctx.import::<crate::build_openhcl_igvm_from_recipe::Node>();
71    }
72
73    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
74        let Params {
75            artifact_dir,
76            done,
77
78            base_recipe,
79            release,
80            release_cfg,
81
82            customizations,
83        } = request;
84
85        let has_customizations = customizations != Customizations::default();
86
87        let Customizations {
88            build_label,
89            custom_directory,
90            custom_kernel,
91            custom_layer,
92            override_manifest,
93            custom_openhcl_boot,
94            custom_openvmm_hcl,
95            custom_sidecar,
96            custom_vtl0_kernel,
97            override_arch,
98            override_kernel_pkg,
99            override_openvmm_hcl_feature,
100            override_max_trace_level,
101            confidential_debug,
102            disable_secure_avic,
103            enable_product_policy,
104            with_debuginfo,
105            with_mi_secure,
106            with_perf_tools,
107            with_sidecar,
108            custom_extra_rootfs,
109        } = customizations;
110
111        if release_cfg && !release {
112            log::warn!(
113                "You are building a debug binary with a release configuration.\n\
114                The produced binary likely will not function properly due to memory restrictions."
115            )
116        }
117
118        if disable_secure_avic && (release_cfg || release) {
119            anyhow::bail!("--disable-secure-avic cannot be used with release builds.");
120        }
121
122        let build_profile = if release {
123            OpenvmmHclBuildProfile::OpenvmmHclShip
124        } else {
125            OpenvmmHclBuildProfile::Debug
126        };
127
128        let recipe_details = {
129            let mut recipe_details = base_recipe.recipe_details(release_cfg);
130
131            let OpenhclIgvmRecipeDetails {
132                local_only,
133                igvm_manifest,
134                openhcl_kernel_package,
135                openvmm_hcl_features,
136                target,
137                vtl0_kernel_type,
138                with_uefi,
139                with_interactive,
140                with_sidecar: with_sidecar_details,
141                max_trace_level,
142            } = &mut recipe_details;
143
144            if custom_kernel.is_some() {
145                *with_uefi = true
146            }
147
148            if with_sidecar || custom_sidecar.is_some() {
149                *with_sidecar_details = true;
150            }
151
152            assert!(local_only.is_none());
153            *local_only = Some(OpenhclIgvmRecipeDetailsLocalOnly {
154                // ensure binary remains un-sripped if perf tooling was also
155                // requested
156                openvmm_hcl_no_strip: with_perf_tools || with_debuginfo,
157                openhcl_initrd_extra_params: Some(OpenhclInitrdExtraParams {
158                    extra_initrd_layers: custom_layer
159                        .into_iter()
160                        .map(|p| p.absolute())
161                        .collect::<Result<_, _>>()?,
162                    extra_initrd_directories: custom_directory
163                        .into_iter()
164                        .map(|p| p.absolute())
165                        .collect::<Result<_, _>>()?,
166                }),
167                custom_openvmm_hcl: custom_openvmm_hcl.map(|p| p.absolute()).transpose()?,
168                custom_openhcl_boot: custom_openhcl_boot.map(|p| p.absolute()).transpose()?,
169                custom_kernel: custom_kernel.map(|p| p.absolute()).transpose()?,
170                custom_sidecar: custom_sidecar.map(|p| p.absolute()).transpose()?,
171                custom_extra_rootfs: custom_extra_rootfs
172                    .into_iter()
173                    .map(|p| p.absolute())
174                    .collect::<Result<_, _>>()?,
175            });
176
177            if let Some(p) = override_manifest {
178                *igvm_manifest = IgvmManifestPath::LocalOnlyCustom(p.absolute()?);
179            }
180
181            if let Some(override_kernel_pkg) = override_kernel_pkg {
182                *openhcl_kernel_package = override_kernel_pkg;
183            }
184
185            if !override_openvmm_hcl_feature.is_empty() {
186                *openvmm_hcl_features = override_openvmm_hcl_feature
187                    .into_iter()
188                    .map(OpenvmmHclFeature::LocalOnlyCustom)
189                    .collect()
190            }
191
192            if with_mi_secure {
193                openvmm_hcl_features.insert(OpenvmmHclFeature::MiSecure);
194            }
195
196            if enable_product_policy {
197                openvmm_hcl_features.insert(OpenvmmHclFeature::ProductPolicy);
198            }
199
200            if let Some(arch) = override_arch {
201                *target = match arch {
202                    CommonArch::X86_64 => CommonTriple::X86_64_LINUX_MUSL,
203                    CommonArch::Aarch64 => CommonTriple::AARCH64_LINUX_MUSL,
204                };
205            }
206
207            if let Some(lvl) = override_max_trace_level {
208                *max_trace_level = lvl;
209            }
210
211            if let Some(p) = custom_vtl0_kernel {
212                *vtl0_kernel_type = Some(Vtl0KernelType::LocalOnlyCustom(p.absolute()?))
213            }
214
215            // Debug configurations already include --interactive by default
216            // on x86 for busybox, gdbserver, and perf (aarch64 is currently
217            // broken and always disabled by default to allow the shell to
218            // work with ohcldiag-dev. see #1234).
219            *with_interactive |= with_perf_tools;
220
221            if *with_interactive && target.common_arch()? == CommonArch::Aarch64 {
222                log::warn!(
223                    "Please note that using perf tools on ARM currently breaks ohcldiag-dev shell"
224                );
225            }
226
227            recipe_details
228        };
229
230        let build_label = if let Some(label) = build_label {
231            label
232        } else {
233            let base = match &recipe_details.igvm_manifest {
234                IgvmManifestPath::InTree(_) => base_recipe.non_production_tag(),
235                IgvmManifestPath::LocalOnlyCustom(path) => path
236                    .file_name()
237                    .unwrap()
238                    .to_str()
239                    .unwrap()
240                    .strip_suffix(".json")
241                    .unwrap()
242                    .to_string(),
243            };
244
245            if has_customizations {
246                format!("{base}-custom")
247            } else {
248                base
249            }
250        };
251
252        let (openhcl_igvm, write_openhcl_igvm) = ctx.new_var();
253        let (openhcl_igvm_extras, write_openhcl_igvm_extras) = ctx.new_var();
254
255        ctx.req(crate::build_openhcl_igvm_from_recipe::Request {
256            build_profile,
257            release_cfg,
258            recipe: OpenhclIgvmRecipeType::LocalOnlyCustom(recipe_details),
259            custom_target: None,
260            extra_features: BTreeSet::new(),
261            disable_secure_avic,
262            uefi_firmware_flavor: None,
263            confidential_debug,
264            openhcl_igvm: write_openhcl_igvm,
265            openhcl_igvm_extras: write_openhcl_igvm_extras,
266        });
267
268        ctx.emit_rust_step("copy to output directory", |ctx| {
269            done.claim(ctx);
270            claim_vars!(ctx, (artifact_dir, openhcl_igvm, openhcl_igvm_extras));
271            move |rt| {
272                read_vars!(rt, (artifact_dir, openhcl_igvm, openhcl_igvm_extras));
273
274                let output_dir = artifact_dir
275                    .join(match build_profile {
276                        OpenvmmHclBuildProfile::Debug => "debug",
277                        OpenvmmHclBuildProfile::Release => "release",
278                        OpenvmmHclBuildProfile::OpenvmmHclShip => "ship",
279                    })
280                    .join(&build_label);
281                fs_err::create_dir_all(&output_dir)?;
282
283                let OpenvmmHclOutput { bin, dbg } = openhcl_igvm_extras.openvmm_hcl;
284                fs_err::copy(bin, output_dir.join("openvmm_hcl"))?;
285                if let Some(dbg) = dbg {
286                    fs_err::copy(dbg, output_dir.join("openvmm_hcl.dbg"))?;
287                }
288
289                let OpenhclBootOutput { bin, dbg } = openhcl_igvm_extras.openhcl_boot;
290                fs_err::copy(bin, output_dir.join("openhcl_boot"))?;
291                fs_err::copy(dbg, output_dir.join("openhcl_boot.dbg"))?;
292
293                if let Some(built_sidecar) = openhcl_igvm_extras.sidecar {
294                    let crate::build_sidecar::SidecarOutput { bin, dbg } = built_sidecar;
295                    fs_err::copy(bin, output_dir.join("sidecar"))?;
296                    fs_err::copy(dbg, output_dir.join("sidecar.dbg"))?;
297                }
298
299                let igvm_bin = openhcl_igvm.igvm_bin();
300                fs_err::copy(
301                    igvm_bin,
302                    output_dir.join(format!("openhcl-{build_label}.bin")),
303                )?;
304                if let Some(igvm_map) = openhcl_igvm_extras.igvm_map {
305                    fs_err::copy(
306                        igvm_map,
307                        output_dir.join(format!("openhcl-{build_label}.bin.map")),
308                    )?;
309                }
310                if let Some(OpenhclIgvmEndorsements::X64 {
311                    igvm_tdx_json,
312                    igvm_snp_json,
313                    igvm_vbs_json,
314                    igvm_snp_idblock,
315                    igvm_tdx_corim,
316                    igvm_snp_corim,
317                    igvm_vbs_corim,
318                }) = openhcl_igvm.endorsements()
319                {
320                    if let Some(igvm_tdx_json) = igvm_tdx_json {
321                        fs_err::copy(igvm_tdx_json, output_dir.join("openhcl-tdx.json"))?;
322                    }
323                    if let Some(igvm_snp_json) = igvm_snp_json {
324                        fs_err::copy(igvm_snp_json, output_dir.join("openhcl-snp.json"))?;
325                    }
326                    if let Some(igvm_vbs_json) = igvm_vbs_json {
327                        fs_err::copy(igvm_vbs_json, output_dir.join("openhcl-vbs.json"))?;
328                    }
329                    if let Some(igvm_snp_idblock) = igvm_snp_idblock {
330                        fs_err::copy(igvm_snp_idblock, output_dir.join("openhcl-snp.idblock"))?;
331                    }
332                    if let Some(igvm_tdx_corim) = igvm_tdx_corim {
333                        fs_err::copy(igvm_tdx_corim, output_dir.join("openhcl-tdx.cbor"))?;
334                    }
335                    if let Some(igvm_snp_corim) = igvm_snp_corim {
336                        fs_err::copy(igvm_snp_corim, output_dir.join("openhcl-snp.cbor"))?;
337                    }
338                    if let Some(igvm_vbs_corim) = igvm_vbs_corim {
339                        fs_err::copy(igvm_vbs_corim, output_dir.join("openhcl-vbs.cbor"))?;
340                    }
341                }
342                for e in fs_err::read_dir(output_dir)? {
343                    let e = e?;
344                    log::info!("{}", e.path().display());
345                }
346
347                Ok(())
348            }
349        });
350
351        Ok(())
352    }
353}