flowey_lib_hvlite/
build_openhcl_initrd.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Wrapper around `update-rootfs.py`
5
6use crate::resolve_openvmm_deps::OpenvmmDepsArch;
7use crate::run_cargo_build::common::CommonArch;
8use flowey::node::prelude::*;
9use std::collections::BTreeMap;
10
11#[derive(Serialize, Deserialize)]
12pub struct OpenhclInitrdOutput {
13    pub initrd: PathBuf,
14}
15
16/// Extra parameters for building specialized initrd files.
17#[derive(Default, Clone, Serialize, Deserialize, Debug)]
18pub struct OpenhclInitrdExtraParams {
19    /// additional layers to be included in the initrd
20    pub extra_initrd_layers: Vec<PathBuf>,
21    /// additional directories to be included in the initrd
22    pub extra_initrd_directories: Vec<PathBuf>,
23}
24
25flowey_request! {
26    pub struct Request {
27        pub arch: CommonArch,
28        /// include --interactive tools
29        pub interactive: bool,
30        /// Extra parameters for building specialized initrd files.
31        pub extra_params: Option<OpenhclInitrdExtraParams>,
32        /// Paths to rootfs.config files
33        pub rootfs_config: Vec<ReadVar<PathBuf>>,
34        /// Extra environment variables to set during the run (e.g: to
35        /// interpolate paths into `rootfs.config`)
36        pub extra_env: Option<ReadVar<BTreeMap<String, String>>>,
37        /// Path to kernel package (for metadata)
38        pub kernel_package_root: ReadVar<PathBuf>,
39        /// Path to kernel modules directory
40        pub kernel_modules: ReadVar<PathBuf>,
41        /// Path to kernel build metadata file
42        pub kernel_metadata: ReadVar<PathBuf>,
43        /// Path to the openhcl bin to use
44        pub bin_openhcl: ReadVar<PathBuf>,
45        /// Output path of generated initrd
46        pub initrd: WriteVar<OpenhclInitrdOutput>,
47    }
48}
49
50new_flow_node!(struct Node);
51
52impl FlowNode for Node {
53    type Request = Request;
54
55    fn imports(ctx: &mut ImportCtx<'_>) {
56        ctx.import::<crate::resolve_openvmm_deps::Node>();
57        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
58        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
59    }
60
61    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
62        // ambient deps required by `update-rootfs.py`
63        let platform = ctx.platform();
64        let python_pkg = match platform {
65            FlowPlatform::Linux(linux_distribution) => match linux_distribution {
66                FlowPlatformLinuxDistro::Fedora
67                | FlowPlatformLinuxDistro::Ubuntu
68                | FlowPlatformLinuxDistro::AzureLinux
69                | FlowPlatformLinuxDistro::Nix => "python3",
70                FlowPlatformLinuxDistro::Arch => "python",
71                FlowPlatformLinuxDistro::Unknown => anyhow::bail!("Unknown Linux distribution"),
72            },
73            _ => anyhow::bail!("Unsupported platform"),
74        };
75
76        // Don't install python when using Nix
77        let pydeps = if !matches!(platform, FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix)) {
78            Some(ctx.reqv(
79                |side_effect| flowey_lib_common::install_dist_pkg::Request::Install {
80                    package_names: [python_pkg].map(Into::into).into(),
81                    done: side_effect,
82                },
83            ))
84        } else {
85            None
86        };
87
88        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
89
90        for Request {
91            arch,
92            extra_params,
93            rootfs_config,
94            kernel_package_root,
95            kernel_modules,
96            kernel_metadata,
97            extra_env,
98            bin_openhcl,
99            initrd,
100            interactive,
101        } in requests
102        {
103            let OpenhclInitrdExtraParams {
104                extra_initrd_layers,
105                extra_initrd_directories,
106            } = extra_params.unwrap_or_default();
107
108            let openvmm_deps_arch = match arch {
109                CommonArch::X86_64 => OpenvmmDepsArch::X86_64,
110                CommonArch::Aarch64 => OpenvmmDepsArch::Aarch64,
111            };
112
113            let interactive_dep = if interactive {
114                ctx.reqv(|v| {
115                    crate::resolve_openvmm_deps::Request::GetOpenhclCpioDbgrd(openvmm_deps_arch, v)
116                })
117            } else {
118                ctx.reqv(|v| {
119                    crate::resolve_openvmm_deps::Request::GetOpenhclCpioShell(openvmm_deps_arch, v)
120                })
121            };
122
123            if rootfs_config.is_empty() {
124                anyhow::bail!("no rootfs files provided");
125            }
126
127            ctx.emit_rust_step("building openhcl initrd", |ctx| {
128                pydeps.clone().claim(ctx);
129                let interactive_dep = interactive_dep.claim(ctx);
130                let rootfs_config = rootfs_config.claim(ctx);
131                let extra_env = extra_env.claim(ctx);
132                let bin_openhcl = bin_openhcl.claim(ctx);
133                let openvmm_repo_path = openvmm_repo_path.clone().claim(ctx);
134                let kernel_package_root = kernel_package_root.claim(ctx);
135                let kernel_modules = kernel_modules.claim(ctx);
136                let kernel_metadata = kernel_metadata.claim(ctx);
137                let initrd = initrd.claim(ctx);
138                move |rt| {
139                    let interactive_dep = rt.read(interactive_dep);
140                    let rootfs_config = rt.read(rootfs_config);
141                    let extra_env = rt.read(extra_env);
142                    let bin_openhcl = rt.read(bin_openhcl);
143                    let openvmm_repo_path = rt.read(openvmm_repo_path);
144                    let kernel_package_root = rt.read(kernel_package_root);
145                    let kernel_metadata = rt.read(kernel_metadata);
146
147                    let initrd_path = rt.sh.current_dir().join("openhcl.cpio.gz");
148
149                    let initrd_contents = {
150                        let mut v = Vec::new();
151
152                        if interactive {
153                            // for busybox, gdbserver, and perf
154                            v.push("--interactive".to_string());
155                        } else {
156                            // just a minimal shell
157                            v.push("--min-interactive".to_string());
158                        }
159
160                        for dir in extra_initrd_layers {
161                            v.push("--layer".into());
162                            v.push(dir.display().to_string());
163                        }
164
165                        for dir in extra_initrd_directories {
166                            v.push("--add-dir".into());
167                            v.push(dir.display().to_string());
168                        }
169
170                        v
171                    };
172
173                    let rootfs_py_arch = match arch {
174                        CommonArch::X86_64 => "x86_64",
175                        CommonArch::Aarch64 => "aarch64",
176                    };
177
178                    let kernel_modules = rt.read(kernel_modules);
179
180                    // FUTURE: to avoid making big changes to update-roots as
181                    // part of the initial OSS workstream, stage the
182                    // interactive-layer packages in the same folder structure
183                    // as the closed-source openhcl-deps.
184                    match arch {
185                        CommonArch::X86_64 => {
186                            rt.sh
187                                .set_var("OPENVMM_DEPS_X64", interactive_dep.parent().unwrap());
188                        }
189                        CommonArch::Aarch64 => {
190                            rt.sh
191                                .set_var("OPENVMM_DEPS_AARCH64", interactive_dep.parent().unwrap());
192                        }
193                    }
194
195                    for (k, v) in extra_env.into_iter().flatten() {
196                        rt.sh.set_var(k, v);
197                    }
198
199                    // FIXME: update-rootfs.py invokes git to obtain a version
200                    // hash to stuff into the initrd.
201                    rt.sh.change_dir(openvmm_repo_path);
202
203                    let rootfs_config = rootfs_config
204                        .iter()
205                        .flat_map(|x| ["--rootfs-config".as_ref(), x.as_os_str()]);
206
207                    flowey::shell_cmd!(
208                        rt,
209                        "python3 openhcl/update-rootfs.py
210                            {bin_openhcl}
211                            {initrd_path}
212                            --arch {rootfs_py_arch}
213                            --package-root {kernel_package_root}
214                            --kernel-modules {kernel_modules}
215                            --kernel-metadata {kernel_metadata}
216                            {rootfs_config...}
217                            {initrd_contents...}
218                        "
219                    )
220                    .run()?;
221
222                    rt.write(
223                        initrd,
224                        &OpenhclInitrdOutput {
225                            initrd: initrd_path,
226                        },
227                    );
228
229                    Ok(())
230                }
231            });
232        }
233
234        Ok(())
235    }
236}