Initial commit

This commit is contained in:
Val Packett 2025-09-25 22:29:24 -03:00
commit 4602228be9
13 changed files with 451 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
result
.direnv/

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "muvm"]
path = muvm
url = https://github.com/valpackett/muvm
[submodule "libkrun"]
path = libkrun
url = https://github.com/valpackett/libkrun

18
LICENSE.md Normal file
View file

@ -0,0 +1,18 @@
Copyright 2025 Invisible Things Lab, Clan contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

24
README.md Normal file
View file

@ -0,0 +1,24 @@
# munix
WIP: A microVM runner for NixOS systems with desktop integration, powered by muvm/libkrun.
## Development
Building an example closure:
```
nix build '.#nixosConfigurations.x86_64-linux.testvm.config.system.build.toplevel'
```
Running the nix build:
```
nix run '.#packages.x86_64-linux.munix' $(readlink result)
```
Working on muvm & munix locally (not built into the nix store):
```
cd muvm && cargo build --locked --release
PATH=$PWD/muvm/target/release:$PATH ./munix $(readlink result)
```

123
flake.lock generated Normal file
View file

@ -0,0 +1,123 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1758078198,
"narHash": "sha256-60ojlXp42UZYTOAgsiKPZUab1Aa8pXTIo+rz0zoaynI=",
"ref": "val/tsvwswkqrrsr",
"rev": "1f0fc70eb049852a18a5203af2204bd9f5729f29",
"shallow": true,
"submodules": true,
"type": "git",
"url": "https://github.com/valpackett/nixpkgs"
},
"original": {
"ref": "val/tsvwswkqrrsr",
"shallow": true,
"submodules": true,
"type": "git",
"url": "https://github.com/valpackett/nixpkgs"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"virtwl": "virtwl"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"virtwl": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1758075582,
"narHash": "sha256-o2lpXQLaM9QcZVr+sAxvh83CqJW1QkFhfja6K40ndmA=",
"ref": "wip",
"rev": "5a5df73a11b2f6bf671a8fc89926ac993e0fbb78",
"shallow": true,
"submodules": true,
"type": "git",
"url": "https://github.com/valpackett/wayland-proxy-virtwl"
},
"original": {
"ref": "wip",
"shallow": true,
"submodules": true,
"type": "git",
"url": "https://github.com/valpackett/wayland-proxy-virtwl"
}
}
},
"root": "root",
"version": 7
}

167
flake.nix Normal file
View file

@ -0,0 +1,167 @@
{
inputs = {
self.submodules = true;
# nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# https://github.com/NixOS/nixpkgs/pull/444133
nixpkgs.url = "git+https://github.com/valpackett/nixpkgs?shallow=1&submodules=1&ref=val/tsvwswkqrrsr";
flake-utils.url = "github:numtide/flake-utils";
virtwl.url = "git+https://github.com/valpackett/wayland-proxy-virtwl?shallow=1&submodules=1&ref=wip";
virtwl.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = {self, nixpkgs, flake-utils, virtwl, ...}:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
in {
packages.libkrunfw = pkgs.libkrunfw;
# packages.libkrunfw = (pkgs.libkrunfw.overrideAttrs {
# version = "4.10.0";
# src = pkgs.fetchFromGitHub {
# owner = "containers";
# repo = "libkrunfw";
# tag = "v4.10.0";
# hash = "sha256-mq2gw0+xL6qUZE/fk0vLT3PEpzPV8p+iwRFJHXVOMnk=";
# };
# kernelSrc = pkgs.fetchurl {
# url = "mirror://kernel/linux/kernel/v6.x/linux-6.12.34.tar.xz";
# hash = "sha256-p/P+OB9n7KQXLptj77YaFL1/nhJ44DYD0P9ak/Jwwk0=";
# };
# cargoHash = "";
# });
packages.libkrun = (pkgs.libkrun.override {
withBlk = true;
withGpu = true;
withSound = true;
withNet = true;
libkrunfw = self.packages.${system}.libkrunfw;
}).overrideAttrs (old: {
src = ./libkrun;
cargoDeps = pkgs.rustPlatform.importCargoLock {
lockFile = ./libkrun/Cargo.lock;
};
# mesonFlags = [ (pkgs.lib.mesonOption "decoders" "gles,vulkan,composer") ]; # no magma(?)
});
packages.muvm = (pkgs.muvm.override {
libkrun = self.packages.${system}.libkrun;
}).overrideAttrs (old: {
postPatch = ""; # no more sysctl; udevd now takes the var anyway; XXX: fex
MUVM_UDEVD_PATH = "${pkgs.systemd}/lib/systemd/systemd-udevd";
src = ./muvm;
cargoDeps = pkgs.rustPlatform.importCargoLock {
lockFile = ./muvm/Cargo.lock;
};
});
packages.munix = let
munixScript = (pkgs.writeScriptBin "munix" (builtins.readFile ./munix)).overrideAttrs(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
munixInitRootHook = (pkgs.writeScriptBin "munix-init-root" (builtins.readFile ./munix-init-root)).overrideAttrs(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
munixInitUserHook = (pkgs.writeScriptBin "munix-init-user" (builtins.readFile ./munix-init-user)).overrideAttrs(old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
in pkgs.symlinkJoin {
name = "munix";
paths = [ munixScript munixInitRootHook munixInitUserHook self.packages.${system}.muvm pkgs.passt pkgs.bubblewrap ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/munix --prefix PATH : $out/bin
'';
};
devShells.default = let
projects = with self.packages.${system}; [ libkrun muvm ];
in pkgs.mkShell {
MUVM_UDEVD_PATH = "${pkgs.systemd}/lib/systemd/systemd-udevd";
nativeBuildInputs = pkgs.lib.concatMap (pkg: pkg.nativeBuildInputs) projects;
buildInputs = (pkgs.lib.concatMap (pkg: pkg.buildInputs) projects) ++ (with self.packages.${system}; [
# virglrenderer
]) ++ (with pkgs; [
cargo
rust-analyzer
rustfmt
passt
bubblewrap
]);
};
packages.mesa = (pkgs.mesa.override {
vulkanDrivers = [
"amd"
"intel"
"microsoft-experimental" # removing this breaks the build
"nouveau"
"swrast"
"virtio"
"gfxstream" # probably not going to use this though
];
}).overrideAttrs (new: old: {
mesonFlags = old.mesonFlags ++ [ (pkgs.lib.mesonBool "amdgpu-virtio" true) ];
patches = old.patches ++ [ ./radvmmio.patch ]; # already merged to git
});
nixosConfigurations.testvm = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
{
system.stateVersion = "25.11";
fileSystems."/".device = pkgs.lib.mkDefault "/dev/sda";
boot.isContainer = true;
users.mutableUsers = false;
users.users.appvm = {
uid = 1001;
isNormalUser = true;
home = "/home/appvm";
description = "microVM User";
extraGroups = [ "wheel" "video" "input" ];
};
users.groups.appvm.gid = 1001;
users.allowNoPasswordLogin = true;
hardware.graphics.enable = true;
hardware.graphics.package = self.packages.${system}.mesa;
system.replaceDependencies.replacements = [
{
original = pkgs.mesa;
replacement = self.packages.${system}.mesa;
}
];
environment.systemPackages = [
pkgs.fastfetch
pkgs.htop
pkgs.radeontop
virtwl.packages.x86_64-linux.proxy
pkgs.wayland-utils
pkgs.weston
pkgs.waycheck
pkgs.vulkan-tools
pkgs.glxinfo
pkgs.glmark2
pkgs.mesa-demos
pkgs.xorg.xeyes
pkgs.xterm
pkgs.vkquake
pkgs.kdePackages.kate
pkgs.adwaita-fonts
pkgs.adwaita-icon-theme
pkgs.gnome-text-editor
pkgs.firefox
pkgs.ffmpeg-full
pkgs.mpv
pkgs.libva-utils
];
}
];
};
});
}

1
libkrun Submodule

@ -0,0 +1 @@
Subproject commit bd97a39bfaa46147933beca7e16513b6820bb031

35
munix Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env bash
: "${MICROVM_SHELL:=bash}"
SCRIPT_PATH=$(dirname $(realpath -s $0))
MUVM_PATH=$(dirname $(which muvm))
PASST_PATH=$(dirname $(which passt))
export TMP=/tmp TMPDIR=/tmp TEMP=/tmp TEMPDIR=/tmp LC_ALL=C
unset DISPLAY XAUTHORITY # or: --bind /tmp/.X11-unix /tmp/.X11-unix --bind $XAUTHORITY $XAUTHORITY
exec bwrap --unshare-all --share-net \
--uid 1001 --gid 1001 \
--tmpfs / \
--dir /run --dir /var --symlink /run /var/run --dir /tmp \
--proc /proc --ro-bind /sys /sys \
--dev /dev --dir /dev/input --dev-bind /dev/kvm /dev/kvm --dev-bind /dev/dri /dev/dri \
--ro-bind "$MUVM_PATH" /run/munix/muvm \
--ro-bind "$PASST_PATH" /run/munix/passt \
--ro-bind "$1/sw/bin/env" /usr/bin/env \
--ro-bind "$SCRIPT_PATH/munix-init-root" /usr/bin/munix-init-root \
--ro-bind "$SCRIPT_PATH/munix-init-user" /usr/bin/munix-init-user \
--ro-bind /nix/store /nix/store \
--ro-bind /run/opengl-driver /run/opengl-driver \
--ro-bind /run/systemd/resolve /run/systemd/resolve \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/group /etc/group \
--ro-bind /etc/passwd /etc/passwd \
--dir "$XDG_RUNTIME_DIR" \
--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" \
--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0" \
--bind $HOME/Downloads/baseq1 $HOME/Downloads/baseq1 \
--setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
--setenv PATH "/run/munix/muvm:/run/munix/passt:$1/sw/bin" \
muvm \
-x /usr/bin/munix-init-root -X /usr/bin/munix-init-user --udevd-path="$1/sw/bin/true" \
-e WAYLAND_DISPLAY=wayland-1 \
-e MICROVM_CLOSURE="$1" \
-i -t "$1/sw/bin/$MICROVM_SHELL"

27
munix-init-root Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
unset LANGUAGE LC_ALL LC_CTYPE LC_NUMERIC LC_COLLATE LC_TIME LC_MESSAGES LC_MONETARY LC_ADDRESS LC_IDENTIFICATION LC_MEASUREMENT LC_PAPER LC_TELEPHONE LC_NAME LANG 2>/dev/null # perl, calm down
PATH=$MICROVM_CLOSURE/sw/bin
echo "=> Initializing microVM environment"
mkdir /run/log
# Prepare mounts for paths that activation writes to
mount -t tmpfs tmpfs /etc
cp /run/muvm-host/etc/resolv.conf /etc/resolv.conf
mount -t tmpfs tmpfs /usr/bin
cp /run/muvm-host/usr/bin/munix-init-user /usr/bin/munix-init-user
mount -t tmpfs tmpfs /dev/shm # tries to remount, fails on virtiofs due to unexpected opts
$MICROVM_CLOSURE/activate >/run/log/activate.spam 2>&1
umount /dev/shm # restore the original virtiofs dax shm though
chown 1001:1001 /run /dev # avoid "Detected unsafe path transition"
systemd-tmpfiles --create >/run/log/tmpfiles.spam 2>&1
systemd-machine-id-setup >/dev/null 2>&1
$MICROVM_CLOSURE/sw/lib/systemd/systemd-udevd >/run/log/udevd.spam 2>&1 &
echo "=> microVM environment ready!"

4
munix-init-user Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
PATH=$MICROVM_CLOSURE/sw/bin
mkdir -p ~/.var/log
wayland-proxy-virtwl --virtio-gpu >~/.var/log/wayland-proxy.log 2>&1 &

1
muvm Submodule

@ -0,0 +1 @@
Subproject commit 70badbc44d421c49f82130321e35b949f8b4ed14

42
radvmmio.patch Normal file
View file

@ -0,0 +1,42 @@
From c2fd030644cf2074f9d2ffd155839b2e943473d0 Mon Sep 17 00:00:00 2001
From: Val Packett <val@packett.cool>
Date: Wed, 10 Sep 2025 15:44:41 -0300
Subject: [PATCH] radv: detect platform:virtio-mmio devices for virtgpu native
context
VirtIO devices can be configured as platform devices instead of PCI,
which is especially common in microVM projects like libkrun.
Let's allow RADV to probe MMIO virtgpu devices.
Signed-off-by: Val Packett <val@invisiblethingslab.com>
---
src/amd/vulkan/radv_physical_device.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/amd/vulkan/radv_physical_device.c b/src/amd/vulkan/radv_physical_device.c
index 07b5b000b3ae..cd6f4c3488a1 100644
--- a/src/amd/vulkan/radv_physical_device.c
+++ b/src/amd/vulkan/radv_physical_device.c
@@ -2507,14 +2507,15 @@ create_drm_physical_device(struct vk_instance *vk_instance, struct _drmDevice *d
#ifndef _WIN32
bool supported_device = false;
- if (!(device->available_nodes & (1 << DRM_NODE_RENDER)) || device->bustype != DRM_BUS_PCI)
+ if (!(device->available_nodes & (1 << DRM_NODE_RENDER)))
return VK_ERROR_INCOMPATIBLE_DRIVER;
#ifdef HAVE_AMDGPU_VIRTIO
- supported_device |= device->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID;
+ supported_device |= device->bustype == DRM_BUS_PCI && device->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID;
+ supported_device |= device->bustype == DRM_BUS_PLATFORM; /* virtio-mmio */
#endif
- supported_device |= device->deviceinfo.pci->vendor_id == ATI_VENDOR_ID;
+ supported_device |= device->bustype == DRM_BUS_PCI && device->deviceinfo.pci->vendor_id == ATI_VENDOR_ID;
if (!supported_device)
return VK_ERROR_INCOMPATIBLE_DRIVER;
--
2.50.1