NetBSD 10.1 on the Pinbook Pro

I’ve been thinking of trying to use NetBSD a bit more. Linux is just too easy.

Support for recent hardware is not as good on NetBSD as it is on some platforms (though it depends exactly what hardware you have). The laptops page on the NetBSD wiki naturally recommends some fairly old Thinkpads. And mentions EeePCs. Not especially encouraging.

That page could probably benefit for a bit more information about newer models, since NetBSD 10 does support some newer hardware, but I do see the Pinebook Pro mentioned, and I have one of those that I’m not doing anything with. NetBSD could be a good fit for the Pinebook Pro, at least if you don’t need accelerated graphics support.

Let’s see how that works.

Disclaimer: This isn’t really a (good) tutorial, or any authoritative source of information on NetBSD or the Pinebook Pro, but here’s a bit of detail on how I set it up and how it seems to work.

Installing

I have a couple spare NVMe SSDs from upgrading devices to have more storage. So I’ll install on one of those with the M.2 adapter for the Pinebook Pro. I already had Tow-Boot, a distribution of U-Boot, flashed to the Pinebook Pro’s SPI flash. The Pinebook Pro can’t boot directly from an NVMe drive, but with U-Boot on the SPI flash (or eMMC or SD card), it will boot a UEFI ARM bootloader from an NVMe drive.

NetBSD has a Pinebook Pro image, but if we’re using a separate U-Boot install, just flashing the generic aarch64 image to the NVMe drive works. We can then select the NVMe drive in U-Boot, and boot into NetBSD, which will automatically resize the partition to fit the drive.

The INSTALL file for NetBSD/evbarm has a bit more information about installing NetBSD on ARM systems.

It seems https://nycdn.netbsd.org rather than https://cdn.netbsd.org host things like board-specific ARM images (as well as daily snapshot builds). The “generic 64-bit” image seems to be bit-for-bit identical to the arm64.img.gz hosted on the main NetBSD ftp/cdn server, so since I chose to use UEFI booting with a separate U-Boot install on the SPI flash, downloading from either works.

wget https://nycdn.netbsd.org/pub/arm/NetBSD-10.1-release/NetBSD-10-aarch64--generic.img.gz
gunzip NetBSD-10-aarch64--generic.img.gz
sudo dd if=NetBSD-10-aarch64--generic.img of=<pinebook pro drive> bs=4096 status=progress

If we then boot the Pinebook Pro from this drive, it will automatically resize the partition to fill the available space, and boot to a login prompt, that accepts root with no password. Then we have a minimal NetBSD install to set up as we’d like.

Then there are the usual things to do after an OS install. The “Config menu” in sysinst can set the time zone, add a user, set up networking (including WiFi), etc. This is probably easier than manually doing all of those things (which is what I ended up doing before realizing this).

Package management

After setting up the network and some other basic things, one probably wants to install some things that aren’t part of the base install.

NetBSD has a package repository called pkgsrc, providing source and binary packages. (And also packages for other OSes, though I’ve only used it on NetBSD.) I suppose compiling packages from source is the traditional way of doing things, but I’m certainly inclined to just use binary packages unless I have a specific reason to do otherwise.

Binary packages can be installed with pkg_add, but it doesn’t seem to be kind of basic and low-level of a package manager. But this isn’t a problem, because it seems the expected way to install packages is through a wrapper called pkgin.

A key menu option in sysinst is “Enable installation of binary packages”. Which uses pkg_add to install pkgin from (by default, with the latest release) https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/10.1/All.

In https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/10.1, we can see there is also packaging for 10.0, 10.0_2024Q2, 10.0_2024Q3. Interesting that there are quarterly updates to pkgsrc, more frequently than NetBSD releases. A bit different from how any Linux system I’m familiar with does things.

Anyway, once that’s set up, we can install binary packages basically like one would expect to on any typical Linux distro. Installing and configuring either sudo or doas is a good thing to do at this point, and otherwise running everything as a non-root user. (Though you can always use su instead).

TTYs

It seems the aarch64 NetBSD image (at least) only has one TTY configured to show a login prompt by default, so Ctrl+Alt+F2 just shows a black screen with a cursor.

/etc/ttys can be modified to change the ttyE0, ttyE1, ttyE2, and ttyE3 from off to on so getty will start on those TTYs and display a login prompt.

That leaves just ttyE4 for X11 to use. But /etc/wscons.conf can be modified to add more TTYs if needed. I do like having plenty of TTYs, so I added lines there for screen 5 to screen 9.

Drivers

The bwfm driver in NetBSD 10 supports the Broadcom WiFi built into the Pinebook Pro. It may be less stable than other drivers though, and I noticed some system freezes downloading things that seem like a WiFi driver bug… may be worth more testing, but disabling it and using a supported USB 2.0 WiFi dongle (a urtwn device) seems to work well. ifconfig bwfm0 down will disable the builtin card.

The USB 3.0 port on the left doesn’t seem to work, while the USB 2.0 port on the right is fine. For audio, sound seems to come out of both the speakers and the headphones at the same time, and I can’t seem to configure it to use only one. I wonder how hard it would be to improve the driver for that, since presumably NetBSD has a better way to handle that sort of thing… These seem to be consistent with what others say about NetBSD on the Pinebook Pro.

The big omission is the lack of accelerated graphics support, but it has a DRM driver which works perfectly fine for just display output. And generally otherwise, things seem to work okay. It looks like this is an issue with NetBSD on ARM in general; not that many of Linux’s DRM graphics drivers have been ported.

Graphical interface

The aarch64 NetBSD image includes X11 already, with the ctwm window manager installed and configured in /etc/X11/xinit/xinitrc. So we can simply run startx to see a somewhat classic Unix looking graphical interface.

My inclination would be for a tiling window manager though. Wayland compositors for the most part don’t support NetBSD quite yet, so i3 seems like a solid option. And trying pkgin install i3 shows it is indeed packaged for NetBSD. Then just add a ~/.xinitrc file:

.xinitrc

feh --bg-scale ~/Pictures/wallpaper.jpg&
xrdb -merge ~/.Xresources
exec i3

Using feh from pkgin to set the background. We’ll get .Xresources later.

i3bar

Generally in i3 and similar window managers, a status-line command provides various system information to display in the panel. i3status is standard, and can be installed with pkgin install i3status. The default configuration doesn’t seem to get much information on NetBSD, though. At least not on the Pinebook Pro.

Instead, let’s just use a hacky bash script as a status-line command instead. This is not optimal, but it can be replaced with something in a compiled language that doesn’t spawn a million subprocceses or parse strings, if needed.

#!/bin/sh

while :
do
        battery=$(envstat -s cwfg0:'battery percent' -x cur-value)
        date=$(date +'%Y-%m-%d %I:%M %p')
        ssid=$(ifconfig urtwn0 | cut -f 2 | grep '^ssid' | cut -d ' ' -f 2)
        free_disk=$(df -hf /)

        pages_active=$(vmstat -s | grep 'pages active$' | tr -s ' ' | cut -d ' ' -f 2)
        gb_active=$(echo "scale = 1; $pages_active * 4096 / 1024 / 1024 / 1024" | bc)
        pages_managed=$(vmstat -s | grep 'pages managed$' | tr -s ' ' | cut -d ' ' -f 2)
        gb_managed=$(echo "scale = 1; $pages_managed * 4096 / 1024 / 1024 / 1024" | bc)

        volume=$(mixerctl outputs.master | cut -d '=' -f 2 | cut -d ',' -f 1)
        volume_percent=$(echo "100 * $volume / 255" | bc)


        echo "$ssid | ${gb_active}G/${gb_managed}G | $free_disk | $volume_percent% volume | $battery% battery | $date"
        sleep 1
done

That seems to work well for what I want my panel to show.

xterm

I’ve never really gotten around to figuring out how to configure xterm to my liking. So it’s always been a terminal that burns my eyes with a bright background while I squint at text that’s two small for the resolution of my display. And I can’t copy and paste anything (except with the X11 primary selection).

But if you want a basic terminal for an X11 system without hardware accelerated graphics, xterm is a solid option, really. It turns out with configuration it can handle everything I want, and seems to perform well.

Xterm is configured in .Xresources, where we can set the colors, font, and also the size of the scrollback buffer, and enable copy to clipboard with Ctrl+C and Ctrl+V:

UXTerm*faceName: Monospace
UXTerm*faceSize: 14
UXTerm*vt100.translations: #override \
  Shift Ctrl <Key> C: copy-selection(CLIPBOARD) \n\
  Shift Ctrl <Key> V: insert-selection(CLIPBOARD)
UXTerm*Background: black
UXTerm*Foreground: white
UXTerm*saveLines: 100000

i3 will use uxterm by default, which will load this configuration from Xresources.

Screen Brightness

Searching with pkgin, I don’t see any packages for setting screen brightness that work. Setting the screen brightness with systemctl hw.pwmbacklight0.level works. But setting the brightness requires root permission, and to increase or decrease the brightness by an increment, we’ll need a bit more logic. So a setuid root helper makes sense here. Not sure what other people on NetBSD are using, but I came up with this little C program:

#include <assert.h>
#include <sys/sysctl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

const char* USAGE = "usage: backlight [value | +value | -value]\n";

const char* SYSCTL_NAME = "hw.pwmbacklight0.level";
const int MAX_BRIGHTNESS = 255;
const int MIN_BRIGHTNESS = 0;

int main(int argc, char **argv) {
    if (argc > 2) {
        fprintf(stderr, USAGE);
        return 1;
    }

    // Parse argument, if any
    bool set_value = (argc == 2);
    int new_value;
    int is_delta;
    if (set_value) {
        char *arg = argv[1];

        char *endptr;
        new_value = strtol(arg, &endptr, 10);
        if (endptr == arg || endptr - arg < strlen(arg)) {
            fprintf(stderr, USAGE);
            return 1;

        }

        is_delta = (arg[0] == '+' || arg[0] == '-');
    }

    // Get current brightness
    int value;
    size_t len = 4;
    int res = sysctlbyname(SYSCTL_NAME, &value, &len, NULL, 0);
    if (res != 0) {
        perror("sysctlbyname");
        return 1;
    }
    assert(len == 4);

    if (set_value) {
        if (is_delta)
            value += new_value;
        else
            value = new_value;

        if (value > MAX_BRIGHTNESS)
            value = MAX_BRIGHTNESS;
        else if (value < MIN_BRIGHTNESS)
            value = MIN_BRIGHTNESS;

        // Set brightness
        res = sysctlbyname(SYSCTL_NAME, NULL, NULL, &value, 4);
        if (res != 0) {
            perror("sysctlbyname");
            return 1;
        }

        // Get new brightness value; may not be exactly the same
        res = sysctlbyname(SYSCTL_NAME, &value, &len, NULL, 0);
        if (res != 0) {
            perror("sysctlbyname");
            return 1;
        }
    }

    // Print final brightness level
    printf("%d\n", value);
}

This is hard-coded for the specific brightness device, and maximum brightness. I’m sure it could be made more generic, but it might need knowledge/testing with other backlight devices. If there are a few drivers.

But this works well for my use. I can configure i3 to bind keys to backlight -16 and backlight +16. I can also use it in the command line to see the brightness or set a specific value.

Screensaver bug?

I’ve noticed a weird issue when running X on the Pinebook Pro where when it’s been running for a while, and the X11 screensaver activates, the screen turns fades to white. And if it’s been in that state for a while, there’s a white vignette around the edges of the screen that persists across reboots (but eventually goes away). xset s activate triggers this behavior, confirming it’s the X11 screensaver feature. Though presumably it’s supposed to fade to black rather than white? Maybe something weird the panel does when it doesn’t get a refresh signal like it expects? Not sure exactly this works, though at least LCD ghosting is temporary…

xset dpms force suspend has the same behavior. So DPMS is definitely not working properly here.

Setting the backlight to 0 works but doesn’t seem to entirely turn off the backlight, and its still emitting light. I guess some kind of driver support is still needed here?

Software available on NetBSD aarch64

At least after the magical incantation to install pkgin, NetBSD has a binary package manager like you’d expect on a Linux distro, with a fairly good selection of software.

I’m sure pkgsrc doesn’t have as much software packaged as something like Debian, but much of the time you can just pkgin install the same things you would on Linux and not really have any more difficulty than you would there. Proprietary software of course is a different matter. I’m not sure if much of anything provides NetBSD binaries, though apparently compat_linux(8) exists. I still need to experiment with that on NetBSD or FreeBSD, though things will also be harder on aarch64.

For Rust development, rustup does offer NetBSD binaries… but not for aarch64. But pkgsrc does package Rust, if you don’t need quite the latest release. If I did, I guess I’d look into cross-compiling a Rust compiler from x86_64 Linux… maybe something to try.

U-Boot configuration

With this build of Towboot/U-Boot, Ctrl-C or Esc to show the boot menu doesn’t seem that reliable. And I have something else installed on the eMMC, which it defaults to booting before the NVMe drive.

Luckily U-Boot does have a console where we can configure things like this without rebuilding:

setenv bootcmd run towboot_menu
setenv bootdelay 0
saveenv
poweroff

Now it shows the boot menu immediately.

Updating NetBSD

NetBSD 10.1 released since I originally installed NetBSD. Not sure if any of the fix/changes from the release notes are that relevant, but it’s good to update.

It has taken me a while to get around to finishing this post… but at least that gives an opertunity to see what the upgrade process is like, for a minor release anyway.

https://www.netbsd.org/docs/guide/en/chap-upgrading.html documents this. Apparently the standard process is to boot from external media and run sysinst, like the typical install process (which we didn’t do here, since the aarch64 image can just be written directly to the boot drive).

A bit annoying to need external media for an upgrade, but easy enough. The same .img can be copied with dd to a USB drive, and we can select the USB drive in the boot menu to boot off of it.

Though the NetBSD bootloader on the USB drive seems to end up by default booting the kernel from the USB drive, but the rootfs from the NVMEe drive. Maybe related to the disk labels being the same, or just the order in which it tries to find a root device.

We can specify the root device:

boot netbsd root=dk7

But for some reason this errors about a read-only fs, and drops to a root shell? Oh well, mount /dev/dk7 / and exit remounts and continues booting, not sure what’s going on there…

Anyway, we can login as root and start sysinst. and select “Upgrade NetBSD on a hard disk”. Select ld4, which is the NVMe drive (in this case).

It errors with “device busy” trying to mount /dev/dk0, the UEFI partition on the NVMe drive. It seems it mounted that as /boot (rather than the partition on the USB drive). umount /boot should fix that.

Back in the upgrade menu, the “Full installation” option seems appropriate (including X11, etc.). The install media (this one anyway) doesn’t seem to have distribution sets, so we can select “Install from: HTTP”. sysinst will offer to configure the network. Then it extracts the sets and runs some post upgrade commands. Which seem to be minor for 10.1 but presumably would do more for a major release.

This is more complicated than upgrades on some OSes, but would have been mostly straightforward if the root disk was correct by default. Perhaps those things could be fixed by changing the partition labels to no longer match the install media. Perhaps it would have been better to start with to just flash the image to an SD card and install to the NVMe drive with sysinst.

I don’t seem mention in the documentation of updating pkgsrc packages after an OS upgrade, but we can update the repository URL in /usr/pkg/etc/pkgin/repositories.conf then run pkgin update and pkgin upgrade. I guess that’s the/a right way to do it?

Actually… further down on the NetBSD upgrading documentation, it mentions that pkgsrc/sysutils/sysupgrade provides a way to upgrade without booting off a separate drive. That might have saved some trouble here. Oh well.

Anyway, from a Linux perspective, it’s interesting how BSDs split base system upgrades from package upgrades. A bit like atomic updates on something like Fedora Silverblue… but rather a bit more limited since it’s not atomic, the root file system is writable, and the base system is not quite so complete of a desktop OS.

Conclusion

I’m not sure I’d recommend the Pinebook Pro overall at this point, given its age, if nothing else. If Pine64 (or anyone else) releases something like that with a newer SoC, that may be interesting though. But given I have one already it’s definitely a neat thing to run NetBSD on. Of course, the age also means there’s been time to develop NetBSD drivers. So it’s perhaps more attractive for those looking for a NetBSD laptop, while Linux offers a much larger range of hardware support.

The performance of the SoC in the Pinebook Pro is not very impressive in general, but it’s certainly plenty for a NetBSD command-line and a basic X session.

Overall I feel Linux (or maybe FreeBSD) would be a more practical choice of OS for most things, particularly for a very general purpose desktop system. But I find a certain charm in NetBSD.

Future

I want to get cosmic-comp and smithay (the Rust Wayland compositor library it uses) working on NetBSD. Though this will require a port of libinput, or a backend for basic NetBSD keyboard/mouse input backend for Smithay that doesn’t use libinput.

There’s now a port of libinput for OpenBSD, which uses a slightly different version of NetBSD’s wscons(4) API. I wonder how hard that would be to port. Or library that provides the libevdev API over wscons could work. Though looking into that libinput does require more symbols from that library than I might have first guessed…

I wonder how hard it might be to improve the audio drivers for the Pinebook Pro to work a bit better.

Otherwise, I could use this install for things that work on NetBSD (and aarch64) and aren’t too performance sensitive. And the Pinebook Pro is light enough to be handy to take anywhere. So for instance if I get around to learning Forth I could always install gforth on the Pinebook Pro and take it to the library or anywhere else. And of course scummvm can run fine. For these sorts of things, NetBSD is no trouble, and the Pinebook Pro’s Rockchip SoC is overkill, really.

Discussion

Mastodon