Skip to main content

flowey_lib_hvlite/
download_uefi_mu_msvm.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Download pre-built mu_msvm package from its GitHub Release.
5
6use crate::common::CommonArch;
7use flowey::node::prelude::*;
8use std::collections::BTreeMap;
9
10/// Firmware core and toolchain used by the RELEASE build.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12pub enum FirmwareFlavor {
13    LegacyVs2022,
14    LegacyClangPdb,
15    PatinaClangPdb,
16}
17
18impl FirmwareFlavor {
19    fn default_for_arch(arch: CommonArch) -> Self {
20        match arch {
21            CommonArch::X86_64 | CommonArch::Aarch64 => Self::LegacyClangPdb,
22        }
23    }
24
25    fn file_name(self, arch: CommonArch) -> anyhow::Result<&'static str> {
26        Ok(match (self, arch) {
27            (Self::LegacyVs2022, CommonArch::X86_64) => "firmware-RELEASE-X64-VS2022.tar.gz",
28            (Self::LegacyVs2022, CommonArch::Aarch64) => {
29                anyhow::bail!("mu_msvm does not support AARCH64 with VS2022")
30            }
31            (Self::LegacyClangPdb, CommonArch::X86_64) => "firmware-RELEASE-X64-CLANGPDB.tar.gz",
32            (Self::LegacyClangPdb, CommonArch::Aarch64) => {
33                "firmware-RELEASE-AARCH64-CLANGPDB.tar.gz"
34            }
35            (Self::PatinaClangPdb, CommonArch::X86_64) => {
36                "firmware-RELEASE-X64-CLANGPDB-patina.tar.gz"
37            }
38            (Self::PatinaClangPdb, CommonArch::Aarch64) => {
39                "firmware-RELEASE-AARCH64-CLANGPDB-patina.tar.gz"
40            }
41        })
42    }
43}
44
45flowey_config! {
46    /// Config for the download_uefi_mu_msvm node.
47    pub struct Config {
48        /// Specify version of mu_msvm to use
49        pub version: Option<String>,
50        /// Use a local MSVM.fd path, keyed by architecture
51        pub local_paths: BTreeMap<CommonArch, ConfigVar<PathBuf>>,
52    }
53}
54
55flowey_request! {
56    pub enum Request {
57        /// Download the mu_msvm package for the given arch
58        GetMsvmFd {
59            arch: CommonArch,
60            flavor: Option<FirmwareFlavor>,
61            msvm_fd: WriteVar<PathBuf>
62        }
63    }
64}
65
66new_flow_node_with_config!(struct Node);
67
68impl FlowNodeWithConfig for Node {
69    type Request = Request;
70    type Config = Config;
71
72    fn imports(ctx: &mut ImportCtx<'_>) {
73        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
74        ctx.import::<flowey_lib_common::download_gh_release::Node>();
75    }
76
77    fn emit(
78        config: Config,
79        requests: Vec<Self::Request>,
80        ctx: &mut NodeCtx<'_>,
81    ) -> anyhow::Result<()> {
82        let version = config.version;
83        let local_paths = config.local_paths;
84        let mut reqs: BTreeMap<(CommonArch, FirmwareFlavor), Vec<WriteVar<PathBuf>>> =
85            BTreeMap::new();
86
87        for req in requests {
88            match req {
89                Request::GetMsvmFd {
90                    arch,
91                    flavor,
92                    msvm_fd,
93                } => reqs
94                    .entry((
95                        arch,
96                        flavor.unwrap_or_else(|| FirmwareFlavor::default_for_arch(arch)),
97                    ))
98                    .or_default()
99                    .push(msvm_fd),
100            }
101        }
102
103        if version.is_some() && !local_paths.is_empty() {
104            anyhow::bail!("Cannot specify both Version and LocalPath requests");
105        }
106
107        if version.is_none() && local_paths.is_empty() {
108            anyhow::bail!("Must specify a Version or LocalPath request");
109        }
110
111        // -- end of req processing -- //
112
113        if reqs.is_empty() {
114            return Ok(());
115        }
116
117        if !local_paths.is_empty() {
118            ctx.emit_rust_step("use local mu_msvm UEFI", |ctx| {
119                let reqs = reqs.claim(ctx);
120                let local_paths: BTreeMap<_, _> = local_paths
121                    .into_iter()
122                    .map(|(arch, var)| (arch, var.claim(ctx)))
123                    .collect();
124                move |rt| {
125                    for ((arch, _flavor), out_vars) in reqs {
126                        let msvm_fd_var = local_paths.get(&arch).ok_or_else(|| {
127                            anyhow::anyhow!("No local path specified for architecture {:?}", arch)
128                        })?;
129                        let msvm_fd = rt.read(msvm_fd_var.clone());
130                        for var in out_vars {
131                            log::info!(
132                                "using local uefi for {} at path {:?}",
133                                match arch {
134                                    CommonArch::X86_64 => "x64",
135                                    CommonArch::Aarch64 => "aarch64",
136                                },
137                                msvm_fd
138                            );
139                            rt.write(var, &msvm_fd);
140                        }
141                    }
142                    Ok(())
143                }
144            });
145
146            return Ok(());
147        }
148
149        let version = version.context("missing mu_msvm version")?;
150        let extract_archive_deps = flowey_lib_common::_util::extract::extract_zip_if_new_deps(ctx);
151
152        for ((arch, flavor), out_vars) in reqs {
153            let file_name = flavor.file_name(arch)?;
154
155            let mu_msvm_archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
156                repo_owner: crate::common::OPENVMM_GITHUB_OWNER.into(),
157                repo_name: "mu_msvm".into(),
158                needs_auth: false,
159                tag: format!("v{version}"),
160                file_name: file_name.into(),
161                path: v,
162            });
163
164            let archive_file_version = format!("{version}-{file_name}");
165
166            ctx.emit_rust_step(
167                {
168                    format!(
169                        "unpack mu_msvm package ({})",
170                        match arch {
171                            CommonArch::X86_64 => "x64",
172                            CommonArch::Aarch64 => "aarch64",
173                        },
174                    )
175                },
176                |ctx| {
177                    let extract_archive_deps = extract_archive_deps.clone().claim(ctx);
178                    let out_vars = out_vars.claim(ctx);
179                    let mu_msvm_archive = mu_msvm_archive.claim(ctx);
180                    move |rt| {
181                        let mu_msvm_archive = rt.read(mu_msvm_archive);
182
183                        let extract_dir = flowey_lib_common::_util::extract::extract_zip_if_new(
184                            rt,
185                            extract_archive_deps,
186                            &mu_msvm_archive,
187                            &archive_file_version,
188                        )?;
189
190                        let msvm_fd = extract_dir.join("FV/MSVM.fd");
191
192                        for var in out_vars {
193                            rt.write(var, &msvm_fd)
194                        }
195
196                        Ok(())
197                    }
198                },
199            );
200        }
201
202        Ok(())
203    }
204}