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 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 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 *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}