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