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: OpenhclIgvmEndorsements,
61 },
62 X64CvmDevkern {
63 #[serde(rename = "openhcl-x64-cvm-devkern.bin")]
64 igvm_bin: PathBuf,
65 #[serde(flatten)]
66 endorsements: 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, .. } => endorsements.as_ref(),
156 OpenhclIgvmOutput::X64Cvm { endorsements, .. }
157 | OpenhclIgvmOutput::X64CvmDevkern { endorsements, .. } => Some(endorsements),
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: endorsements
227 .take()
228 .filter(OpenhclIgvmEndorsements::is_complete)
229 .expect("missing endorsements"),
230 },
231 OpenhclIgvmRecipe::X64CvmDevkern => OpenhclIgvmOutput::X64CvmDevkern {
232 igvm_bin,
233 endorsements: endorsements
234 .take()
235 .filter(OpenhclIgvmEndorsements::is_complete)
236 .expect("missing endorsements"),
237 },
238 OpenhclIgvmRecipe::Aarch64 => OpenhclIgvmOutput::Aarch64 { igvm_bin },
239 OpenhclIgvmRecipe::Aarch64Devkern => {
240 OpenhclIgvmOutput::Aarch64Devkern { igvm_bin }
241 }
242 };
243 if endorsements.is_some() {
244 panic!("unexpected endorsements");
245 }
246 output
247 }
248 }
249 }
250}
251
252#[derive(Serialize, Deserialize)]
254pub struct OpenhclIgvmExtrasOutput {
255 #[serde(flatten)]
256 pub openhcl_boot: OpenhclBootOutput,
257 #[serde(flatten)]
258 pub openvmm_hcl: OpenvmmHclOutput,
259 #[serde(flatten)]
260 pub sidecar: Option<SidecarOutput>,
261 #[serde(rename = "openhcl.bin.map")]
262 pub igvm_map: Option<PathBuf>,
263}
264
265impl Artifact for OpenhclIgvmExtrasOutput {}
266
267#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
268pub enum OpenhclKernelPackage {
269 Main,
271 Cvm,
273 Dev,
275 CvmDev,
277}
278
279#[derive(Serialize, Deserialize, Debug, Clone)]
281pub enum Vtl0KernelType {
282 Example,
283 LocalOnlyCustom(PathBuf),
284}
285
286#[derive(Serialize, Deserialize, Clone, Debug)]
287pub enum IgvmManifestPath {
288 InTree(String),
290 LocalOnlyCustom(PathBuf),
292}
293
294#[derive(Serialize, Deserialize, Clone, Debug)]
295pub struct OpenhclIgvmRecipeDetails {
296 pub local_only: Option<OpenhclIgvmRecipeDetailsLocalOnly>,
297
298 pub igvm_manifest: IgvmManifestPath,
299 pub openhcl_kernel_package: OpenhclKernelPackage,
300 pub openvmm_hcl_features: BTreeSet<OpenvmmHclFeature>,
301 pub target: CommonTriple,
302 pub vtl0_kernel_type: Option<Vtl0KernelType>,
303 pub with_uefi: bool,
304 pub with_interactive: bool,
305 pub with_sidecar: bool,
306 pub max_trace_level: MaxTraceLevel,
307}
308
309#[derive(Serialize, Deserialize, Clone, Debug)]
310pub struct OpenhclIgvmRecipeDetailsLocalOnly {
311 pub openvmm_hcl_no_strip: bool,
312 pub openhcl_initrd_extra_params: Option<OpenhclInitrdExtraParams>,
313 pub custom_openvmm_hcl: Option<PathBuf>,
314 pub custom_openhcl_boot: Option<PathBuf>,
315 pub custom_kernel: Option<PathBuf>,
316 pub custom_sidecar: Option<PathBuf>,
317 pub custom_extra_rootfs: Vec<PathBuf>,
318}
319
320#[expect(clippy::large_enum_variant)]
321#[derive(Debug, Serialize, Deserialize, Clone)]
322pub enum OpenhclIgvmRecipeType {
323 LocalOnlyCustom(OpenhclIgvmRecipeDetails),
324 WellKnown(OpenhclIgvmRecipe),
325}
326
327#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
328pub enum OpenhclIgvmRecipe {
329 X64,
330 X64Devkern,
331 X64TestLinuxDirect,
332 X64TestLinuxDirectDevkern,
333 X64Cvm,
334 X64CvmDevkern,
335 Aarch64,
336 Aarch64Devkern,
337}
338
339impl ArtifactType for OpenhclIgvmRecipe {
340 fn name(&self, prefix: Option<&str>, suffix: Option<&str>) -> String {
341 [
342 Some(self.arch()),
343 prefix,
344 Some("openhcl"),
345 Some("igvm"),
346 self.flavor(),
347 suffix,
348 ]
349 .into_iter()
350 .flatten()
351 .collect::<Vec<_>>()
352 .join("-")
353 }
354}
355
356impl OpenhclIgvmRecipe {
357 pub fn uses_dev_kernel(&self) -> bool {
358 match self {
359 Self::X64 | Self::X64TestLinuxDirect | Self::X64Cvm | Self::Aarch64 => false,
360 Self::X64Devkern
361 | Self::X64TestLinuxDirectDevkern
362 | Self::X64CvmDevkern
363 | Self::Aarch64Devkern => true,
364 }
365 }
366
367 pub fn non_production_tag(&self) -> String {
368 let mut tag = self.arch().to_string();
369 if let Some(flavor) = self.flavor() {
370 tag.push('-');
371 tag.push_str(flavor);
372 }
373 tag
374 }
375
376 pub fn non_production_name(&self) -> String {
377 format!("openhcl-{}", self.non_production_tag())
378 }
379
380 fn flavor(&self) -> Option<&'static str> {
381 match self {
382 OpenhclIgvmRecipe::X64 | OpenhclIgvmRecipe::Aarch64 => None,
383 OpenhclIgvmRecipe::X64Devkern | OpenhclIgvmRecipe::Aarch64Devkern => Some("devkern"),
384 OpenhclIgvmRecipe::X64TestLinuxDirect => Some("test-linux-direct"),
385 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => Some("test-linux-direct-devkern"),
386 OpenhclIgvmRecipe::X64Cvm => Some("cvm"),
387 OpenhclIgvmRecipe::X64CvmDevkern => Some("cvm-devkern"),
388 }
389 }
390
391 fn arch(&self) -> &'static str {
392 match self {
393 OpenhclIgvmRecipe::X64
394 | OpenhclIgvmRecipe::X64Devkern
395 | OpenhclIgvmRecipe::X64TestLinuxDirect
396 | OpenhclIgvmRecipe::X64TestLinuxDirectDevkern
397 | OpenhclIgvmRecipe::X64Cvm
398 | OpenhclIgvmRecipe::X64CvmDevkern => "x64",
399 OpenhclIgvmRecipe::Aarch64 | OpenhclIgvmRecipe::Aarch64Devkern => "aarch64",
400 }
401 }
402}
403
404impl OpenhclIgvmRecipeType {
405 pub fn recipe_details(&self, release_cfg: bool) -> OpenhclIgvmRecipeDetails {
406 match self {
407 Self::LocalOnlyCustom(details) => details.clone(),
408 Self::WellKnown(recipe) => recipe.recipe_details(release_cfg),
409 }
410 }
411
412 pub fn recipe(&self) -> Option<OpenhclIgvmRecipe> {
413 match self {
414 Self::LocalOnlyCustom(_) => None,
415 Self::WellKnown(recipe) => Some(recipe.clone()),
416 }
417 }
418}
419
420impl OpenhclIgvmRecipe {
421 pub fn recipe_details(&self, release_cfg: bool) -> OpenhclIgvmRecipeDetails {
422 let base_openvmm_hcl_features = || {
423 let mut m = BTreeSet::new();
424
425 m.insert(OpenvmmHclFeature::Tpm);
426
427 if !release_cfg {
428 m.insert(OpenvmmHclFeature::Gdb);
429 }
430
431 m
432 };
433
434 let in_repo_template = |debug_manifest: &'static str, release_manifest: &'static str| {
435 IgvmManifestPath::InTree(if release_cfg {
436 release_manifest.into()
437 } else {
438 debug_manifest.into()
439 })
440 };
441
442 let with_interactive = !release_cfg;
444
445 let max_trace_level = if release_cfg {
448 MaxTraceLevel::Debug
449 } else {
450 MaxTraceLevel::Trace
451 };
452
453 match self {
454 Self::X64 => OpenhclIgvmRecipeDetails {
455 local_only: None,
456 igvm_manifest: in_repo_template("openhcl-x64-dev.json", "openhcl-x64-release.json"),
457 openhcl_kernel_package: OpenhclKernelPackage::Main,
458 openvmm_hcl_features: base_openvmm_hcl_features(),
459 target: CommonTriple::X86_64_LINUX_MUSL,
460 vtl0_kernel_type: None,
461 with_uefi: true,
462 with_interactive,
463 with_sidecar: true,
464 max_trace_level,
465 },
466 Self::X64Devkern => OpenhclIgvmRecipeDetails {
467 local_only: None,
468 igvm_manifest: in_repo_template("openhcl-x64-dev.json", "openhcl-x64-release.json"),
469 openhcl_kernel_package: OpenhclKernelPackage::Dev,
470 openvmm_hcl_features: base_openvmm_hcl_features(),
471 target: CommonTriple::X86_64_LINUX_MUSL,
472 vtl0_kernel_type: None,
473 with_uefi: true,
474 with_interactive,
475 with_sidecar: true,
476 max_trace_level,
477 },
478 Self::X64CvmDevkern => OpenhclIgvmRecipeDetails {
479 local_only: None,
480 igvm_manifest: in_repo_template(
481 "openhcl-x64-cvm-dev.json",
482 "openhcl-x64-cvm-release.json",
483 ),
484 openhcl_kernel_package: OpenhclKernelPackage::CvmDev,
485 openvmm_hcl_features: base_openvmm_hcl_features(),
486 target: CommonTriple::X86_64_LINUX_MUSL,
487 vtl0_kernel_type: None,
488 with_uefi: true,
489 with_interactive,
490 with_sidecar: false,
491 max_trace_level,
492 },
493 Self::X64TestLinuxDirect => OpenhclIgvmRecipeDetails {
494 local_only: None,
495 igvm_manifest: in_repo_template(
496 "openhcl-x64-direct-dev.json",
497 "openhcl-x64-direct-release.json",
498 ),
499 openhcl_kernel_package: OpenhclKernelPackage::Main,
500 openvmm_hcl_features: base_openvmm_hcl_features(),
501 target: CommonTriple::X86_64_LINUX_MUSL,
502 vtl0_kernel_type: Some(Vtl0KernelType::Example),
503 with_uefi: false,
504 with_interactive,
505 with_sidecar: true,
506 max_trace_level,
507 },
508 Self::X64TestLinuxDirectDevkern => OpenhclIgvmRecipeDetails {
509 local_only: None,
510 igvm_manifest: in_repo_template(
511 "openhcl-x64-direct-dev.json",
512 "openhcl-x64-direct-release.json",
513 ),
514 openhcl_kernel_package: OpenhclKernelPackage::Dev,
515 openvmm_hcl_features: base_openvmm_hcl_features(),
516 target: CommonTriple::X86_64_LINUX_MUSL,
517 vtl0_kernel_type: Some(Vtl0KernelType::Example),
518 with_uefi: false,
519 with_interactive,
520 with_sidecar: true,
521 max_trace_level,
522 },
523 Self::X64Cvm => OpenhclIgvmRecipeDetails {
524 local_only: None,
525 igvm_manifest: in_repo_template(
526 "openhcl-x64-cvm-dev.json",
527 "openhcl-x64-cvm-release.json",
528 ),
529 openhcl_kernel_package: OpenhclKernelPackage::Cvm,
530 openvmm_hcl_features: base_openvmm_hcl_features(),
531 target: CommonTriple::X86_64_LINUX_MUSL,
532 vtl0_kernel_type: None,
533 with_uefi: true,
534 with_interactive,
535 with_sidecar: false,
536 max_trace_level,
537 },
538 Self::Aarch64 => OpenhclIgvmRecipeDetails {
539 local_only: None,
540 igvm_manifest: in_repo_template(
541 "openhcl-aarch64-dev.json",
542 "openhcl-aarch64-release.json",
543 ),
544 openhcl_kernel_package: OpenhclKernelPackage::Main,
545 openvmm_hcl_features: base_openvmm_hcl_features(),
546 target: CommonTriple::AARCH64_LINUX_MUSL,
547 vtl0_kernel_type: None,
548 with_uefi: true,
549 with_interactive: false, with_sidecar: false,
551 max_trace_level,
552 },
553 Self::Aarch64Devkern => OpenhclIgvmRecipeDetails {
554 local_only: None,
555 igvm_manifest: in_repo_template(
556 "openhcl-aarch64-dev.json",
557 "openhcl-aarch64-release.json",
558 ),
559 openhcl_kernel_package: OpenhclKernelPackage::Dev,
560 openvmm_hcl_features: base_openvmm_hcl_features(),
561 target: CommonTriple::AARCH64_LINUX_MUSL,
562 vtl0_kernel_type: None,
563 with_uefi: true,
564 with_interactive: false, with_sidecar: false,
566 max_trace_level,
567 },
568 }
569 }
570}
571
572flowey_request! {
573 pub struct Request {
574 pub build_profile: OpenvmmHclBuildProfile,
575 pub release_cfg: bool,
576 pub recipe: OpenhclIgvmRecipeType,
577 pub custom_target: Option<CommonTriple>,
578 pub extra_features: BTreeSet<OpenvmmHclFeature>,
580 pub disable_secure_avic: bool,
581 pub confidential_debug: bool,
584
585 pub openhcl_igvm: WriteVar<OpenhclIgvmOutput>,
586 pub openhcl_igvm_extras: WriteVar<OpenhclIgvmExtrasOutput>,
587 }
588}
589
590new_simple_flow_node!(struct Node);
591
592impl SimpleFlowNode for Node {
593 type Request = Request;
594
595 fn imports(ctx: &mut ImportCtx<'_>) {
596 ctx.import::<crate::build_igvmfilegen::Node>();
597 ctx.import::<crate::build_openhcl_boot::Node>();
598 ctx.import::<crate::build_openhcl_initrd::Node>();
599 ctx.import::<crate::build_openvmm_hcl::Node>();
600 ctx.import::<crate::build_sidecar::Node>();
601 ctx.import::<crate::resolve_openhcl_kernel_package::Node>();
602 ctx.import::<crate::resolve_openvmm_deps::Node>();
603 ctx.import::<crate::resolve_openvmm_test_initrd::Node>();
604 ctx.import::<crate::resolve_openvmm_test_linux_kernel::Node>();
605 ctx.import::<crate::download_uefi_mu_msvm::Node>();
606 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
607 ctx.import::<crate::run_igvmfilegen::Node>();
608 ctx.import::<crate::run_split_debug_info::Node>();
609 }
610
611 fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
612 let Request {
613 build_profile,
614 release_cfg,
615 recipe,
616 custom_target,
617 extra_features,
618 disable_secure_avic,
619 confidential_debug,
620 openhcl_igvm,
621 openhcl_igvm_extras,
622 } = request;
623
624 let OpenhclIgvmRecipeDetails {
625 local_only,
626 igvm_manifest,
627 openhcl_kernel_package,
628 mut openvmm_hcl_features,
629 target,
630 vtl0_kernel_type,
631 with_uefi,
632 with_interactive,
633 with_sidecar,
634 max_trace_level,
635 } = recipe.recipe_details(release_cfg);
636
637 openvmm_hcl_features.extend(extra_features);
638
639 if disable_secure_avic {
640 openvmm_hcl_features.insert(OpenvmmHclFeature::LocalOnlyCustom(
641 "disable_secure_avic".into(),
642 ));
643 }
644
645 let OpenhclIgvmRecipeDetailsLocalOnly {
646 openvmm_hcl_no_strip,
647 openhcl_initrd_extra_params,
648 custom_openvmm_hcl,
649 custom_openhcl_boot,
650 custom_kernel,
651 custom_sidecar,
652 custom_extra_rootfs,
653 } = local_only.unwrap_or(OpenhclIgvmRecipeDetailsLocalOnly {
654 openvmm_hcl_no_strip: false,
655 openhcl_initrd_extra_params: None,
656 custom_openvmm_hcl: None,
657 custom_openhcl_boot: None,
658 custom_kernel: None,
659 custom_sidecar: None,
660 custom_extra_rootfs: Vec::new(),
661 });
662
663 let target = custom_target.unwrap_or(target);
664 let arch = CommonArch::from_triple(&target.as_triple())
665 .with_context(|| format!("cannot build openHCL from recipe on {target}"))?;
666
667 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
668
669 let kernel_kind = match openhcl_kernel_package {
670 OpenhclKernelPackage::Main => OpenhclKernelPackageKind::Main,
671 OpenhclKernelPackage::Cvm => OpenhclKernelPackageKind::Cvm,
672 OpenhclKernelPackage::Dev => OpenhclKernelPackageKind::Dev,
673 OpenhclKernelPackage::CvmDev => OpenhclKernelPackageKind::CvmDev,
674 };
675
676 let vtl2_kernel_package_root =
678 ctx.reqv(
679 |v| crate::resolve_openhcl_kernel_package::Request::GetPackageRoot {
680 kind: kernel_kind,
681 arch,
682 pkg: v,
683 },
684 );
685
686 let vtl2_kernel_modules =
688 ctx.reqv(
689 |v| crate::resolve_openhcl_kernel_package::Request::GetModules {
690 kind: kernel_kind,
691 arch,
692 modules: v,
693 },
694 );
695
696 let vtl2_kernel_metadata =
698 ctx.reqv(
699 |v| crate::resolve_openhcl_kernel_package::Request::GetMetadata {
700 kind: kernel_kind,
701 arch,
702 metadata: v,
703 },
704 );
705
706 let uefi_resource: Option<UefiResource> = with_uefi.then(|| UefiResource {
707 msvm_fd: ctx
708 .reqv(|v| crate::download_uefi_mu_msvm::Request::GetMsvmFd { arch, msvm_fd: v }),
709 });
710
711 let vtl0_kernel_resource = vtl0_kernel_type.map(|typ| {
712 let kernel = if let Vtl0KernelType::LocalOnlyCustom(path) = typ {
713 ReadVar::from_static(path)
714 } else {
715 match typ {
716 Vtl0KernelType::Example => ctx.reqv(|v| {
717 crate::resolve_openvmm_test_linux_kernel::Request::Get(
718 crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel,
719 arch,
720 crate::resolve_openvmm_test_linux_kernel::DEFAULT_LINUX_TEST_KERNEL_VERSION,
721 v,
722 )
723 }),
724 Vtl0KernelType::LocalOnlyCustom(_) => unreachable!("special cased above"),
725 }
726 };
727
728 let initrd = ctx.reqv(|v| {
729 crate::resolve_openvmm_test_initrd::Request::Get(arch, v)
730 });
731
732 Vtl0KernelResource { kernel, initrd }
733 });
734
735 let sidecar = if with_sidecar {
737 let sidecar_bin = if let Some(path) = custom_sidecar {
738 ctx.emit_rust_stepv("set custom_sidecar", |_ctx| {
739 |_rt| {
740 let fake_dbg_path = std::env::current_dir()?
741 .join("fake_sidecar.dbg")
742 .absolute()?;
743 fs_err::write(&fake_dbg_path, "")?;
744
745 Ok(SidecarOutput {
746 bin: path,
747 dbg: fake_dbg_path,
748 })
749 }
750 })
751 } else {
752 ctx.reqv(|v| crate::build_sidecar::Request {
753 build_params: crate::build_sidecar::SidecarBuildParams {
754 arch,
755 profile: match build_profile {
756 OpenvmmHclBuildProfile::Debug => {
757 crate::build_sidecar::SidecarBuildProfile::Debug
758 }
759 OpenvmmHclBuildProfile::Release
760 | OpenvmmHclBuildProfile::OpenvmmHclShip => {
761 crate::build_sidecar::SidecarBuildProfile::Release
762 }
763 },
764 },
765 sidecar: v,
766 })
767 };
768 Some(sidecar_bin)
769 } else {
770 None
771 };
772
773 let openvmm_hcl = if let Some(ref path) = custom_openvmm_hcl {
775 let path = path.clone();
776 ctx.emit_rust_stepv("set custom_openvmm_hcl", |_ctx| {
777 |_rt| {
778 Ok(OpenvmmHclOutput {
779 bin: path,
780 dbg: None,
781 })
782 }
783 })
784 } else {
785 ctx.reqv(|v| {
786 crate::build_openvmm_hcl::Request {
787 build_params: crate::build_openvmm_hcl::OpenvmmHclBuildParams {
788 target: target.clone(),
789 profile: build_profile,
790 features: openvmm_hcl_features,
791 no_split_dbg_info: true,
793 max_trace_level,
794 },
795 openvmm_hcl_output: v,
796 }
797 })
798 };
799
800 let igvmfilegen_arch: CommonArch = ctx.arch().try_into()?;
802
803 let igvmfilegen = ctx.reqv(|v| crate::build_igvmfilegen::Request {
804 build_params: crate::build_igvmfilegen::IgvmfilegenBuildParams {
805 target: CommonTriple::Common {
806 arch: igvmfilegen_arch,
807 platform: CommonPlatform::LinuxGnu,
808 },
809 profile: BuildProfile::Light,
810 },
811 igvmfilegen: v,
812 });
813
814 let openhcl_boot = if let Some(path) = custom_openhcl_boot {
816 ctx.emit_rust_stepv("set custom_openhcl_boot", |_ctx| {
817 |_rt| {
818 let fake_dbg_path = std::env::current_dir()?.join("fake.dbg").absolute()?;
819 fs_err::write(&fake_dbg_path, "")?;
820
821 Ok(OpenhclBootOutput {
822 bin: path,
823 dbg: fake_dbg_path,
824 })
825 }
826 })
827 } else {
828 ctx.reqv(|v| crate::build_openhcl_boot::Request {
829 build_params: crate::build_openhcl_boot::OpenhclBootBuildParams {
830 arch,
831 profile: match build_profile {
832 OpenvmmHclBuildProfile::Debug => {
833 crate::build_openhcl_boot::OpenhclBootBuildProfile::Debug
834 }
835 OpenvmmHclBuildProfile::Release
836 | OpenvmmHclBuildProfile::OpenvmmHclShip => {
837 crate::build_openhcl_boot::OpenhclBootBuildProfile::Release
838 }
839 },
840 },
841 openhcl_boot: v,
842 })
843 };
844
845 let use_stripped_openvmm_hcl = {
846 if custom_openvmm_hcl.is_some() {
847 false
850 } else {
851 !openvmm_hcl_no_strip
852 }
853 };
854
855 let openvmm_hcl = if use_stripped_openvmm_hcl {
857 let (read, write) = ctx.new_var();
858 let (read_dbg, write_dbg) = ctx.new_var();
859
860 let in_bin = openvmm_hcl.map(ctx, |o| o.bin);
861 ctx.req(crate::run_split_debug_info::Request {
862 arch,
863 in_bin,
864 out_bin: write,
865 out_dbg_info: write_dbg,
866 reproducible_without_debuglink: matches!(
867 ctx.platform(),
868 FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix)
869 ),
870 });
871
872 read.zip(ctx, read_dbg)
873 .map(ctx, |(bin, dbg)| OpenvmmHclOutput {
874 bin,
875 dbg: Some(dbg),
876 })
877 } else {
878 openvmm_hcl
879 };
880
881 let initrd = {
882 let rootfs_config = [openvmm_repo_path.map(ctx, |p| p.join("openhcl/rootfs.config"))]
883 .into_iter()
884 .chain(
885 custom_extra_rootfs
886 .into_iter()
887 .map(|p| ReadVar::from_static(p)),
888 )
889 .collect();
890 let openvmm_hcl_bin = openvmm_hcl.map(ctx, |o| o.bin);
891
892 ctx.reqv(|v| crate::build_openhcl_initrd::Request {
893 interactive: with_interactive,
894 arch,
895 extra_params: openhcl_initrd_extra_params,
896 rootfs_config,
897 extra_env: None,
898 kernel_package_root: vtl2_kernel_package_root.clone(),
899 kernel_modules: vtl2_kernel_modules,
900 kernel_metadata: vtl2_kernel_metadata,
901 bin_openhcl: openvmm_hcl_bin,
902 initrd: v,
903 })
904 };
905
906 let kernel = if let Some(path) = custom_kernel {
907 ReadVar::from_static(path)
908 } else {
909 ctx.reqv(
910 |v| crate::resolve_openhcl_kernel_package::Request::GetKernel {
911 kind: kernel_kind,
912 arch,
913 kernel: v,
914 },
915 )
916 };
917
918 let sidecar_bin = sidecar.clone().map(|x| x.map(ctx, |y| y.bin));
919 let openhcl_boot_bin = openhcl_boot.map(ctx, |x| x.bin);
920 let resources = ctx.emit_minor_rust_stepv("enumerate igvm resources", |ctx| {
921 claim_vars!(
922 ctx,
923 (
924 initrd,
925 kernel,
926 openhcl_boot_bin,
927 sidecar_bin,
928 uefi_resource,
929 vtl0_kernel_resource
930 )
931 );
932 |rt| {
933 let mut resources = BTreeMap::<ResourceType, PathBuf>::new();
934 resources.insert(ResourceType::UnderhillKernel, rt.read(kernel));
935 resources.insert(ResourceType::UnderhillInitrd, rt.read(initrd).initrd);
936 resources.insert(ResourceType::OpenhclBoot, rt.read(openhcl_boot_bin));
937 if let Some(sidecar_bin) = sidecar_bin {
938 resources.insert(ResourceType::UnderhillSidecar, rt.read(sidecar_bin));
939 }
940 if let Some(uefi_resource) = uefi_resource {
941 uefi_resource.add_to_resources(&mut resources, rt);
942 }
943 if let Some(vtl0_kernel_resource) = vtl0_kernel_resource {
944 vtl0_kernel_resource.add_to_resources(&mut resources, rt);
945 }
946 resources
947 }
948 });
949
950 let igvmfilegen = igvmfilegen.map(ctx, |o| match o {
951 crate::build_igvmfilegen::IgvmfilegenOutput::LinuxBin { bin, dbg: _ } => bin,
952 crate::build_igvmfilegen::IgvmfilegenOutput::WindowsBin { exe, pdb: _ } => exe,
953 });
954
955 let manifest = match igvm_manifest {
956 IgvmManifestPath::InTree(path) => {
957 openvmm_repo_path.map(ctx, |p| p.join("vm/loader/manifests").join(path))
958 }
959 IgvmManifestPath::LocalOnlyCustom(p) => ReadVar::from_static(p),
960 };
961
962 let igvm = ctx.reqv(|v| crate::run_igvmfilegen::Request {
963 igvmfilegen,
964 manifest,
965 resources,
966 disable_secure_avic,
967 confidential_debug,
968 add_temp_snp_id_block: true,
972 igvm: v,
973 });
974
975 let igvm_map = igvm.map(ctx, |x| x.igvm_map);
976 ctx.emit_minor_rust_step("construct openhcl extras", move |ctx| {
977 claim_vars!(
978 ctx,
979 (
980 openhcl_boot,
981 openvmm_hcl,
982 sidecar,
983 igvm_map,
984 openhcl_igvm_extras
985 )
986 );
987
988 |rt| {
989 read_vars!(rt, (openhcl_boot, openvmm_hcl, sidecar, igvm_map));
990
991 rt.write(
992 openhcl_igvm_extras,
993 &OpenhclIgvmExtrasOutput {
994 openhcl_boot,
995 openvmm_hcl,
996 sidecar,
997 igvm_map,
998 },
999 );
1000 }
1001 });
1002
1003 igvm.write_into_with(ctx, openhcl_igvm, move |igvm| {
1004 OpenhclIgvmOutput::new(recipe.recipe(), igvm)
1005 });
1006
1007 Ok(())
1008 }
1009}
1010
1011#[derive(Debug)]
1012pub struct UefiResource<C = VarNotClaimed> {
1013 pub msvm_fd: ReadVar<PathBuf, C>,
1014}
1015
1016impl ClaimVar for UefiResource {
1017 type Claimed = UefiResource<VarClaimed>;
1018
1019 fn claim(self, ctx: &mut StepCtx<'_>) -> UefiResource<VarClaimed> {
1020 UefiResource {
1021 msvm_fd: self.msvm_fd.claim(ctx),
1022 }
1023 }
1024}
1025
1026impl UefiResource<VarClaimed> {
1027 pub fn add_to_resources(
1028 self,
1029 resources: &mut BTreeMap<ResourceType, PathBuf>,
1030 rt: &mut RustRuntimeServices<'_>,
1031 ) {
1032 let path = rt.read(self.msvm_fd);
1033 resources.insert(ResourceType::Uefi, path);
1034 }
1035}
1036
1037pub struct Vtl0KernelResource<C = VarNotClaimed> {
1038 pub kernel: ReadVar<PathBuf, C>,
1039 pub initrd: ReadVar<PathBuf, C>,
1040}
1041
1042impl ClaimVar for Vtl0KernelResource {
1043 type Claimed = Vtl0KernelResource<VarClaimed>;
1044
1045 fn claim(self, ctx: &mut StepCtx<'_>) -> Vtl0KernelResource<VarClaimed> {
1046 Vtl0KernelResource {
1047 kernel: self.kernel.claim(ctx),
1048 initrd: self.initrd.claim(ctx),
1049 }
1050 }
1051}
1052
1053impl Vtl0KernelResource<VarClaimed> {
1054 pub fn add_to_resources(
1055 self,
1056 resources: &mut BTreeMap<ResourceType, PathBuf>,
1057 rt: &mut RustRuntimeServices<'_>,
1058 ) {
1059 let kernel = rt.read(self.kernel);
1060 let initrd = rt.read(self.initrd);
1061 resources.insert(ResourceType::LinuxKernel, kernel);
1062 resources.insert(ResourceType::LinuxInitrd, initrd);
1063 }
1064}