Reviving Cubox with Arch Linux ARM

Posted on 2018-11-11.
I decided it was time to try and revive my old Cubox that has been lying around unused for a very long time. Back then it was running a custom Armbian (Debian for ARM) release from SolidRun, but SolidRun have long since abandoned the old Cubox and deleted all content from their servers. In this tutorial I'll share how I got the Cubox up and running again with the latest Linux kernel. It is currently working as a small backup server running two USB harddrives in a Btrfs mirror.

Getting Arch Linux for ARM

Arch Linux ARM is a great Linux distribution for ARM based computers. They provide targeted kernels and software support for soft-float ARMv5te, hard-float ARMv6 and ARMv7, and ARMv8 AArch64 instruction sets on a variety of consumer devices and development platforms. The projects collaboration with Arch Linux brings users the best platform, newest packages, and installation support.

Arch Linux ARM carries forward the Arch Linux philosophy of simplicity and the entire distribution is on a rolling-release cycle that can be updated daily through small packages instead of huge updates on a defined release schedule. Most packages are unmodified from what the upstream developer originally released.

The relevant page on the website for Cubox is: https://archlinuxarm.org/platforms/armv7/marvell/cubox and you can download the latest image (as of writing it is the "2018-11-05" image) here: https://archlinuxarm.org/os/ArchLinuxARM-cubox-latest.tar.gz

The instructions for the installation is currently outdated and you'll run into trouble with bsdtar being unable to handle ext3. Also there is no reason to run the outdated ext3 file system when the installation can run just fine using both ext4 and xfs.

You can run the Cubox of a SD card or directly of an USB drive, but you have to remember to connect the USB drive to the upper USB port as that is the only bootable port on the device.

Installation

Partition the hard drive into a single ext4 partition and create the filesystem. Be sure that the "64bit feature" is disabled https://bbs.archlinux.org/viewtopic.php?id=217482

# cfdisk /dev/sdX
# mkfs.ext4 -O ^metadata_csum,^64bit /dev/sdX1

Then mount the drive and extract the files from the tar archive:

# mkdir arch
# mount /dev/sdX1 arch/
# bsdtar -xpf ArchLinuxARM-cubox-latest.tar.gz -C arch/
# sync

Then you need to create your own bootloader as the one provided in the Arch ARM image isn't working. Create a file called "boot.txt" with your favorite editor and setup the relevant data:

This is for booting from USB:

setenv bootargs 'console=ttyS0,115200n8 vmalloc=384M root=/dev/sda1 rootfstype=ext4 rw rootwait usb0Mode=host usb1Mode=host video=dovefb:lcd0:1920x1080-32@60-edid clcd.lcd0_enable=1 clcd.lcd1_enable=0'

This is for booting from the SD card:

setenv bootargs 'console=ttyS0,115200n8 vmalloc=384M root=/dev/mmcblk0p1 rootfstype=ext4 rw rootwait video=dovefb:lcd0:1920x1080-32@60-edid clcd.lcd0_enable=1 clcd.lcd1_enable=0'

Then the rest:

ext4load mmc 0:1 0x00200000 /boot/uImage
bootm

In my case I am booting from the SD card so my full boot.txt looks like this:

setenv bootargs 'console=ttyS0,115200n8 vmalloc=384M root=/dev/mmcblk0p1 rootfstype=ext4 rw rootwait video=dovefb:lcd0:1920x1080-32@60-edid clcd.lcd0_enable=1 clcd.lcd1_enable=0'
ext4load mmc 0:1 0x00200000 /boot/uImage
bootm

Then create the boot loader and copy it to the partition. I am doing this on an Intel based computer running Arch Linux and Arch Linux has "mkimage" from uboot-tools for that:

# pacman -S uboot-tools
# mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "My boot script" -d boot.txt boot.scr
# cp boot.scr arch/boot/boot.scr

systemd bug

Normally you would now unmount the partition and boot up the Cubox, but there is a systemd bug in the current image which will halt the Cubox during boot.

If you're reading this long after the 2018-11-05 release of Arch Linux ARM you can try to boot and see if the bug has been fixed, otherwise you can use this simple workaround:

# rm arch/etc/localtime
# cp arch/usr/share/zoneinfo/UTC arch/etc/localtime
# umount arch

You can always change the timezone to something else once you have upgraded the box to the latest kernel and a newer systemd release.

Getting the terminal up and running

Then it's time to fire up a terminal, using a micro USB cable, and then turn on the Cubox.

I prefer to use the screen VT100/ANSI terminal emulation for easy connection to the Cubox.

# screen /dev/ttyUSB0 115200

If you have done the above steps correct and nothing else has gone wrong the Cubox should boot up and into Arch Linux ARM.

Login as the default user alarm with the password alarm. The default root password is root.

Problems with networking

When I got the box up and running I noticed that eth0 wasn't getting an IP address using DHCP. After some digging I eventually found out that the problem was caused by running systemd-networkd with an unsupported and extremely old 3.5 kernel.

I solved the problem by temporarily disabling systemd networking daemons and manually configuring the network.

First, disable all the networking daemons from systemd:

# systemctl stop systemd-networkd
# systemctl disable systemd-networkd
# systemctl stop systemd-resolved
# systemctl disable systemd-resolved
# systemctl stop systemd-timesyncd 
# systemctl disable systemd-timesyncd

Then manually setup networking to fit your network options. In my case I am using a local DNS server, but you can change that to whatever you normally use.

# rm /etc/resolv.conf
# echo "nameserver 192.168.1.1" > /etc/resolv.conf

Then fire up the network card and provide it with an IP address and setup the default route:

# ip link set eth0 up
# ip address add 192.168.1.19/24 broadcast + dev eth0
# route add default gw 192.168.1.1 eth0

Verify everything is working:

# ip a
# ip route show
# ping google.com

Getting pacman up

Then you need to initialize pacman. During the initialization you need to create some entropy (some randomness) otherwise this can literally take hours. Now that the network is up I logged in using SSH and created a small Bash loop script for entropy. Run the script while you initialize pacman in the serial terminal or logged in using a second SSH connection.

You can use vi or nano to create the script.

#!/bin/bash
for i in {1..100000}
do
   echo "Hello $i times"
done

Run the script while initializing pacman in the other terminal. You should only need to run the script once or twice before pacman is done.

# pacman-key --init

Then populate the pacman with the latest keys and update the installation.

# pacman-key --populate archlinuxarm
# pacman -Syu

Before rebooting I decided to install Neovim as that is my favorite text editor.

# pacman -S neovim
# reboot

Updating the kernel

The old Cubox is supported by the upstream Linux kernel and it's possible to upgrade to that version without doing anything else. This is the related dts which shows the support for the Cubox.

So get the network up again and upgrade the kernel:

# ip link set eth0 up
# ip address add 192.168.1.19/24 broadcast + dev eth0
# route add default gw 192.168.1.1 eth0
# ip a
# ip route show
# ping google.com

Install the latest upstream kernel and answer yes to the replacement.

# pacman -S linux-armv7-cubox

During installation I got a small warning about the kernel requiring a newer U-Boot, but (as of writing) you can ignore that. The upstream kernel runs well with the provided U-Boot from the Arch Linux ARM image.

...
1/2) installing linux-armv7-cubox [############################] 100%
>>> Updating module dependencies. Please wait ...
NOTE: Using this kernel requires an updated U-Boot!
...

Once the upgrade has completed reboot:

# reboot

After the reboot check that everything is working.

# dmesg|less
# journalctl -b
# systemctl --failed

No complaints.

Then you can get systemd-networkd working again. I am skipping systemd-resolved as I don't want that.

# nvim /etc/systemd/network/eth0.network
[Match]
Name=eth0
[Network]
DHCP=yes
# systemctl enable systemd-network
# systemctl start systemd-network

We can also use systemd-timesyncd again.

# systemctl enable systemd-timesyncd
# systemctl start systemd-timesyncd

If you're booting from a USB hard drive that needs to spin down you can't use systemd-timesyncd as it checks the time every half hour and stores that to disk. As a result the disk spins up every 30 minutes. You can read more about that on https://wiki.archlinux.org/index.php/Systemd-timesyncd. I tried to set the PollIntervalMinSec and PollIntervalMaxSec values, but - and the man page is pretty vague - it doesn't change anything, which means that at least every 2048 seconds the NTP server gets polled.

On https://www.freedesktop.org/software/systemd/man/timesyncd.conf.html it says:

The minimum and maximum poll intervals for NTP messages. Each setting takes a time value (in seconds). PollIntervalMinSec must not be smaller than 16 seconds. PollIntervalMaxSec= must be larger than PollIntervalMinSec=. PollIntervalMinSec defaults to 32 seconds, and PollIntervalMaxSec= defaults to 2048 seconds.

I eventually chose ntpdate from ntp as it also fix'es the date upon boot. ntpdate is a part of ntp and you just need to enable it together with ntp.

# pacman -S ntp
# systemctl enable ntpdate
# systemctl enable ntpd
# systemctl start ntpd

ntpdate will sync the clock upon boot while ntpd keeps it synced.

Actually ntpdate is just ntp run as a systemd service before the regular ntpd daemon.

# cat /lib/systemd/system/ntpdate.service
[Unit]
Description=One-Shot Network Time Service
After=network.target nss-lookup.target
Before=ntpd.service

[Service]
Type=oneshot
PrivateTmp=true
ExecStart=/usr/bin/ntpd -q -n -g -u ntp:ntp

[Install]
WantedBy=multi-user.target

The regular ntpd.service:

# cat /lib/systemd/system/ntpd.service 
[Unit]
Description=Network Time Service
After=network.target nss-lookup.target
Conflicts=systemd-timesyncd.service

[Service]
Type=forking
PrivateTmp=true
ExecStart=/usr/bin/ntpd -g -u ntp:ntp
Restart=always

[Install]
WantedBy=multi-user.target

The strange part about this is that ntpd.service should work as it also has both -g and After=network.target, but it doesn't. It doesn't set the clock upon boot if the time is too far off, but ntpdate does.

Anyway, if ntpdate becomes deprecated (as is noted in the man page), then you can just create your own service and run that. Copy the above ntpdate.service and use that then.

Last we need to fix the timezone:

# cp /usr/share/zoneinfo/Europe/Copenhagen /etc/localtime

And then a final reboot:

# reboot

That's it. The old Cubox has been revived with a brand new Arch Linux ARM installation with the latest upstream Linux kernel!

Alternative startup service for networking

During my initial "trial and error" testing I suspected that the latest upstream Linux kernel wouldn't run on the device and that I had to live with the networking problems faced by the Cubox. As an alternative solution to running systemd-networkd I created a small systemd service script that would get the network automatically up during boot. I have provided it here just in case it might benefit someone in the future.

First, create a systemd startup service:

# nvim /etc/systemd/system/auto-fix-network.service
[Unit]
Description=Autostart network custom script

[Install]
WantedBy=multi-user.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/auto-fix-network.sh

Then the shell script:

# nvim /etc/auto-fix-network.sh
#!/bin/sh

# Get the network card up
ip link set eth0 up

# Set the address
ip address add 192.168.1.19/24 broadcast + dev eth0

# Fix the default route.
route add default gw 192.168.1.1 eth0

Make sure it can execute:

# chmod +x /etc/auto-fix-network.sh

Enable it:

# systemctl enable auto-fix-network

That's it.

Feel free to email me if you have any suggestions or comments.