Customizing an Arch ARM image using QEMU

Alexander Rüedlinger - - ARM , QEMU , Tools , chroot , Arch Linux

A really nice tool to customize disk images for ARM-based single board computers, like the Raspberry Pi, is QEMU. QEMU advertises itself as a generic and open source machine emulator and virtualizer [1, 2]. In other words, QEMU can run operating systems and programs made for different architectures, like ARM, on a developer's x86-64 development machine.

This is why QEMU is an ideal tool in combination with chroot [2,3] to customize Arch Linux ARM images for ARM-based devices, such as the RPi. So once we have an ARM image, we can simply chroot into the root filesystem and install extra software packages, like vim, zsh or avahi. In brief, chroot is an operation that allows a user on a Unix-like operating system to change the root directory of the current running process and its children [2]. Basically, this allows one to run commands within a different root filesystem environment also known as a chroot jail.

On most GNU/Linux distributions these handy tools can be easily installed via the package management. The rest of this blog post provides instructions how we can customize an existing Arch Linux ARM image on an Arch-based system. As described in my last blog post, these images can be created with basic Linux/CLI knowledge.

Preparation

Basically, we just need to install three packages, including qemu, qemu-arch-extra, update-binfmts, so we can emulate an ARM root filesystem on a x86-64 host system:

pacman -S qemu qemu-arch-extra

Lastly, we need to install update-binfmts from the AUR:

git clone https://aur.archlinux.org/binfmt-support
cd binfmt-support
makepkg -si

The package binftm-support contains a helper script to easily register and unregister binary formats with the kernel using the binfrm_misc module. In brief, this module provides the Linux kernel on host system to recognize arbitrary executable file formats and to pass them to certain user space applications [5].

update-binfmts --enable qemu-arm

With the help of the command update-binfmts --display we can double check if the binfmt entry for ARM was successfully registered:

update-binfmts --display | grep -w 'qemu-arm' 
qemu-arm (enabled):
interpreter = /usr/bin/qemu-arm-static

Mount the image

In this step, it's assumed that we have already created an Arch Linux image. These images can be created with basic Linux/CLI knowledge as described in this blog post.

First, we need to mount the root filesystem of the image. This can be done with the help of the command losetup:

losetup -f image.img

Second, we need to find out the corresponding loopback device for the image:

losetup -a
/dev/loop0: []: (/media/data/xander/src/archlinux-arm/raspberry-pi2/image.img)

In my case this is the loop device loop0. Now, we need to reload the partition table of the device loop0 so the partitions are known to the host OS.

partprobe /dev/loop0

To double check that the OS knows the partition table of the device loop0, we can use the command lsblk, which is used to list block devices:

 lsblk /dev/loop0 
NAME      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
loop0       7:0    0    1G  0 loop 
├─loop0p1 259:0    0  100M  0 loop 
└─loop0p2 259:1    0  923M  0 loop 

As one can see, both partitions of the image are known to our host OS.

Chroot into the root filesystem

In this last step, we chroot into the root filesystem of the Arch Linux ARM image. First, we create a directory called arm-chroot in /mnt/ on the host system:

mkdir /mnt/arm-chroot

Now, we can mount root filesystem under the path /mnt/arm-chroot:

mount /dev/loop0p2 /mnt/arm-chroot 

However, before we can chroot into the root filesystem, it is necessary to copy the qemu-arm-static binary to /mnt/arm-chroot/usr/bin. Thanks to this binary, it is possible to execute non-native target executables just as native exeutables once we are in the chroot jail.

For copying the qemu-arm-static binary, we can run the following command:

cp $(which qemu-arm-static) /mnt/arm-chroot/usr/bin

Moreover, it's a good idea to copy temporarily a working resolv.conf file from your host system to the image's root filesystem, so you have a working DNS resolution once you chroot into it:

mv /mnt/arm-chroot/etc/resolv.conf /mnt/arm-chroot/etc/resolv.conf.bak
cp /etc/resolv.conf /mnt/arm-chroot/etc/resolv.conf

As a last step before we can run chroot, we need to mount the necessary device files so we have a working chroot environment where we can install software via pacman. In short, we need to mount sys, proc, and dev:

mount --bind /proc /mnt/arm-chroot/proc
mount --bind /sys /mnt/arm-chroot/sys
mount --bind /dev /mnt/arm-chroot/dev
mount --bind /dev/pts /mnt/arm-chroot/dev/pts

Finally, we are ready to chroot into the root filesystem:

chroot /mnt/arm-chroot /bin/bash

Customizing the image

Now that we are in the chroot environment, we can customize the image to our liking. But before we do that, we should upgrade the system and double check if we have a working package mirror list:

pacman -Syu

Useful development tools

In case you intend to compile and develop some Arch Linux ARM packages, you should definitely install the following packges:

pacman -S base-devel git vim zsh sudo wget

Hostname

Changing the hostname is just a matter of editing the files /etc/hostname and /etc/hosts.

echo chaotix > /etc/hostname

In case of the hosts file we should add an extra line like the one below. For example, I decided to use the hostname chaotix:

127.0.1.1   chaotix.localdomain chaotix

Create a new user

A default user on a new Arch Linux ARM system is the user alarm. If you like to create a new user you can do that as follows:

useradd -m -s /bin/zsh xander
passwd xander

As you can see, my new user is called xander and uses the awesome zsh shell :-).

Setup sudo

I often use sudo instead of a root account to administer my Linux systems, so I added my user account to the group wheel:

gpasswd -a xander wheel

To complete the sudo configuration, we must not forget to uncomment the first line starting with #%wheel in /etc/sudoers. In other words, remove the Hashbang # before %wheel. Also note that you should use visudo to edit the sudoers file.

Delete default user

And if you're like me and don't need the existing alarm user, you can simply remove the user from the image:

userdel -r alarm

Root password

Lastly, we should change the default root password of the Arch Linux ARM image:

passwd

Avahi

In order to make the single board computer discoverable on the home network, we can install avahi and mdns. Overall, the installation procedure is the same as described on the Arch Linux Wiki [6].

pacman -S avahi nss-mdns

Once installed, we should adapt the /etc/nsswitch.conf so the hostname resolution using the .local naming scheme works:

hosts: ... mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns ...

And finally, to enable the avahi service on every boot, we can run the following systemctl command:

systemctl enable avahi-daemon.service

Leave the chroot and unmount things

Once you made all the customization, leave the chroot, unmount the root filesystem of the image, and detach the loop device /dev/loop0:

exit
umount -R /mnt/arm-chroot
losetup -d /dev/loop0

Talk is cheap, show me something!

It's testing time. So let's test the customized image on real RPi2 hardware!

Arch RPi2 Mote Arch RPi2 Neofetch

References