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:

  1. 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.
  2. 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.
  3. You can set up port forwarding, e.g. virsh qemu-monitor-command --hmp arm64-vm 'hostfwd_add ::2222-:22'. This will forward localhost:2222 to port 22 in the VM. You can then run ssh -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!