Skip to main content

flowey_lib_hvlite/
build_vmfirmwareigvm_dll.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Build an instance of `vmfirmwareigvm.dll`
5
6use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmOutput;
7use crate::common::CommonArch;
8use crate::common::CommonTriple;
9use flowey::node::prelude::*;
10use std::collections::BTreeMap;
11
12pub const UNUSED_DLL_VERSION: (u16, u16, u16, u16) = (0, 0, 0, 0);
13pub const SNP_RESOURCE_ID: u32 = 13515;
14
15#[derive(Serialize, Deserialize)]
16pub struct VmfirmwareigvmDllOutput {
17    #[serde(rename = "vmfirmwareigvm.dll")]
18    pub dll: PathBuf,
19}
20
21impl Artifact for VmfirmwareigvmDllOutput {}
22
23flowey_request! {
24    pub struct Request {
25        pub arch: CommonArch,
26        pub openhcl_igvm: ReadVar<OpenhclIgvmOutput>,
27        /// ID to store the IGVM under within the `VMFW` resource type.
28        pub resource_id: u32,
29        /// (major, minor, patch, revision)
30        pub dll_version: ReadVar<(u16, u16, u16, u16)>,
31        pub internal_dll_name: String,
32        pub vmfirmwareigvm_dll: WriteVar<VmfirmwareigvmDllOutput>,
33    }
34}
35
36new_simple_flow_node!(struct Node);
37
38impl SimpleFlowNode for Node {
39    type Request = Request;
40
41    fn imports(ctx: &mut ImportCtx<'_>) {
42        ctx.import::<crate::run_cargo_build::Node>();
43        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
44    }
45
46    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
47        let Request {
48            arch,
49            openhcl_igvm,
50            resource_id,
51            internal_dll_name,
52            dll_version,
53            vmfirmwareigvm_dll,
54        } = request;
55
56        let igvm_bin = openhcl_igvm.map(ctx, |o| o.igvm_bin().to_path_buf());
57
58        let mut pre_build_deps = Vec::new();
59        let mut cross_env = BTreeMap::new();
60        if let FlowPlatform::Linux(distro) = ctx.platform() {
61            if distro != FlowPlatformLinuxDistro::Nix {
62                pre_build_deps.push(ctx.reqv(|v| {
63                    flowey_lib_common::install_dist_pkg::Request::Install {
64                        package_names: vec!["clang".into(), "llvm".into(), "lld".into()],
65                        done: v,
66                    }
67                }));
68            }
69
70            // Resource-only DLLs need no Windows SDK or runtime libraries.
71            let target = CommonTriple::Common {
72                arch,
73                platform: crate::common::CommonPlatform::WindowsMsvc,
74            }
75            .as_triple()
76            .to_string()
77            .replace('-', "_");
78            cross_env.insert(format!("CC_{target}"), "clang".into());
79            cross_env.insert(format!("RC_{target}"), "llvm-rc".into());
80            cross_env.insert(
81                format!("CARGO_TARGET_{}_LINKER", target.to_uppercase()),
82                "lld-link".into(),
83            );
84        }
85
86        let extra_env = ctx.emit_rust_stepv("determine vmfirmwareigvm_dll env vars", |ctx| {
87            let igvm_bin = igvm_bin.claim(ctx);
88            let dll_version = dll_version.claim(ctx);
89            move |rt| {
90                let mut extra_env = cross_env;
91
92                // set the various build-time env vars `vmfirmwareigvm_dll` expects
93                {
94                    extra_env.insert("UH_DLL_NAME".into(), internal_dll_name);
95                    extra_env.insert(
96                        "UH_IGVM_PATH".into(),
97                        // rc.exe treats '\' in windows paths as escape sequences.
98                        // there is likely a more robust solution to fix this, but
99                        // a simple swap from '\' to '/' seems to work fine for now
100                        rt.read(igvm_bin)
101                            .absolute()
102                            .context("Failed to make igvm bin path absolute")?
103                            .display()
104                            .to_string()
105                            .replace('\\', "/"),
106                    );
107                    extra_env.insert("UH_RESOURCE_ID".into(), resource_id.to_string());
108                    let (major, minor, patch, revision) = rt.read(dll_version);
109                    extra_env.insert("UH_MAJOR".into(), major.to_string());
110                    extra_env.insert("UH_MINOR".into(), minor.to_string());
111                    extra_env.insert("UH_PATCH".into(), patch.to_string());
112                    extra_env.insert("UH_REVISION".into(), revision.to_string());
113                }
114
115                // workaround for the fact that hvlite's root-level `.cargo/config.toml`
116                // currently sets a bunch of extraneous linker flags via
117                //
118                // [target.'cfg(all(windows, target_env = "msvc"))']
119                extra_env.insert("RUSTFLAGS".into(), "".into());
120
121                Ok(extra_env)
122            }
123        });
124
125        let output = ctx.reqv(|v| crate::run_cargo_build::Request {
126            crate_name: "vmfirmwareigvm_dll".into(),
127            out_name: "vmfirmwareigvm_dll".into(),
128            crate_type: flowey_lib_common::run_cargo_build::CargoCrateType::DynamicLib,
129            profile: crate::run_cargo_build::BuildProfile::Release,
130            features: Default::default(),
131            target: CommonTriple::Common {
132                arch,
133                platform: crate::common::CommonPlatform::WindowsMsvc,
134            }
135            .as_triple(),
136            no_split_dbg_info: false,
137            extra_env: Some(extra_env),
138            pre_build_deps,
139            output: v,
140        });
141
142        ctx.emit_minor_rust_step("report built vmfirmwareigvm_dll", |ctx| {
143            let vmfirmwareigvm_dll = vmfirmwareigvm_dll.claim(ctx);
144            let output = output.claim(ctx);
145            move |rt| {
146                let output = match rt.read(output) {
147                    crate::run_cargo_build::CargoBuildOutput::WindowsDynamicLib {
148                        dll,
149                        // this is a resource dll, so these don't matter
150                        dll_lib: _,
151                        pdb: _,
152                    } => VmfirmwareigvmDllOutput { dll },
153                    _ => unreachable!(),
154                };
155
156                rt.write(vmfirmwareigvm_dll, &output);
157            }
158        });
159
160        Ok(())
161    }
162}