Prerequisites:
- A PC with a Linux distribution - Arch Linux
amd64
used 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)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
60G
with your desired maximum size.
wget http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz
qemu-img create -f qcow2 arch-aarch64.qcow2 60G
Tip
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 60G
Users of Debian guest should now skip to step 7.
- Become
root
, connect the image tonbd
and 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+400M
and Enter - then
t
(to change the type), then1
for EFI System Partition - then
n
and Enter three times, thenw
to 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
vfat
partition in/dev/nbd0p1
looks likeUUID="XXXX-XXXX"
and the UUID of theext4
partition in/dev/nbd0p2
looks 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
X
with the corresponding digit of the UUID of the corresponding partition,/dev/nbd0p1
and/dev/nbd0p2
respectively, 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
X
with the corresponding digit of the UUID of the/dev/nbd0p2
partition, 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 theroot
shell.
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.fd
s (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 none
Tip
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
pacman
keyring, update the system and installefibootmgr
, replacing each instance ofX
with the corresponding digit of the UUID of the/dev/nbd0p2
partition 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'
poweroff
Tip
Step 9 equivalent for Debian guest:
- At the EDK II Shell, type this command
FS1:\efi\boot\grubaa64.efi
Users 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 none
Tip
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
argumnet if you want using my minimal tap0
guide.
I have successfully installed an app that I needed within this for a long time but only yesterday finally got to work within this QEMU, redroid, so I am documenting the steps here. I haven't got GPU acceleration working in this so its graphics are a lot slower than the Arch Linux ARM emulator itself, but it works with software rendering.
Prerequisites
hostfwd=tcp::5555-:5555
setting to forward theadb
port through QEMU forscrcpy
Build necessary kernel
Install Docker
sudo pacman -S docker sudo systemctl enable --now docker
Install and run redroid
sudo docker run -itd --privileged -v ~/redroid-data:/data -p 5555:5555 -p 8022:8022 --name redroid redroid/redroid:14.0.0_64only-latest
Important
There is one step during redroid's boot that sometimes takes an extremely long time within the emulator. The longest I had to wait was 7 hours and then it eventually booted fully. You can use
dmesg -w
within QEMU to monitor redroid's boot progress (and compare it with the kernel log of a native redroid instance outside the emulator to see what point you're at)Install
scrcpy
on the hostamd64
OS and connect to the emulated redroidNote
These commands can be run outside the emulator