Hi,
I recently got my hands on an M2-series mac and installed Asahi Linux on it. So far I’ve been really impressed and would like to say thanks to everyone that has made this possible!
I got the M2 because I’d read that it actually supports nested virtualization, so I’ve been playing around to see if it works. It’s early days but I believe I have something working so I thought I’d share the steps I’ve taken in case anyone else is interested.
Make sure you’re running Linux kernel 6.16+. There are some talks from Marc Zyngier about a major arm64 kvm refactor that was merged into 6.16; you’ll need this change.
You need an M2 series mac (ARMv8.6-A); I don’t know if the changes in the 6.16 kernel support nested virtualization in the M1 series (ARMV8.4-A?).
Download and build the latest version of QEMU (10.2.0 at this time). The default version I was able to get through dnf is 9.2.4, which seems to be missing support for some arm64 kvm virtualization.
You need to add an argument (kvm-arm.mode=nested) to the kernel commandline. I’m not a grub expert but I did this by updating GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub, then running sudo grub2-mkconfig -o /etc/grub2.cfg.
Reboot and you should see something like this in dmesg: kvm [1]: VHE+NV2 mode initialized successfully
Update your qemu commandline to include -M virt,accel=kvm,virtualization=on
I ran this with an Ubuntu 25.10 vm (kernel 6.17.0) and got this in the vm’s dmesg:
kvm [1]: nv: 567 coarse grained trap handlers
kvm [1]: nv: 664 fine grained trap handlers
kvm [1]: PAGE_SIZE not supported at Stage-2, giving up
I assume this has something to do with 16k page sizes but haven’t dug into it yet. I also tried running a Windows 11 arm64 vm with nested virtualization but it hung at bootmgr.
The rough steps I took to do this (all in the Ubuntu 25.10 vm):
Get the source for current kernel
Copy the current kernel config into the kernel source directory (eg cp /boot/config-6.17.0-8-generic .config)
Modify the config to use 16k pages. I diff’d the current config against the (Ubuntu) Asahi Linux kernel config to figure out what needed changing.
Remove some Ubuntu-specific config options (CONFIG_SYSTEM_TRUSTED_KEYS and CONFIG_SYSTEM_REVOCATION_KEYS)
Direct the build to use the existing config present in the directory (make olddefconfig)
Install many build deps (see one of the guides for building kernel source for the full list)
Build the kernel (eg make -j2 olddefconfig bindeb-pkg LOCALVERSION=-stock)
Once building has finished, install the new kernel build (eg cd .. && sudo dpkg -i linux-headers-6.17.2-stock_6.17.2-10_arm64.deb linux-libc-dev_6.17.2-10_arm64.deb linux-image-6.17.2-stock_6.17.2-10_arm64.deb). I believe that adds a new boot option but ran sudo update-grub anyway.
Reboot
Here’s the (massaged) diff for what I changed in the config (new vs old, with new first):
Thank you very much for your detailed document on this topic. I was trying to do the same thing and came across your post. I have some questions regarding your setup though.
The machine I’m working on doesn’t have an Apple M2 CPU, instead it has an Nvidia Grace (with Neoverse core).
I have updated the machine to use Kernel 6.16, and added kvm-arm.mode=nested to its boot command. I also updated the machine to use QEMU 10.1.2 which should have support for nested KVM in ARM processor (QEMU version 10.1.0 released - QEMU)
The dmesg log on my box shows the following when booting:
Note that we have the VHE+NV2 mode initialized successfully, same as what you had.
However, when I tried to run qemu-system-aarch64 with virtualization=on, the command would hang. Searching around, it seems this line GICv3: no GICV resource entry might be the reason.
I saw that you have that line as well, but it’s from dmesg of the L1 host instead. Can you share the dmesg of your host (ie. the L0 machine) so I can check if GICv3: no GICV resource entry is the cause or not?
This is enlightening. For your case some dmesg lines are different, but you do also have GICv3: no GICV resource entry so I think that’s not the reason.
I’ll try your qemu-system-aarch64 command and report back if that works