embedded adventures

Getting Linux on Valve Steam Link

Update: Kernel image and modules have been updated to 5.10.32.

I have toyed with embedded ARM devices for a long while. Starting with NSLU2 (Intel XScale) some 15 years ago through various PogoPlugs (Marvell Kirkwood) and QNAP NASes (Marvell Orion) I find myself yet again inexplicably drawn, like a moth to the flame, to strange little device this time based on the Marvell Armada platform.

Steam Link

This handsome device shares a lot of hardware with Google Chromecast. In particular, both are based on a similar SoC which is some kind of Marvell Armada 1500 derivative (also referred to as Berlin). Out of the box, Steam Link runs the 3.8 series kernel with a bunch of custom drivers.

Fortunately getting access is easy via a little bit of filesystem overlay magic. The userspace is rather poor, not much beyond a Busybox and some binaries running the Steam application. While you could cross-compile what you need with the valve sdk toolchain it is a bit cumbersome. An easier solution is to use an Arch Linux ARM snapshot and chroot into in (Note: do not use ext4 as the 3.8 steamlink kernel does not support some of the newer features. Ext3 is fine).

There are perfectly valid reasons to stop here. However, if you need a more modern kernel or it offends your free software sensibilities, you can go further and replace the kernel too. There is, however, a snag. The bootloader will only load a kernel that was signed by Valve. Onto plan B then.


Kexec is a system call which lets you boot into a kernel from an already running machine. To do this, you will need a kernel module for the host system provided here as well as the userspace tool (Here at version 2.0.15 which works, unlike 2.0.20 which does not - replace the binary in the userland chroot).

Then you need the actual kernel. If you are lazy and have no problem with running kernels compiled by strangers you can find the files here. Otherwise, building the kernel is fairly straighforward. We are again using the valve sdk toolchain. We will also need the kernel sources (I will be using 5.4.x 5.10.x longterm maintenance release) and a basic config file.

To set the cross-compilation environment use 'source setenv.sh'. Copy the config file as .config and run 'make oldconfig' or 'make menuconfig' to make adjustments as necessary. Then it is just a matter of 'make && make modules && make modules_install'.

As a final step we need initramfs. Copy your zImage, kernel modules and berlin2cd-valve-steamlink.dtb to the Arch chroot, create a new mkinitcpio preset and generate your image.

Now, to piece it all together:

mount -t proc proc /mnt/disk/proc
mount -o rbind /sys /mnt/disk/sys
mount -o rbind /dev /mnt/disk/dev

insmod /mnt/disk/boot/kexec_load.ko
chroot /mnt/disk/ /usr/bin/kexec -l /boot/zImage --initrd /boot/initramfs-linux-steam.img --dtb /boot/berlin2cd-valve-steamlink.dtb --command-line "root=/dev/sda1 rootwait rw init=/lib/systemd/systemd console=ttyS0,115200n8 usbcore.autosuspend=-1"
chroot /mnt/disk/ /usr/bin/kexec -e

(In fact, you can save this as a bash script under /steamlink/factory_test/run.sh and Steam Link will run it on every reboot.)

If all went well the device will boot into the new kernel and a while later reappear on the network. The default Arch Linux ARM credentials are alarm/alarm. To prevent having some of the custom files overwritten by pacman on system updates you might want to add the following line to pacman.conf

IgnorePkg   = linux-api-headers linux-armv7 linux-armv7-headers kexec-tools

Stuff that works:

  • Ethernet
  • USB
  • WLAN (mwifiex mwifiex_sdio)
  • Bluetooth (btmrl btmrvl_sdio)
  • i2c (i2c-designware)
  • temp sensor (berlin2-adc)
  • UART

Stuff that does not:

  • NAND driver
  • DMA controller
  • video/audio output
  • suspend/resume/halt

The next post will be adventures in Wifi-land or how to turn the Steam Link into a 802.11ac SoftAP.

Useful files: