From: colbyt <colby@colbyt.com>
To: devel@spectrum-os.org
Cc: colbyt <colby@colbyt.com>
Subject: [PATCH 2/5] vm: add shared initramfs for verified roots
Date: Thu, 25 Jun 2026 13:20:10 -0700 [thread overview]
Message-ID: <20260625202013.1417254-3-colby@colbyt.com> (raw)
In-Reply-To: <20260625202013.1417254-1-colby@colbyt.com>
Add a shared initramfs for the app VM and net VM. It loads dm-mod and
dm-verity, finds the root and verity partitions from the supplied root
hash, opens root-verity, and switches into it as the read-only root.
Doing this in the initramfs lets the VM kernels keep dm-verity modular
instead of forcing it built in.
Signed-off-by: colbyt <colby@colbyt.com>
---
img/app/Makefile | 5 ++
img/app/default.nix | 7 ++-
pkgs/default.nix | 1 +
vm/initramfs/Makefile | 37 ++++++++++++
vm/initramfs/default.nix | 113 +++++++++++++++++++++++++++++++++++++
vm/initramfs/etc/fstab | 5 ++
vm/initramfs/etc/getuuids | 18 ++++++
vm/initramfs/etc/init | 58 +++++++++++++++++++
vm/initramfs/etc/mdev.conf | 4 ++
vm/initramfs/etc/probe | 17 ++++++
vm/sys/net/Makefile | 5 ++
vm/sys/net/default.nix | 7 ++-
12 files changed, 275 insertions(+), 2 deletions(-)
create mode 100644 vm/initramfs/Makefile
create mode 100644 vm/initramfs/default.nix
create mode 100644 vm/initramfs/etc/fstab
create mode 100755 vm/initramfs/etc/getuuids
create mode 100755 vm/initramfs/etc/init
create mode 100644 vm/initramfs/etc/mdev.conf
create mode 100755 vm/initramfs/etc/probe
diff --git a/img/app/Makefile b/img/app/Makefile
index 6e47570..3f60ef8 100644
--- a/img/app/Makefile
+++ b/img/app/Makefile
@@ -14,6 +14,7 @@ imgdir = $(libdir)/spectrum/img
VMM = cloud-hypervisor
HOST_BUILD_FILES = \
+ $(imgdir)/appvm/initramfs \
$(imgdir)/appvm/blk/root.img \
$(imgdir)/appvm/blk/rootfs.verity.roothash \
$(imgdir)/appvm/vmlinux
@@ -21,6 +22,10 @@ HOST_BUILD_FILES = \
all: $(HOST_BUILD_FILES)
.PHONY: all
+$(imgdir)/appvm/initramfs: $(INITRAMFS)
+ mkdir -p $$(dirname $@)
+ cp $(INITRAMFS) $@
+
$(imgdir)/appvm/vmlinux: $(KERNEL)
mkdir -p $$(dirname $@)
cp $(KERNEL) $@
diff --git a/img/app/default.nix b/img/app/default.nix
index ebd37e7..23487e3 100644
--- a/img/app/default.nix
+++ b/img/app/default.nix
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: 2021-2025 Alyssa Ross <hi@alyssa.is>
import ../../lib/call-package.nix (
-{ spectrum-app-tools, spectrum-build-tools, src, terminfo
+{ callSpectrumPackage, spectrum-app-tools, spectrum-build-tools, src, terminfo
, lib, appimageTools, buildFHSEnv, runCommand, stdenvNoCC, writeClosure
, cryptsetup
, erofs-utils, jq, s6-rc, util-linux, xorg
@@ -46,6 +46,10 @@ let
];
});
+ vmInitramfs = callSpectrumPackage ../../vm/initramfs {
+ kernel = kernel.modules;
+ };
+
appimageFhsenv = (buildFHSEnv (appimageTools.defaultFhsEnvArgs // {
name = "vm-fhs-env";
targetPkgs = pkgs: appimageTools.defaultFhsEnvArgs.targetPkgs pkgs ++ [
@@ -118,6 +122,7 @@ stdenvNoCC.mkDerivation {
];
env = {
+ INITRAMFS = vmInitramfs;
KERNEL = "${kernel}/${baseNameOf kernelTarget}";
PACKAGES = runCommand "packages" {} ''
printf "%s\n/\n" ${packagesSysroot} >$out
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 4dbdfee..6d179b5 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -75,6 +75,7 @@ let
../tools/mount-flatpak/target
../tools/router/target
../tools/xdg-desktop-portal-spectrum-host/target
+ ../vm/initramfs/build
../vm/sys/net/build
]));
diff --git a/vm/initramfs/Makefile b/vm/initramfs/Makefile
new file mode 100644
index 0000000..03ae1b4
--- /dev/null
+++ b/vm/initramfs/Makefile
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2026 Colby Russell <colby@colbyt.com>
+
+.POSIX:
+
+include ../../lib/common.mk
+
+dest = build/initramfs
+
+$(dest): build/local.cpio $(PACKAGES_CPIO)
+ cat build/local.cpio $(PACKAGES_CPIO) | gzip -9n > $@
+
+# etc/init isn't included in ETC_FILES, because it gets installed to
+# the root.
+ETC_FILES = etc/getuuids etc/probe etc/fstab etc/mdev.conf
+MOUNTPOINTS = dev mnt/root proc sys tmp
+
+build/local.cpio: $(ETC_FILES) etc/init build/mountpoints
+ printf "%s\n" $(ETC_FILES) | \
+ awk '{while (length) { print; sub("/?[^/]*$$", "") }}' | \
+ sort -u | \
+ $(CPIO) -o $(CPIOFLAGS) > $@
+ cd etc && echo init | $(CPIO) -o $(CPIOFLAGS) -AF ../$@
+ cd build/mountpoints && printf "%s\n" $(MOUNTPOINTS) | \
+ awk '{while (length) { print; sub("/?[^/]*$$", "") }}' | \
+ sort -u | \
+ $(CPIO) -o $(CPIOFLAGS) -AF ../../$@
+
+build/mountpoints:
+ rm -rf build/mountpoints
+ mkdir -p build/mountpoints
+ cd build/mountpoints && mkdir -p $(MOUNTPOINTS)
+ find build/mountpoints -mindepth 1 -exec touch -d @0 {} ';'
+
+clean:
+ rm -rf build
+.PHONY: clean
diff --git a/vm/initramfs/default.nix b/vm/initramfs/default.nix
new file mode 100644
index 0000000..59b01f0
--- /dev/null
+++ b/vm/initramfs/default.nix
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2026 Colby Russell <colby@colbyt.com>
+
+import ../../lib/call-package.nix (
+{ src, spectrum-build-tools, kernel ? null
+, lib, stdenvNoCC, makeModulesClosure, runCommand, writeClosure, pkgsStatic
+, busybox, cpio
+}:
+
+pkgsStatic.callPackage ({ execline, kmod, mdevd, cryptsetup, util-linuxMinimal }:
+
+let
+ inherit (lib) concatMapStringsSep filter foldl isString last split tail;
+
+ emptyFirmware = runCommand "empty-firmware" {} ''
+ mkdir -p $out/lib/firmware
+ '';
+
+ modules = if kernel == null then null else makeModulesClosure {
+ firmware = emptyFirmware;
+ inherit kernel;
+ rootModules = [ "dm-mod" "dm-verity" ];
+ };
+
+ packages = [
+ execline kmod mdevd
+
+ (cryptsetup.override {
+ programs = {
+ cryptsetup = false;
+ cryptsetup-reencrypt = false;
+ integritysetup = false;
+ };
+ })
+
+ (busybox.override {
+ enableStatic = true;
+ extraConfig = ''
+ CONFIG_DEPMOD n
+ CONFIG_FINDFS n
+ CONFIG_INSMOD n
+ CONFIG_LSMOD n
+ CONFIG_MODINFO n
+ CONFIG_MODPROBE n
+ CONFIG_RMMOD n
+ '';
+ })
+ ];
+
+ packagesSysroot = runCommand "vm-initramfs-packages-sysroot" {} ''
+ mkdir -p $out/bin
+ ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/bin
+ ln -s /bin $out/sbin
+ ${lib.optionalString (modules != null) "cp -R ${modules}/lib $out"}
+
+ # TODO: this is a hack and we should just build the util-linux
+ # programs we want.
+ # https://lore.kernel.org/util-linux/87zgrl6ufb.fsf@alyssa.is/
+ cp ${util-linuxMinimal}/bin/{findfs,lsblk} $out/bin
+ '';
+
+ storeComponents = tail (filter isString (split "/" builtins.storeDir));
+
+ packagesCpio = runCommand "vm-initramfs-packages.cpio" {
+ nativeBuildInputs = [ cpio ];
+
+ storePaths = writeClosure [ packagesSysroot ];
+ storePrefixes = foldl
+ (acc: elem: acc ++ [ "${if acc == [] then "" else last acc}/${elem}" ])
+ []
+ storeComponents;
+
+ __structuredAttrs = true;
+ unsafeDiscardReferences = { out = true; };
+ dontFixup = true;
+ } ''
+ cd ${packagesSysroot}
+ (printf "%s\n" "''${storePrefixes[@]}" && find . $(< $storePaths)) |
+ cpio -o -H newc -R +0:+0 --reproducible > $out
+ '';
+in
+
+stdenvNoCC.mkDerivation {
+ name = "spectrum-vm-initramfs";
+
+ src = lib.fileset.toSource {
+ root = ../..;
+ fileset = lib.fileset.intersection src (lib.fileset.unions [
+ ./.
+ ../../lib/common.mk
+ ]);
+ };
+ sourceRoot = "source/vm/initramfs";
+
+ env = {
+ PACKAGES_CPIO = packagesCpio;
+ };
+
+ nativeBuildInputs = [ cpio spectrum-build-tools ];
+
+ makeFlags = [ "dest=$(out)" ];
+
+ dontInstall = true;
+
+ enableParallelBuilding = true;
+
+ __structuredAttrs = true;
+ unsafeDiscardReferences = { out = true; };
+ dontFixup = true;
+
+ passthru = { inherit packagesSysroot; };
+}
+) {}) (_: {})
diff --git a/vm/initramfs/etc/fstab b/vm/initramfs/etc/fstab
new file mode 100644
index 0000000..4387224
--- /dev/null
+++ b/vm/initramfs/etc/fstab
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2026 Colby Russell <colby@colbyt.com>
+devtmpfs /dev devtmpfs nosuid 0 0
+proc /proc proc nosuid,nodev,noexec 0 0
+sysfs /sys sysfs nosuid,nodev,noexec 0 0
diff --git a/vm/initramfs/etc/getuuids b/vm/initramfs/etc/getuuids
new file mode 100755
index 0000000..62f8935
--- /dev/null
+++ b/vm/initramfs/etc/getuuids
@@ -0,0 +1,18 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2021, 2023 Alyssa Ross <hi@alyssa.is>
+
+function to_uuid(hex) {
+ return substr(hex, 1, 8) "-" substr(hex, 9, 4) "-" substr(hex, 13, 4) "-" \
+ substr(hex, 17, 4) "-" substr(hex, 21)
+}
+
+BEGIN {
+ if (length(ENVIRON["roothash"]) != 64) {
+ system("echo 'roothash invalid or missing' >&2")
+ exit 1
+ }
+
+ print to_uuid(substr(ENVIRON["roothash"], 1, 32))
+ print to_uuid(substr(ENVIRON["roothash"], 33, 32))
+}
diff --git a/vm/initramfs/etc/init b/vm/initramfs/etc/init
new file mode 100755
index 0000000..4e2df2b
--- /dev/null
+++ b/vm/initramfs/etc/init
@@ -0,0 +1,58 @@
+#!/bin/execlineb -WS0
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2026 Colby Russell <colby@colbyt.com>
+# SPDX-License-Identifier: EUPL-1.2+
+
+export PATH /bin
+
+if { mount -a }
+
+if { mkdir -p /sys/kernel/security }
+foreground { mount -t securityfs securityfs /sys/kernel/security }
+
+piperw 3 4
+if { fdmove 1 4 /etc/getuuids }
+fdclose 4
+# head -1 would be clearer, but it might use buffered I/O and consume
+# too much from the fifo. Ideally we'd have line(1) from illumos.
+backtick ROOTFS_UUID { fdmove 0 3 dd count=1 bs=37 status=none }
+backtick VERITY_UUID { fdmove 0 3 dd count=1 bs=37 status=none }
+fdclose 3
+
+if { mkfifo /dev/rootfs.poll }
+
+background {
+ fdclose 3
+ mdevd -C -b134217728
+}
+importas -iu mdevd_pid !
+
+if { modprobe dm-mod }
+if { modprobe dm-verity }
+
+# Do a blocking read on the fifo to wait for mdevd to find the
+# partition.
+if {
+ redirfd -r 0 /dev/rootfs.poll
+ redirfd -w 1 /dev/null
+ head -c 1
+}
+background { kill $mdevd_pid }
+background { rm /dev/rootfs.poll }
+
+if {
+ importas -Si roothash
+ veritysetup open /dev/rootfs root-verity /dev/verity $roothash
+}
+
+background { rm /dev/rootfs /dev/verity }
+
+if { mount -o ro,nosuid,nodev /dev/mapper/root-verity /mnt/root }
+wait { $mdevd_pid }
+
+if { mount --move /proc /mnt/root/proc }
+if { mount --move /sys /mnt/root/sys }
+if { mount --move /dev /mnt/root/dev }
+
+switch_root /mnt/root
+/etc/init
diff --git a/vm/initramfs/etc/mdev.conf b/vm/initramfs/etc/mdev.conf
new file mode 100644
index 0000000..0f4655d
--- /dev/null
+++ b/vm/initramfs/etc/mdev.conf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+#
+$DEVTYPE=partition 0:0 660 +/etc/probe
diff --git a/vm/initramfs/etc/probe b/vm/initramfs/etc/probe
new file mode 100755
index 0000000..2ebeb8b
--- /dev/null
+++ b/vm/initramfs/etc/probe
@@ -0,0 +1,17 @@
+#!/bin/execlineb -WP
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+# Check whether we now have all the partitions we need to boot.
+
+importas -i rootfs_uuid ROOTFS_UUID
+importas -i verity_uuid VERITY_UUID
+
+backtick -E rootfs_dev { findfs PARTUUID=${rootfs_uuid} }
+backtick -E verity_dev { findfs PARTUUID=${verity_uuid} }
+
+if { rm -f /dev/rootfs /dev/verity }
+if { ln -s $rootfs_dev /dev/rootfs }
+if { ln -s $verity_dev /dev/verity }
+
+redirfd -w -nb 1 /dev/rootfs.poll echo
diff --git a/vm/sys/net/Makefile b/vm/sys/net/Makefile
index c6cf7f2..d01d766 100644
--- a/vm/sys/net/Makefile
+++ b/vm/sys/net/Makefile
@@ -13,6 +13,7 @@ vmdir = $(libdir)/spectrum/vm
VMM = cloud-hypervisor
HOST_BUILD_FILES = \
+ $(vmdir)/netvm/initramfs \
$(vmdir)/netvm/blk/root.img \
$(vmdir)/netvm/blk/rootfs.verity.roothash \
$(vmdir)/netvm/vmlinux
@@ -20,6 +21,10 @@ HOST_BUILD_FILES = \
all: $(HOST_BUILD_FILES)
.PHONY: all
+$(vmdir)/netvm/initramfs: $(INITRAMFS)
+ mkdir -p $$(dirname $@)
+ cp $(INITRAMFS) $@
+
$(vmdir)/netvm/vmlinux: $(KERNEL)
mkdir -p $$(dirname $@)
cp $(KERNEL) $@
diff --git a/vm/sys/net/default.nix b/vm/sys/net/default.nix
index 8549bf3..7f077a9 100644
--- a/vm/sys/net/default.nix
+++ b/vm/sys/net/default.nix
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is>
import ../../../lib/call-package.nix (
-{ spectrum-build-tools, src, terminfo, pkgsMusl }:
+{ callSpectrumPackage, spectrum-build-tools, src, terminfo, pkgsMusl }:
pkgsMusl.callPackage (
{ lib, stdenvNoCC, nixos, runCommand, writeClosure
@@ -52,6 +52,10 @@ let
];
});
+ vmInitramfs = callSpectrumPackage ../../initramfs {
+ kernel = kernel.modules;
+ };
+
packages = [
dbus execline kmod mdevd s6 s6-linux-init s6-rc xdp-tools
@@ -124,6 +128,7 @@ stdenvNoCC.mkDerivation {
];
env = {
+ INITRAMFS = vmInitramfs;
KERNEL = "${kernel}/${baseNameOf kernelTarget}";
PACKAGES = runCommand "packages" {} ''
printf "%s\n/\n" ${packagesSysroot} >$out
--
2.54.0
next prev parent reply other threads:[~2026-06-25 20:21 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-25 20:20 [PATCH 0/5] vm: boot sub-VMs from dm-verity roots and enable IPE colbyt
2026-06-25 20:20 ` [PATCH 1/5] vm: generate dm-verity metadata for sub-VM roots colbyt
2026-06-25 20:20 ` colbyt [this message]
2026-06-25 20:20 ` [PATCH 3/5] vm: boot sub-VMs through verified roots colbyt
2026-06-25 20:20 ` [PATCH 4/5] vm: enable IPE in sub-VM kernels colbyt
2026-06-25 20:20 ` [PATCH 5/5] host/rootfs: enable IPE in the host kernel colbyt
2026-07-02 13:44 ` Alyssa Ross
2026-06-26 15:14 ` [PATCH 0/5] vm: boot sub-VMs from dm-verity roots and enable IPE Alyssa Ross
2026-06-26 19:12 ` colby
2026-07-02 13:43 ` Alyssa Ross
2026-07-02 23:16 ` colby
2026-07-03 14:06 ` Alyssa Ross
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260625202013.1417254-3-colby@colbyt.com \
--to=colby@colbyt.com \
--cc=devel@spectrum-os.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://spectrum-os.org/git/crosvm
https://spectrum-os.org/git/doc
https://spectrum-os.org/git/mktuntap
https://spectrum-os.org/git/nixpkgs
https://spectrum-os.org/git/spectrum
https://spectrum-os.org/git/ucspi-vsock
https://spectrum-os.org/git/www
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).