Skip to main content

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