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/// Raw bytes of the `smpark.ko` Linux kernel module (~10 KiB,
107/// aarch64). Loaded by `init-oci` BEFORE the rootfs pivot via
108/// `finit_module`. Provides `/dev/smpark` with PARK/UNPARK
109/// ioctls; the host's bake driver brackets the BASE+WARM
110/// snapshot captures with PARK/UNPARK RPCs to drive secondary
111/// vCPUs into a known byte-identical parked-WFI state that
112/// HVF can round-trip cleanly. Required for `vcpus > 1`
113/// reliability — see `docs/design/multi-vcpu-shipped-2026-05-18.md`.
114///
115/// vermagic is pinned to the kernel build that produced
116/// [`KERNEL_BYTES`] (rebuild via `kernel-build/scripts/build.sh`
117/// keeps them in sync). On a mismatch the module load fails
118/// silently and multi-vCPU bakes fall back to the pre-fix
119/// intermittent capture path; single-vCPU is unaffected.
120pub const SMPARK_KO_BYTES: &[u8] =
121 include_bytes!(concat!(env!("OUT_DIR"), "/smpark.ko"));
122
123/// Length of the smpark.ko module in bytes.
124pub const SMPARK_KO_LEN: usize = SMPARK_KO_BYTES.len();
125
126/// Write the bundled kernel image to `dest`. Overwrites any
127/// existing file. Caller is responsible for the parent dir
128/// existing — use [`extract_kernel_to_with_parents`] if you'd
129/// rather mkdir -p.
130///
131/// ```no_run
132/// supermachine_kernel::extract_kernel_to(
133/// std::path::Path::new("/tmp/supermachine/kernel"),
134/// ).unwrap();
135/// ```
136pub fn extract_kernel_to(dest: &std::path::Path) -> std::io::Result<()> {
137 std::fs::write(dest, KERNEL_BYTES)
138}
139
140/// Like [`extract_kernel_to`] but `mkdir -p`'s the parent dir first.
141pub fn extract_kernel_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
142 if let Some(parent) = dest.parent() {
143 std::fs::create_dir_all(parent)?;
144 }
145 extract_kernel_to(dest)
146}
147
148/// Write the bundled init-oci binary to `dest`. Sets it executable
149/// (mode 0o755) on Unix.
150pub fn extract_init_oci_to(dest: &std::path::Path) -> std::io::Result<()> {
151 std::fs::write(dest, INIT_OCI_BYTES)?;
152 #[cfg(unix)]
153 {
154 use std::os::unix::fs::PermissionsExt;
155 let mut perms = std::fs::metadata(dest)?.permissions();
156 perms.set_mode(0o755);
157 std::fs::set_permissions(dest, perms)?;
158 }
159 Ok(())
160}
161
162/// Like [`extract_init_oci_to`] but `mkdir -p`'s the parent dir first.
163pub fn extract_init_oci_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
164 if let Some(parent) = dest.parent() {
165 std::fs::create_dir_all(parent)?;
166 }
167 extract_init_oci_to(dest)
168}
169
170/// Write the bundled `supermachine-agent` binary to `dest`. Sets it
171/// executable (mode 0o755) on Unix.
172pub fn extract_supermachine_agent_to(dest: &std::path::Path) -> std::io::Result<()> {
173 std::fs::write(dest, SUPERMACHINE_AGENT_BYTES)?;
174 #[cfg(unix)]
175 {
176 use std::os::unix::fs::PermissionsExt;
177 let mut perms = std::fs::metadata(dest)?.permissions();
178 perms.set_mode(0o755);
179 std::fs::set_permissions(dest, perms)?;
180 }
181 Ok(())
182}
183
184/// Like [`extract_supermachine_agent_to`] but `mkdir -p`'s the parent dir first.
185pub fn extract_supermachine_agent_to_with_parents(
186 dest: &std::path::Path,
187) -> std::io::Result<()> {
188 if let Some(parent) = dest.parent() {
189 std::fs::create_dir_all(parent)?;
190 }
191 extract_supermachine_agent_to(dest)
192}
193
194/// Write the bundled smpark.ko kernel module to `dest`. Caller
195/// is responsible for the parent dir existing — use
196/// [`extract_smpark_ko_to_with_parents`] for `mkdir -p`.
197pub fn extract_smpark_ko_to(dest: &std::path::Path) -> std::io::Result<()> {
198 std::fs::write(dest, SMPARK_KO_BYTES)
199}
200
201/// Like [`extract_smpark_ko_to`] but `mkdir -p`'s the parent dir first.
202pub fn extract_smpark_ko_to_with_parents(
203 dest: &std::path::Path,
204) -> std::io::Result<()> {
205 if let Some(parent) = dest.parent() {
206 std::fs::create_dir_all(parent)?;
207 }
208 extract_smpark_ko_to(dest)
209}
210
211// ---------- backward compat ----------
212// The earlier 0.1.0-pre layout shipped only the kernel. Keep these
213// names available for one minor so anyone who tracked HEAD doesn't
214// break.
215
216/// Deprecated alias for [`extract_kernel_to`]. Will be removed in 0.2.
217#[deprecated(since = "0.1.0", note = "use extract_kernel_to")]
218pub fn extract_to(dest: &std::path::Path) -> std::io::Result<()> {
219 extract_kernel_to(dest)
220}
221
222/// Deprecated alias for [`extract_kernel_to_with_parents`].
223#[deprecated(since = "0.1.0", note = "use extract_kernel_to_with_parents")]
224pub fn extract_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
225 extract_kernel_to_with_parents(dest)
226}