ARM local debug

ARM VMs are quite slow on amd64, to the point they introduce new issues. For Debian and Debian LTS we have porter boxes. For archived/ELTS dists however we don’t have these test environments anymore so let’s create a cheap one.

Hardware

The Raspberry Pi 400 is a convenient Raspberry Pi v4 ARM board, fan-less, with integrated keyboard and built-in WiFi, hence requiring far fewer cables.

../_images/r400.jpg

The architecture is arm64 and also handles armhf/armel (32-bit) builds. It also supports KVM virtualization. There’s 4GB RAM.

R400 supports booting from USB out of the box. We can use a USB hard-disk exclusively and avoid SD cards which tend to wear down quickly with many write operations.

It costs less than $100 with a power supply and a micro-HDMI/HDMI cable. You’ll also need an USB external hard-disk or a cheap USB/SATA adapter.

Other Raspberry Pi models: all models >= v2 can run Debian armhf or arm64. Model 1 is in-between armel and armhf, and can run Debian armel or the Raspberry Pi OS specific armhf (“armhf~”), which is incompatible with Debian armhf, so not useful here. Some other models also support USB boot but may need a firmware update first. v5 is not fully supported by mainline kernels as of 2025-09.

Possible alternatives:

Base system

The raspi.debian.net (pre-built images) appears to be dead-ish. There’s a lengthy manual installation method at wiki:RaspberryPi4 which I didn’t try.

Conversely Raspberry Pi OS (ex-raspbian) uses Debian arm64 directly for latest Raspberry Pi models, though it’s currently (2025-09) stuck on bookworm, but let’s use that with some Debian backports (compatible).

Download the latest full version and flash it on a fast hard-disk:

xzcat 2025-05-13-raspios-bookworm-arm64.img.xz \
  | sudo dd of=/dev/sdX bs=64k status=progress oflag=sync

Unplug any SD card, plug your hard-disk to an USB port, and boot.

Basic hardening:

  • Menu > Preferences > Raspberry Pi Configuration (or rc_gui, or sudo raspi-config)

  • Disable auto-login:

    • System > Console/Desktop auto login > [uncheck]

  • Enable SSH:

    • Interfaces > SSH > [check]

  • Save your screen:

    • Display > Screen Blanking > [check]

  • Adjust password-less sudo for the main user:

    visudo /etc/sudoers.d/010_pi-nopasswd
    # drop NOPASSWD:
    
  • Controlled package installation:

    echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/00InstallRecommends
    

Emergency UI shortcuts in the default LXDE-based UI:

  • Super for menu (buggy with keyboard -> killall wf-panel-pi to restart)

  • Ctrl+Alt+T for a terminal

  • Ctrl+Alt+Del for shutdown menu

Check your disk’s performances with Menu > Accessories > Raspberry Pi Diagnostics (or agnostics).

Support check:

$ dpkg --print-architecture
arm64
$ arch-test
arm64
armel
armhf

TODO: purer Debian base install, either with the manual procedure, or using mmdebstrap. Though, we won’t compile or test any package in this base system, only use it to run ARM chroot/unshare/qemu containers, so it doesn’t matter much.

Using sbuild/unshare

sbuild documentation and cheat sheets:

We’ll install the latest sbuild with unshare support, like Debian buildds and debusine. Moreover there’s no need for chroots configuration anymore:

echo "deb http://deb.debian.org/debian bookworm-backports main" >> /etc/apt/sources.list
apt install sbuild/bookworm-backports mmdebstrap/bookworm-backports uidmap arch-test
apt install autopkgtest/bookworm-backports autodep8  # for --run-autopkgtest
mkdir ~/.config/sbuild/
wget https://deb.freexian.com/extended-lts/archive-key.gpg -O /var/tmp/freexian-archive-keyring.gpg

Some default configuration:

cat <<'EOF' > ~/.config/sbuild/config.pl
# Use newer unshare backend
$chroot_mode = "unshare";

# Investigate failures
$external_commands = { "build-failed-commands" => [ [ '%SBUILD_SHELL' ] ] };

# Cache chroot tarball
$unshare_mmdebstrap_keep_tarball = 1;
$unshare_mmdebstrap_max_age = 8640000;  # 10 days

# not required in bookworm, required in trixie where /tmp is tmpfs
# and large builds will fail after filling all memory..
#$unshare_tmpdir_template = '/var/tmp/tmp.sbuild.XXXXXXXXXX';

# Transparent ELTS support
push @{$unshare_mmdebstrap_extra_args},
   qr/^(buster.*|stretch.*)$/,
  ["--include=freexian-archive-keyring",
   "--keyring=/var/tmp/freexian-archive-key.gpg",
   "http://deb.freexian.com/extended-lts/"];
EOF

Tests:

sbuild -d unstable hello

sbuild -d bookworm hello  # KO, can't auto-add multiple source lines
sbuild -d bookworm hello_2.10-2.dsc

sbuild -d buster hello_2.10-2.dsc
sbuild -d buster gimp_2.10.8-2+deb10u3.dsc  # OK, 30mn

sbuild -d stretch mercurial_4.0-1+deb9u3.dsc  # KO, no stretch/arm64 support
sbuild -d stretch --arch=armhf mercurial_4.0-1+deb9u3.dsc  # OK, 30mn

sbuild -d bookworm hello_2.10-3.dsc --run-autopkgtest
# Note: uses the host's autopkgtest, unlike lintian which is installed in the chroot

# Note: autopkgtest-virt-unshare reuses sbuild's chroot tarballs cache (~/.cache/build/)
# This works directly too:
autopkgtest hello_2.10-3.dsc hello_2.10-3_arm64.changes -- unshare --release bookworm --arch arm64

sbuild -d buster hello_2.10-2.dsc --run-autopkgtest
sbuild -d buster --arch=armhf hello_2.10-2.dsc --run-autopkgtest
sbuild -d stretch --arch=armhf hello_2.10-1+deb9u1.dsc --run-autopkgtest

Troubleshootings:

sbuild -d buster hello_2.10-2.dsc
# KO, buster is archived and not available
# -> see configuration to auto-add mmdebstrap ELTS parameters

sbuild -d stretch mercurial_4.0-1+deb9u3.dsc  # KO, no stretch/arm64 support
# add --arch=armhf; requires 'arch-test'

sbuild -d bookworm hello_2.10-3.dsc --run-autopkgtest  # KO, not installed
# needs autopkgtest installed on the host

sbuild -d bookworm hello_2.10-3.dsc --run-autopkgtest  # KO, missing apt-utils??
# fails very early, looks like a bug; use autopkgtest/bookworm-backports

Piuparts+unshare support currently broken on bookworm-backports:

apt install piuparts adequate
sbuild -d bookworm hello_2.10-3.dsc --run-piuparts
# KO: piuparts/bookworm doesn't handle mmdebstrap/unshare/etc., nor uncompressed tarballs
# https://bugs.debian.org/1115932

Let’s just run piuparts manually for our purposes, until Raspberry Pi OS upgrades to trixie.

TODO: apt-cacher config

QEMU/KVM

We can run full ARM VMs on the R400, with native speed, the original Debian kernels, graphics support, etc.

# basic support
apt install qemu-system-arm
# for the full virt-manager GUI and network
apt install virt-manager gir1.2-spiceclientglib-2.0 gir1.2-spiceclientgtk-3.0 libvirt-daemon-system

Trixie VM install: run a classical install from virt-manager with debian-13.1.0-arm64-netinst.iso. Older debian dists may need work-arounds such as extracting vmlinuz/initrd (ex: https://0xca7.github.io/notes/virt-manager-arm/).

To test a graphic system, add some virtual hardware:

  • Details > Add Hardware

    • Video > Virtio

    • Graphics > Spice server

    • Input > USB Keyboard (VirtIO doesn’t work in early phases like GRUB)

    • Input > EvTouch USB Graphics Tablet

# TODO: test buster/stretch ISOs

debvm works too, see Create an arm* VM for more detailed instructions.

apt install debvm/bookworm-backports
debvm-create -r buster -o buster.ext4 -- --include=freexian-archive-keyring --keyring=/var/tmp/freexian-archive-key.gpg ... http://deb.freexian.com/extended-lts/
debvm-run -i buster.ext4 ...

Note: this is an ad-hoc VM: there’s no GRUB, there’s no graphics, we’re using a root user by default, etc. This may not be suitable for all kinds of testing, but this works fine for running full isolation-machine autopkgtest.

TODO: test stretch

TODO: armhf VM aren’t KVM-accelerated in this configuration, only arm64. This should be possible with some debvm-run tweaking, see https://wiki.debian.org/Arm64Qemu .

Copyright (C) 2025 Sylvain Beucler