Skip to content

Instantly share code, notes, and snippets.

@robertkirkman
Forked from thalamus/ArchLinuxARM-M1
Last active April 11, 2024 23:17
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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 in QEMU full system emulator

How to install an Arch Linux ARM emulator

Prerequisites:

  • A PC with a Linux distribution - Arch Linux amd64 used here

Dependencies (for Arch Linux amd64):

  • edk2-armvirt
  • qemu-arch-extra
  • wget
  • gvim
  • openssh
  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 archlinuxarm.qcow2 60G
  1. Become root, connect the image to nbd and partition it with fdisk.
$ sudo su
# modprobe nbd
# qemu-nbd --connect=/dev/nbd0 archlinuxarm.qcow2
# 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.
# mkfs.vfat /dev/nbd0p1
# mkfs.ext4 /dev/nbd0p2
# mkdir rootfs
# mount /dev/nbd0p2 rootfs
# mkdir rootfs/boot
# mount /dev/nbd0p1 rootfs/boot 
# 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":
# blkid
  • Then, edit rootfs/etc/fstab:
# 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:
# 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.
# umount -R rootfs
# sync
# qemu-nbd --disconnect /dev/nbd0
# rmmod nbd
# exit
  1. Create flash images for the UEFI firmware and variables:
$ 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=archlinuxarm.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
  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
  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=archlinuxarm.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
  1. Proceed with configuring Arch Linux ARM as normal (time, locales, users, software, configuration), using the Arch Linux Wiki as a guide.
@ViktorShell
Copy link

Fantastic tutorial.
I did not understood the passage 7, what it does?

@robertkirkman
Copy link
Author

Fantastic tutorial. I did not understood the passage 7, what it does?

That prepares flash0.img as the UEFI firmware ROM for the emulator and flash1.img as a writable file for persistent storage of the UEFI variables (efivars).

@joanbm
Copy link

joanbm commented Apr 7, 2024

Thank you for the guide, worked fine. However, the EFI System Partition (i.e. /dev/nbd0p1) appears to be too small for current requirements, I got a "No Space Left on Device" when generating the initramfs on the first pacman -Syu. So I'd suggest bumping it to e.g. 400MB (final used size was 212MB, though it's probably worth having some buffer anyway).

@robertkirkman
Copy link
Author

robertkirkman commented Apr 7, 2024

@joanbm thank you very much I have edited that.
I still use this often. I have a sad confession. I'm so sorry.
When I encountered an initramfs error as a result of updating my existing installation, without thinking enough about the new user experience, I shutdown QEMU, connected the image to nbd, expanded the partition to 512MB, expanded the filesystem to fit, disconnected the image and relaunched QEMU - then didn't change anything here. This oversight might have happened because I regularly seem to fill up my .qcow2 files' root partitions as well and I am used to having to increase the file manually.
Here are some pages that helped me a lot and which still have some additional useful information for this topic, that I probably should have posted links to here to begin with.

@joanbm
Copy link

joanbm commented Apr 7, 2024

@robertkirkman No worries, the guide was clear and really useful, and I was gratefully surprised that everything booted first try. Having the manually resize the ESP was a very minor thing compared to the value I got from it.

@robertkirkman
Copy link
Author

robertkirkman commented Apr 8, 2024

I'm glad to hear that!

I'm not sure whether you'd need this at all, but this QEMU has some very useful OpenGL acceleration capabilities that I never really explained here before either, and at least for me, there is a quirk with it I can explain that causes it to seem stuck in a permanent multiseat mode. That is to say, It might seem like you can't interact your keyboard with the QEMU window itself, and have to use ssh. But the real truth is that you can if you add certain types of QEMU arguments - and for me, it's only worked with a second keyboard/mouse plugged into the host!

Here are my current QEMU arguments for an Emulator with virgl OpenGL passthrough to real GPU and multiseat input with a USB mouse and keyboard.

#!/bin/bash
qemu-system-aarch64 -M virt \
                    -m 16384 \
                    -cpu cortex-a72 \
                    -smp 32 \
                    -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=archarmvm.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,hostfwd=tcp::5555-:5555 \
                    -device virtio-rng-device \
                    -device virtio-balloon-device \
                    -device virtio-serial-device \
                    -device virtio-tablet-device \
                    -object cryptodev-backend-builtin,id=cryptodev0 \
                    -device virtio-crypto-pci,id=crypto0,cryptodev=cryptodev0  \
                    -device virtio-gpu-gl-pci,xres=1280,yres=1024 \
                    -display sdl,gl=on \
                    -device qemu-xhci,id=xhci \
                    -device usb-host,bus=xhci.0,hostdevice=/dev/bus/usb/003/009 \
                    -device usb-host,bus=xhci.0,hostdevice=/dev/bus/usb/003/010

When I change the USB port that the keyboard or mouse is plugged into, or sometimes update the host OS which can also change how those numbers are assigned, I use the lsusb -tvv command on the host to find their new device file paths, by finding the paths associated with the names of the USB devices that match their manufacturers.

First, you would make sure the virglrenderer package is installed in your host, then the -device virtio-gpu-gl-pci argument combined with the -display sdl,gl=on argument should allow the guest to spawn a DRM TTY within the QEMU SDL window which you should then be able to launch Xorg on with OpenGL acceleration using the virgl Mesa driver.

Unfortunately, it seems like there is a hardware-cursor-rendering-related issue with QEMU and Xorg operating together in this mode. To work around it, I use andykitchen/swcursor in the guest.

Of course, this is all very useless for you if you only need the ssh connection and no GUI, but I always wanted to add it as an update here since I feel that it is a very underrated configuration that I've never seen anyone else actually using.

@joanbm
Copy link

joanbm commented Apr 11, 2024

Nice! I don't usually need high-performance graphics on VMs, but I remember trying to get OpenGL acceleration to work about a year or two ago but didn't manage to get it to work due to my old NVIDIA GPU (oof). Nowadays I also have an AMD APU around, so I will keep that in mind if I need it again :)

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