2.1 · Getting a guest running
How to Check KVM Is Enabled on Raspberry Pi 5 (/dev/kvm, lsmod, dmesg)
The theory articles are behind us; from here the series is hands-on. All output below is real, from a Raspberry Pi 5 (8 GB) running Raspberry Pi OS Bookworm, kernel 6.12 aarch64.
Before installing anything, confirm the host can actually do the job. This takes three commands, and the second one returns empty on a Raspberry Pi in a way that looks alarming and isn’t.
Doing this first is worth the two minutes. Installing the full virtualization stack and then discovering acceleration was never available is a considerably worse sequence.
Command 1: does /dev/kvm exist?
$ ls -l /dev/kvm
crw-rw----+ 1 root kvm 10, 232 Jun 11 15:08 /dev/kvm
This is the single most important check, and article 2 explained why: KVM initializes only if the CPU’s virtualization extension is present and active. If initialization succeeds, KVM creates this device file. Its existence is therefore proof that the entire chain worked — the silicon feature is there, the kernel found it, switched it on, and opened the door QEMU will later use.
Three details in that line are worth reading rather than skimming.
The leading c marks this as a character device — a byte-stream device rather than a block device or a regular file. That matters because it’s the interface QEMU opens and then drives with ioctl, exactly as described in article 5.
root kvm is the owner and group. The file is owned by root but group-owned by kvm, which is what makes unprivileged use possible: adding your user to the kvm group grants access without sudo. That’s a step in the next article.
The trailing + indicates an ACL is set on the file, granting access beyond the standard owner/group/other bits. Raspberry Pi OS does this by default, so permissions are typically friendlier than the base bits suggest.
If instead you get No such file or directory, KVM did not initialize. On a Raspberry Pi 5 the usual cause is running a 32-bit OS — check with uname -m, which must report aarch64. On x86 hardware the usual cause is virtualization being disabled in firmware, which is a BIOS/UEFI setting.
Command 2: is the kvm module loaded?
$ lsmod | grep kvm
$
Nothing. No output at all.
This is where a lot of people conclude something is broken, because every KVM guide says to check lsmod and every guide shows output. On a Raspberry Pi you get an empty result while /dev/kvm sits right there, and the two facts appear to contradict each other.
They don’t, and the explanation is worth understanding because it generalizes.
The puzzle: built-in versus loadable
A kernel feature can exist in two forms.
It can be a loadable module — compiled separately, loaded at runtime with modprobe, listed by lsmod. This is the flexible option, and it’s what most distributions do for most drivers.
Or it can be built into the kernel image — compiled directly into the kernel binary, present from boot, never loaded because it was never separate. This is what Raspberry Pi OS does with KVM.
lsmod only lists loadable modules. Built-in code never appears there, because there is no module to list. So an empty lsmod | grep kvm doesn’t mean KVM is missing — it means KVM isn’t a module. The capability is fully present either way; only the packaging differs.
The usual way to confirm this is the kernel config, but that has its own wrinkle on the Pi:
$ zcat /proc/config.gz | grep -i kvm
gzip: /proc/config.gz: No such file or directory
/proc/config.gz exposes the running kernel’s build configuration, where CONFIG_KVM=y would mean built-in and =m would mean module. Raspberry Pi OS doesn’t ship it — that’s an optional kernel feature and the Pi kernel omits it. On distributions that do provide it, that grep is the direct answer.
So we need a different route.
Command 3: what did the kernel say at boot?
$ dmesg | grep -i kvm
[ 0.046684] kvm [1]: nv: 554 coarse grained trap handlers
[ 0.046799] kvm [1]: IPA Size Limit: 40 bits
[ 0.046811] kvm [1]: GICV region size/alignment is unsafe, using trapping (reduced performance)
[ 0.046835] kvm [1]: vgic interrupt IRQ9
[ 0.046846] kvm [1]: VHE mode initialized successfully
This resolves it. KVM printed initialization messages at boot — at timestamp 0.046 seconds, before userspace existed, which is itself the giveaway that this is built-in code running during kernel startup rather than a module loaded later.
VHE mode initialized successfully is the line that matters. Article 2 covered what it means: KVM found the virtualization extension and is running in the modern configuration where the host kernel executes at EL2 without a split. Not merely working — working well.
The GICV region size/alignment is unsafe, using trapping (reduced performance) line is a Raspberry Pi–specific quirk and not a problem. One optimization for injecting interrupts directly into guests isn’t safely available on this hardware, so KVM traps them instead. In the terms of article 5, that means more VM exits — but cheap ones, handled inside the kernel without waking QEMU. It does not affect whether acceleration works, and it isn’t worth chasing.
If dmesg requires root on your system, prefix it with sudo.
The verdict
/dev/kvm exists ✓ KVM initialized and exposed its interface
crw-rw----+ ... root kvm ✓ character device, kvm group, ACL present
lsmod empty for kvm ✓ because KVM is BUILT IN, not a module
dmesg: VHE initialized ✓ riding the EL2 extension, in good mode
Three commands, one apparent contradiction, one clean resolution. The host can run accelerated aarch64 guests.
One practical consequence of the built-in arrangement is worth noting now, because it removes a diagnostic step that other guides rely on. A loadable module has a use count in lsmod output showing how many things currently hold it open, and a common demonstration is to start a VM and watch that number rise as QEMU opens /dev/kvm. With a built-in KVM there’s no module row and no counter, so that particular check isn’t available. Confirming that a running VM is genuinely using KVM has to be done another way — by inspecting the QEMU process, or by reading <domain type='kvm'> in the VM’s XML.
A note on what this does and doesn’t prove
/dev/kvm existing proves the kernel-side foundation is sound. It does not prove the userspace stack is installed, that libvirt is running, that your user has permission to use any of it, or that a virtual network exists.
Those are separate checks, and libvirt ships a tool that performs them all at once — virt-host-validate. It can’t run yet because it arrives with the libvirt packages, but it’s the natural next step, and reading its output correctly is its own skill given how many warnings it produces on a Raspberry Pi. That’s the next article, along with what each of the nine packages in the stack actually does.
Summary
ls -l /dev/kvmis the primary check. The file exists only if KVM initialized against a real CPU virtualization extension.- The leading
cmeans character device;root kvmownership is why thekvmgroup matters; a trailing+means an ACL is set. lsmod | grep kvmreturns nothing on Raspberry Pi OS because KVM is built into the kernel rather than compiled as a loadable module.lsmodlists only loadable modules./proc/config.gzwould normally settle built-in versus module, but Raspberry Pi OS doesn’t ship it.dmesg | grep -i kvmshows KVM’s boot-time initialization.VHE mode initialized successfullyconfirms the efficient EL2 configuration.- The
GICV ... reduced performancewarning is a known Pi quirk affecting one interrupt fast path. It costs cheap in-kernel exits, not acceleration. - Because KVM is built in, the “watch the module use count rise” trick isn’t available on the Pi.
Comments