flowey_lib_hvlite/
build_openhcl_initrd.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Wrapper around `update-rootfs.py`

use crate::download_openvmm_deps::OpenvmmDepsArch;
use crate::run_cargo_build::common::CommonArch;
use flowey::node::prelude::*;
use std::collections::BTreeMap;

#[derive(Serialize, Deserialize)]
pub struct OpenhclInitrdOutput {
    pub initrd: PathBuf,
}

/// Extra parameters for building specialized initrd files.
#[derive(Default, Clone, Serialize, Deserialize, Debug)]
pub struct OpenhclInitrdExtraParams {
    /// additional layers to be included in the initrd
    pub extra_initrd_layers: Vec<PathBuf>,
    /// additional directories to be included in the initrd
    pub extra_initrd_directories: Vec<PathBuf>,
    /// Path to custom kernel modules. If not provided, uses modules under the
    /// kernel package path.
    pub custom_kernel_modules: Option<PathBuf>,
}

flowey_request! {
    pub struct Request {
        pub arch: CommonArch,
        /// include --interactive tools
        pub interactive: bool,
        /// Extra parameters for building specialized initrd files.
        pub extra_params: Option<OpenhclInitrdExtraParams>,
        /// Paths to rootfs.config files
        pub rootfs_config: Vec<ReadVar<PathBuf>>,
        /// Extra environment variables to set during the run (e.g: to
        /// interpolate paths into `rootfs.config`)
        pub extra_env: Option<ReadVar<BTreeMap<String, String>>>,
        /// Path to kernel package
        pub kernel_package_root: ReadVar<PathBuf>,
        /// Path to the openhcl bin to use
        pub bin_openhcl: ReadVar<PathBuf>,
        /// Output path of generated initrd
        pub initrd: WriteVar<OpenhclInitrdOutput>,
    }
}

new_flow_node!(struct Node);

impl FlowNode for Node {
    type Request = Request;

    fn imports(ctx: &mut ImportCtx<'_>) {
        ctx.import::<crate::download_openvmm_deps::Node>();
        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
    }

    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
        // ambient deps required by `update-rootfs.py`
        let pydeps =
            ctx.reqv(
                |side_effect| flowey_lib_common::install_dist_pkg::Request::Install {
                    package_names: ["python3"].map(Into::into).into(),
                    done: side_effect,
                },
            );

        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);

        for Request {
            arch,
            extra_params,
            rootfs_config,
            kernel_package_root,
            extra_env,
            bin_openhcl,
            initrd,
            interactive,
        } in requests
        {
            let OpenhclInitrdExtraParams {
                extra_initrd_layers,
                extra_initrd_directories,
                custom_kernel_modules,
            } = extra_params.unwrap_or_default();

            let openvmm_deps_arch = match arch {
                CommonArch::X86_64 => OpenvmmDepsArch::X86_64,
                CommonArch::Aarch64 => OpenvmmDepsArch::Aarch64,
            };

            let interactive_dep = if interactive {
                ctx.reqv(|v| {
                    crate::download_openvmm_deps::Request::GetOpenhclCpioDbgrd(openvmm_deps_arch, v)
                })
            } else {
                ctx.reqv(|v| {
                    crate::download_openvmm_deps::Request::GetOpenhclCpioShell(openvmm_deps_arch, v)
                })
            };

            if rootfs_config.is_empty() {
                anyhow::bail!("no rootfs files provided");
            }

            ctx.emit_rust_step("building openhcl initrd", |ctx| {
                pydeps.clone().claim(ctx);
                let interactive_dep = interactive_dep.claim(ctx);
                let rootfs_config = rootfs_config.claim(ctx);
                let extra_env = extra_env.claim(ctx);
                let bin_openhcl = bin_openhcl.claim(ctx);
                let openvmm_repo_path = openvmm_repo_path.clone().claim(ctx);
                let kernel_package_root = kernel_package_root.claim(ctx);
                let initrd = initrd.claim(ctx);
                move |rt| {
                    let interactive_dep = rt.read(interactive_dep);
                    let rootfs_config = rootfs_config
                        .into_iter()
                        .map(|x| rt.read(x))
                        .collect::<Vec<_>>();
                    let extra_env = extra_env.map(|x| rt.read(x));
                    let bin_openhcl = rt.read(bin_openhcl);
                    let openvmm_repo_path = rt.read(openvmm_repo_path);
                    let kernel_package_root = rt.read(kernel_package_root);

                    let sh = xshell::Shell::new()?;

                    let initrd_path = sh.current_dir().join("openhcl.cpio.gz");

                    let initrd_contents = {
                        let mut v = Vec::new();

                        if interactive {
                            // for busybox, gdbserver, and perf
                            v.push("--interactive".to_string());
                        } else {
                            // just a minimal shell
                            v.push("--min-interactive".to_string());
                        }

                        for dir in extra_initrd_layers {
                            v.push("--layer".into());
                            v.push(dir.display().to_string());
                        }

                        for dir in extra_initrd_directories {
                            v.push("--add-dir".into());
                            v.push(dir.display().to_string());
                        }

                        v
                    };

                    let kernel_modules =
                        custom_kernel_modules.unwrap_or(kernel_package_root.join("."));

                    let rootfs_py_arch = match arch {
                        CommonArch::X86_64 => "x86_64",
                        CommonArch::Aarch64 => "aarch64",
                    };

                    // FUTURE: to avoid making big changes to update-roots as
                    // part of the initial OSS workstream, stage the
                    // interactive-layer packages in the same folder structure
                    // as the closed-source openhcl-deps.
                    match arch {
                        CommonArch::X86_64 => {
                            sh.set_var("OPENVMM_DEPS_X64", interactive_dep.parent().unwrap());
                        }
                        CommonArch::Aarch64 => {
                            sh.set_var("OPENVMM_DEPS_AARCH64", interactive_dep.parent().unwrap());
                        }
                    }

                    for (k, v) in extra_env.into_iter().flatten() {
                        sh.set_var(k, v);
                    }

                    // FIXME: update-rootfs.py invokes git to obtain a version
                    // hash to stuff into the initrd.
                    sh.change_dir(openvmm_repo_path);

                    let rootfs_config = rootfs_config
                        .iter()
                        .flat_map(|x| ["--rootfs-config".as_ref(), x.as_os_str()]);

                    xshell::cmd!(
                        sh,
                        "python3 openhcl/update-rootfs.py
                            {bin_openhcl}
                            {initrd_path}
                            --arch {rootfs_py_arch}
                            --package-root {kernel_package_root}
                            --kernel-modules {kernel_modules}
                            {rootfs_config...}
                            {initrd_contents...}
                        "
                    )
                    .run()?;

                    rt.write(
                        initrd,
                        &OpenhclInitrdOutput {
                            initrd: initrd_path,
                        },
                    );

                    Ok(())
                }
            });
        }

        Ok(())
    }
}