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