How to make a better ARM virtual machine (armhf/aarch64) with UEFI
Over a year ago, I wrote about making ARM virtual machines. Times have changed a lot since then — the release of Apple M1 has dramatically changed the perception of ARM. No longer is ARM a niche platform for low-power gadgets like phones or tablets, but a viable desktop computing platform. Similarly, in the server space, Amazon Graviton and Ampere Altra have gained traction. My old blog post presented only a quick hack to get the ARM virtual machine to boot — by copying the kernel image. This leaves a lot to be desired. Somehow, despite this, it quickly became one of the popular posts on my blog. Today, we shall rectify that flaw and present a way to boot the latest kernel installed in the virtual machine using the Unified Extensible Firmware Interface (UEFI).
Again, like before, this tutorial will use Debian as an example, but the same methodology should work for other distributions. If you are looking for a simple chroot, you should instead follow the original post.
Step 1: Image Creation
Like last time, we will start by creating a raw disk image file. To do this, run:
$ sudo fallocate -l 8G /var/lib/libvirt/images/arm64-vm.img
Step 2: Run virt-install
to install Debian
With UEFI support, this process is very simple — you can simply run
virt-install
and follow the on-screen instructions:
$ virt-install \
--name arm64-vm \
--arch aarch64 \
--machine virt \
--os-type Linux \
--os-variant debian11 \
--ram 2048 \
--vcpus 2 \
--import \
--disk /var/lib/libvirt/images/arm64-vm.img,bus=virtio \
--graphics none \
--network user,model=virtio \
--features acpi=off \
--boot uefi \
--location https://deb.debian.org/debian/dists/bullseye/main/installer-arm64/
Note that you might need to pass --os-variant debian10
if debian11
is not
available.
The key flags here are --boot uefi
, which enables UEFI, and --location
,
which passes the path to the Debian installer to virt-install
. It will then
automatically download the installer and run it, without requiring you to
download the disk image. You can also pass --vcpus
to give the VM more cores,
e.g. --vcpus 4
for a quad core VM.
For 32-bit ARM, the --location
trick doesn’t work. Instead, you should
download the latest armhf
netinst image for Debian and use --cdrom
:
$ wget https://cdimage.debian.org/debian-cd/current/armhf/iso-cd/debian-11.2.0-armhf-netinst.iso
$ virt-install \
--name armhf-vm \
--arch armv7l \
--machine virt \
--os-type Linux \
--os-variant debian11 \
--ram 2048 \
--vcpus 2 \
--import \
--disk /var/lib/libvirt/images/armhf-vm.img,bus=virtio \
--graphics none \
--network user,model=virtio \
--features acpi=off \
--boot uefi \
--cdrom debian-11.2.0-armhf-netinst.iso
Step 3: Use debian-installer
Simply follow the on-screen instructions to select your language and country. If everything goes well, it should automatically configure the network. If not, you can follow the instructions on the screen to set up networking, but that is outside the scope of this quick tutorial.
Assuming network autoconfiguration worked, you should end up on this screen:
[ (1*installer) 2 shell 3 shell 4- log ][ Mar 19 7:19 ]
┌─────────────────────┤ [!] Configure the network ├─────────────────────┐
│ │
│ Please enter the hostname for this system. │
│ │
│ The hostname is a single word that identifies your system to the │
│ network. If you don't know what your hostname should be, consult your │
│ network administrator. If you are setting up your own home network, │
│ you can make something up here. │
│ │
│ Hostname: │
│ │
│ debian_______________________________________________________________ │
│ │
│ <Go Back> <Continue> │
│ │
└───────────────────────────────────────────────────────────────────────┘
<Tab> moves; <Space> selects; <Enter> activates buttons
Continue to follow on-screen instructions. When prompted to select a Debian
archive mirror, you are highly recommended to select enter information
manually
. Debian installer will populate it with deb.debian.org
, which is
usually the fastest option anywhere in the world (it uses a CDN).
The installer will download some components. Eventually, it will prompt you to set up users and passwords, but simply follow on-screen instructions. Then, wait for the installer to load additional components.
Once this is done, you will be prompted to partition your system:
[ (1*installer) 2 shell 3 shell 4- log ][ Mar 19 7:27 ]
┌────────────────────────┤ [!!] Partition disks ├─────────────────────────┐
│ │
│ The installer can guide you through partitioning a disk (using │
│ different standard schemes) or, if you prefer, you can do it │
│ manually. With guided partitioning you will still have a chance later │
│ to review and customise the results. │
│ │
│ If you choose guided partitioning for an entire disk, you will next │
│ be asked which disk should be used. │
│ │
│ Partitioning method: │
│ │
│ Guided - use entire disk │
│ Guided - use entire disk and set up LVM │
│ Guided - use entire disk and set up encrypted LVM │
│ Manual │
│ │
│ <Go Back> │
│ │
└─────────────────────────────────────────────────────────────────────────┘
<Tab> moves; <Space> selects; <Enter> activates buttons
Using Guided - use entire disk
and then All files in one partition
is
sufficient for our purposes. The installer will do something like:
[ (1*installer) 2 shell 3 shell 4- log ][ Mar 19 7:28 ]
┌────────────────────────┤ [!!] Partition disks ├─────────────────────────┐
│ │
│ This is an overview of your currently configured partitions and mount │
│ points. Select a partition to modify its settings (file system, mount │
│ point, etc.), a free space to create partitions, or a device to │
│ initialize its partition table. │
│ │
│ Virtual disk 1 (vda) - 8.6 GB Virtio Block Device - │
│ > 1.0 MB FREE SPACE ▒ │
│ > #1 536.9 MB B f ESP ▒ │
│ > #2 7.0 GB f ext4 / ▒ │
│ > #3 1.0 GB f swap swap ▒ │
│ > 1.0 MB FREE SPACE ▒ │
│ ▒ │
│ Undo changes to partitions 0 │
│ Finish partitioning and write changes to disk . │
│ │
│ <Go Back> │
│ │
└─────────────────────────────────────────────────────────────────────────┘
<F1> for help; <Tab> moves; <Space> selects; <Enter> activates buttons
This is fine, select Finish partitioning and write changes to disk
, and the
process should continue. When prompted to write the changes to disk, select
Yes
.
You can also choose to manually partition, but keep in mind you will need an EFI system partition (ESP), and the minimum size for that is 100 MiB.
Either way, the installer will do its work. Once the base system is installed, it’ll prompt you to install additional software:
[ (1*installer) 2 shell 3 shell 4- log ][ Mar 19 7:56 ]
┌───────────────────────┤ [!] Software selection ├────────────────────────┐
│ │
│ At the moment, only the core of the system is installed. To tune the │
│ system to your needs, you can choose to install one or more of the │
│ following predefined collections of software. │
│ │
│ Choose software to install: │
│ │
│ [ ] ... KDE Plasma - │
│ [ ] ... Cinnamon ▒ │
│ [ ] ... MATE ▒ │
│ [ ] ... LXDE ▒ │
│ [ ] ... LXQt ▒ │
│ [ ] web server ▒ │
│ [*] SSH server 0 │
│ [ ] standard system utilities . │
│ │
│ <Continue> │
│ │
└─────────────────────────────────────────────────────────────────────────┘
<Tab> moves; <Space> selects; <Enter> activates buttons
Since everything can be installed later, you don’t have to install anything. However, the SSH server is rather useful, especially since SSHing into the VM offers a massively better experience than typing into the console.
Once the system finishes installing whatever additional software, it will
install grub
, the bootloader, which will be loaded by the UEFI firmware to
boot the system. At this point, the installation will be complete:
[ (1*installer) 2 shell 3 shell 4- log ][ Mar 19 8:01 ]
┌───────────────────┤ [!!] Finish the installation ├────────────────────┐
│ │
┌│ Installation complete │
││ Installation is complete, so it is time to boot into your new system. │
││ Make sure to remove the installation media, so that you boot into the │
││ new system rather than restarting the installation. │
││ │
││ <Go Back> <Continue> │
└│ │
└───────────────────────────────────────────────────────────────────────┘
<Tab> moves; <Space> selects; <Enter> activates buttons
Simply press Continue
, and the system will reboot. virt-install
will take
care of removing the installation media. After rebooting, you should be in a
serial console. Your VM is now ready!
To leave the console, press Ctrl+]. To return, run virsh
console arm64-vm
(or whatever name you passed to --name
argument of
virt-install
).
SSH Access
By default, virt-install
will use user-mode networking. This is the easiest
form of networking to set up, but the internal IPs generated are not actually
accessible from the outside, including your host machine. You have two options:
- You could reconfigure the VM to use a virtual NAT network, which would allow
access from the host. The easiest way to do this is to open the VM in
virt-manager
and change the network source. - You could reconfigure the VM to use a bridged network, which would allow it to show up on your local network. This is out-of-scope for this quick guide.
- You can set up port forwarding, e.g.
virsh qemu-monitor-command --hmp arm64-vm 'hostfwd_add ::2222-:22'
. This will forwardlocalhost:2222
to port22
in the VM. You can then runssh -p 2222 localhost
to access the VM.
Conclusion
Congratulations! You now have an ARM virtual machine. Unlike last time, upgrading the kernel should be completely seamless. Enjoy!