feat: arch packages with dkms kvm modules and patched qemu

This commit is contained in:
2026-09-06 00:14:56 +02:00
parent 07522614a7
commit 7313d04ae7
11 changed files with 436 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
pkgbase = vfio-native-kvm-dkms
pkgdesc = KVM modules patched to raise the exceptions the architecture specifies (DKMS, 7.2.x kernels)
pkgver = 7.2.3
pkgrel = 1
url = https://git.archworks.co/sandwich/vfio-native
install = vfio-native-kvm-dkms.install
arch = x86_64
license = GPL-2.0-only
makedepends = git
depends = dkms
optdepends = vfio-native: the setup and verification tooling
provides = vfio-native-kvm
conflicts = vfio-native-kvm
noextract = linux-7.2.3.tar.xz
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
source = https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.2.3.tar.xz
source = dkms.conf
source = dkms-out-of-tree.patch
source = kvm-native.hook
sha256sums = SKIP
sha256sums = 8ba259e8e7b13ec6ef0941c8a39ad90b24bd4a4d6c0010ba6bafb794550ecd03
sha256sums = SKIP
sha256sums = SKIP
sha256sums = SKIP
pkgname = vfio-native-kvm-dkms

View File

@@ -0,0 +1,74 @@
# Maintainer: Sandwich <sandwich@archworks.co>
#
# KVM modules patched for architectural conformance, built by DKMS against
# whichever 7.2.x kernels are installed. Three VMAware checks stop firing as a
# direct consequence, because the fix is to raise the exception the
# architecture specifies:
#
# SVM_EXCEPTIONS a CPL>0 SVM instruction must raise #UD, not #GP
# KVM_INTERCEPTION a CPL>0 VMCALL/hypercall must raise #UD, not #GP or nothing
# DBVM an ICEBP #DB must report the RIP after the instruction
#
# The experimental CPUID-passthrough patch is built in as well but is OFF by
# default (module parameter cpuid_passthrough). It clears the last software
# check, TIMER, on a 1:1-pinned guest; vm-native-setup prints how to enable it.
#
# KVM is in-tree, so the DKMS tree carries arch/x86/kvm and virt/kvm from the
# vanilla kernel of the same version and builds them against the installed
# headers; the two Makefile paths that assume an in-tree build are redirected.
# The KVM sources do not differ between the Arch flavours of one version (the
# module srcversion is identical on linux and linux-cachyos-bore 7.2.3), so one
# source tree serves all of them.
#
# THE PIN: the sources are 7.2.3's. They build against any 7.2.x headers and
# are refused for anything else, so after a kernel upgrade past 7.2 the stock
# modules load silently and the three checks come back. Bump _kver here.
pkgname=vfio-native-kvm-dkms
_pkgbase=vfio-native-kvm
_kver=7.2.3
_tag=1.1.0
pkgver=${_kver}
pkgrel=1
pkgdesc="KVM modules patched to raise the exceptions the architecture specifies (DKMS, 7.2.x kernels)"
arch=('x86_64')
url="https://git.archworks.co/sandwich/vfio-native"
license=('GPL-2.0-only')
depends=('dkms')
makedepends=('git')
optdepends=('vfio-native: the setup and verification tooling')
provides=("${_pkgbase}")
conflicts=("${_pkgbase}")
install=${pkgname}.install
source=("git+${url}.git#tag=v${_tag}"
"https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-${_kver}.tar.xz"
'dkms.conf'
'dkms-out-of-tree.patch'
'kvm-native.hook')
noextract=("linux-${_kver}.tar.xz")
sha256sums=('SKIP'
'8ba259e8e7b13ec6ef0941c8a39ad90b24bd4a4d6c0010ba6bafb794550ecd03'
'SKIP'
'SKIP'
'SKIP')
prepare() {
tar -xf "linux-${_kver}.tar.xz" --wildcards \
"linux-${_kver}/arch/x86/kvm/*" "linux-${_kver}/virt/kvm/*"
cd "linux-${_kver}"
for p in "${srcdir}"/vfio-native/patches/kvm/000[1234]-KVM-*.patch \
"${srcdir}"/vfio-native/patches/kvm/EXPERIMENTAL-0006-*.patch; do
msg2 "applying ${p##*/}"
patch -Np1 -i "$p"
done
patch -Np1 -i "${srcdir}/dkms-out-of-tree.patch"
}
package() {
local dest="${pkgdir}/usr/src/${_pkgbase}-${pkgver}"
install -dm755 "${dest}/arch/x86" "${dest}/virt"
cp -r "linux-${_kver}/arch/x86/kvm" "${dest}/arch/x86/"
cp -r "linux-${_kver}/virt/kvm" "${dest}/virt/"
sed "s/@PKGVER@/${pkgver}/" dkms.conf > "${dest}/dkms.conf"
install -Dm644 kvm-native.hook "${pkgdir}/usr/share/libalpm/hooks/90-vfio-native-kvm.hook"
}

View File

@@ -0,0 +1,27 @@
Build arch/x86/kvm as an external module: the include path and the shared
virt/kvm makefile must come from the module tree, not from the headers.
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -1,9 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y += -I $(srctree)/arch/x86/kvm
+ccflags-y += -I $(src)
ccflags-$(CONFIG_KVM_WERROR) += -Werror
-include $(srctree)/virt/kvm/Makefile.kvm
+include $(src)/../../../virt/kvm/Makefile.kvm
kvm-y += x86.o emulate.o irq.o lapic.o cpuid.o pmu.o mtrr.o \
debugfs.o mmu/mmu.o mmu/page_track.o mmu/spte.o
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -1968,7 +1968,7 @@
#endif /* _TRACE_KVM_H */
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../../arch/x86/kvm
+#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace

View File

@@ -0,0 +1,16 @@
PACKAGE_NAME="vfio-native-kvm"
PACKAGE_VERSION="@PKGVER@"
# 7.2.3's KVM sources build against 7.2.x headers only; refuse anything else
# loudly instead of producing a module that will not load.
BUILD_EXCLUSIVE_KERNEL="^7\.2\."
MAKE[0]="make -C /usr/lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build/arch/x86/kvm modules"
BUILT_MODULE_NAME[0]="kvm"
BUILT_MODULE_LOCATION[0]="arch/x86/kvm"
DEST_MODULE_LOCATION[0]="/updates"
BUILT_MODULE_NAME[1]="kvm-amd"
BUILT_MODULE_LOCATION[1]="arch/x86/kvm"
DEST_MODULE_LOCATION[1]="/updates"
BUILT_MODULE_NAME[2]="kvm-intel"
BUILT_MODULE_LOCATION[2]="arch/x86/kvm"
DEST_MODULE_LOCATION[2]="/updates"
AUTOINSTALL="yes"

View File

@@ -0,0 +1,16 @@
# Warns when a kernel outside the DKMS tree's 7.2.x range is installed.
#
# Without this the failure is silent: DKMS skips the build, the stock modules
# load, the guest boots normally, and the only symptom is three detections
# coming back.
[Trigger]
Operation = Install
Operation = Upgrade
Type = Path
Target = usr/lib/modules/*/vmlinuz
[Action]
Description = Checking that vfio-native-kvm-dkms can build for the new kernel...
When = PostTransaction
NeedsTargets
Exec = /usr/bin/sh -c 'while read -r p; do k=${p#usr/lib/modules/}; k=${k%/vmlinuz}; case "$k" in 7.2.*) ;; *) echo ">>> vfio-native-kvm-dkms carries 7.2.3 sources and will not build for $k."; echo ">>> Stock KVM loads there and SVM_EXCEPTIONS, KVM_INTERCEPTION and DBVM come back.";; esac; done'

View File

@@ -0,0 +1,24 @@
post_install() {
cat <<'MSG'
vfio-native-kvm-dkms: DKMS builds the patched kvm, kvm-amd and kvm-intel
for every installed 7.2.x kernel with headers, into updates/, which modprobe
prefers over the in-tree modules. They are NOT loaded yet.
With every VM shut down:
sudo modprobe -r kvm_amd kvm && sudo modprobe kvm_amd # or kvm_intel
Confirm BOTH took - checking one passes on a stale build of the other:
cat /sys/module/kvm/srcversion /sys/module/kvm_amd/srcversion
modinfo -F srcversion kvm kvm_amd
THE PIN: these are 7.2.3's KVM sources. DKMS refuses to build them for a
kernel outside 7.2.x, the stock modules load silently, and three detections
come back. A pacman hook warns when that happens.
MSG
}
post_upgrade() { post_install; }
post_remove() { echo " Stock KVM loads on the next boot, or reload the modules now."; }

View File

@@ -0,0 +1,46 @@
pkgbase = vfio-native-qemu
pkgdesc = QEMU 11.1.1 with the vfio-native platform-identity patches, in /opt/qemu-native
pkgver = 11.1.1
pkgrel = 1
url = https://git.archworks.co/sandwich/vfio-native
arch = x86_64
license = GPL-2.0-only
makedepends = git
makedepends = meson
makedepends = ninja
makedepends = python
depends = glib2
depends = pixman
depends = zlib
depends = zstd
depends = libaio
depends = liburing
depends = libcap-ng
depends = libseccomp
depends = gnutls
depends = nettle
depends = libslirp
depends = spice
depends = spice-protocol
depends = libepoxy
depends = virglrenderer
depends = mesa
depends = libjpeg-turbo
depends = libpng
depends = libusb
depends = usbredir
depends = numactl
depends = libxkbcommon
depends = libgcrypt
depends = libtasn1
depends = lzo
depends = snappy
depends = pipewire
optdepends = vfio-native: the setup tooling that points a domain at this build
options = !strip
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
source = https://download.qemu.org/qemu-11.1.1.tar.xz
sha256sums = SKIP
sha256sums = 079ffbff8a7111bbc89022107cbabf3bbfd614d5fc9d7cc675991196aca12482
pkgname = vfio-native-qemu

View File

@@ -0,0 +1,50 @@
# Maintainer: Sandwich <sandwich@archworks.co>
#
# QEMU 11.1.1 with the platform-identity patches from vfio-native, installed
# under /opt/qemu-native so the system qemu package is never touched. Clears
# FIRMWARE, VIRTUAL_DISK, DISK_SERIAL, PCI_VENDOR, DEVICES, QEMU_FW_CFG and
# QEMU_USB. Only the x86_64 system emulator is built.
pkgname=vfio-native-qemu
_qver=11.1.1
_tag=1.1.0
pkgver=${_qver}
pkgrel=1
pkgdesc="QEMU ${_qver} with the vfio-native platform-identity patches, in /opt/qemu-native"
arch=('x86_64')
url="https://git.archworks.co/sandwich/vfio-native"
license=('GPL-2.0-only')
depends=('glib2' 'pixman' 'zlib' 'zstd' 'libaio' 'liburing' 'libcap-ng' 'libseccomp'
'gnutls' 'nettle' 'libslirp' 'spice' 'spice-protocol' 'libepoxy' 'virglrenderer'
'mesa' 'libjpeg-turbo' 'libpng' 'libusb' 'usbredir' 'numactl' 'libxkbcommon'
'libgcrypt' 'libtasn1' 'lzo' 'snappy' 'pipewire')
makedepends=('git' 'meson' 'ninja' 'python')
optdepends=('vfio-native: the setup tooling that points a domain at this build')
options=('!strip')
source=("git+${url}.git#tag=v${_tag}"
"https://download.qemu.org/qemu-${_qver}.tar.xz")
sha256sums=('SKIP'
'079ffbff8a7111bbc89022107cbabf3bbfd614d5fc9d7cc675991196aca12482')
prepare() {
cd "qemu-${_qver}"
for p in "${srcdir}"/vfio-native/patches/qemu/0[1-8]-*.patch; do
msg2 "applying ${p##*/}"
patch -Np1 --no-backup-if-mismatch -i "$p"
done
}
build() {
cd "qemu-${_qver}"
mkdir -p build && cd build
../configure --prefix=/opt/qemu-native --target-list=x86_64-softmmu \
--enable-kvm --enable-spice --enable-opengl --enable-virglrenderer \
--enable-tpm --enable-slirp --disable-docs --disable-werror --disable-user \
--disable-gtk --disable-sdl
ninja
}
package() {
cd "qemu-${_qver}/build"
DESTDIR="${pkgdir}" ninja install
}

View File

@@ -0,0 +1,23 @@
pkgbase = vfio-native
pkgdesc = Present a libvirt guest as a self-consistent physical machine, and tune it
pkgver = 1.1.0
pkgrel = 1
url = https://git.archworks.co/sandwich/vfio-native
install = vfio-native.install
arch = any
license = GPL-2.0-only
makedepends = git
depends = libvirt
depends = qemu-base
depends = python
depends = openssh
depends = bash
depends = ethtool
optdepends = mingw-w64-gcc: build the in-guest benchmark and the TIMER probe
optdepends = cpupower: set the host CPU governor
optdepends = vfio-native-kvm-dkms: patched KVM modules for the full level
optdepends = vfio-native-qemu: patched QEMU for the full level
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
sha256sums = SKIP
pkgname = vfio-native

View File

@@ -0,0 +1,51 @@
# Maintainer: Sandwich <sandwich@archworks.co>
#
# Platform-fidelity corrections and VFIO tuning for libvirt guests.
#
# Three packages:
# vfio-native scripts, patches, ACPI tables and the benchmark
# vfio-native-kvm-dkms the patched KVM modules, rebuilt by DKMS per kernel
# vfio-native-qemu QEMU 11.1.1 with the platform-identity patches, in /opt
pkgname=vfio-native
pkgver=1.1.0
pkgrel=1
pkgdesc="Present a libvirt guest as a self-consistent physical machine, and tune it"
arch=('any')
url="https://git.archworks.co/sandwich/vfio-native"
license=('GPL-2.0-only')
depends=('libvirt' 'qemu-base' 'python' 'openssh' 'bash' 'ethtool')
makedepends=('git')
optdepends=('mingw-w64-gcc: build the in-guest benchmark and the TIMER probe'
'cpupower: set the host CPU governor'
'vfio-native-kvm-dkms: patched KVM modules for the full level'
'vfio-native-qemu: patched QEMU for the full level')
install=vfio-native.install
source=("git+${url}.git#tag=v${pkgver}")
sha256sums=('SKIP')
package() {
cd "${srcdir}/${pkgname}"
install -Dm755 scripts/setup-vm.sh "${pkgdir}/usr/bin/vm-native-setup"
install -Dm755 scripts/verify-perf.sh "${pkgdir}/usr/bin/vm-native-verify"
install -Dm755 scripts/gpu-passthrough.sh "${pkgdir}/usr/bin/vm-native-gpu"
local share="${pkgdir}/usr/share/vfio-native"
install -Dm644 bench/vmbench.c "${share}/bench/vmbench.c"
install -Dm644 bench/timerprobe.c "${share}/bench/timerprobe.c"
install -Dm755 scripts/install-modules.sh "${share}/scripts/install-modules.sh"
install -Dm755 scripts/restore-stock-kvm.sh "${share}/scripts/restore-stock-kvm.sh"
install -Dm755 scripts/generate-tables.py "${share}/scripts/generate-tables.py"
install -Dm644 -t "${share}/acpi" acpi/*.aml acpi/*.dsl
install -dm755 "${share}/patches"
cp -r patches/. "${share}/patches/"
install -Dm644 -t "${share}/doc" README.md docs/*.md
# libvirt runs every executable in qemu.d/ after the main qemu hook, so
# this coexists with whatever hook the host already has.
install -Dm755 scripts/libvirt-hook-cpu-isolation.sh \
"${pkgdir}/etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh"
# e1000e offloads corrupt integrity-checked traffic on libvirt taps; host-wide by nature
install -Dm644 scripts/99-vfio-native-vnet-offload.rules \
"${pkgdir}/usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules"
}

View File

@@ -0,0 +1,83 @@
post_install() {
cat <<'MSG'
vfio-native installed.
Three commands. You do not need to read anything else.
vm-native-setup configure a libvirt domain
vm-native-verify check it is actually working
vm-native-gpu set up GPU passthrough
---------------------------------------------------------------------------
Getting a corrected, tuned guest:
1. In the Windows guest, once - turn off its own hypervisor. This is the
single most valuable change and it makes the VM FASTER:
bcdedit /set hypervisorlaunchtype off
Disable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -NoRestart
Reboot twice. Uninstall the QEMU and SPICE guest agents.
2. Move the guest disk to emulated NVMe (bus='nvme'), and give it a <serial>.
3. On the host, once:
sudo cpupower frequency-set -g performance
4. Shut the domain down and run:
vm-native-setup
It detects your CPU layout - AMD CCDs or Intel P/E cores - and asks four
questions, all with sensible defaults. It pins vCPUs onto real SMT pairs,
keeps the emulator off the vCPU cores, and writes the corrected CPU and
firmware settings. It backs the domain up first and prints how to revert.
5. Start the guest, let it idle a minute, then:
vm-native-verify
---------------------------------------------------------------------------
Fidelity levels, asked in step 4:
tuned performance tuning only, no platform corrections
native tuning + domain-level corrections. No patched binaries, no upkeep.
full lowest score. Needs vfio-native-kvm and a patched QEMU, and a
module rebuild after each kernel update.
'native' is a good default if you would rather not maintain anything.
A udev rule (99-vfio-native-vnet-offload.rules) turns TX offloads off on every
libvirt tap as it appears. The emulated e1000e NIC corrupts integrity-checked
traffic with them on; SSH to the guest fails with "Corrupted MAC on input".
It applies to every VM on the host; the throughput cost on a host<->guest
link is not measurable.
A libvirt hook is installed at /etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh.
It keeps host processes off the cores the guest is pinned to, automatically,
whenever the guest runs. Measured: stalls over 10us in a 10 second window fell
from ~100 to ~36. If your libvirt does not read qemu.d/, chain it from your
existing /etc/libvirt/hooks/qemu.
---------------------------------------------------------------------------
Leave the host at least 4 physical cores. Windows calibrates the TSC once at
boot, and if the host cannot schedule the guest's vCPU threads cleanly while
that runs, Windows abandons the TSC and every QueryPerformanceCounter call
costs ~1300ns instead of ~15 for the rest of that boot. Software that polls
the clock in a tight loop makes that call thousands of times a second, so it
shows up as constant hitching in an interactive session. 24 of 32 threads is
reliable, 32 of 32 is not, and vm-native-setup warns you when you go under
the margin.
It is a race, so one slow reading proves nothing. If vm-native-verify shows
QPC over 1000ns, reboot and measure again before changing any setting.
Patches, grouped by the detection each one clears:
/usr/share/vfio-native/patches/README.md
MSG
}
post_upgrade() { post_install; }