supermachine_kernel/lib.rs
1//! Pre-built runtime assets for supermachine, packaged as a Rust
2//! crate so embedders can `cargo add supermachine-kernel` instead
3//! of fetching binaries out-of-band.
4//!
5//! Two assets are bundled:
6//!
7//! 1. **Linux kernel image** ([`KERNEL_BYTES`]) — ~29 MiB aarch64
8//! `Image` format with the AF_TSI patch series applied.
9//! 2. **`init-oci` shim** ([`INIT_OCI_BYTES`]) — ~1.6 MiB statically
10//! linked aarch64-musl binary. PID 1 inside each microVM. Sets
11//! up overlayfs, mounts /proc + /dev, exec's the customer's
12//! image entrypoint. Only needed if you bake images at runtime
13//! (i.e. via the future `Image::from_oci(...)` API); restoring
14//! a snapshot doesn't need it.
15//!
16//! Versioned in lockstep with the [`supermachine`] library — pin
17//! both to the same version.
18//!
19//! ## Use cases
20//!
21//! ### From a `build.rs` (recommended for `.app` bundles)
22//!
23//! Pre-stage the assets into your bundle's Resources at build time
24//! so the resulting `.app` is self-contained:
25//!
26//! ```no_run
27//! // build.rs
28//! fn main() {
29//! let resources = std::path::PathBuf::from(
30//! std::env::var("OUT_DIR").unwrap()
31//! ).join("../../../bundle-resources");
32//! std::fs::create_dir_all(&resources).unwrap();
33//! supermachine_kernel::extract_kernel_to(&resources.join("kernel")).unwrap();
34//! supermachine_kernel::extract_init_oci_to(&resources.join("init-oci")).unwrap();
35//! }
36//! ```
37//!
38//! ### From runtime code
39//!
40//! Extract once on first start to a writable scratch dir:
41//!
42//! ```no_run
43//! use std::path::PathBuf;
44//!
45//! fn ensure_assets() -> std::io::Result<PathBuf> {
46//! let dir = std::env::temp_dir().join("supermachine-assets");
47//! std::fs::create_dir_all(&dir)?;
48//! supermachine_kernel::extract_kernel_to(&dir.join("kernel"))?;
49//! supermachine_kernel::extract_init_oci_to(&dir.join("init-oci"))?;
50//! Ok(dir)
51//! }
52//! ```
53//!
54//! Then point [`supermachine::AssetPaths::from_dir`] at the result.
55//!
56//! [`supermachine`]: https://crates.io/crates/supermachine
57//! [`supermachine::AssetPaths::from_dir`]: https://docs.rs/supermachine/latest/supermachine/assets/struct.AssetPaths.html#method.from_dir
58
59/// Raw bytes of the kernel image — an aarch64 Linux `Image` format
60/// binary (raw kernel, no ELF wrapper). About 29 MiB.
61///
62/// The supermachine VMM expects the bytes loaded into guest RAM at
63/// the boot offset (0x80000 from RAM start) and PC pointed at the
64/// load address. The library handles that wiring internally; you
65/// just need the path on disk.
66///
67/// The bytes are staged into `OUT_DIR` by `build.rs`, which
68/// resolves them from one of three sources (env override, dev tree,
69/// or download from the matching GitHub release). See `build.rs`
70/// for the full discovery order.
71pub const KERNEL_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/kernel"));
72
73/// Length of the kernel image in bytes — equivalent to
74/// `KERNEL_BYTES.len()` but evaluable in `const` contexts.
75pub const KERNEL_LEN: usize = KERNEL_BYTES.len();
76
77/// Raw bytes of the in-VM init shim (statically-linked aarch64-musl
78/// ELF executable, ~1.6 MiB). The CLI's bake step copies this into
79/// the guest's initramfs as PID 1; it sets up overlayfs, mounts
80/// /proc + /dev, then exec's the OCI image's entrypoint.
81///
82/// Embedders that only restore snapshots don't need to extract this
83/// — restoring brings back the post-init state directly. It's only
84/// needed if you bake fresh images at runtime, which today still
85/// goes through the `supermachine` CLI (the future
86/// `Image::from_oci(...)` library API will use this constant).
87pub const INIT_OCI_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/init-oci"));
88
89/// Length of the init-oci binary in bytes.
90pub const INIT_OCI_LEN: usize = INIT_OCI_BYTES.len();
91
92/// Raw bytes of the in-VM `supermachine-agent` binary
93/// (statically-linked aarch64-musl ELF, ~430 KiB). The CLI's bake
94/// step copies this into the delta layer at `/supermachine-agent`;
95/// `init-oci` forks + exec's it post-pivot to provide
96/// docker-style `exec` and other in-guest control RPCs over vsock.
97///
98/// Like [`INIT_OCI_BYTES`], only the bake path uses this — pure
99/// snapshot-restore embedders don't need to extract it.
100pub const SUPERMACHINE_AGENT_BYTES: &[u8] =
101 include_bytes!(concat!(env!("OUT_DIR"), "/supermachine-agent"));
102
103/// Length of the supermachine-agent binary in bytes.
104pub const SUPERMACHINE_AGENT_LEN: usize = SUPERMACHINE_AGENT_BYTES.len();
105
106/// Write the bundled kernel image to `dest`. Overwrites any
107/// existing file. Caller is responsible for the parent dir
108/// existing — use [`extract_kernel_to_with_parents`] if you'd
109/// rather mkdir -p.
110///
111/// ```no_run
112/// supermachine_kernel::extract_kernel_to(
113/// std::path::Path::new("/tmp/supermachine/kernel"),
114/// ).unwrap();
115/// ```
116pub fn extract_kernel_to(dest: &std::path::Path) -> std::io::Result<()> {
117 std::fs::write(dest, KERNEL_BYTES)
118}
119
120/// Like [`extract_kernel_to`] but `mkdir -p`'s the parent dir first.
121pub fn extract_kernel_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
122 if let Some(parent) = dest.parent() {
123 std::fs::create_dir_all(parent)?;
124 }
125 extract_kernel_to(dest)
126}
127
128/// Write the bundled init-oci binary to `dest`. Sets it executable
129/// (mode 0o755) on Unix.
130pub fn extract_init_oci_to(dest: &std::path::Path) -> std::io::Result<()> {
131 std::fs::write(dest, INIT_OCI_BYTES)?;
132 #[cfg(unix)]
133 {
134 use std::os::unix::fs::PermissionsExt;
135 let mut perms = std::fs::metadata(dest)?.permissions();
136 perms.set_mode(0o755);
137 std::fs::set_permissions(dest, perms)?;
138 }
139 Ok(())
140}
141
142/// Like [`extract_init_oci_to`] but `mkdir -p`'s the parent dir first.
143pub fn extract_init_oci_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
144 if let Some(parent) = dest.parent() {
145 std::fs::create_dir_all(parent)?;
146 }
147 extract_init_oci_to(dest)
148}
149
150/// Write the bundled `supermachine-agent` binary to `dest`. Sets it
151/// executable (mode 0o755) on Unix.
152pub fn extract_supermachine_agent_to(dest: &std::path::Path) -> std::io::Result<()> {
153 std::fs::write(dest, SUPERMACHINE_AGENT_BYTES)?;
154 #[cfg(unix)]
155 {
156 use std::os::unix::fs::PermissionsExt;
157 let mut perms = std::fs::metadata(dest)?.permissions();
158 perms.set_mode(0o755);
159 std::fs::set_permissions(dest, perms)?;
160 }
161 Ok(())
162}
163
164/// Like [`extract_supermachine_agent_to`] but `mkdir -p`'s the parent dir first.
165pub fn extract_supermachine_agent_to_with_parents(
166 dest: &std::path::Path,
167) -> std::io::Result<()> {
168 if let Some(parent) = dest.parent() {
169 std::fs::create_dir_all(parent)?;
170 }
171 extract_supermachine_agent_to(dest)
172}
173
174// ---------- backward compat ----------
175// The earlier 0.1.0-pre layout shipped only the kernel. Keep these
176// names available for one minor so anyone who tracked HEAD doesn't
177// break.
178
179/// Deprecated alias for [`extract_kernel_to`]. Will be removed in 0.2.
180#[deprecated(since = "0.1.0", note = "use extract_kernel_to")]
181pub fn extract_to(dest: &std::path::Path) -> std::io::Result<()> {
182 extract_kernel_to(dest)
183}
184
185/// Deprecated alias for [`extract_kernel_to_with_parents`].
186#[deprecated(since = "0.1.0", note = "use extract_kernel_to_with_parents")]
187pub fn extract_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
188 extract_kernel_to_with_parents(dest)
189}