Trying an image before flashing
boot2deb try boots the built image under QEMU and asserts that the userland
works. It runs before a card is written, a board is opened, or a serial console
is wired.
It is the answer to the failure class that survives a clean build and a clean flash:
- The journald that SIGILLs on its first message.
- The first-boot hook that shipped without its executable bit, and bricks the second boot.
- The account whose generated password does not actually log in.
# Boot the built image twice under QEMU and assert the userland works.
boot2deb try turing-rk1/forky
# Keep the booted disk for a post-mortem, and allow a slower first boot.
boot2deb try turing-rk1/forky --keep-disk --timeout 1800
try turing-rk1/forky: PASS
first boot running, first-boot completed, selftest: 11 ok, 4 not applicable.
second boot running, first-boot did not re-run, selftest: 11 ok, 4 not applicable.
What it asserts
One run is two boots of the same disk, and each boot must pass all of:
- systemd settles as
running— no unit insystemctl --failed. - The image’s account logs in on the serial console with the generated first-boot password from the provenance manifest. The first login walks the forced password change the image ships with. That the whole conversation works is the account assertion, not a side effect of it.
first-bootran to completion and wrote its stamp — and, on the second boot, did not run again. The second boot is the whole reasontryexists as more than a smoke test: a brick-on-second-boot image passes every single-boot check ever written.- The on-image self-test passes in userland mode. That is the
disk-content half of the board’s expectations (kernel on
/boot, firmware files, initramfs modules), checked inside the guest, with the hardware checks reported not-applicable rather than failed.
try exits non-zero on the first assertion that fails, with the guest’s
console tail in the error.
What it deliberately does not test
The board. The guest machine is -M virt: no RK3588, no panel, no codec.
The hardware half of the selftest runs on the device, not here.
The boot path, too. QEMU loads the kernel directly, so u-boot, extlinux, and
the depthcharge signing are exercised on hardware only. try replaces a
flash-plus-serial-console cycle for userland faults, and replaces nothing else.
The shipped kernel is not booted either. It is configured from the board’s fragments and has no reason to carry virtio drivers. Adding them would change the shipped kernel to serve the test, which is backwards.
The guest boots the suite’s own generic kernel (linux-image-arm64 /
linux-image-armmp), fetched and installed through the same provisioner every
root here is built with. Its initramfs is built by initramfs-tools inside a
target-arch root try provisions for the harvest and discards afterwards. The
kernel is a fixture, and the userland is what is under test. For a
distro-package kernel build the two coincide, and try says so. The pair is
cached under the recipe’s work dir, and --refresh-fixture re-harvests it when
the suite’s kernel moves.
Mechanics worth knowing
- The image artifact is never booted directly. The run decompresses a copy
under
<work-dir>/try/and boots that.--keep-diskkeeps it afterwards. The forced first-login change means the kept disk’s password is no longer the generated one, and the run’s report prints the one now set. - The run needs the build’s artifacts and its provenance manifest (for the
password), so it follows
boot2deb build— the build’s closing hint says so. systemd-modules-load.serviceis masked on the guest’s kernel command line. The image force-loads board-kernel modules the fixture kernel cannot have, and that is a fact about this boot, not about the image.- Runtime is minutes under TCG emulation on an x86 host — this replaces a
flash-and-serial cycle, not a unit test. On a matching host with
/dev/kvm(an arm64 box building arm64 images), KVM makes it fast. - The fixture root is
try’s own rather than one of the build’s compile roots. It is provisioned with a real range of ids. Thesystemdin it chowns tosystemd-networkas it configures, and sets a POSIX ACL namingadm. A compile root maps one id, so it could not configure that package at all. The practical consequence is thattryneeds the samenewuidmap/newgidmaphelpers and subuid range a build does, whichdoctoralready checks. qemu-system-aarch64/qemu-system-armis the one host tool involved, and it is optional.boot2deb doctor <recipe>lists it as such, with the package name for the host distro.