Mobile NixOS on PinePhone

Posted on by Chris Warburton

Background

I’ve been using my Pinephone for a few years now (upgrading from a Freerunner); sticking with the Manjaro OS it came with, for compatibility reasons (important when using obscure/niche hardware!). There were some breakages (e.g. the switch from Pulseaudio to Pipewire broke callaudiod), but it was mostly fine.

Unfortunately, the stable Manjaro ARM repos seem to have stopped receiving updates; despite the unstable repos marching ahead. I eventually decided to switch pacman to those unstable repos, and that was nice for a while. Until, a couple of weeks ago, I decided to install updates; and the Pinephone stopped booting entirely! (Looks like some Plymouth-style graphical spinner was added; but, ironically, it gets stuck in a loop).

Now, this wasn’t my first rodeo but, given that I’d need to expend a bunch of effort debugging embedded ARM boot processes anyway; I decided to invest that time into getting NixOS booting instead of Manjaro. In fact, I knew it should work, since mobile-nixos claims to support the Pinephone, and years ago I’d managed to grab a pre-built image off Hydra and boot it as far as a TTY login prompt; but I’d held off switching wholesale to avoid losing access to a working telephony stack.

Decision time

Before going any further, I had a few decisions to make!

Where

The most important choice is whether to run off a μSD card, or the on-board eMMC storage. The latter is faster, but only has 32GB of space. I decided to use a 128GB μSD card, since that would also let me keep the Manjaro system on the eMMC intact.

What

Next was deciding what to put on my μSD card. The official docs steer us towards an “installer” image which, when booted, will perform a series of steps that put NixOS on the Pinephone (similar to installing software from a floppy or CD, back in the day).

However, whenever I encounter software that performs a series of steps to alter a system, I always ask myself the question: could I do that with Nix instead? In this case, it turns out we can! If we keep reading, the section “Development notes” (the first of which says “The following sections only apply for development”…) tells us how to build a root filesystem image, that we can just write to our μSD and boot as-is. No need for separate “installation”!

Such rootfs images can be build using Mobile NixOS’s helpful default.nix file, which will try to build natively (if the builtins.currentSystem allows) or else cross-compile. The latter makes it easy to build on, say, an x86_64-linux laptop. Though keep in mind that many things in Nixpkgs aren’t well tested for cross-compilation, and may fail to build (e.g. I hit problems with Qt libraries) so I’d recommend building a minimal image, with just enough to get Nix building things natively on the Pinephone (e.g. a user account, Git, OpenSSH and maybe some network tools). With that working, you’ll be able to nixos-rebuild it into a fully configured system.

Whence

Pinephones come with the U-Boot bootloader, but Mobile NixOS only has official support for Tow-Boot (a fork of U-Boot). They recommend installing Tow-Boot first, then holding the volume button when powering on to boot from μSD card.

Except, I’ll always be booting from μSD (at least, when a card is present and bootable); I don’t want that to require manual intervention!

To make our rootfs images bootable from μSD by U-Boot, they’ll need to contain a bootloader of their own, somewhere within their first MiB. I chose to use U-Boot again, which I took pre-built from a postmarketOS APK package.

It required a bit of trial-and-error to find an offset that doesn’t overwrite the image’s partition table, yet is still found by the Pinephone’s U-Boot. Eventually I found that a 128KiB offset works:

dd if=u-boot-sunxi-with-spl-528.bin of=mobile-nixos-pine64-pinephone.img bs=1024 seek=128 conv=notrunc

Full details, including a default.nix for building a bootable image, can be found in the nixos/pinephone directory of my nix-config repo.

Tips

Once the Pinephone was booting, I plugged it into a USB-C dock with ethernet. I needed network access to build up my minimal image into something usable; and did not want to faff around with WiFi passwords!

Putting services.openssh.enable = true; in our minimal image lets us SSH into it, rather than having to use the touchscreen. Dongles/docks with a proper keyboard and monitor are useful too, although the TTY doesn’t work well on my vertically-oriented monitor.

According to the Mobile NixOS documentation, updating the boot partition may require overwriting it (via dd or such). I’ve not needed to do that yet, but I guess it would be required for kernel updates to take effect.

Speaking of kernels, it appears that Mobile NixOS does not use Linux kernel modules, so we have to bake-in all of the drivers we care about. I only realised this when I couldn’t mount a CIFS share (though I’m gonna try RClone instead). I think bluetooth may require baking in too, since I can’t enable it from Plasma Mobile (whilst it did work in Phosh on Manjaro).

Again, speaking of Plasma Mobile: version 5 gives a deprecation warning, but I don’t think Mobile NixOS lets us use version 6 yet (it’s in Nixpkgs, but the NixOS options that enable Plasma Mobile 5 don’t seem to exist for 6).

NixOS on my Pinephone