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