I have a SolidRun MACCHIATObin Single Shot, and you can build and use UEFI on it. This involves compiling code from a variety of repos, one of those being mv-ddr-marvell. To do this, I made a git repo full of helper scripts. I don’t use the dev board much anymore, and it is mostly there to possibly help others build the collection of firmware that results in the image that I burn onto a microSD card.
The Problem
So, I wouldn’t be making a blog post on a blog I haven’t posted on for years if I hadn’t ran into some issue, and I did, between the aformentioned Marvell DDR code and recent versions of GCC (>=12). One of the last steps of building the firmware is building TrustedFirmware-A, and getting some Marvell Binaries, and that DRR code and throwing it all together in some way (I don’t claim to understand the build process super well). My build environment is a Debian 12 VM running on Hyper V on the Windows Dev Kit 2023. So what exactly happens? This happens:
$ bash tfa.sh
make: Entering directory '/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a'
Building DRAM driver
CC apn806/mv_ddr_plat.c
CC apn806/mv_ddr_static.c
CC apn806/mv_ddr_validate.c
CC ddr_init.c
CC ddr3_init.c
CC ddr3_training_db.c
CC mv_ddr_build_message.c
CC mv_ddr_common.c
CC mv_ddr_spd.c
CC mv_ddr_mrs.c
CC mv_ddr_topology.c
CC mv_ddr4_training_db.c
CC drivers/mv_ddr_mc6.c
CC drivers/mv_ddr_xor_v2.c
CC ddr3_debug.c
CC ddr3_training.c
CC ddr3_training_bist.c
CC ddr3_training_centralization.c
CC ddr3_training_hw_algo.c
CC ddr3_training_ip_engine.c
CC ddr3_training_leveling.c
In file included from mv_ddr_atf_wrapper.h:232,
from ddr3_init.h:105,
from ddr3_training_leveling.c:98:
In function ‘mmio_read_32’,
inlined from ‘mv_ddr_rl_dqs_burst’ at ddr3_training_leveling.c:1980:5:
/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/include/lib/mmio.h:46:16: error: array subscript 0 is outside array bounds of ‘volatile uint32_t[0]’ {aka ‘volatile unsigned int[]’} [-Werror=array-bounds]
46 | return *(volatile uint32_t*)addr;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
In function ‘mmio_read_64’,
inlined from ‘mv_ddr_rl_dqs_burst’ at ddr3_training_leveling.c:1978:5:
/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/include/lib/mmio.h:56:16: error: array subscript 0 is outside array bounds of ‘volatile uint64_t[0]’ {aka ‘volatile long unsigned int[]’} [-Werror=array-bounds]
56 | return *(volatile uint64_t*)addr;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:473: /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/ble/ddr3_training_leveling.o] Error 1
make: *** [plat/marvell/armada/a8k/common/ble/ble.mk:35: /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/ble/mv_ddr_lib.a] Error 2
make: Leaving directory '/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a'
cp: cannot stat 'trusted-firmware-a/build/a80x0_mcbin/release/flash-image.bin': No such file or directory
tfa.sh
is one of the helper scripts, and its contents are:
#!/usr/bin/env bash
BDIR=e2b
cd ${BDIR}
export WORKSPACE=$PWD
export PACKAGES_PATH=$PWD/edk2:$PWD/edk2-platforms:$PWD/edk2-non-osi
make -C trusted-firmware-a \
PLAT=a80x0_mcbin \
MV_DDR_PATH=$PWD/mv-ddr-marvell \
SCP_BL2=$PWD/binaries/mrvl_scp_bl2.img \
BL33=$PWD/Build/Armada80x0McBin-AARCH64/RELEASE_GCC5/FV/ARMADA_EFI.fd \
all fip mrvl_flash
It sets a variable for our build directory, goes to that directory, and some others that I believe the build process expects, and then kicks off make
for TF-A.
To drill down to the error more specifically:
In function ‘mmio_read_32’,
inlined from ‘mv_ddr_rl_dqs_burst’ at ddr3_training_leveling.c:1980:5:
/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/include/lib/mmio.h:46:16: error: array subscript 0 is outside array bounds of ‘volatile uint32_t[0]’ {aka ‘volatile unsigned int[]’} [-Werror=array-bounds]
46 | return *(volatile uint32_t*)addr;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
In function ‘mmio_read_64’,
inlined from ‘mv_ddr_rl_dqs_burst’ at ddr3_training_leveling.c:1978:5:
/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/include/lib/mmio.h:56:16: error: array subscript 0 is outside array bounds of ‘volatile uint64_t[0]’ {aka ‘volatile long unsigned int[]’} [-Werror=array-bounds]
56 | return *(volatile uint64_t*)addr;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
gcc (Debian 12.2.0-14) 12.2.0
doesn’t care for how Marvell decided to handle this array code, and I don’t blame it, I’m not a firmware developer, but it looks a bit…weird to me.
The “Solution”
This started my quest of finding a version of gcc
that would compile this code and not complain, and I started with gcc
7.5.0, but I eventually ended up finding out that gcc
11.4.0 is the latest version (as of the time I tried) that copmiled this code. After some googling, I found out how to declare a toolchain for TF-A.
Prep
Before going on to compiling gcc
or binutils
, you’ll want to make sure you have some stuff installed:
$ sudo apt install build-essential acpica-tools device-tree-compiler uuid-dev libssl-dev gcc-aarch64-linux-gnu texinfo bison help2man --install-recommends -y
Compiling GCC
This is easy enough, there’s documentation on how to build it from source, and so I followed it.
alc@devvm:~/toolchain-src/$ wget https://ftp.gnu.org/gnu/gcc/gcc-11.4.0/gcc-11.4.0.tar.gz
alc@devvm:~/toolchain-src/$ tar xvf gcc-11.4.0.tar.gz
alc@devvm:~/toolchain-src/$ cd gcc-11.4.0
alc@devvm:~/toolchain-src/gcc-11.4.0$ ./contrib/download_prerequisites
alc@devvm:~/toolchain-src/$ cd ..
alc@devvm:~/toolchain-src/$ mkdir objdir
alc@devvm:~/toolchain-src/$ cd objdir
alc@devvm:~/toolchain-src/objdir$ $PWD/../gcc-11.4.0/configure --prefix=$HOME/buildroot/11.4.0 --enable-languages=c,c++,lto --enable-lto --disable-nls --disable-libquadmath --disable-libquadmath-support --enable-default-pie --disable-werror --enable-linker-build-id
alc@devvm:~/toolchain-src/objdir$ make -j5
alc@devvm:~/toolchain-src/objdir$ make install
Actually building gcc
on my system takes a bit over half an hour, and I’ll admit some of the enable/disable statements here aren’t exactly scientific, but it’s what made the process work for me.
Compiling Binutils
This one is even more straight forward, and we need this for things like ld
and ar
.
alc@devvm:~/toolchain-src$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.xz
alc@devvm:~/toolchain-src$ tar xvf binutils-2.40.tar.xz
alc@devvm:~/toolchain-src$ cd binutils-2.40
alc@devvm:~/toolchain-src/binutils-2.40$ ./configure --prefix=$HOME/buildroot/11.4.0/ ./configure --prefix=$HOME/buildroot/11.4.0/ --enable-lto --enable-year2038 --with-pic --enable-deterministic-archives
alc@devvm:~/toolchain-src/binutils-2.40$ make -j5
alc@devvm:~/toolchain-src/binutils-2.40$ make install
Modifying tfa.sh
As seen as in the TF-A documentation, we need to add export CROSS_COMPILE=<path-to-aarch64-gcc>/bin/aarch64-none-elf-
to tfa.sh
so that it will use our version of gcc
and binutils, although I found I needed something more like export CROSS_COMPILE=<path-to-aarch64-gcc>/bin/
, for me tfa.sh
ended up looking like this:
#!/usr/bin/env bash
BDIR=e2b
cd ${BDIR}
export WORKSPACE=$PWD
export PACKAGES_PATH=$PWD/edk2:$PWD/edk2-platforms:$PWD/edk2-non-osi
export CROSS_COMPILE="$HOME/buildroot/11.4.0/bin/"
make -C trusted-firmware-a \
PLAT=a80x0_mcbin \
MV_DDR_PATH=$PWD/mv-ddr-marvell \
SCP_BL2=$PWD/binaries/mrvl_scp_bl2.img \
BL33=$PWD/Build/Armada80x0McBin-AARCH64/RELEASE_GCC5/FV/ARMADA_EFI.fd \
all fip mrvl_flash
cp trusted-firmware-a/build/a80x0_mcbin/release/flash-image.bin uefi-mcbin-spi.bin
now if we run tfa.sh
the final bit of output we get is:
Built /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/bl31.bin successfully
OD /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/bl31/bl31.dump
HOSTCC fiptool.c
HOSTCC tbbr_config.c
HOSTLD fiptool
Built fiptool successfully
Trusted Boot Firmware BL2: offset=0xD8, size=0x5321, cmdline="--tb-fw"
SCP Firmware SCP_BL2: offset=0x53F9, size=0x28A2C, cmdline="--scp-fw"
EL3 Runtime Firmware BL31: offset=0x2DE25, size=0xD253, cmdline="--soc-fw"
Non-Trusted Firmware BL33: offset=0x3B078, size=0x200000, cmdline="--nt-fw"
Built /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/fip.bin successfully
Built /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/boot-image.bin successfully
make[1]: Nothing to be done for 'all'.
Building flash image
Built /home/alc/macc-uefi-build-script/e2b/trusted-firmware-a/build/a80x0_mcbin/release/flash-image.bin successfully
make: Leaving directory '/home/alc/macc-uefi-build-script/e2b/trusted-firmware-a'
Hurray, e2b/uefi-mcbin-spi.bin
is what we wanted in the end, and everything builds.