- Nix 100%
nixos-pvm #
PVM (pagetable-based virtual machine) kernels
and NixOS modules. PVM is a KVM implementation that needs no hardware
virtualization extensions, so cloud VMs that do not expose /dev/kvm can still
run KVM-accelerated microVMs (QEMU, Cloud Hypervisor).
- Host kernel (
linux-pvm): itskvm-pvm.koregisters/dev/kvmwithout VT-x/SVM. - Guest kernel (
linux-pvm-guest): guests of a PVM host must boot it; stock kernels cannot boot under PVM. - NixOS modules
hostandguest, which do nothing until enabled.
Both kernels are Linux 6.12.33 from
virt-pvm/linux (pvm-612, pinned by
commit in pkgs/linux-pvm.nix), built with nixpkgs'
buildLinux and its common config, like nixpkgs' own kernels. The host adds
CONFIG_KVM_PVM=m; the guest adds CONFIG_X86_PIE=y and CONFIG_PVM_GUEST=y.
Host and guest share PVM's hypercall interface, so both come from the same
commit.
Before you start #
- You build the kernels yourself. There is no binary cache. Each kernel takes about an hour to build, and a host with PVM guests needs both. So does every update of this repo that changes them.
- Do not make this repo's nixpkgs follow yours. The modules use kernels
built from the nixpkgs pinned in this repo's
flake.lock, whatever nixpkgs your system uses, because that is what the kernels are tested against. Withinputs.nixos-pvm.inputs.nixpkgs.follows = "nixpkgs"you get kernels built from your nixpkgs instead, which may not build. boot.kernelPatchesmeans your own kernel. Extra kernel options go through the standardboot.kernelPatches; that changes the kernel derivation, so you build and maintain that kernel. Options whose dependencies are unmet fail the build rather than being dropped silently.- The fork is behind 6.12 stable. virt-pvm/linux is based on 6.12.33,
while nixpkgs' 6.12 is at 6.12.112: about 80 stable releases of fixes,
security fixes included, are missing. Building with
buildLinuxdoes not change that; only a rebase of virt-pvm/linux does. - No Rust. 6.12.33's Rust support does not build with the pinned rustc,
so the kernels are built without it.
pkgs/linux-pvm.nixlists this and the other differences from nixpkgs' common config. - x86_64 only.
Usage #
Flakes #
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# Not `follows`; see above.
inputs.nixos-pvm.url = "git+https://tangled.org/jacobs.land/nixos-pvm";
outputs =
{ nixpkgs, nixos-pvm, ... }:
{
nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
modules = [
./configuration.nix
nixos-pvm.nixosModules.host
{ pvm.host.enable = true; }
];
};
};
}
Plain Nix #
let
nixos-pvm = import (builtins.fetchGit {
url = "https://tangled.org/jacobs.land/nixos-pvm";
rev = "...";
}) { };
in
{
imports = [ nixos-pvm.nixosModules.host ];
pvm.host.enable = true;
}
default.nix is the source of truth; flake.nix wraps it. It takes optional
nixpkgs and pkgs arguments, which default to the pin in flake.lock.
The modules #
pvm.host.enable #
Runs the system as a PVM host:
boot.kernelPackagesdefaults (mkDefault) to the host kernel;- loads
kvm-pvm; - blacklists
kvm-intelandkvm-amd, which cannot coexist withkvm-pvm; - boots with
pti=off nokaslr, becausekvm-pvmrefuses to load with page table isolation or KASLR active. The kernel only enables PTI on CPUs affected by Meltdown; on those,pti=offremoves that mitigation. Check/sys/devices/system/cpu/vulnerabilities/meltdown.
Nothing else changes: the kernel has nixpkgs' common config, so the default initrd modules, the firewall (nf_tables) and Docker work as on a stock kernel.
A new kernel only takes effect at the next boot, and nixos-rebuild switch
does not replace the running one. Deploy kernel changes with
nixos-rebuild boot, reboot, and keep the previous generation in the boot
menu in case the new kernel does not come back.
pvm.guest.enable #
Runs the system as a PVM guest: boot.kernelPackages defaults to the guest
kernel, and it boots with pti=off nokaslr. The guest kernel is modular, like
nixpkgs' own kernels.
Packages #
| Output | Contents |
|---|---|
packages.x86_64-linux.linux-pvm |
host kernel |
packages.x86_64-linux.linux-pvm-guest |
guest kernel (bzImage for direct boot) |
nixosModules.host, nixosModules.guest |
the modules above |
From plain Nix, (import ./. { }) has kernels.{host,guest},
linuxPackages.{host,guest} and nixosModules.{host,guest}.
To build the kernels from another nixpkgs on purpose, for example to try a
nixpkgs bump, pass it as pkgs: (import ./. { pkgs = …; }).kernels. That is
untested. The modules ignore it and always use the pin.
Known limitations #
- One kernel cannot be both host and guest:
kvm-pvm.kodoes not build in aPVM_GUESTkernel (seepkgs/linux-pvm.nix). - The guest kernel has no KVM PTP clock (
ptp_kvm): as a module it does not link, because the fork does not exportpvm_hypercall. kvm-pvmrefuses to load on hosts with 5-level paging (LA57), virt-pvm/linux#6. The workaround isX86_5LEVEL = nothroughboot.kernelPatches.- QEMU: SeaBIOS does not work under PVM. Use direct kernel boot with the
microvmmachine (qboot by default) or-bios qboot.rom. - Cloud Hypervisor supports PVM from v35. Avoid a virtio-pmem root filesystem, virt-pvm/linux#1.
- The host logs
kvm_pvm: Inject event in non-PVM modeand a KVM PFN-cache warning while guests boot, virt-pvm/linux#21. Guests still run. - No out-of-tree
kvm-pvmfor nixpkgs' kernels. virt-pvm'spvm-6.12-host-ootbranch builds against nixpkgs' 6.12 with small fixes, but keeps 6.12.33's KVM code and can break on any 6.12.y bump, so the host boots the fork; guests need it regardless. virt-pvm/linux#26 proposes a supported out-of-tree mode.
Development #
nix flake check # builds both kernels (about an hour each)
nix flake check --no-build # evaluation only
nix fmt
The checks build both kernels, and evaluate a minimal NixOS system with each module and one with neither enabled. They also check that the modules boot the same kernel derivations as the packages, so one build serves both.
CI (.tangled/workflows/eval.yml) only
evaluates: a kernel build does not fit a workflow VM. It prints both kernels'
derivation paths, which show when a change means rebuilding them.
References #
- virt-pvm: https://github.com/virt-pvm/linux, https://github.com/virt-pvm/misc
- Getting started (Kata): https://github.com/virt-pvm/misc/blob/main/pvm-get-started-with-kata.md
- ATC'23 paper: https://dl.acm.org/doi/10.1145/3600006.3613158
- LKML announcement: https://lore.kernel.org/lkml/CABgObfaSGOt4AKRF5WEJt2fGMj_hLXd7J2x2etce2ymvT4HkpA@mail.gmail.com/
- fcjr/nix-pvm, another packaging of PVM for Nix: https://github.com/fcjr/nix-pvm