Installing Archlinux on the Raspberry pi 4 with pacstrap

Archlinux for aarch64 and installing it on a Pi

About

Archlinux has builds for aarch64 including a pre-packed ArchLinuxARM-aarch64-latest.tar.gz which can be directly extracted into freshly MBR-partitioned storage device with vvat+ext4 boot/root partitions mounted for getting started immediately. But why rely on a tarball at all?

pacstrapping a new AArch64 installation for the Pi 4

Given my machines already run Archlinux (x86_64) I figured it would only be fitting to pacstrap the Raspberry Pi 4's USB stick manually. Here are some instructions to get started:

Partitioning

Use fdisk to create a new MBR table with part1 being (200M) and part2 with the remainder of the space.

Or something small like >4GB for working with virtual storage such as a ZVOL for easily fitting this prepared image onto storage of various sizes.

Make sure to set the first partition's type to c which is W95 FAT32 (LBA) so the Pi's bootloader firmware knows where to look.

Formatting

Format them as vfat and ext4: sudo mkfs.vfat /dev/disk/by-id/usb-TheDrive-0\:0-part1 sudo mkfs.ext4 /dev/disk/by-id/usb-TheDrive-0\:0-part2

Mounting

Mount both in order with something like sudo mount /dev/disk/by-id/usb-TheDrive-0\:0-part2 /mnt sudo mkdir -p /mnt/boot sudo mount /dev/disk/by-id/usb-TheDrive-0\:0-part1 /mnt/boot/

Pacstrapping

The host needs to trust the Archlinux ARM build servers for installing packages. This can be done by downloading it from the repository into pacman's keydir and then importing then signing the key. The installation itself comes with this file already.

sudo wget https://raw.githubusercontent.com/archlinuxarm/archlinuxarm-keyring/master/archlinuxarm.gpg sudo pacman-key --populate archlinuxarm sudo pacman-key --lsign-key archlinuxarm

We must also create a replica of the expected Archlinux AArch64 pacman.conf for our host's instance to use. It can be short and cut-down as below with a single mirror for the various repos:

echo '[options]
HoldPkg     = pacman glibc
Architecture = aarch64

CheckSpace
SigLevel    = Required DatabaseOptional
LocalFileSigLevel = Optional

[core]
Server = http://mirror.archlinuxarm.org/$arch/$repo
[extra]
Server = http://mirror.archlinuxarm.org/$arch/$repo
[community]
Server = http://mirror.archlinuxarm.org/$arch/$repo
[alarm]
Server = http://mirror.archlinuxarm.org/$arch/$repo
[aur]
Server = http://mirror.archlinuxarm.org/$arch/$repo
' > ~/pacman.aarch64.conf

Now we can pacstrapinto the Pi's new rootfs referencing this config and some good starter packages:

sudo pacstrap -M -K -C ~/pacman.aarch64.conf /mnt archlinuxarm-keyring base chrony openssh raspberrypi-bootloader vim networkmanager linux-rpi

We use these flags to avoid using anything from the host in this process:

  • -M (Avoid copying the host’s mirrorlist to the target.)
  • -K (Initialize an empty pacman keyring in the target (implies -G).)
  • -C (Use an alternate config file for pacman.)

Chrooting into different architectures

Its difficult to chroot/arch-chroot into a rootfs of a differing architecture as none of its binaries will run. Luckily QEMU provides a solution for user-mode emulation which leverages binfmt to invoke qemu-aarch64-static for running those binaries instead of trying to execute it natively ourselves.

Fetch the relevant packages with:

pacman -S qemu-user-static-binfmt qemu-user-static

Activate it by copying its configuration into binfmt.d's working area:

  1. sudo cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /etc/binfmt.d/

You can verify it's active with: ls -lah /proc/sys/fs/binfmt_misc/qemu-aarch64

Now we need to copy it into the raspberry-pi's rootfs for chroot to find:

sudo cp $(which qemu-aarch64-static) /mnt/usr/bin

We can now chroot into the rootfs for this Pi.

Configuring the inside

Chroot into the Pi's rootfs with sudo arch-chroot /mnt qemu-aarch64-static /bin/bash and configure the last bits and pieces:

systemctl enable NetworkManager sshd chronyd # Enable some critical networking and remote-access services.
pacman-key --init ; pacman-key --populate archlinuxarm # Initialize pacman's keyring and add archlinuxarm's keys.
passwd               # Set a root password.
vim /etc/hostname     # to set a hostname.
vim /etc/fstab        # Or use `genfstab /mnt | sudo tee /mnt/etc/fstab` on the host and verify.
vim /boot/cmdline.txt # Don't forget to change root= to either a root=UUID=YourRootfsUUID or /dev/sda

Its advisable to use UUID=xxx for /mnt/etc/fstab to avoid device path changes from potentially borking the boot. genfstab /mnt | sudo tee /mnt/etc/fstab will create the entries for you but you will still have to uncomment the UUID lines and use them at the start of the other lines instead of the disk dev path.

You can place some ssh keys in /root/.ssh/authorized_keys or add new users.

Password authentication for root is only possible after setting PermitRootLogin yes in /etc/ssh/sshd_config either in the chroot session or outside.

You can then set it up like any other Archlinux box. At a minimum you should probably uncomment your desired locale in /etc/locale.gen, run locale-gen and set it in /etc/locale.conf as LANG=xxx_yyy-zzz

Finishing up

Finally leave the chroot. You can expand the partition if needed and sync the USB before pulling it out (I can never trust USB device writes), remove the SD card or USB stick and boot the Pi.

Keep in mind Pi's (For some reason of choice) cannot USB-boot without being told to do so earlier in their life. This creates a bootstrap paradox requiring the Pi to be booted into some distro and enabling USB-booting before trying to use a USB stick.

Extra goodies

Serial over USB-C

Thanks to DWC2, the various available Pi drivers and the USB ports of the Pi being wired directly to the CPU - The USB-C port used for powering the device it can be configured to present as various Human Input Devices (HIDs) over a data cable while receiving power. It can even present as multiple of them at once and most usefully in this context an ethernet or serial interface.

To enable serial create /etc/modules-load.d/usb-c_serial.conf with the below lines (drivers):

dwc2
g_serial

You also need to append dtoverlay=dwc2 to your /boot/config.txt for this feature.

Finally, enable agetty to present a serial login console on the serial interface to-be: systemctl enable getty@ttyGS0.service.

You can now reboot to load those two modules and start shelling in over USB-C. Well, when there's enough power delivery which most desktop and laptop USB-C ports won't do. Single cable for the win.