flowey_lib_hvlite/
artifact_openhcl_igvm_from_recipe.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Artifact: A collection of OpenHCL IGVM files.
5
6use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
7
8// marked pub(crate), as this is shared with sibling _from_recipe artifacts
9pub(crate) fn recipe_to_filename(flavor: &OpenhclIgvmRecipe) -> &str {
10    match flavor {
11        OpenhclIgvmRecipe::X64 => "openhcl",
12        OpenhclIgvmRecipe::X64Devkern => "openhcl-dev",
13        OpenhclIgvmRecipe::X64TestLinuxDirect => "openhcl-direct",
14        OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => "openhcl-direct-dev",
15        OpenhclIgvmRecipe::X64Cvm => "openhcl-cvm",
16        OpenhclIgvmRecipe::X64CvmDevkern => "openhcl-cvm-dev",
17        OpenhclIgvmRecipe::Aarch64 => "openhcl-aarch64",
18        OpenhclIgvmRecipe::Aarch64Devkern => "openhcl-aarch64-dev",
19        OpenhclIgvmRecipe::LocalOnlyCustom(_) => unreachable!(),
20    }
21}
22
23// marked pub(crate), as this is shared with sibling _from_recipe artifacts
24pub(crate) fn filename_to_recipe(filename: &str) -> Option<OpenhclIgvmRecipe> {
25    let ret = match filename {
26        "openhcl" => OpenhclIgvmRecipe::X64,
27        "openhcl-dev" => OpenhclIgvmRecipe::X64Devkern,
28        "openhcl-direct" => OpenhclIgvmRecipe::X64TestLinuxDirect,
29        "openhcl-direct-dev" => OpenhclIgvmRecipe::X64TestLinuxDirectDevkern,
30        "openhcl-cvm" => OpenhclIgvmRecipe::X64Cvm,
31        "openhcl-cvm-dev" => OpenhclIgvmRecipe::X64CvmDevkern,
32        "openhcl-aarch64" => OpenhclIgvmRecipe::Aarch64,
33        "openhcl-aarch64-dev" => OpenhclIgvmRecipe::Aarch64Devkern,
34        _ => return None,
35    };
36
37    Some(ret)
38}
39
40/// Publish the artifact.
41pub mod publish {
42    use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
43    use crate::run_igvmfilegen::IgvmOutput;
44    use flowey::node::prelude::*;
45
46    flowey_request! {
47        pub struct Request {
48            pub openhcl_igvm_files: Vec<ReadVar<(OpenhclIgvmRecipe, IgvmOutput)>>,
49            pub artifact_dir: ReadVar<PathBuf>,
50            pub done: WriteVar<SideEffect>,
51        }
52    }
53
54    new_simple_flow_node!(struct Node);
55
56    impl SimpleFlowNode for Node {
57        type Request = Request;
58
59        fn imports(ctx: &mut ImportCtx<'_>) {
60            ctx.import::<flowey_lib_common::copy_to_artifact_dir::Node>();
61        }
62
63        fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
64            let Request {
65                openhcl_igvm_files,
66                artifact_dir,
67                done,
68            } = request;
69
70            let files = ctx.emit_minor_rust_stepv("describe OpenHCL igvm artifact", |ctx| {
71                let openhcl_igvm_files = openhcl_igvm_files.claim(ctx);
72                |rt| {
73                    let mut files = Vec::new();
74                    for igvm in openhcl_igvm_files {
75                        let (recipe, igvm) = rt.read(igvm);
76
77                        let IgvmOutput {
78                            igvm_bin,
79                            igvm_tdx_json,
80                            igvm_snp_json,
81                            igvm_vbs_json,
82                            ..
83                        } = igvm;
84                        files.push((
85                            format!("{}.bin", super::recipe_to_filename(&recipe)).into(),
86                            igvm_bin,
87                        ));
88
89                        if let Some(igvm_tdx_json) = igvm_tdx_json {
90                            files.push((
91                                format!("{}-tdx.json", super::recipe_to_filename(&recipe)).into(),
92                                igvm_tdx_json,
93                            ));
94                        }
95
96                        if let Some(igvm_snp_json) = igvm_snp_json {
97                            files.push((
98                                format!("{}-snp.json", super::recipe_to_filename(&recipe)).into(),
99                                igvm_snp_json,
100                            ));
101                        }
102
103                        if let Some(igvm_vbs_json) = igvm_vbs_json {
104                            files.push((
105                                format!("{}-vbs.json", super::recipe_to_filename(&recipe)).into(),
106                                igvm_vbs_json,
107                            ));
108                        }
109                    }
110                    files
111                }
112            });
113
114            ctx.req(flowey_lib_common::copy_to_artifact_dir::Request {
115                debug_label: "OpenHCL igvm files".into(),
116                files,
117                artifact_dir,
118                done,
119            });
120
121            Ok(())
122        }
123    }
124}
125
126/// Resolve the contents of an existing artifact.
127pub mod resolve {
128    use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
129    use crate::run_igvmfilegen::IgvmOutput;
130    use flowey::node::prelude::*;
131
132    flowey_request! {
133        pub struct Request {
134            pub artifact_dir: ReadVar<PathBuf>,
135            pub igvm_files: WriteVar<Vec<(OpenhclIgvmRecipe, IgvmOutput)>>,
136        }
137    }
138
139    new_simple_flow_node!(struct Node);
140
141    impl SimpleFlowNode for Node {
142        type Request = Request;
143
144        fn imports(ctx: &mut ImportCtx<'_>) {
145            ctx.import::<flowey_lib_common::copy_to_artifact_dir::Node>();
146        }
147
148        fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
149            let Request {
150                artifact_dir,
151                igvm_files,
152            } = request;
153
154            ctx.emit_rust_step("resolve OpenHCL igvm artifact", |ctx| {
155                let artifact_dir = artifact_dir.claim(ctx);
156                let igvm_files = igvm_files.claim(ctx);
157                move |rt| {
158                    let artifact_dir = rt.read(artifact_dir);
159
160                    let mut files = Vec::new();
161                    for entry in fs_err::read_dir(&artifact_dir)? {
162                        let entry = entry?;
163                        if entry.file_type()?.is_dir() {
164                            anyhow::bail!("unexpected folder in root");
165                        }
166
167                        // For each binary in this directory, we create an IgvmOutput,
168                        // and search for accompanying json endoresements to include from
169                        // the same dir.
170                        let entry_file_name = entry
171                            .file_name()
172                            .into_string()
173                            .map_err(|_| anyhow::anyhow!("unexpected filename"))?;
174                        let entry_file_stem =
175                            if let Some(file_stem) = entry_file_name.strip_suffix(".bin") {
176                                file_stem
177                            } else {
178                                continue;
179                            };
180
181                        let Some(recipe) = super::filename_to_recipe(entry_file_stem) else {
182                            anyhow::bail!(
183                                "unexpected file in openhcl_igvm artifact folder: {}",
184                                entry.path().display()
185                            );
186                        };
187
188                        // For each openhcl bin, we search for corresponding tdx, snp, and vbs endorsements.
189                        let igvm_path_file_name = super::recipe_to_filename(&recipe);
190                        let igvm_tdx_json = {
191                            let path = artifact_dir.join(format!("{igvm_path_file_name}-tdx.json"));
192                            path.exists().then_some(path)
193                        };
194                        let igvm_snp_json = {
195                            let path = artifact_dir.join(format!("{igvm_path_file_name}-snp.json"));
196                            path.exists().then_some(path)
197                        };
198                        let igvm_vbs_json = {
199                            let path = artifact_dir.join(format!("{igvm_path_file_name}-vbs.json"));
200                            path.exists().then_some(path)
201                        };
202
203                        files.push((
204                            recipe,
205                            IgvmOutput {
206                                igvm_bin: entry.path(),
207                                igvm_map: None, // resolved through _extras
208                                igvm_tdx_json,
209                                igvm_snp_json,
210                                igvm_vbs_json,
211                            },
212                        ))
213                    }
214
215                    rt.write(igvm_files, &files);
216
217                    Ok(())
218                }
219            });
220
221            Ok(())
222        }
223    }
224}