Skip to main content

flowey_lib_hvlite/
run_igvmfilegen.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Raw bindings to `igvmfilegen`, used to build an igvm file from a manifest +
5//! set of resources.
6
7use flowey::node::prelude::*;
8use igvmfilegen_config::ResourceType;
9use std::collections::BTreeMap;
10
11#[derive(Serialize, Deserialize)]
12pub struct IgvmOutput {
13    pub igvm_bin: PathBuf,
14    pub igvm_map: Option<PathBuf>,
15    pub igvm_tdx_json: Option<PathBuf>,
16    pub igvm_snp_json: Option<PathBuf>,
17    pub igvm_vbs_json: Option<PathBuf>,
18    /// The unsigned SNP ID block signing payload (`<base>-snp.idblock`), if the
19    /// manifest produced a measurable SEV-SNP platform.
20    pub igvm_snp_idblock: Option<PathBuf>,
21    pub igvm_tdx_corim: Option<PathBuf>,
22    pub igvm_snp_corim: Option<PathBuf>,
23    pub igvm_vbs_corim: Option<PathBuf>,
24}
25
26flowey_request! {
27    pub struct Request {
28        /// Path to igvmfilegen bin to use
29        pub igvmfilegen: ReadVar<PathBuf>,
30        /// IGVM manifest to build
31        pub manifest: ReadVar<PathBuf>,
32        /// Resources required by the provided IGVM manifest
33        pub resources: ReadVar<BTreeMap<ResourceType, PathBuf>>,
34        /// Whether to patch the manifest to set secure_avic to disabled
35        pub disable_secure_avic: bool,
36        /// Whether to add the confidential debug flag to the measured OpenHCL
37        /// command line, enabling confidential diagnostics on CVM builds even
38        /// in release builds.
39        pub confidential_debug: bool,
40        /// For SEV-SNP builds, add an SNP ID block signed by an ephemeral key
41        /// (via `igvmfilegen add-snp-id-block --manifest`). This restores the
42        /// pre-migration behavior for open-source builds, where every SNP IGVM
43        /// carried a temporary-key ID block so that `id_block_en = 1` at launch.
44        /// Production pipelines set this to `false` and instead add an ID block
45        /// signed with a real key out-of-band.
46        pub add_temp_snp_id_block: bool,
47        /// Output path of generated igvm file
48        pub igvm: WriteVar<IgvmOutput>,
49    }
50}
51
52new_simple_flow_node!(struct Node);
53
54impl SimpleFlowNode for Node {
55    type Request = Request;
56
57    fn imports(_ctx: &mut ImportCtx<'_>) {}
58
59    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
60        let Request {
61            igvmfilegen,
62            manifest,
63            resources,
64            disable_secure_avic,
65            confidential_debug,
66            add_temp_snp_id_block,
67            igvm,
68        } = request;
69
70        ctx.emit_rust_step("building igvm file", |ctx| {
71            let igvm = igvm.claim(ctx);
72            let igvmfilegen = igvmfilegen.claim(ctx);
73            let manifest = manifest.claim(ctx);
74            let resources = resources.claim(ctx);
75            move |rt| {
76                let igvmfilegen = rt.read(igvmfilegen);
77                let manifest = rt.read(manifest);
78                let resources = rt.read(resources);
79
80                let igvm_file_stem = "igvm";
81                let igvm_path = rt.sh.current_dir().join(format!("{igvm_file_stem}.bin"));
82                let resources_path = rt.sh.current_dir().join("igvm.json");
83
84                let resources = igvmfilegen_config::Resources::new(resources.into_iter().collect())
85                    .context("creating igvm resources")?;
86                std::fs::write(&resources_path, serde_json::to_string_pretty(&resources)?)
87                    .context("writing resources")?;
88
89                let mut cmd = flowey::shell_cmd!(
90                    rt,
91                    "{igvmfilegen} manifest
92                            -m {manifest}
93                            -r {resources_path}
94                            --debug-validation
95                            -o {igvm_path}
96                        "
97                );
98
99                if disable_secure_avic {
100                    cmd = cmd.arg("--disable-secure-avic");
101                }
102
103                if confidential_debug {
104                    cmd = cmd.arg("--confidential-debug");
105                }
106
107                cmd.run()?;
108
109                let igvm_map_path = igvm_path.with_extension("bin.map");
110                let igvm_map_path = igvm_map_path.exists().then_some(igvm_map_path);
111                let igvm_tdx_json = {
112                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-tdx.json"));
113                    path.exists().then_some(path)
114                };
115                let igvm_snp_json = {
116                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-snp.json"));
117                    path.exists().then_some(path)
118                };
119                let igvm_vbs_json = {
120                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-vbs.json"));
121                    path.exists().then_some(path)
122                };
123                let igvm_snp_idblock = {
124                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-snp.idblock"));
125                    path.exists().then_some(path)
126                };
127
128                // For open-source SEV-SNP builds, embed an ID block signed by an
129                // ephemeral key so the file launches with `id_block_en = 1`, as
130                // it did before the ID block was split into a separate step.
131                // The presence of `<stem>-snp.idblock` means the manifest built
132                // a measurable SNP platform.
133                if add_temp_snp_id_block && igvm_snp_idblock.is_some() {
134                    flowey::shell_cmd!(
135                        rt,
136                        "{igvmfilegen} add-snp-id-block
137                                --input {igvm_path}
138                                --output {igvm_path}
139                                --manifest {manifest}
140                            "
141                    )
142                    .run()?;
143                }
144                let igvm_tdx_corim = {
145                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-tdx.cbor"));
146                    path.exists().then_some(path)
147                };
148                let igvm_snp_corim = {
149                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-snp.cbor"));
150                    path.exists().then_some(path)
151                };
152                let igvm_vbs_corim = {
153                    let path = igvm_path.with_file_name(format!("{igvm_file_stem}-vbs.cbor"));
154                    path.exists().then_some(path)
155                };
156
157                rt.write(
158                    igvm,
159                    &IgvmOutput {
160                        igvm_bin: igvm_path,
161                        igvm_map: igvm_map_path,
162                        igvm_tdx_json,
163                        igvm_snp_json,
164                        igvm_vbs_json,
165                        igvm_snp_idblock,
166                        igvm_tdx_corim,
167                        igvm_snp_corim,
168                        igvm_vbs_corim,
169                    },
170                );
171
172                Ok(())
173            }
174        });
175
176        Ok(())
177    }
178}