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            confidential_debug,
263            openhcl_igvm: write_openhcl_igvm,
264            openhcl_igvm_extras: write_openhcl_igvm_extras,
265        });
266
267        ctx.emit_rust_step("copy to output directory", |ctx| {
268            done.claim(ctx);
269            claim_vars!(ctx, (artifact_dir, openhcl_igvm, openhcl_igvm_extras));
270            move |rt| {
271                read_vars!(rt, (artifact_dir, openhcl_igvm, openhcl_igvm_extras));
272
273                let output_dir = artifact_dir
274                    .join(match build_profile {
275                        OpenvmmHclBuildProfile::Debug => "debug",
276                        OpenvmmHclBuildProfile::Release => "release",
277                        OpenvmmHclBuildProfile::OpenvmmHclShip => "ship",
278                    })
279                    .join(&build_label);
280                fs_err::create_dir_all(&output_dir)?;
281
282                let OpenvmmHclOutput { bin, dbg } = openhcl_igvm_extras.openvmm_hcl;
283                fs_err::copy(bin, output_dir.join("openvmm_hcl"))?;
284                if let Some(dbg) = dbg {
285                    fs_err::copy(dbg, output_dir.join("openvmm_hcl.dbg"))?;
286                }
287
288                let OpenhclBootOutput { bin, dbg } = openhcl_igvm_extras.openhcl_boot;
289                fs_err::copy(bin, output_dir.join("openhcl_boot"))?;
290                fs_err::copy(dbg, output_dir.join("openhcl_boot.dbg"))?;
291
292                if let Some(built_sidecar) = openhcl_igvm_extras.sidecar {
293                    let crate::build_sidecar::SidecarOutput { bin, dbg } = built_sidecar;
294                    fs_err::copy(bin, output_dir.join("sidecar"))?;
295                    fs_err::copy(dbg, output_dir.join("sidecar.dbg"))?;
296                }
297
298                let igvm_bin = openhcl_igvm.igvm_bin();
299                fs_err::copy(
300                    igvm_bin,
301                    output_dir.join(format!("openhcl-{build_label}.bin")),
302                )?;
303                if let Some(igvm_map) = openhcl_igvm_extras.igvm_map {
304                    fs_err::copy(
305                        igvm_map,
306                        output_dir.join(format!("openhcl-{build_label}.bin.map")),
307                    )?;
308                }
309                if let Some(OpenhclIgvmEndorsements::X64 {
310                    igvm_tdx_json,
311                    igvm_snp_json,
312                    igvm_vbs_json,
313                    igvm_snp_idblock,
314                    igvm_tdx_corim,
315                    igvm_snp_corim,
316                    igvm_vbs_corim,
317                }) = openhcl_igvm.endorsements()
318                {
319                    if let Some(igvm_tdx_json) = igvm_tdx_json {
320                        fs_err::copy(igvm_tdx_json, output_dir.join("openhcl-tdx.json"))?;
321                    }
322                    if let Some(igvm_snp_json) = igvm_snp_json {
323                        fs_err::copy(igvm_snp_json, output_dir.join("openhcl-snp.json"))?;
324                    }
325                    if let Some(igvm_vbs_json) = igvm_vbs_json {
326                        fs_err::copy(igvm_vbs_json, output_dir.join("openhcl-vbs.json"))?;
327                    }
328                    if let Some(igvm_snp_idblock) = igvm_snp_idblock {
329                        fs_err::copy(igvm_snp_idblock, output_dir.join("openhcl-snp.idblock"))?;
330                    }
331                    if let Some(igvm_tdx_corim) = igvm_tdx_corim {
332                        fs_err::copy(igvm_tdx_corim, output_dir.join("openhcl-tdx.cbor"))?;
333                    }
334                    if let Some(igvm_snp_corim) = igvm_snp_corim {
335                        fs_err::copy(igvm_snp_corim, output_dir.join("openhcl-snp.cbor"))?;
336                    }
337                    if let Some(igvm_vbs_corim) = igvm_vbs_corim {
338                        fs_err::copy(igvm_vbs_corim, output_dir.join("openhcl-vbs.cbor"))?;
339                    }
340                }
341                for e in fs_err::read_dir(output_dir)? {
342                    let e = e?;
343                    log::info!("{}", e.path().display());
344                }
345
346                Ok(())
347            }
348        });
349
350        Ok(())
351    }
352}