2.1 · Getting a guest running

How to Check KVM Is Enabled on Raspberry Pi 5 (/dev/kvm, lsmod, dmesg)

· updated 2026-10-16

KVM · Linux · Raspberry Pi · Virtualization

Part 1 of this series explained how virtualization works, from the processor up to QEMU and libvirt. Part 2 is hands-on: it sets up the host and builds a first VM. The output in Part 2 was captured on home1, an SD card for my Raspberry Pi 5 (8 GB), running Raspberry Pi OS Bookworm with Linux kernel 6.12, 64-bit.

Before installing anything, it is worth confirming that the host can actually run accelerated VMs. It takes three commands, and the second one returns nothing on a Raspberry Pi, in a way that looks alarming and is not.

Doing this first takes two minutes. Installing the whole virtualization stack and then discovering that acceleration was never available is a much worse way round.

What each check proves

Each command checks a different layer, and each layer only exists if the one below it worked:

Four layers, from bottom to top: the Cortex-A76 processor at EL2, checked by dmesg showing VHE mode initialized; KVM in the kernel, checked by lsmod, which finds nothing because KVM is built in; /dev/kvm, checked by ls, which shows it exists; and your user, whose access the same ls shows through the kvm group and an ACL.
From the processor up to your user: four layers, and the command that checks each.

Check 1: does /dev/kvm exist?

home1@home1:~ $ ls -l /dev/kvm
crw-rw----+ 1 root kvm 10, 232 Jun 11 15:08 /dev/kvm

This is the most important check. As post 1.2 explained, KVM only starts if the processor’s virtualization extension is present and usable, and when it starts, it creates this file. So the file existing proves the whole chain worked: the processor has the feature, the kernel found it and switched it on, and the interface QEMU will use is in place.

Three details in that line are worth reading:

DetailWhat it means
The leading cA character device: a stream of bytes, rather than a disk or an ordinary file. QEMU opens it, and then controls it with ioctl calls, as post 1.5 described.
root kvmOwned by root, but belonging to the kvm group. That is what lets an ordinary user use it, by joining the group, without sudo. The next post does that.
The trailing +An ACL, an extra permission rule, is set on the file. Raspberry Pi OS adds one by default, so access is usually more generous than the basic permissions suggest.

If instead you get No such file or directory, KVM did not start. On a Raspberry Pi 5, the usual cause is a 32-bit operating system: uname -m must report aarch64. On x86 computers, the usual cause is virtualization being switched off in the firmware settings.

Check 2: is the kvm module loaded?

home1@home1:~ $ lsmod | grep kvm

Nothing at all.

This is where many people decide something is broken. Most KVM guides tell you to check lsmod, and they all show output. On a Raspberry Pi you get nothing, while /dev/kvm is clearly there, and the two seem to contradict each other.

They do not, and the explanation is worth knowing, because it applies well beyond KVM.

Built in, or loaded?

A part of the Linux kernel can be included in one of two ways:

A loadable moduleBuilt into the kernel
How it is includedCompiled separately, and loaded while the system runs, with modprobeCompiled into the kernel file itself
When it is presentFrom when it is loadedFrom the moment the kernel starts
Listed by lsmodYesNo: there is no separate module to list

Most distributions build most drivers as modules, because it is flexible. Raspberry Pi OS builds KVM into the kernel.

lsmod only lists loadable modules. Code built into the kernel never appears, because there is no module. So an empty lsmod | grep kvm does not mean KVM is missing. It means KVM is not a module. The capability is the same either way; only the packaging differs.

The usual way to confirm this is the kernel’s build settings, which many kernels make available in /proc/config.gz. On the Pi, that has a catch of its own:

home1@home1:~ $ zcat /proc/config.gz 2>/dev/null | grep -i kvm
home1@home1:~ $ zcat /proc/config.gz 2>/dev/null
home1@home1:~ $ zcat /proc/config.gz
gzip: /proc/config.gz: No such file or directory

The first two commands printed nothing, because 2>/dev/null hides error messages. Without it, the reason appears: the file does not exist. On kernels that provide it, it would show CONFIG_KVM=y for built in, or CONFIG_KVM=m for a module. Raspberry Pi OS’s kernel does not provide it by default, so another route is needed.

Check 3: what did the kernel say when it started?

home1@home1:~ $ dmesg 2>/dev/null | 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 settles it. KVM printed these messages as the kernel started, 0.046 seconds after power-on, before any programs were running. That timing is itself the clue: this is code built into the kernel, running during start-up, not a module loaded later.

VHE mode initialized successfully is the line that matters. As post 1.2 explained, it means KVM found the virtualization extension and is running in the efficient arrangement where the host’s kernel itself runs at EL2, the processor’s virtualization level. So KVM is not just working, it is working in its best mode.

The other lines are details of how KVM was set up:

  • IPA Size Limit: 40 bits means a guest’s physical memory addresses can use up to 40 bits: up to 1 TiB of address space, far more than any VM on this Pi will need.
  • GICV region size/alignment is unsafe, using trapping (reduced performance) is specific to this hardware, and not a problem. Part of the ARM interrupt controller cannot be mapped straight through to guests on this board, so KVM catches the guests’ accesses to it and handles them itself, inside the kernel. In the terms of post 1.5, that means extra VM exits, but quick ones, handled without involving QEMU. It does not affect whether acceleration works.

dmesg may need sudo on some systems.

The verdict

CheckResultMeaning
/dev/kvm existsYesKVM started, and its interface is in place
crw-rw----+ ... root kvmAs expectedA character device, the kvm group, and an ACL
lsmod | grep kvmNothingBecause KVM is built into the kernel, not a module
dmesgVHE mode initialized successfullyRunning at EL2, in its efficient mode

Three commands, one apparent contradiction, one clean explanation. This host can run accelerated ARM64 guests.

One consequence of KVM being built in removes a check other guides use. A loadable module shows a count in lsmod of how many things are using it, and a common demonstration is to start a VM and watch that number rise as QEMU opens /dev/kvm. With KVM built in, there is no module line and no count. To confirm a running VM really uses KVM, look elsewhere: at <domain type='kvm'> in its XML, as post 3.4 does, or at -accel kvm in the QEMU command, as post 3.9 does.

What this does not prove

/dev/kvm existing proves the kernel’s side is sound. It does not prove that the programs on top are installed, that libvirt is running, that your user is allowed to use them, or that a virtual network exists.

Those are separate checks, and libvirt comes with a tool that runs them all at once, virt-host-validate. It is not available yet, because it comes with libvirt. That is the next post, along with what each of the nine packages in the stack does.

Summary

  • ls -l /dev/kvm is the main check. The file only exists if KVM started on a processor with a working virtualization extension.
  • The leading c means a character device, root kvm is why the kvm group matters, and a trailing + means an ACL is set.
  • lsmod | grep kvm shows nothing on Raspberry Pi OS, because KVM is built into the kernel, not a loadable module. lsmod only lists modules.
  • /proc/config.gz would normally show which, but Raspberry Pi OS’s kernel does not provide it.
  • dmesg | grep -i kvm shows KVM starting with the kernel. VHE mode initialized successfully confirms the efficient EL2 mode.
  • The GICV ... reduced performance line is specific to this board: some interrupt controller accesses are handled by KVM in the kernel, which costs quick VM exits, not acceleration.
  • Because KVM is built in, lsmod cannot show whether a VM is using it. Check the VM’s XML or its QEMU command instead.

Comments

get new posts

About one email a week, and only when there is something new.

Subscribe →