Moving to ARM (Part 3 – Upgrading the firmware)

(Continued from Part 2)

So after rebooting back into the 4.x kernel, I started looking into what it takes to upgrade the firmware on the board. It was around this point where the technical wiki (which is either maintained by Marvell, or Solid-Run… I can’t tell which) started to fail. If I had to guess, they upgraded the underlying PHP on their webhost without checking to see if their webapps supported it. (It’s still broken to this day, but some of the (slightly outdated) information has eventually been transplanted to another wiki.)

Upgrading the firmware isn’t terribly hard, but I would only recommend doing it when you have to (ie: if you want to ever use a kernel newer than 4.x), I’ll touch more on that later. The firmware is made up of a few components:

Setting up the build environment was a piece of cake, since Gentoo already has a full toolchain ready to go. Or at least that was the case a couple years ago. Current ATF git head won’t compile with GCC 12+ or binutils 2.39+ [1] [2] [3], but as long as Gentoo still keeps the older packages in the portage tree, you can switch back and forth as needed with “eselect”.

eselect gcc set aarch64-unknown-linux-gnu-11
eselect binutils set aarch64-unknown-linux-gnu-2.38

It turns out ATF ships their own version of GCC to build the firmware with. I don’t see why they do this, because regular GCC used to work just fine… but I digress.

The build script looks something like this…

#!/bin/bash

BUILD_DIR="/root/mcbin"


cd "$BUILD_DIR"/u-boot
make distclean
make mvebu_mcbin-88f8040_defconfig
make -j4

export BL33=$BUILD_DIR/u-boot/u-boot.bin
export SCP_BL2=$BUILD_DIR/binaries-marvell/mrvl_scp_bl2.img
export MV_DDR_PATH=$BUILD_DIR/mv-ddr-marvell

cd "$BUILD_DIR"/arm-trusted-firmware
make distclean
make -j4 USE_COHERENT_MEM=0 LOG_LEVEL=20 PLAT=a80x0_mcbin all fip mrvl_flash

cp "$BUILD_DIR"/arm-trusted-firmware/build/a80x0_mcbin/release/flash-image.bin "$BUILD_DIR"

Compiling the firmware only takes a few minutes once the repositories are all checked out and ready to go. When you’re done, you have a shiny new “flash-image.bin”, but how do you go about installing it?

Inside U-Boot, Marvell included a command called “bubt”. I couldn’t tell you what that stands for… Regardless, you have to have the bin file in a place where U-Boot can read it, which usually means the eMMC, you may run into issues if the file is on a USB stick for some reason… at least I seem to recall that being an issue at one point.

So “bubt flash-image.bin spi mmc” and you’re done right? Well, not so fast. After running “reset” to reboot U-Boot (or power-cycling), you’ll probably notice your U-Boot environment variables are all gone, which includes your kernel bootcmd config lines, as well as your ethernet MAC addresses. Probably should have saved those somewhere, but it wasn’t documented anywhere on the wikis.

Thankfully, I had ran a “printenv” in U-Boot before running “bubt”, so I was able to scroll back in my terminal, re-entering my missing variables, running “saveenv” to make sure they get saved, and rebooting again. I suppose I shouldn’t be that surprised, because the same thing happens on x86 when you upgrade your BIOS. You would think that after 40+ years of computing that this kind of thing should persist upgrades, but I digress again.

Now I mentioned earlier that I would only recommend doing the firmware upgrade only when necessary. There was a time when I was checking the firmware repositories every couple weeks for updates, that was a mistake on my part. At some point I had pulled a version of U-Boot head which broke SDHCI support, which basically meant it couldn’t boot any kernels off eMMC or USB.

Thankfully for those jumpers I mentioned back in part 2, I was able to temporarily jumper the board to booting U-Boot off the SD card, flashing an older firmware back to the eMMC, flipping the jumpers back again, and try to figure out where things went wrong with U-Boot.

I didn’t know who to reach out to initially, because U-Boot doesn’t really have a support structure, but I found a relevant commit by a friendly gentleman called Marko, so I emailed him to see if we could figure something out. Turns out he didn’t have the same board I did, but the issue also affected several other Marvell boards he owned, which seemed to point to an issue in the Xenon SDHCI driver. A couple weeks later, a patch was merged… I tested it and thankfully it was working again.

I should probably write a part 4 at some point, since I’m kindof glossing over a lot of the minor details, like what devicetree files are for, how the kernel and U-Boot have their own files, and how they should probably stay synchronized if you want your stuff to work correctly, but I’ll save that for another time.