Skip to content

Instantly share code, notes, and snippets.

@robertkirkman
Forked from thalamus/ArchLinuxARM-M1
Last active April 20, 2025 17:54
Show Gist options
  • Save robertkirkman/f79441c79811ad263f2f881f7864e793 to your computer and use it in GitHub Desktop.
Save robertkirkman/f79441c79811ad263f2f881f7864e793 to your computer and use it in GitHub Desktop.
How to install Arch Linux ARM or Debian ARM in QEMU full system emulator

How to install a GNU/Linux ARM emulator

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 command qemu-system-aarch64)
  • wget (or any downloader)
  • gvim (or any editor)
  • openssh (or QEMU console)
  1. 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.

  1. Become root, connect the image to nbd and partition it with fdisk.
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), then 1 for EFI System Partition
  • then n and Enter three times, then w to write changes and exit
  1. 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
  1. Edit fstab.
  • You will need both partitions' UUIDs - the UUID of the vfat partition in /dev/nbd0p1 looks like UUID="XXXX-XXXX" and the UUID of the ext4 partition in /dev/nbd0p2 looks like UUID="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
  1. 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
  1. Unmount the partitions, sync, disconnect the image from nbd, and exit the root shell.
sudo umount -R rootfs
sudo sync
sudo qemu-nbd --disconnect /dev/nbd0
sudo rmmod nbd
  1. 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
  1. 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
  1. Upon successful first boot, initialize Arch Linux ARM and install a new bootloader.
  • Log in as alarm, password alarm:
ssh -p 2222 alarm@localhost
  • Become root, password root:
su
  • Initialize the pacman keyring, update the system and install efibootmgr, replacing each instance of X 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.

  1. 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
  1. 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
Copy link

This was in /rootfs/boot

root@a8host:/naspool/virtualstore/arch_arm# ls /mnt/boot/
bcm2708-rpi-b-plus.dtb	  bcm2710-rpi-zero-2-w.dtb  bcm2835-rpi-cm1-io1.dtb   fixup.dat		   start4.elf
bcm2708-rpi-b-rev1.dtb	  bcm2710-rpi-zero-2.dtb    bcm2835-rpi-zero-w.dtb    fixup4.dat	   start4cd.elf
bcm2708-rpi-b.dtb	  bcm2711-rpi-4-b.dtb	    bcm2835-rpi-zero.dtb      fixup4cd.dat	   start4db.elf
bcm2708-rpi-cm.dtb	  bcm2711-rpi-400.dtb	    bcm2836-rpi-2-b.dtb       fixup4db.dat	   start4x.elf
bcm2708-rpi-zero-w.dtb	  bcm2711-rpi-cm4-io.dtb    bcm2837-rpi-3-a-plus.dtb  fixup4x.dat	   start_cd.elf
bcm2708-rpi-zero.dtb	  bcm2711-rpi-cm4.dtb	    bcm2837-rpi-3-b-plus.dtb  fixup_cd.dat	   start_db.elf
bcm2709-rpi-2-b.dtb	  bcm2711-rpi-cm4s.dtb	    bcm2837-rpi-3-b.dtb       fixup_db.dat	   start_x.elf
bcm2709-rpi-cm2.dtb	  bcm2835-rpi-a-plus.dtb    bcm2837-rpi-cm3-io3.dtb   fixup_x.dat	   startup.nsh
bcm2710-rpi-2-b.dtb	  bcm2835-rpi-a.dtb	    bcm2837-rpi-zero-2-w.dtb  initramfs-linux.img
bcm2710-rpi-3-b-plus.dtb  bcm2835-rpi-b-plus.dtb    bootcode.bin	      kernel7.img
bcm2710-rpi-3-b.dtb	  bcm2835-rpi-b-rev2.dtb    cmdline.txt		      overlays
bcm2710-rpi-cm3.dtb	  bcm2835-rpi-b.dtb	    config.txt		      start.elf
root@a8host:/naspool/virtualstore/arch_arm# 

I copied kernel7.img to Image , because seemed that the Arch didn't used the Image name.

But still drops this :
`UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a0b:;BLK1:
PciRoot(0x0)/Pci(0x2,0x0)/Scsi(0x0,0x0)/HD(1,GPT,2D4D7735-2D4F-4D64-9B36-7DDE99A411D3,0x800,0x64000)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x2,0x0)/Scsi(0x0,0x0)
BLK2: Alias(s):
PciRoot(0x0)/Pci(0x2,0x0)/Scsi(0x0,0x0)/HD(2,GPT,95B5CA11-34A9-44F8-95CC-50489FF853E3,0x64800,0x99B000)

Press ESC in 1 seconds to skip startup.nsh or any other key to continue.
Shell> Image root=UUID=02298819-b38e-4a2f-91c8-13a2e1022ea1 rw initrd=\initramfs-linux.img
Script Error Status: Unsupported (line number 1)

Shell>
`

So something really strange is going on here. Luckily it's not urgent to me to have this VM, so i can let this go if you have no time :)

@robertkirkman
Copy link
Author

I copied kernel7.img to Image , because seemed that the Arch didn't used the Image name.

That doesn't look right,
are you sure you used this Arch Linux ARM installer image?

http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz

This is what I see in /boot of my installation, it should look like this:

[tacokoneko@archarm ~]$ ls /boot
dtbs  Image  Image.gz  initramfs-linux-fallback.img  initramfs-linux.img  startup.nsh

If you extract that archive http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz, do you see the same thing as me, or are you trying to use a different version of Arch Linux ARM?

A different version of Arch Linux ARM might have a kernel that is not compatible with this mode of QEMU.

@horvathcsabalaszlo
Copy link

You were right, at first i followed the Qemu Raspberry guide, and it referred a different rootfs package. With your suggestion, it booted (in emergency, but this is another problem).

Thanks for your time :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment