Infecting a Steam Link with NixOS

Translated by a model; the original is in Russian.

· Linux and open source
After finding a Steam Link forgotten since 2018 in his closet, a developer set out to turn it into a tiny always-on ARM server running NixOS — sidestepping the signed bootloader with a homemade kernel module.

cover.svg

While clearing out his closet, George Huebner rediscovered a Steam Link he had picked up on a flash sale back in 2018, still dutifully humming along ever since. The idea suggested itself: an always-on, low-power ARM device with Ethernet, Wi-Fi, Bluetooth, and several USB ports makes a fine little home server. All that remained was to get NixOS onto it.

Getting around the signed bootloader

The hard parts had already been worked out earlier by an enthusiast named fijam. The Steam Link's bootloader will only boot kernels signed by Valve. The trick: boot into the Valve-blessed kernel, then kexec into your own. The catch is that the stock kernel is built without CONFIG_KEXEC. The clever fix is to compile the relevant kexec sources into a standalone kernel module that adds the syscall to the already-running system.

Many people simply copied the prebuilt binary and module from fijam's site. Huebner, wary of downloading kernel modules off the internet, chose to build everything himself.

A thirteen-year-old kernel and an old GCC

Building a NixOS userspace and kernel is straightforward, but the target architecture proved subtler: Valve's toolchain uses armv7a, which plays badly with the Go build machinery, so he settled on the equivalent armv7l. The real ordeal was compiling the module against a vendored fork of a 3.8.13-mrvl kernel — thirteen years old. Several hacks were needed to coax it through a modern GCC.

Next came packaging it all together: the initrd, the kernel, the device tree (already upstreamed into Linux), a statically built kexec binary, and a small glue script. The disk image itself is conveniently produced with the sd-image module.

Debugging blind

Verifying the kexec handoff was painful: HDMI didn't work with the new kernel, and rather than crack open the case for UART access, he flew completely blind. First he booted a minimal BusyBox initramfs that rebooted after a set interval to signal success. Then he switched to the NixOS initrd with networking and a netcat reverse shell back to his laptop. He had to add the reset_berlin module to read the USB drive, and fall back to the old initrd instead of the new systemd-based one. Eventually: a boot into userspace and an SSH login.

Trimming the fat

The first image weighed a hefty 2.3 GB. As it turned out, of the enormous linux-firmware package (1.8 GB compressed) exactly one Marvell blob was needed. Nix itself is nearly useless on such a system, so that went too. Reaching the point where new tweaks only broke things, he declared the resulting 1.2 GB image "good enough."

Right before publishing, he found that someone else had "vibed" their way to a bootable NixOS install too — but with a sloppier config that mishandles reboots and leans on unnecessary binary blobs instead of building from source.


Source: Lobsters

Comments

Sign in, to comment.

  • No comments yet.