microsoft/openvmm
Publicmirrored from https://github.com/microsoft/openvmmAvailable
flowey/flowey_lib_hvlite/src/artifact_openhcl_igvm_from_recipe.rs
224lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | //! Artifact: A collection of OpenHCL IGVM files. |
| 5 | |
| 6 | use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe; |
| 7 | |
| 8 | // marked pub(crate), as this is shared with sibling _from_recipe artifacts |
| 9 | pub(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 |
| 24 | pub(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. |
| 41 | pub 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. |
| 127 | pub 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 | } |
| 225 | |