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