Prerequisites:
- A PC with a Linux distribution - Arch Linux
amd64used here
Dependencies (for Arch Linux amd64 but very easy to get on most distros):
edk2-armvirt(or a prebuilt firmware binary from the Unofficial EDK2 nightly build project [some builds may have issues, here's a direct link to a build I confirmed to work])qemu-arch-extra(provides the commandqemu-system-aarch64)wget(or any downloader)gvim(or any editor)openssh(or QEMU console)
- Download the Arch Linux ARM generic tarball and create an image, replacing
60Gwith your desired maximum size.
wget http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz
qemu-img create -f qcow2 arch-aarch64.qcow2 60GTip
Step 1 equivalent for Debian guest:
wget https://cdimage.debian.org/cdimage/release/current/arm64/iso-dvd/debian-12.7.0-arm64-DVD-1.iso
wget https://cdimage.debian.org/cdimage/release/current/arm64/iso-dvd/SHA256SUMS
shasum -a 256 --ignore-missing -c SHA256SUMS
qemu-img create -f qcow2 debian-aarch64.qcow2 60GUsers of Debian guest should now skip to step 7.
- Become
root, connect the image tonbdand partition it withfdisk.
sudo modprobe nbd
sudo qemu-nbd --connect=/dev/nbd0 arch-aarch64.qcow2
sudo fdisk /dev/nbd0- then
g(to create a new GPT partition table) - then
n(to create a new partition), then Enter twice, then+400Mand Enter - then
t(to change the type), then1for EFI System Partition - then
nand Enter three times, thenwto write changes and exit
- Format the partitions of the image, mount them, and extract the Arch Linux ARM tarball to them.
sudo mkfs.vfat /dev/nbd0p1
sudo mkfs.ext4 /dev/nbd0p2
sudo mkdir rootfs
sudo mount /dev/nbd0p2 rootfs
sudo mkdir rootfs/boot
sudo mount /dev/nbd0p1 rootfs/boot
sudo bsdtar -xpf ArchLinuxARM-aarch64-latest.tar.gz -C rootfs- Edit
fstab.
- You will need both partitions' UUIDs - the UUID of the
vfatpartition in/dev/nbd0p1looks likeUUID="XXXX-XXXX"and the UUID of theext4partition in/dev/nbd0p2looks likeUUID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX":
sudo blkid
- Then, edit
rootfs/etc/fstab:
sudo vim rootfs/etc/fstab
- Paste the following, replacing each instance of
Xwith the corresponding digit of the UUID of the corresponding partition,/dev/nbd0p1and/dev/nbd0p2respectively, then save the file:
/dev/disk/by-uuid/XXXX-XXXX /boot vfat defaults 0 0
/dev/disk/by-uuid/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX / ext4 defaults 0 0
- Create
startup.nsh, which is read by the UEFI firmware to initially boot.
- Edit
rootfs/boot/startup.nsh:
sudo vim rootfs/boot/startup.nsh- Paste the following, replacing each instance of
Xwith the corresponding digit of the UUID of the/dev/nbd0p2partition, then save the file:
Image root=UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw initrd=\initramfs-linux.img
- Unmount the partitions,
sync, disconnect the image fromnbd, and exit therootshell.
sudo umount -R rootfs
sudo sync
sudo qemu-nbd --disconnect /dev/nbd0
sudo rmmod nbd- Create flash images for the UEFI firmware and variables
Note
if you downloaded the RELEASEAARCH64_QEMU_EFI.fd instead of using the one from your distro's package, use that here in place of the QEMU_CODE.fd. AARCH64_QEMU_EFI.fds (which are installed into "flash0.img" here) can very slowly become slightly outdated over time. After using one with an Arch Linux ARM emulator for several years, then installing a new emulator from scratch and comparing its behavior with the old one, I've noticed very slight, subtle differences in behavior between them in the pre-boot stage before guest OS code runs. I would say the newer one's behavior seems slightly more polished and desirable.
truncate -s 64M flash0.img
truncate -s 64M flash1.img
dd if=/usr/share/edk2-armvirt/aarch64/QEMU_CODE.fd of=flash0.img conv=notrunc- Launch QEMU, removing or adding anything you see fit.
qemu-system-aarch64 -M virt -m 8192 -cpu cortex-a72 -smp 8 \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash0.img \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash1.img \
-drive if=none,file=arch-aarch64.qcow2,format=qcow2,id=hd0 \
-device virtio-scsi-pci,id=scsi0 \
-device scsi-hd,bus=scsi0.0,drive=hd0,bootindex=1 \
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
-monitor none -display none -vga noneTip
Step 8 equivalent for Debian guest:
qemu-system-aarch64 -M virt -m 8192 -cpu cortex-a72 -smp 8 \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash0.img \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash1.img \
-drive if=none,file=debian-aarch64.qcow2,format=qcow2,id=hd0 \
-cdrom debian-12.7.0-arm64-DVD-1.iso \
-device virtio-scsi-pci,id=scsi0 \
-device scsi-hd,bus=scsi0.0,drive=hd0,bootindex=2 \
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
-nographic- Upon successful first boot, initialize Arch Linux ARM and install a new bootloader.
- Log in as
alarm, passwordalarm:
ssh -p 2222 alarm@localhost
- Become
root, passwordroot:
su- Initialize the
pacmankeyring, update the system and installefibootmgr, replacing each instance ofXwith the corresponding digit of the UUID of the/dev/nbd0p2partition from earlier (which is now/dev/sda2), then shut down:
pacman-key --init
pacman-key --populate archlinuxarm
pacman -Syu
pacman -S efibootmgr
efibootmgr --disk /dev/sda --part 1 --create --label "Arch Linux ARM" --loader /Image --verbose \
--unicode 'root=UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw initrd=\initramfs-linux.img'
poweroffTip
Step 9 equivalent for Debian guest:
- At the EDK II Shell, type this command
FS1:\efi\boot\grubaa64.efiUsers of Debian guest can choose "Install" and use the guided installer.
- Launch QEMU again, exactly as in step 8.
qemu-system-aarch64 -M virt -m 8192 -cpu cortex-a72 -smp 8 \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash0.img \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash1.img \
-drive if=none,file=arch-aarch64.qcow2,format=qcow2,id=hd0 \
-device virtio-scsi-pci,id=scsi0 \
-device scsi-hd,bus=scsi0.0,drive=hd0,bootindex=1 \
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
-monitor none -display none -vga noneTip
Step 10 equivalent for Debian guest:
qemu-system-aarch64 -M virt -m 8192 -cpu cortex-a72 -smp 8 \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash0.img \
-drive if=pflash,media=disk,format=raw,cache=writethrough,file=flash1.img \
-drive if=none,file=debian-aarch64.qcow2,format=qcow2,id=hd0 \
-device virtio-scsi-pci,id=scsi0 \
-device scsi-hd,bus=scsi0.0,drive=hd0,bootindex=2 \
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
-nographic- Proceed with configuring Arch Linux ARM as normal (time, locales, users, software, configuration), using the Arch Linux Wiki as a guide.
Note
If your host has a simple network configuration, you can replace the slower -nic user argument with an efficient -netdev tap argument if you want using my minimal tap0 guide.

@horvathcsabalaszlo interesting, i'm not able to reproduce that. The Arch Linux ARM directions are still booting for me when clean installed with a working
AARCH64_QEMU_EFI.fd.The behavior of the EDK2 Shell could be influenced by what specific
QEMU_CODE.fd, AKAQEMU_EFI.fd, AKAAARCH64_QEMU_EFI.fdyou used in step 7. Each copy of one you can get from different places is very subtly different, and some of them have bugs that others do not.Could you try this one, like this? This one is working for me.
https://raw.githubusercontent.com/retrage/edk2-nightly/b5e504a31ea2469306a20876e19bbb058afeacb5/bin/RELEASEAARCH64_QEMU_EFI.fd
Then try running step 8 and step 9 again.