December 29, 2021

Orange Pi 4 (Rockchip 3399) building U-Boot from source

Why do this? I have a version of U-Boot that came on my Debian distribution on SD card, and it does work to boot debian, both from SD card and from emmc. However, no matter how hard I try, I seem to end up having to build U-Boot from source for every ARM project I get involved in. Here are the reasons why in this case: The first point is as follows. I like to interrupt U-boot, change some enviroment variables, then do a saveenv to preserve my changes. Apparently the U-boot that is shipped will only do a saveenv to mmc. I would like to modify the setup for U-Boot on an SD ard, and that is currently impossible, at least without some serious hacking.

The second point is less obvious. The story is that if you build for your target, the build will leave ".o" files all through the U-Boot source tree and those serve to indicate which files were actually used in the build. I find this extremely handy, indispensible in fact. Just for the record, the current mainlin U-Boot has config files for 1432 different targets and not all of these are even ARM boards. This means that only a small fraction of the files in the source tree are actually used to build for my target, and it is all but impossible to figure out which without having the .o files as a helpful guide.

Let's do a build

I use git to fetch the latest "Denx" mainline sources from github. I am going to do an "as-is" build first, before screwing with anything pertaining to moving the ENV store from mmc to sd.

I issue commands as follows:

cd /u1/Projects/OrangePi/Rockchip/U-boot
make orangepi-rk3399_defconfig
CROSS_COMPILE=aarch64-linux-gnu-
export CROSS_COMPILE
make clean
make
Note that typing "make clean" preserves the config you selected, but in general will be a bad idea as it forces a full compile of everything.

Also note that you can just set CROSS_COMPILE=aarch64-linux-gnu- near the top of the Makefile and avoid having to set the enviroment variable each time, which I find handy. Everything goes nice and smooth except for this:

./"arch/arm/mach-rockchip/make_fit_atf.py" \
arch/arm/dts/rk3399-orangepi.dtb > u-boot.its
WARNING: BL31 file bl31.elf NOT found, resulting binary is non-functional
WARNING: Please read Building section in doc/README.rockchip
  MKIMAGE u-boot.itb
  MKIMAGE tpl/u-boot-tpl-rockchip.bin

What the heck is bl31

Well, I do know a little bit about this, just not how to quickly and easily make the U-Boot build process happy. The bl31 executable is part of the ATF (Arm Trusted Firware) scheme. This is something I do intend to dig into someday. All I know at this point is that bl31 is expected to run before U-Boot as part of the boot sequence. In the setup to generate a bootable Debian on emmc there is a file called "trust.bin" that contains bl31. The file "trust.bin" just gets slapped onto the SD card in the proper place using "dd".

What we should do at this point is to take a look at README.rockchip, as advised, and also inspect the U-Boot Makefile and see what is triggering its complaint as well as what it would like to find and what it intends to do with it.

A peek at README.rockchip is quite interesting. They offer two paths. One is to use something called rkbin to create trust.img. The other is to get the ATF sources and build bl31.bin as well as a thing that runs on a Cortex-M0 core in the RK3399, namely the Cortex-M0 PMU. This requires a Cortex M0 cross compiler, which they describe. (they also say the Cortex M0 PMU business is optional.

Now let's look at the Makefile. The message we see comes from a Python script "arch/arm/mach-rockchip/make_fit_atf.py" as indicated in the message. The section in the Makefile is:

# Boards with more complex image requirements can provide an .its source file
# or a generator script
# NOTE: Please do not use this. We are migrating away from Makefile rules to use
# binman instead.

ifneq ($(CONFIG_SPL_FIT_SOURCE),"")
U_BOOT_ITS := u-boot.its
$(U_BOOT_ITS): $(subst ",,$(CONFIG_SPL_FIT_SOURCE))
        $(call if_changed,copy)
else
ifneq ($(CONFIG_USE_SPL_FIT_GENERATOR),)
U_BOOT_ITS := u-boot.its
ifeq ($(CONFIG_SPL_FIT_GENERATOR),"arch/arm/mach-rockchip/make_fit_atf.py")
U_BOOT_ITS_DEPS += u-boot
endif
$(U_BOOT_ITS): $(U_BOOT_ITS_DEPS) FORCE
        $(srctree)/$(CONFIG_SPL_FIT_GENERATOR) \
        $(patsubst %,arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) > $@
endif
endif
A look at u-boot.cfg shows:
#define CONFIG_SPL_FIT_SOURCE ""
#define CONFIG_SPL_FIT_GENERATOR "arch/arm/mach-rockchip/make_fit_atf.py"
We can run make as "make V=1" to get a lot more information:
  ./tools/mkimage -f auto -A arm -T firmware -C none -O u-boot -a 0x00200000 -e 0x00200000 -p 0x0 -n "U-Boot 2022.01""-00450-g25711b07ca-dirty for evb_rk3399 board" -E  -b arch/arm/dts/rk3399-orangepi.dtb  -d u-boot-nodtb.bin u-boot-dtb.img

./"arch/arm/mach-rockchip/make_fit_atf.py" \
arch/arm/dts/rk3399-orangepi.dtb > u-boot.its
WARNING: BL31 file bl31.elf NOT found, resulting binary is non-functional
WARNING: Please read Building section in doc/README.rockchip

./tools/mkimage -E -B 0x8 -f u-boot.its -p 0x0 u-boot.itb >/dev/null  && cat /dev/null
./tools/mkimage -n "rk3399" -T rksd -d tpl/u-boot-tpl.bin tpl/u-boot-tpl-rockchip.bin

cat tpl/u-boot-tpl-rockchip.bin spl/u-boot-spl.bin > idbloader.img
aarch64-linux-gnu-objcopy --gap-fill=0xff -j .text -j .secure_text -j .secure_data -j .rodata -j .data -j .u_boot_list -j .rela.dyn -j .got -j .got.plt -j .binman_sym_table -j .text_rest -j .dtb.init.rodata -j .efi_runtime -j .efi_runtime_rel -I binary -O binary --pad-to=8355840 --gap-fill=0xff idbloader.img u-boot-rockchip.bin && cat u-boot.itb >> u-boot-rockchip.bin || { rm -f u-boot-rockchip.bin; false; }
We only get the BL31 image the first time we run make. U-Boot has a stub bl31.c file that it compiles to bl31.elf and that eliminates the complaint on the next pass. Will it work though?

Try something

Maybe I can use my existing setup and just replace the u-boot binary, keeping trust and idbloader as is. I do this, copying u-boot.bin from the u-boot directory (which includes the dtb). It does not work. Neither does u-boot.img.

So this is an unfinished puzzle -- how to build U-boot from source and stick it on an SD card.

Here are some nice instructions for how to do it (involving bl31) for the pine rock64. The Rock64 is based on the Rockchip RK3328.

My network boot customization

I have explanation elswhere, but what I want to inject into the U-Boot environment settings (and save) is this:
setenv bootaddr 0x02000000
setenv kyu_bootcmd "echo Booting Kyu via dhcp ; dhcp ${bootaddr}; go ${bootaddr}"
setenv bootcmd "run kyu_bootcmd"
saveenv


Have any comments? Questions? Drop me a line!

Tom's electronics pages / [email protected]