1use crate::build_openhcl_boot::OpenhclBootOutput;
13use crate::build_openhcl_initrd::OpenhclInitrdExtraParams;
14use crate::build_openvmm_hcl::MaxTraceLevel;
15use crate::build_openvmm_hcl::OpenvmmHclBuildProfile;
16use crate::build_openvmm_hcl::OpenvmmHclFeature;
17use crate::build_openvmm_hcl::OpenvmmHclOutput;
18use crate::build_sidecar::SidecarOutput;
19use crate::common::CommonArch;
20use crate::common::CommonPlatform;
21use crate::common::CommonTriple;
22use crate::resolve_openhcl_kernel_package::OpenhclKernelPackageKind;
23use crate::run_cargo_build::BuildProfile;
24use crate::run_igvmfilegen::IgvmOutput;
25use flowey::node::prelude::*;
26use igvmfilegen_config::ResourceType;
27use std::collections::BTreeMap;
28use std::collections::BTreeSet;
29
30#[derive(Serialize, Deserialize)]
32#[serde(untagged)]
33pub enum OpenhclIgvmOutput {
34 LocalOnlyCustom {
35 #[serde(rename = "openhcl-custom.bin")]
36 igvm_bin: PathBuf,
37 #[serde(flatten)]
38 endorsements: Option<OpenhclIgvmEndorsements>,
39 },
40 X64 {
41 #[serde(rename = "openhcl-x64.bin")]
42 igvm_bin: PathBuf,
43 },
44 X64Devkern {
45 #[serde(rename = "openhcl-x64-devkern.bin")]
46 igvm_bin: PathBuf,
47 },
48 X64TestLinuxDirect {
49 #[serde(rename = "openhcl-x64-test-linux-direct.bin")]
50 igvm_bin: PathBuf,
51 },
52 X64TestLinuxDirectDevkern {
53 #[serde(rename = "openhcl-x64-test-linux-direct-devkern.bin")]
54 igvm_bin: PathBuf,
55 },
56 X64Cvm {
57 #[serde(rename = "openhcl-x64-cvm.bin")]
58 igvm_bin: PathBuf,
59 #[serde(flatten)]
60 endorsements: Option<OpenhclIgvmEndorsements>,
61 },
62 X64CvmDevkern {
63 #[serde(rename = "openhcl-x64-cvm-devkern.bin")]
64 igvm_bin: PathBuf,
65 #[serde(flatten)]
66 endorsements: Option<OpenhclIgvmEndorsements>,
67 },
68 Aarch64 {
69 #[serde(rename = "openhcl-aarch64.bin")]
70 igvm_bin: PathBuf,
71 },
72 Aarch64Devkern {
73 #[serde(rename = "openhcl-aarch64-devkern.bin")]
74 igvm_bin: PathBuf,
75 },
76}
77
78#[derive(Serialize, Deserialize, Clone)]
79#[serde(untagged)]
80pub enum OpenhclIgvmEndorsements {
81 X64 {
82 #[serde(rename = "openhcl-tdx.json")]
83 #[serde(skip_serializing_if = "Option::is_none")]
84 igvm_tdx_json: Option<PathBuf>,
85 #[serde(rename = "openhcl-snp.json")]
86 #[serde(skip_serializing_if = "Option::is_none")]
87 igvm_snp_json: Option<PathBuf>,
88 #[serde(rename = "openhcl-vbs.json")]
89 #[serde(skip_serializing_if = "Option::is_none")]
90 igvm_vbs_json: Option<PathBuf>,
91 #[serde(rename = "openhcl-snp.idblock")]
96 #[serde(skip_serializing_if = "Option::is_none")]
97 igvm_snp_idblock: Option<PathBuf>,
98 #[serde(rename = "openhcl-tdx.cbor")]
99 #[serde(skip_serializing_if = "Option::is_none")]
100 igvm_tdx_corim: Option<PathBuf>,
101 #[serde(rename = "openhcl-snp.cbor")]
102 #[serde(skip_serializing_if = "Option::is_none")]
103 igvm_snp_corim: Option<PathBuf>,
104 #[serde(rename = "openhcl-vbs.cbor")]
105 #[serde(skip_serializing_if = "Option::is_none")]
106 igvm_vbs_corim: Option<PathBuf>,
107 },
108}
109
110impl OpenhclIgvmEndorsements {
111 fn is_complete(&self) -> bool {
112 match self {
113 OpenhclIgvmEndorsements::X64 {
114 igvm_tdx_json,
115 igvm_snp_json,
116 igvm_vbs_json,
117 igvm_tdx_corim,
118 igvm_snp_corim,
119 igvm_vbs_corim,
120 igvm_snp_idblock: _,
124 } => {
125 igvm_tdx_json.is_some()
126 && igvm_snp_json.is_some()
127 && igvm_vbs_json.is_some()
128 && igvm_tdx_corim.is_some()
129 && igvm_snp_corim.is_some()
130 && igvm_vbs_corim.is_some()
131 }
132 }
133 }
134}
135
136impl Artifact for OpenhclIgvmOutput {}
137
138impl OpenhclIgvmOutput {
139 pub fn igvm_bin(&self) -> &Path {
140 match self {
141 OpenhclIgvmOutput::LocalOnlyCustom { igvm_bin, .. }
142 | OpenhclIgvmOutput::X64 { igvm_bin }
143 | OpenhclIgvmOutput::X64Devkern { igvm_bin }
144 | OpenhclIgvmOutput::X64TestLinuxDirect { igvm_bin }
145 | OpenhclIgvmOutput::X64TestLinuxDirectDevkern { igvm_bin }
146 | OpenhclIgvmOutput::X64Cvm { igvm_bin, .. }
147 | OpenhclIgvmOutput::X64CvmDevkern { igvm_bin, .. }
148 | OpenhclIgvmOutput::Aarch64 { igvm_bin }
149 | OpenhclIgvmOutput::Aarch64Devkern { igvm_bin } => igvm_bin,
150 }
151 }
152
153 pub fn endorsements(&self) -> Option<&OpenhclIgvmEndorsements> {
154 match self {
155 OpenhclIgvmOutput::LocalOnlyCustom { endorsements, .. }
156 | OpenhclIgvmOutput::X64Cvm { endorsements, .. }
157 | OpenhclIgvmOutput::X64CvmDevkern { endorsements, .. } => endorsements.as_ref(),
158 _ => None,
159 }
160 }
161
162 pub fn recipe(&self) -> Option<OpenhclIgvmRecipe> {
163 match self {
164 OpenhclIgvmOutput::LocalOnlyCustom { .. } => None,
165 OpenhclIgvmOutput::X64 { .. } => Some(OpenhclIgvmRecipe::X64),
166 OpenhclIgvmOutput::X64Devkern { .. } => Some(OpenhclIgvmRecipe::X64Devkern),
167 OpenhclIgvmOutput::X64TestLinuxDirect { .. } => {
168 Some(OpenhclIgvmRecipe::X64TestLinuxDirect)
169 }
170 OpenhclIgvmOutput::X64TestLinuxDirectDevkern { .. } => {
171 Some(OpenhclIgvmRecipe::X64TestLinuxDirectDevkern)
172 }
173 OpenhclIgvmOutput::X64Cvm { .. } => Some(OpenhclIgvmRecipe::X64Cvm),
174 OpenhclIgvmOutput::X64CvmDevkern { .. } => Some(OpenhclIgvmRecipe::X64CvmDevkern),
175 OpenhclIgvmOutput::Aarch64 { .. } => Some(OpenhclIgvmRecipe::Aarch64),
176 OpenhclIgvmOutput::Aarch64Devkern { .. } => Some(OpenhclIgvmRecipe::Aarch64Devkern),
177 }
178 }
179
180 pub fn new(recipe: Option<OpenhclIgvmRecipe>, igvm: IgvmOutput) -> Self {
181 let IgvmOutput {
182 igvm_bin,
183 igvm_map: _,
184 igvm_tdx_json,
185 igvm_snp_json,
186 igvm_vbs_json,
187 igvm_snp_idblock,
188 igvm_tdx_corim,
189 igvm_snp_corim,
190 igvm_vbs_corim,
191 } = igvm;
192 let mut endorsements = if igvm_tdx_json.is_some()
193 || igvm_snp_json.is_some()
194 || igvm_vbs_json.is_some()
195 || igvm_snp_idblock.is_some()
196 {
197 Some(OpenhclIgvmEndorsements::X64 {
198 igvm_tdx_json,
199 igvm_snp_json,
200 igvm_vbs_json,
201 igvm_snp_idblock,
202 igvm_tdx_corim,
203 igvm_snp_corim,
204 igvm_vbs_corim,
205 })
206 } else {
207 None
208 };
209 match recipe {
210 None => OpenhclIgvmOutput::LocalOnlyCustom {
211 igvm_bin,
212 endorsements,
213 },
214 Some(recipe) => {
215 let output = match recipe {
216 OpenhclIgvmRecipe::X64 => OpenhclIgvmOutput::X64 { igvm_bin },
217 OpenhclIgvmRecipe::X64Devkern => OpenhclIgvmOutput::X64Devkern { igvm_bin },
218 OpenhclIgvmRecipe::X64TestLinuxDirect => {
219 OpenhclIgvmOutput::X64TestLinuxDirect { igvm_bin }
220 }
221 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => {
222 OpenhclIgvmOutput::X64TestLinuxDirectDevkern { igvm_bin }
223 }
224 OpenhclIgvmRecipe::X64Cvm => OpenhclIgvmOutput::X64Cvm {
225 igvm_bin,
226 endorsements: Some(
227 endorsements
228 .take()
229 .filter(OpenhclIgvmEndorsements::is_complete)
230 .expect("missing endorsements"),
231 ),
232 },
233 OpenhclIgvmRecipe::X64CvmDevkern => OpenhclIgvmOutput::X64CvmDevkern {
234 igvm_bin,
235 endorsements: Some(
236 endorsements
237 .take()
238 .filter(OpenhclIgvmEndorsements::is_complete)
239 .expect("missing endorsements"),
240 ),
241 },
242 OpenhclIgvmRecipe::Aarch64 => OpenhclIgvmOutput::Aarch64 { igvm_bin },
243 OpenhclIgvmRecipe::Aarch64Devkern => {
244 OpenhclIgvmOutput::Aarch64Devkern { igvm_bin }
245 }
246 };
247 if endorsements.is_some() {
248 panic!("unexpected endorsements");
249 }
250 output
251 }
252 }
253 }
254}
255
256#[derive(Serialize, Deserialize)]
258pub struct OpenhclIgvmExtrasOutput {
259 #[serde(flatten)]
260 pub openhcl_boot: OpenhclBootOutput,
261 #[serde(flatten)]
262 pub openvmm_hcl: OpenvmmHclOutput,
263 #[serde(flatten)]
264 pub sidecar: Option<SidecarOutput>,
265 #[serde(rename = "openhcl.bin.map")]
266 pub igvm_map: Option<PathBuf>,
267}
268
269impl Artifact for OpenhclIgvmExtrasOutput {}
270
271#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
272pub enum OpenhclKernelPackage {
273 Main,
275 Cvm,
277 Dev,
279 CvmDev,
281}
282
283#[derive(Serialize, Deserialize, Debug, Clone)]
285pub enum Vtl0KernelType {
286 Example,
287 LocalOnlyCustom(PathBuf),
288}
289
290#[derive(Serialize, Deserialize, Clone, Debug)]
291pub enum IgvmManifestPath {
292 InTree(String),
294 LocalOnlyCustom(PathBuf),
296}
297
298#[derive(Serialize, Deserialize, Clone, Debug)]
299pub struct OpenhclIgvmRecipeDetails {
300 pub local_only: Option<OpenhclIgvmRecipeDetailsLocalOnly>,
301
302 pub igvm_manifest: IgvmManifestPath,
303 pub openhcl_kernel_package: OpenhclKernelPackage,
304 pub openvmm_hcl_features: BTreeSet<OpenvmmHclFeature>,
305 pub target: CommonTriple,
306 pub vtl0_kernel_type: Option<Vtl0KernelType>,
307 pub with_uefi: bool,
308 pub with_interactive: bool,
309 pub with_sidecar: bool,
310 pub max_trace_level: MaxTraceLevel,
311}
312
313#[derive(Serialize, Deserialize, Clone, Debug)]
314pub struct OpenhclIgvmRecipeDetailsLocalOnly {
315 pub openvmm_hcl_no_strip: bool,
316 pub openhcl_initrd_extra_params: Option<OpenhclInitrdExtraParams>,
317 pub custom_openvmm_hcl: Option<PathBuf>,
318 pub custom_openhcl_boot: Option<PathBuf>,
319 pub custom_kernel: Option<PathBuf>,
320 pub custom_sidecar: Option<PathBuf>,
321 pub custom_extra_rootfs: Vec<PathBuf>,
322}
323
324#[expect(clippy::large_enum_variant)]
325#[derive(Debug, Serialize, Deserialize, Clone)]
326pub enum OpenhclIgvmRecipeType {
327 LocalOnlyCustom(OpenhclIgvmRecipeDetails),
328 WellKnown(OpenhclIgvmRecipe),
329}
330
331#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
332pub enum OpenhclIgvmRecipe {
333 X64,
334 X64Devkern,
335 X64TestLinuxDirect,
336 X64TestLinuxDirectDevkern,
337 X64Cvm,
338 X64CvmDevkern,
339 Aarch64,
340 Aarch64Devkern,
341}
342
343impl ArtifactType for OpenhclIgvmRecipe {
344 fn name(&self, prefix: Option<&str>, suffix: Option<&str>) -> String {
345 [
346 Some(self.arch()),
347 prefix,
348 Some("openhcl"),
349 Some("igvm"),
350 self.flavor(),
351 suffix,
352 ]
353 .into_iter()
354 .flatten()
355 .collect::<Vec<_>>()
356 .join("-")
357 }
358}
359
360impl OpenhclIgvmRecipe {
361 pub fn common_arch(&self) -> CommonArch {
362 match self {
363 OpenhclIgvmRecipe::X64
364 | OpenhclIgvmRecipe::X64Devkern
365 | OpenhclIgvmRecipe::X64TestLinuxDirect
366 | OpenhclIgvmRecipe::X64TestLinuxDirectDevkern
367 | OpenhclIgvmRecipe::X64Cvm
368 | OpenhclIgvmRecipe::X64CvmDevkern => CommonArch::X86_64,
369 OpenhclIgvmRecipe::Aarch64 | OpenhclIgvmRecipe::Aarch64Devkern => CommonArch::Aarch64,
370 }
371 }
372
373 pub fn uses_dev_kernel(&self) -> bool {
374 match self {
375 Self::X64 | Self::X64TestLinuxDirect | Self::X64Cvm | Self::Aarch64 => false,
376 Self::X64Devkern
377 | Self::X64TestLinuxDirectDevkern
378 | Self::X64CvmDevkern
379 | Self::Aarch64Devkern => true,
380 }
381 }
382
383 pub fn non_production_tag(&self) -> String {
384 let mut tag = self.arch().to_string();
385 if let Some(flavor) = self.flavor() {
386 tag.push('-');
387 tag.push_str(flavor);
388 }
389 tag
390 }
391
392 pub fn non_production_name(&self) -> String {
393 format!("openhcl-{}", self.non_production_tag())
394 }
395
396 fn flavor(&self) -> Option<&'static str> {
397 match self {
398 OpenhclIgvmRecipe::X64 | OpenhclIgvmRecipe::Aarch64 => None,
399 OpenhclIgvmRecipe::X64Devkern | OpenhclIgvmRecipe::Aarch64Devkern => Some("devkern"),
400 OpenhclIgvmRecipe::X64TestLinuxDirect => Some("test-linux-direct"),
401 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => Some("test-linux-direct-devkern"),
402 OpenhclIgvmRecipe::X64Cvm => Some("cvm"),
403 OpenhclIgvmRecipe::X64CvmDevkern => Some("cvm-devkern"),
404 }
405 }
406
407 fn arch(&self) -> &'static str {
408 match self.common_arch() {
409 CommonArch::X86_64 => "x64",
410 CommonArch::Aarch64 => "aarch64",
411 }
412 }
413}
414
415impl OpenhclIgvmRecipeType {
416 pub fn recipe_details(&self, release_cfg: bool) -> OpenhclIgvmRecipeDetails {
417 match self {
418 Self::LocalOnlyCustom(details) => details.clone(),
419 Self::WellKnown(recipe) => recipe.recipe_details(release_cfg),
420 }
421 }
422
423 pub fn recipe(&self) -> Option<OpenhclIgvmRecipe> {
424 match self {
425 Self::LocalOnlyCustom(_) => None,
426 Self::WellKnown(recipe) => Some(recipe.clone()),
427 }
428 }
429}
430
431impl OpenhclIgvmRecipe {
432 pub fn recipe_details(&self, release_cfg: bool) -> OpenhclIgvmRecipeDetails {
433 let base_openvmm_hcl_features = || {
434 let mut m = BTreeSet::new();
435
436 m.insert(OpenvmmHclFeature::Tpm);
437
438 if !release_cfg {
439 m.insert(OpenvmmHclFeature::Gdb);
440 }
441
442 m
443 };
444
445 let in_repo_template = |debug_manifest: &'static str, release_manifest: &'static str| {
446 IgvmManifestPath::InTree(if release_cfg {
447 release_manifest.into()
448 } else {
449 debug_manifest.into()
450 })
451 };
452
453 let with_interactive = !release_cfg;
455
456 let max_trace_level = if release_cfg {
459 MaxTraceLevel::Debug
460 } else {
461 MaxTraceLevel::Trace
462 };
463
464 match self {
465 Self::X64 => OpenhclIgvmRecipeDetails {
466 local_only: None,
467 igvm_manifest: in_repo_template("openhcl-x64-dev.json", "openhcl-x64-release.json"),
468 openhcl_kernel_package: OpenhclKernelPackage::Main,
469 openvmm_hcl_features: base_openvmm_hcl_features(),
470 target: CommonTriple::X86_64_LINUX_MUSL,
471 vtl0_kernel_type: None,
472 with_uefi: true,
473 with_interactive,
474 with_sidecar: true,
475 max_trace_level,
476 },
477 Self::X64Devkern => OpenhclIgvmRecipeDetails {
478 local_only: None,
479 igvm_manifest: in_repo_template("openhcl-x64-dev.json", "openhcl-x64-release.json"),
480 openhcl_kernel_package: OpenhclKernelPackage::Dev,
481 openvmm_hcl_features: base_openvmm_hcl_features(),
482 target: CommonTriple::X86_64_LINUX_MUSL,
483 vtl0_kernel_type: None,
484 with_uefi: true,
485 with_interactive,
486 with_sidecar: true,
487 max_trace_level,
488 },
489 Self::X64CvmDevkern => OpenhclIgvmRecipeDetails {
490 local_only: None,
491 igvm_manifest: in_repo_template(
492 "openhcl-x64-cvm-dev.json",
493 "openhcl-x64-cvm-release.json",
494 ),
495 openhcl_kernel_package: OpenhclKernelPackage::CvmDev,
496 openvmm_hcl_features: base_openvmm_hcl_features(),
497 target: CommonTriple::X86_64_LINUX_MUSL,
498 vtl0_kernel_type: None,
499 with_uefi: true,
500 with_interactive,
501 with_sidecar: false,
502 max_trace_level,
503 },
504 Self::X64TestLinuxDirect => OpenhclIgvmRecipeDetails {
505 local_only: None,
506 igvm_manifest: in_repo_template(
507 "openhcl-x64-direct-dev.json",
508 "openhcl-x64-direct-release.json",
509 ),
510 openhcl_kernel_package: OpenhclKernelPackage::Main,
511 openvmm_hcl_features: base_openvmm_hcl_features(),
512 target: CommonTriple::X86_64_LINUX_MUSL,
513 vtl0_kernel_type: Some(Vtl0KernelType::Example),
514 with_uefi: false,
515 with_interactive,
516 with_sidecar: true,
517 max_trace_level,
518 },
519 Self::X64TestLinuxDirectDevkern => OpenhclIgvmRecipeDetails {
520 local_only: None,
521 igvm_manifest: in_repo_template(
522 "openhcl-x64-direct-dev.json",
523 "openhcl-x64-direct-release.json",
524 ),
525 openhcl_kernel_package: OpenhclKernelPackage::Dev,
526 openvmm_hcl_features: base_openvmm_hcl_features(),
527 target: CommonTriple::X86_64_LINUX_MUSL,
528 vtl0_kernel_type: Some(Vtl0KernelType::Example),
529 with_uefi: false,
530 with_interactive,
531 with_sidecar: true,
532 max_trace_level,
533 },
534 Self::X64Cvm => OpenhclIgvmRecipeDetails {
535 local_only: None,
536 igvm_manifest: in_repo_template(
537 "openhcl-x64-cvm-dev.json",
538 "openhcl-x64-cvm-release.json",
539 ),
540 openhcl_kernel_package: OpenhclKernelPackage::Cvm,
541 openvmm_hcl_features: base_openvmm_hcl_features(),
542 target: CommonTriple::X86_64_LINUX_MUSL,
543 vtl0_kernel_type: None,
544 with_uefi: true,
545 with_interactive,
546 with_sidecar: false,
547 max_trace_level,
548 },
549 Self::Aarch64 => OpenhclIgvmRecipeDetails {
550 local_only: None,
551 igvm_manifest: in_repo_template(
552 "openhcl-aarch64-dev.json",
553 "openhcl-aarch64-release.json",
554 ),
555 openhcl_kernel_package: OpenhclKernelPackage::Main,
556 openvmm_hcl_features: base_openvmm_hcl_features(),
557 target: CommonTriple::AARCH64_LINUX_MUSL,
558 vtl0_kernel_type: None,
559 with_uefi: true,
560 with_interactive: false, with_sidecar: false,
562 max_trace_level,
563 },
564 Self::Aarch64Devkern => OpenhclIgvmRecipeDetails {
565 local_only: None,
566 igvm_manifest: in_repo_template(
567 "openhcl-aarch64-dev.json",
568 "openhcl-aarch64-release.json",
569 ),
570 openhcl_kernel_package: OpenhclKernelPackage::Dev,
571 openvmm_hcl_features: base_openvmm_hcl_features(),
572 target: CommonTriple::AARCH64_LINUX_MUSL,
573 vtl0_kernel_type: None,
574 with_uefi: true,
575 with_interactive: false, with_sidecar: false,
577 max_trace_level,
578 },
579 }
580 }
581}
582
583flowey_request! {
584 pub struct Request {
585 pub build_profile: OpenvmmHclBuildProfile,
586 pub release_cfg: bool,
587 pub recipe: OpenhclIgvmRecipeType,
588 pub custom_target: Option<CommonTriple>,
589 pub extra_features: BTreeSet<OpenvmmHclFeature>,
591 pub disable_secure_avic: bool,
592 pub uefi_firmware_flavor: Option<crate::download_uefi_mu_msvm::FirmwareFlavor>,
593 pub confidential_debug: bool,
596
597 pub openhcl_igvm: WriteVar<OpenhclIgvmOutput>,
598 pub openhcl_igvm_extras: WriteVar<OpenhclIgvmExtrasOutput>,
599 }
600}
601
602new_simple_flow_node!(struct Node);
603
604impl SimpleFlowNode for Node {
605 type Request = Request;
606
607 fn imports(ctx: &mut ImportCtx<'_>) {
608 ctx.import::<crate::build_igvmfilegen::Node>();
609 ctx.import::<crate::build_openhcl_boot::Node>();
610 ctx.import::<crate::build_openhcl_initrd::Node>();
611 ctx.import::<crate::build_openvmm_hcl::Node>();
612 ctx.import::<crate::build_sidecar::Node>();
613 ctx.import::<crate::resolve_openhcl_kernel_package::Node>();
614 ctx.import::<crate::resolve_openvmm_deps::Node>();
615 ctx.import::<crate::resolve_openvmm_test_initrd::Node>();
616 ctx.import::<crate::resolve_openvmm_test_linux_kernel::Node>();
617 ctx.import::<crate::download_uefi_mu_msvm::Node>();
618 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
619 ctx.import::<crate::run_igvmfilegen::Node>();
620 ctx.import::<crate::run_split_debug_info::Node>();
621 }
622
623 fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
624 let Request {
625 build_profile,
626 release_cfg,
627 recipe,
628 custom_target,
629 extra_features,
630 disable_secure_avic,
631 uefi_firmware_flavor,
632 confidential_debug,
633 openhcl_igvm,
634 openhcl_igvm_extras,
635 } = request;
636
637 let OpenhclIgvmRecipeDetails {
638 local_only,
639 igvm_manifest,
640 openhcl_kernel_package,
641 mut openvmm_hcl_features,
642 target,
643 vtl0_kernel_type,
644 with_uefi,
645 with_interactive,
646 with_sidecar,
647 max_trace_level,
648 } = recipe.recipe_details(release_cfg);
649
650 openvmm_hcl_features.extend(extra_features);
651
652 if disable_secure_avic {
653 openvmm_hcl_features.insert(OpenvmmHclFeature::LocalOnlyCustom(
654 "disable_secure_avic".into(),
655 ));
656 }
657
658 let OpenhclIgvmRecipeDetailsLocalOnly {
659 openvmm_hcl_no_strip,
660 openhcl_initrd_extra_params,
661 custom_openvmm_hcl,
662 custom_openhcl_boot,
663 custom_kernel,
664 custom_sidecar,
665 custom_extra_rootfs,
666 } = local_only.unwrap_or(OpenhclIgvmRecipeDetailsLocalOnly {
667 openvmm_hcl_no_strip: false,
668 openhcl_initrd_extra_params: None,
669 custom_openvmm_hcl: None,
670 custom_openhcl_boot: None,
671 custom_kernel: None,
672 custom_sidecar: None,
673 custom_extra_rootfs: Vec::new(),
674 });
675
676 let target = custom_target.unwrap_or(target);
677 let arch = CommonArch::from_triple(&target.as_triple())
678 .with_context(|| format!("cannot build openHCL from recipe on {target}"))?;
679
680 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
681
682 let kernel_kind = match openhcl_kernel_package {
683 OpenhclKernelPackage::Main => OpenhclKernelPackageKind::Main,
684 OpenhclKernelPackage::Cvm => OpenhclKernelPackageKind::Cvm,
685 OpenhclKernelPackage::Dev => OpenhclKernelPackageKind::Dev,
686 OpenhclKernelPackage::CvmDev => OpenhclKernelPackageKind::CvmDev,
687 };
688
689 let vtl2_kernel_package_root =
691 ctx.reqv(
692 |v| crate::resolve_openhcl_kernel_package::Request::GetPackageRoot {
693 kind: kernel_kind,
694 arch,
695 pkg: v,
696 },
697 );
698
699 let vtl2_kernel_modules =
701 ctx.reqv(
702 |v| crate::resolve_openhcl_kernel_package::Request::GetModules {
703 kind: kernel_kind,
704 arch,
705 modules: v,
706 },
707 );
708
709 let vtl2_kernel_metadata =
711 ctx.reqv(
712 |v| crate::resolve_openhcl_kernel_package::Request::GetMetadata {
713 kind: kernel_kind,
714 arch,
715 metadata: v,
716 },
717 );
718
719 let uefi_resource: Option<UefiResource> = with_uefi.then(|| UefiResource {
720 msvm_fd: ctx.reqv(|msvm_fd| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
721 arch,
722 flavor: uefi_firmware_flavor,
723 msvm_fd,
724 }),
725 });
726
727 let vtl0_kernel_resource = vtl0_kernel_type.map(|typ| {
728 let kernel = if let Vtl0KernelType::LocalOnlyCustom(path) = typ {
729 ReadVar::from_static(path)
730 } else {
731 match typ {
732 Vtl0KernelType::Example => ctx.reqv(|v| {
733 crate::resolve_openvmm_test_linux_kernel::Request::Get(
734 crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel,
735 arch,
736 crate::resolve_openvmm_test_linux_kernel::DEFAULT_LINUX_TEST_KERNEL_VERSION,
737 v,
738 )
739 }),
740 Vtl0KernelType::LocalOnlyCustom(_) => unreachable!("special cased above"),
741 }
742 };
743
744 let initrd = ctx.reqv(|v| {
745 crate::resolve_openvmm_test_initrd::Request::Get(arch, v)
746 });
747
748 Vtl0KernelResource { kernel, initrd }
749 });
750
751 let sidecar = if with_sidecar {
753 let sidecar_bin = if let Some(path) = custom_sidecar {
754 ctx.emit_rust_stepv("set custom_sidecar", |_ctx| {
755 |_rt| {
756 let fake_dbg_path = std::env::current_dir()?
757 .join("fake_sidecar.dbg")
758 .absolute()?;
759 fs_err::write(&fake_dbg_path, "")?;
760
761 Ok(SidecarOutput {
762 bin: path,
763 dbg: fake_dbg_path,
764 })
765 }
766 })
767 } else {
768 ctx.reqv(|v| crate::build_sidecar::Request {
769 build_params: crate::build_sidecar::SidecarBuildParams {
770 arch,
771 profile: match build_profile {
772 OpenvmmHclBuildProfile::Debug => {
773 crate::build_sidecar::SidecarBuildProfile::Debug
774 }
775 OpenvmmHclBuildProfile::Release
776 | OpenvmmHclBuildProfile::OpenvmmHclShip => {
777 crate::build_sidecar::SidecarBuildProfile::Release
778 }
779 },
780 },
781 sidecar: v,
782 })
783 };
784 Some(sidecar_bin)
785 } else {
786 None
787 };
788
789 let openvmm_hcl = if let Some(ref path) = custom_openvmm_hcl {
791 let path = path.clone();
792 ctx.emit_rust_stepv("set custom_openvmm_hcl", |_ctx| {
793 |_rt| {
794 Ok(OpenvmmHclOutput {
795 bin: path,
796 dbg: None,
797 })
798 }
799 })
800 } else {
801 ctx.reqv(|v| {
802 crate::build_openvmm_hcl::Request {
803 build_params: crate::build_openvmm_hcl::OpenvmmHclBuildParams {
804 target: target.clone(),
805 profile: build_profile,
806 features: openvmm_hcl_features,
807 no_split_dbg_info: true,
809 max_trace_level,
810 },
811 openvmm_hcl_output: v,
812 }
813 })
814 };
815
816 let igvmfilegen_arch: CommonArch = ctx.arch().try_into()?;
818
819 let igvmfilegen = ctx.reqv(|v| crate::build_igvmfilegen::Request {
820 build_params: crate::build_igvmfilegen::IgvmfilegenBuildParams {
821 target: CommonTriple::Common {
822 arch: igvmfilegen_arch,
823 platform: CommonPlatform::LinuxGnu,
824 },
825 profile: BuildProfile::Light,
826 },
827 igvmfilegen: v,
828 });
829
830 let openhcl_boot = if let Some(path) = custom_openhcl_boot {
832 ctx.emit_rust_stepv("set custom_openhcl_boot", |_ctx| {
833 |_rt| {
834 let fake_dbg_path = std::env::current_dir()?.join("fake.dbg").absolute()?;
835 fs_err::write(&fake_dbg_path, "")?;
836
837 Ok(OpenhclBootOutput {
838 bin: path,
839 dbg: fake_dbg_path,
840 })
841 }
842 })
843 } else {
844 ctx.reqv(|v| crate::build_openhcl_boot::Request {
845 build_params: crate::build_openhcl_boot::OpenhclBootBuildParams {
846 arch,
847 profile: match build_profile {
848 OpenvmmHclBuildProfile::Debug => {
849 crate::build_openhcl_boot::OpenhclBootBuildProfile::Debug
850 }
851 OpenvmmHclBuildProfile::Release
852 | OpenvmmHclBuildProfile::OpenvmmHclShip => {
853 crate::build_openhcl_boot::OpenhclBootBuildProfile::Release
854 }
855 },
856 },
857 openhcl_boot: v,
858 })
859 };
860
861 let use_stripped_openvmm_hcl = {
862 if custom_openvmm_hcl.is_some() {
863 false
866 } else {
867 !openvmm_hcl_no_strip
868 }
869 };
870
871 let openvmm_hcl = if use_stripped_openvmm_hcl {
873 let (read, write) = ctx.new_var();
874 let (read_dbg, write_dbg) = ctx.new_var();
875
876 let in_bin = openvmm_hcl.map(ctx, |o| o.bin);
877 ctx.req(crate::run_split_debug_info::Request {
878 arch,
879 in_bin,
880 out_bin: write,
881 out_dbg_info: write_dbg,
882 reproducible_without_debuglink: matches!(
883 ctx.platform(),
884 FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix)
885 ),
886 });
887
888 read.zip(ctx, read_dbg)
889 .map(ctx, |(bin, dbg)| OpenvmmHclOutput {
890 bin,
891 dbg: Some(dbg),
892 })
893 } else {
894 openvmm_hcl
895 };
896
897 let initrd = {
898 let rootfs_config = [openvmm_repo_path.map(ctx, |p| p.join("openhcl/rootfs.config"))]
899 .into_iter()
900 .chain(
901 custom_extra_rootfs
902 .into_iter()
903 .map(|p| ReadVar::from_static(p)),
904 )
905 .collect();
906 let openvmm_hcl_bin = openvmm_hcl.map(ctx, |o| o.bin);
907
908 ctx.reqv(|v| crate::build_openhcl_initrd::Request {
909 interactive: with_interactive,
910 arch,
911 extra_params: openhcl_initrd_extra_params,
912 rootfs_config,
913 extra_env: None,
914 kernel_package_root: vtl2_kernel_package_root.clone(),
915 kernel_modules: vtl2_kernel_modules,
916 kernel_metadata: vtl2_kernel_metadata,
917 bin_openhcl: openvmm_hcl_bin,
918 initrd: v,
919 })
920 };
921
922 let kernel = if let Some(path) = custom_kernel {
923 ReadVar::from_static(path)
924 } else {
925 ctx.reqv(
926 |v| crate::resolve_openhcl_kernel_package::Request::GetKernel {
927 kind: kernel_kind,
928 arch,
929 kernel: v,
930 },
931 )
932 };
933
934 let sidecar_bin = sidecar.clone().map(|x| x.map(ctx, |y| y.bin));
935 let openhcl_boot_bin = openhcl_boot.map(ctx, |x| x.bin);
936 let resources = ctx.emit_minor_rust_stepv("enumerate igvm resources", |ctx| {
937 claim_vars!(
938 ctx,
939 (
940 initrd,
941 kernel,
942 openhcl_boot_bin,
943 sidecar_bin,
944 uefi_resource,
945 vtl0_kernel_resource
946 )
947 );
948 |rt| {
949 let mut resources = BTreeMap::<ResourceType, PathBuf>::new();
950 resources.insert(ResourceType::UnderhillKernel, rt.read(kernel));
951 resources.insert(ResourceType::UnderhillInitrd, rt.read(initrd).initrd);
952 resources.insert(ResourceType::OpenhclBoot, rt.read(openhcl_boot_bin));
953 if let Some(sidecar_bin) = sidecar_bin {
954 resources.insert(ResourceType::UnderhillSidecar, rt.read(sidecar_bin));
955 }
956 if let Some(uefi_resource) = uefi_resource {
957 uefi_resource.add_to_resources(&mut resources, rt);
958 }
959 if let Some(vtl0_kernel_resource) = vtl0_kernel_resource {
960 vtl0_kernel_resource.add_to_resources(&mut resources, rt);
961 }
962 resources
963 }
964 });
965
966 let igvmfilegen = igvmfilegen.map(ctx, |o| match o {
967 crate::build_igvmfilegen::IgvmfilegenOutput::LinuxBin { bin, dbg: _ } => bin,
968 crate::build_igvmfilegen::IgvmfilegenOutput::WindowsBin { exe, pdb: _ } => exe,
969 });
970
971 let manifest = match igvm_manifest {
972 IgvmManifestPath::InTree(path) => {
973 openvmm_repo_path.map(ctx, |p| p.join("vm/loader/manifests").join(path))
974 }
975 IgvmManifestPath::LocalOnlyCustom(p) => ReadVar::from_static(p),
976 };
977
978 let igvm = ctx.reqv(|v| crate::run_igvmfilegen::Request {
979 igvmfilegen,
980 manifest,
981 resources,
982 disable_secure_avic,
983 confidential_debug,
984 add_temp_snp_id_block: true,
988 igvm: v,
989 });
990
991 let igvm_map = igvm.map(ctx, |x| x.igvm_map);
992 ctx.emit_minor_rust_step("construct openhcl extras", move |ctx| {
993 claim_vars!(
994 ctx,
995 (
996 openhcl_boot,
997 openvmm_hcl,
998 sidecar,
999 igvm_map,
1000 openhcl_igvm_extras
1001 )
1002 );
1003
1004 |rt| {
1005 read_vars!(rt, (openhcl_boot, openvmm_hcl, sidecar, igvm_map));
1006
1007 rt.write(
1008 openhcl_igvm_extras,
1009 &OpenhclIgvmExtrasOutput {
1010 openhcl_boot,
1011 openvmm_hcl,
1012 sidecar,
1013 igvm_map,
1014 },
1015 );
1016 }
1017 });
1018
1019 igvm.write_into_with(ctx, openhcl_igvm, move |igvm| {
1020 OpenhclIgvmOutput::new(recipe.recipe(), igvm)
1021 });
1022
1023 Ok(())
1024 }
1025}
1026
1027#[derive(Debug)]
1028pub struct UefiResource<C = VarNotClaimed> {
1029 pub msvm_fd: ReadVar<PathBuf, C>,
1030}
1031
1032impl ClaimVar for UefiResource {
1033 type Claimed = UefiResource<VarClaimed>;
1034
1035 fn claim(self, ctx: &mut StepCtx<'_>) -> UefiResource<VarClaimed> {
1036 UefiResource {
1037 msvm_fd: self.msvm_fd.claim(ctx),
1038 }
1039 }
1040}
1041
1042impl UefiResource<VarClaimed> {
1043 pub fn add_to_resources(
1044 self,
1045 resources: &mut BTreeMap<ResourceType, PathBuf>,
1046 rt: &mut RustRuntimeServices<'_>,
1047 ) {
1048 let path = rt.read(self.msvm_fd);
1049 resources.insert(ResourceType::Uefi, path);
1050 }
1051}
1052
1053pub struct Vtl0KernelResource<C = VarNotClaimed> {
1054 pub kernel: ReadVar<PathBuf, C>,
1055 pub initrd: ReadVar<PathBuf, C>,
1056}
1057
1058impl ClaimVar for Vtl0KernelResource {
1059 type Claimed = Vtl0KernelResource<VarClaimed>;
1060
1061 fn claim(self, ctx: &mut StepCtx<'_>) -> Vtl0KernelResource<VarClaimed> {
1062 Vtl0KernelResource {
1063 kernel: self.kernel.claim(ctx),
1064 initrd: self.initrd.claim(ctx),
1065 }
1066 }
1067}
1068
1069impl Vtl0KernelResource<VarClaimed> {
1070 pub fn add_to_resources(
1071 self,
1072 resources: &mut BTreeMap<ResourceType, PathBuf>,
1073 rt: &mut RustRuntimeServices<'_>,
1074 ) {
1075 let kernel = rt.read(self.kernel);
1076 let initrd = rt.read(self.initrd);
1077 resources.insert(ResourceType::LinuxKernel, kernel);
1078 resources.insert(ResourceType::LinuxInitrd, initrd);
1079 }
1080}