Skip Navigation

Using qemu-system-aarch64

One of the commands provided by QEMU is qemu-system-aarch64, which provides a virtual machine supporting the AArch64 instruction set architecture. AArch64 is the 64-bit version of the Arm instruction set. This document explains how to use qemu-system-aarch64 with the “virt” machine type, booting in UEFI mode.

Page Contents

Video Demonstration


Watch at Internet Archive

Arm Architecture Basics

Arm processors are integrated into System-on-a-Chip (SoC) packages that are then soldered to the hardware devices that utilize them. This approach to packaging is fundamentally different from the x86_64 standard we see with AMD and Intel CPUs, which are normally sold by themselves and then dropped into a socket on the motherboard. (AMD and Intel CPU packages that have to be soldered to the motherboard do exist and can be found on single-board computers and other small devices.)

Since Arm CPUs are part of an appliance that is sold to the consumer, it is up to the appliance vendor to decide how to implement the details of the boot process. Most Arm-based appliances also ship with a preinstalled operating system of some kind, allowing the vendor to maintain vertical control of both the hardware and software stack. Historically, vendors who used Linux would maintain a private fork of the entire kernel, with their own modifications for their specific board added.

To make maintenance easier, the Linux kernel abstracts away some of the hardware differences between different Arm-based devices. This abstraction, called a device tree, allows the kernel to be adapted to different Arm-based systems dynamically at boot time.1 A device tree is simply a data structure that describes the details and locations of hardware devices.2 On the Arm platform, both Das U-Boot3 and the Unified Extensible Firmware Interface4 are able to pass the device tree to the Linux kernel, allowing it to boot.1 Recent versions of QEMU include the TianoCore reference UEFI implementation, originally created by Intel.5 The PC System Flash interface allows TianoCore to be used when starting a QEMU VM.6

The TianoCore code is split into two files: a read-only file containing the actual UEFI code, and a read/write file for storing UEFI variables (such as boot preferences). We can use the read-only file from its default location in the QEMU share directory; however, each AArch64 VM that uses UEFI needs to have its own copy of the edk2-arm-vars.fd file.

Initial Preparation

These steps only need to be completed once when the virtual machine is first created. When restarting the VM at a later time, simply double-click the batch file.

  1. Download the latest Alpine Linux “Virtual” ISO file for the aarch64 architecture.
  2. Create a dedicated directory (folder) on your host system to contain this virtual machine.
  3. Move the downloaded ISO file from the first step into the newly created directory.
  4. Copy the edk2-arm-vars.fd file from your QEMU share directory into the newly created directory. Look for the QEMU share directory at /usr/share/qemu (Linux), /opt/homebrew/share/qemu or /usr/local/share/qemu (macOS), or C:\Program Files\qemu\share (Windows).
  5. Open a terminal window, and use the cd command to navigate to the directory you created.
  6. Create the disk image by running the following command:
qemu-img create -f qcow2 alpine.qcow2 40G
  1. Prepare a script to make it easier to run QEMU (see below).
  2. Run your script to launch the virtual machine.

The QEMU Command

The following example assumes Alpine Linux version 3.16.2 and will need to be adjusted for the version you actually downloaded. The QEMU command looks like this on Linux:

qemu-system-aarch64 \
    -name "Alpine Linux AArch64" \
    -machine type=virt,iommu=smmuv3 \
    -accel tcg \
    -cpu cortex-a57 \
    -m 2048 \
    -rtc base=utc \
    -drive if=pflash,format=raw,file="/usr/share/qemu/edk2-aarch64-code.fd",readonly=on \
    -drive if=pflash,format=raw,file=edk2-arm-vars.fd \
    -drive file="alpine.qcow2",if=virtio,format=qcow2 \
    -drive file="alpine-virt-3.16.2-aarch64.iso",format=raw,readonly=on \
    -boot menu=on \
    -display gtk \
    -device virtio-vga \
    -nic user,id=NAT,model=virtio-net-pci,mac=02:00:00:00:00:01 \
    -device qemu-xhci \
    -device usb-kbd \
    -device usb-tablet

Let’s take a look at what each argument to qemu-system-aarch64 means:

-name
This is a human-readable name for the VM. In this case, I called it “Alpine Linux AArch64”, but it really could be named anything you want.
-machine
This is the machine type to emulate, along with any machine-specific options. In this case, we’re using the “virt” machine type, which is unique to QEMU and doesn’t represent any real hardware device. We’re enabling an input-output memory management unit (IOMMU) with the “iommu” setting.
-accel
This option specifies the accelerator to use. Since you’re probably running this command on an AMD or Intel CPU, the only valid option here is “tcg” for Tiny Code Generator. This is a dynamic recompiler that emulates the desired CPU. If you happen to have a Mac with Apple silicon (M1 or subsequent CPU), you could configure a Hypervisor.Framework entitlement for qemu-system-aarch64 and use the “hvf” accelerator here. Linux users with an Arm CPU could use the “kvm” accelerator here (if the host hardware supports it).
-cpu
On most Arm boards, the CPU type is fixed by the machine type. However, the “virt” board works more like a PC and supports different CPU types. Here, I chose the Cortex A57, which is a fairly capable processor.
-m
This option specifies the amount of RAM in MiB (mebibytes, or binary megabytes, which are multiples of 220). In this example, I specified 2048, which corresponds to 2 GiB RAM. Be careful not to give the guest system more RAM than your host computer physically has! Leave at least 2 GiB for the host operating system.
-rtc
The -rtc option specifies the type of real time clock to give to the guest machine. UNIX (and therefore Linux and BSD) guests expect the rtc to use UTC time, while Windows guests expect the rtc to use localtime. Remember this setting is based on the guest system, not whatever system you have on your host computer. In short, if you are creating a Windows virtual machine, set -rtc base=localtime. Otherwise, leave this option as I have it in my example.
-drive
Each virtual hard disk that you connect to the virtual machine will have a -drive option associated with it. There are a significant number of parameters that can be specified for a drive, depending on what type of disk image you’re using, what type of disk controller the virtual machine will support, and so forth. In this case, we have 4 drives: two PC System Flash drives to implement UEFI support, our virtual disk image (alpine.qcow2), and the CD-ROM ISO file.
-boot
In UEFI mode, we use this option to enable the boot menu.
-display
QEMU supports different kinds of display window for the virtual machine’s screen. Depending on what you’re doing with the VM, and what outputs are available on your computer, you might use a different display. I recommend “gtk” if it is available for most general use cases. If that doesn’t work for some reason, try “sdl”. On a Mac system, you could also try “cocoa”.
-device virtio-vga
This might be a little confusing at first, but displaying output in QEMU is controlled by two different things. The -display option specifies what kind of window to use for the output on the host system. However, we still need to specify what kind of video card to emulate in the guest system. With current versions of QEMU, “virtio-vga” is normally a safe choice.
-nic
The -nic option is a shortcut option that configures the network that the virtual machine will see. QEMU supports a wide array of different network configurations. However, if you just need your Internet connection to work inside your VM, the “user” network is sufficient. You can set the MAC address of your virtual network card to any value, but the standard for locally-assigned MAC addresses is to have the twos bit set in the first octet of the MAC address. The easiest way to do this is to start your VM MAC addresses with 02:.
-device qemu-xhci
This option enables the Extensible Host Controller Interface (xHCI), which is a USB 3.0 host controller.
-device usb-kbd
The usb-kbd is a USB keyboard, which is plugged into the xHCI controller. Without a keyboard, the VM would ignore any keystrokes we sent to it, which would make it difficult to use.
-device usb-tablet
The usb-tablet device adds a virtual USB tablet to the VM. Unlike a virtual mouse, QEMU doesn’t have to capture the mouse pointer and restrict it to the VM window when using this device.

Many other options are available when starting a QEMU virtual machine. These are listed in the Invocation section of the QEMU documentation.

Scripting QEMU

Starting the virtual machine by hand with the full command line each time would be cumbersome and error-prone. Consequently, I recommend creating a script that contains the QEMU command. The following example is a Bourne shell script for Linux or macOS.

#!/bin/sh
#
# Alpine Linux on AArch64
#

qemu-system-aarch64 \
    -name "Alpine Linux AArch64" \
    -machine type=virt,iommu=smmuv3 \
    -accel tcg \
    -cpu cortex-a57 \
    -m 2048 \
    -rtc base=utc \
    -drive if=pflash,format=raw,file="/usr/share/qemu/edk2-aarch64-code.fd",readonly=on \
    -drive if=pflash,format=raw,file=edk2-arm-vars.fd \
    -drive file="alpine.qcow2",if=virtio,format=qcow2 \
    -drive file="alpine-virt-3.16.2-aarch64.iso",format=raw,readonly=on \
    -boot menu=on \
    -display gtk \
    -device virtio-vga \
    -nic user,id=NAT,model=virtio-net-pci,mac=02:00:00:00:00:01 \
    -device qemu-xhci \
    -device usb-kbd \
    -device usb-tablet

echo "QEMU has finished. Press Enter to continue."
read throwaway

I have downloadable templates in Bourne shell script format for Linux/macOS and batch file and PowerShell formats for Windows. These templates can be used as a starting point for your own VM scripts.

References and Further Reading


  1. Device Tree. Arm Community. 

  2. Linux and the Devicetree. Linux Kernel Documentation. 

  3. Das U-Boot – the Universal Boot Loader

  4. Unified Extensible Firmware Interface Forum

  5. What is TianoCore?

  6. Features/PC System Flash. QEMU Wiki. 

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.