Skip to main content

flowey_lib_hvlite/
build_openvmm_hcl.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Build `openvmm_hcl` binaries (NOT IGVM FILES!)
5
6use crate::common::CommonArch;
7use crate::common::CommonTriple;
8use flowey::node::prelude::*;
9use flowey_lib_common::run_cargo_build::CargoFeatureSet;
10use std::collections::BTreeMap;
11use std::collections::BTreeSet;
12
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
14pub enum OpenvmmHclFeature {
15    Gdb,
16    MiSecure,
17    Tpm,
18    ProductPolicy,
19    LocalOnlyCustom(String),
20}
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
23pub enum OpenvmmHclBuildProfile {
24    Debug,
25    Release,
26    OpenvmmHclShip,
27}
28
29#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
30pub enum MaxTraceLevel {
31    Trace,
32    Debug,
33    Info,
34    Warn,
35    Error,
36    Off,
37}
38
39impl MaxTraceLevel {
40    pub fn features(&self) -> Vec<String> {
41        let name = match self {
42            MaxTraceLevel::Trace => return Vec::new(),
43            MaxTraceLevel::Debug => "debug",
44            MaxTraceLevel::Info => "info",
45            MaxTraceLevel::Warn => "warn",
46            MaxTraceLevel::Error => "error",
47            MaxTraceLevel::Off => "off",
48        };
49        // Add both release and non-release variants of the feature
50        // regardless of the profile to work around `tracing` bugs.
51        vec![
52            format!("tracing/max_level_{}", name),
53            format!("tracing/release_max_level_{}", name),
54        ]
55    }
56}
57
58#[derive(Serialize, Deserialize)]
59pub struct OpenvmmHclOutput {
60    #[serde(rename = "openvmm_hcl")]
61    pub bin: PathBuf,
62    #[serde(rename = "openvmm_hcl.dbg")]
63    pub dbg: Option<PathBuf>,
64}
65
66impl Artifact for OpenvmmHclOutput {}
67
68#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
69pub struct OpenvmmHclBuildParams {
70    pub target: CommonTriple,
71    pub profile: OpenvmmHclBuildProfile,
72    pub features: BTreeSet<OpenvmmHclFeature>,
73    pub no_split_dbg_info: bool,
74    pub max_trace_level: MaxTraceLevel,
75}
76
77flowey_request! {
78    pub struct Request {
79        pub build_params: OpenvmmHclBuildParams,
80        pub openvmm_hcl_output: WriteVar<OpenvmmHclOutput>,
81    }
82}
83
84new_flow_node!(struct Node);
85
86impl FlowNode for Node {
87    type Request = Request;
88
89    fn imports(ctx: &mut ImportCtx<'_>) {
90        ctx.import::<crate::run_cargo_build::Node>();
91        ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
92    }
93
94    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
95        // de-dupe incoming requests
96        let requests = requests
97            .into_iter()
98            .fold(BTreeMap::<_, Vec<_>>::new(), |mut m, r| {
99                let Request {
100                    build_params,
101                    openvmm_hcl_output,
102                } = r;
103                m.entry(build_params).or_default().push(openvmm_hcl_output);
104                m
105            });
106
107        // -- end of req processing -- //
108
109        for (
110            OpenvmmHclBuildParams {
111                target,
112                profile,
113                features,
114                no_split_dbg_info,
115                max_trace_level,
116            },
117            outvars,
118        ) in requests
119        {
120            let mut pre_build_deps = Vec::new();
121
122            let target = target.as_triple();
123
124            let arch = CommonArch::from_triple(&target)
125                .with_context(|| format!("cannot build openvmm_hcl on {}", target.architecture))?;
126
127            let openhcl_deps_path = ctx
128                .reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request { arch, path: v });
129
130            // required due to ambient dependencies in openvmm_hcl's source code
131            pre_build_deps.push(openhcl_deps_path.into_side_effect());
132
133            let mut features = features
134                .into_iter()
135                .map(|f| match f {
136                    OpenvmmHclFeature::Gdb => "gdb".into(),
137                    OpenvmmHclFeature::MiSecure => "mi-secure".into(),
138                    OpenvmmHclFeature::Tpm => "tpm".into(),
139                    OpenvmmHclFeature::ProductPolicy => "product_policy".into(),
140                    OpenvmmHclFeature::LocalOnlyCustom(s) => s,
141                })
142                .collect::<Vec<String>>();
143
144            features.extend(max_trace_level.features());
145
146            // Forbid cc-rs and CMake from compiling anything for the openvmm_hcl build.
147            // Every C library it links comes prebuilt out of the openvmm-deps
148            // sdk sysroot, so a build script reaching for either is a bug.
149            let extra_env = Some(ReadVar::from_static(
150                [
151                    ("CC_FORCE_DISABLE".to_string(), "1".to_string()),
152                    (
153                        "CMAKE".to_string(),
154                        "cmake-is-forbidden-during-openvmm-hcl-build".to_string(),
155                    ),
156                ]
157                .into_iter()
158                .collect(),
159            ));
160
161            let output = ctx.reqv(|v| crate::run_cargo_build::Request {
162                crate_name: "openvmm_hcl".into(),
163                out_name: "openvmm_hcl".into(),
164                crate_type: flowey_lib_common::run_cargo_build::CargoCrateType::Bin,
165                profile: match profile {
166                    OpenvmmHclBuildProfile::Debug => crate::run_cargo_build::BuildProfile::Debug,
167                    OpenvmmHclBuildProfile::Release => {
168                        crate::run_cargo_build::BuildProfile::Release
169                    }
170                    OpenvmmHclBuildProfile::OpenvmmHclShip => {
171                        crate::run_cargo_build::BuildProfile::UnderhillShip
172                    }
173                },
174                features: CargoFeatureSet::Specific(features),
175                target,
176                no_split_dbg_info,
177                extra_env,
178                pre_build_deps,
179                output: v,
180            });
181
182            ctx.emit_minor_rust_step("report built openvmm_hcl", |ctx| {
183                let outvars = outvars.claim(ctx);
184                let output = output.claim(ctx);
185                move |rt| {
186                    let output = match rt.read(output) {
187                        crate::run_cargo_build::CargoBuildOutput::ElfBin { bin, dbg } => {
188                            OpenvmmHclOutput { bin, dbg }
189                        }
190                        _ => unreachable!(),
191                    };
192
193                    for var in outvars {
194                        rt.write(var, &output);
195                    }
196                }
197            });
198        }
199
200        Ok(())
201    }
202}