Introduction #
So because i previously ordered the wrong SPI flash chips, i decided to not waste time and instead try to compile U-Boot for the board. So if the SPI flash chips arrive that i can flash Uboot to those SPI flash chips and then already have a bit of progress done before.
Research #
So i decided to pour some time into researching how the boot chain/process worked. Its not like the Allwinner SOC’s/orangepi board where you can simply use sunxi-tools to flash a Uboot image with SPL either into ram or to a SPI flash chip and have it work. Rockchip chips boot a bit different. So i first searched a bit on the radxa website and visited This page on there. They say that they recommend that you use their BSP tools/environment but i want to compile it without depending too much on vendor specific tools. They do also have a section about compiling Uboot manually. That page is not really helpfull and also wont provide much info but it did link to this Uboot documentation page for rockchip SOC’s. I also decided to manually search arround a bit how the boot process worked and found This.
Boot process #
SO after the research i got some more info about the boot process. And that rockchip website is really helpfull as it lists the bootflow in some easily explained steps and also a flowchart. And it seems it has 2 boot methods/supports 2. On the website we also see that that spl_loader.bin file is used for upgrading/flashing the EMMC as i already suspected. Also it seems like both methods require a idbloader.img as a sort of preloader but this is where the differences in boot flow between method 1 and 2 start.

Method 1 #
So method 1 makes use of a file called idbloader which consists of a miniloader, which is a combination of a DDR initialisation payload which initialises the dram and also a miniloader which is the rest of the preloader code. This miniloader then chainloads a uboot image which simply is Uboot’s code (uboot.bin). It also often needs another image file called ’trust.img’ which hoplds the trusted execution environment and all the ARM trusted firmware and such code that makes trusted execution possible as required on ARM chips. And after this ofcourse comes the OS stage which loads the kernel and rootfs of an os via UBOOT. But we dont have to care about that as thats just regular uboot stuff. Load a kernel from some address off a bootdevice or from a file and then load it into ram. Also pas it a rootfs and some bootargs and a devicetree blob and you are done, anyways not important for now.
Method 2 #
So method 2 also makes use of a file called idbloader but instead of consisting of a miniloader and DDR dram initialisation payload, it consists of a U-boot TPL (tertiary program loader) payload and a Uboot SPL (Secondary program loader) payload. The TPL basically does the tasks of that DDR initialisation blob and the SPL is that miniloader “sort off”. After this it chainloads a u-boot.itb which in reality is just a UBOOT FIT(Flat-Image-Tree) Image. This is basically a file that packages multiple payloads into 1 file. This package contains the raw Uboot code like also in method 1 (obviously) but it also integrates that Trusted execution environment stuff that ARM chips need, packaged inside the same Image and it contains the Device tree of Uboot. SO it basically packages everything thats not the SPL or TPL inside 1 Image which is perfect. I believe this is also what Mainline U-boot uses but you will see a bit more about this in this article.
Mainline-Uboot. #
So first i decided to use mainline Uboot. While it wont have all the features/handy bits the vendor Uboot uses, it will be a simple baseline that just works and i can use to do furhter tetsing and or if the vendor uboot wont work out. Also Mainline has a ton of community support and wont have any weird vendor compilation quirks or custom scripts/commands that are required to build it. And i can just follow Uboot’s documentation which is handy as Uboot’s documentation is always a lot clearer then some random chinese sbc maker’s documentation either due to a lot being behind NDA’s (Non disclosure agreements) or a language barier or simply lack of documentation.
Dependencies first #
So lets first install the dependencies or else something will complain about stuff it needs that is not available and we dont want that so lets prepare our system. Install the following packages. Just note that if a program errors out due to a missing dependency, it will often tell you the name and then you can install those and no guarantee the list i provided is complete and also some packages might be named different on some distros and you have to do a bit of manual research.
build-essential gcc-aarch64-linux-gnu bintutils bc flex bison make git cpio lz4Compile Trusted firmware. #
So i made a simple script that compiles it. Because there is no rk3566 target, i chose the rk3568 target which is a close match and will work. Script is pasted below but you can also download it Here. So make sure to first clone the source code using the command below from the official repository. Make sure git is installed first.
git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.gitAnd after that download the script i made. This does nothing else than simply use the ‘make’ command to build the trusted firmware exactly how Uboot/ATF wants/needs. You can see the source code below if you are curious/suspicious.
#!/bin/bash
atf_dir=trusted-firmware-a
echo "building ARM Trusted firmware.."
if [ ! -d "$atf_dir" ]; then
echo "ARM Trusted firmware source tree not found. Please clone it first in $(pwd)"
exit 1
fi
cd "$atf_dir"
make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3568 -j $(nproc)
if [ ! $? = 0 ]; then
echo "build process failed!"
exit 1
fi
echo "Build success!"
cp build/rk3568/release/bl31/bl31.elf ../So just place the script in the same folder where you cloned the folder of the trusted firmware source code and run it (so dont place it directly in the source code folder but directly outside it). Then execute it and after a few seconds it will be done and you will see in the same directory of the script, a new file called bl31.elf. Make sure to have make and gcc for ARM installed. You can search on the internet on how to install them for your distro. Now lets move onto compiling U-boot itself!
Rockchip TPL #
So we also need the TPL which has the task to Initialise the DRAM as i explained before. So we need to also download the rkbin source, but we dont have to build it, we just need the precompiled files which is handy.
git clone --depth 1 https://github.com/rockchip-linux/rkbinFinally, lets Compile U-boot mainline #
SO lets compile Uboot now. So if you did not already, clone the source code from the official repository first.
git clone https://source.denx.de/u-boot/u-boot.git Uboot-mainlineAfter that, i made another little script which will make things easier. It will automatically apply a default configuration and will even ask you if you want to modify the Uboot install by opening a menuconfig menu, a bit like the Linux kernel. And it crosscompiles with ARM gcc the uboot code into a working binary/image for your board. And it checks if the ‘rkbin’ directory which has the precompiled TPL files and checks if the bl31.elf file is available (if it has been compiled which it should be able to find, unless you skiped over before steps). And you can also download it by clicking This button.
#!/bin/bash
LOG_FILE="../uboot_build_$(date +%Y%m%d_%H%M%S).log"
BL31="bl31.elf"
TPL="rkbin/bin/rk35/rk3566_ddr_1056MHz_v1.23.bin"
UBOOT_DIR="$(pwd)/Uboot-mainline"
echo "Checking Files/directories..."
if [ ! -d "$UBOOT_DIR" ]; then
echo "No U-BOOT source directory is found at $UBOOT_DIR. Please clone the source first"
exit 1
fi
echo "U-BOOT source is available!"
if [ ! -f "$BL31" ]; then
echo "BL31 not found!"
exit 1
fi
echo "BL31 trusted firmware is available!"
if [ ! -f "$TPL" ]; then
echo "Rockchip TPL not found!"
exit 1
fi
echo "Rockchip TPL is available!"
echo "Checking config status..."
cd "$UBOOT_DIR" || exit 1
if [ ! -f .config ]; then
echo "U-BOOT is not yet configured. Configuring with board default..."
make CROSS_COMPILE=aarch64-linux-gnu- radxa-cm3-io-rk3566_defconfig
echo "U-BOOT has been configured with the board default!"
fi
read -p "Do you want to make changes to the config? (y/n): " p1
case $p1 in
[Yy]* )
echo "Opening menuconfig..."
make CROSS_COMPILE=aarch64-linux-gnu- menuconfig
;;
esac
echo "Compiling U-boot..."
make -j $(nproc) CROSS_COMPILE=aarch64-linux-gnu- BL31=../$BL31 ROCKCHIP_TPL=../$TPL KCFLAGS="-Wno-maybe-uninitialized" | tee -a $LOG_FILE
if [ ! $? = 0 ]; then
echo "build failed!"
exit 1
fi
echo "Done!"
echo "Wrapping up..."
cp u-boot.itb ../
cp idbloader.img ../So simply run the script, complete what it asks and you should after a few seconds (depending how fast your pc is) get 2 files, the ‘idbloader.img’ and ‘u-boot.itb’. These are the files that boot method 2 requires. If not, check the logfile it generated and see what needs to be done. It could be as simple as forgetting some step or having a tool not installed.
Flashing Mainline-Uboot to the Board’s EMMC. #
So finally lets flash the bootloader to the board’s EMMC. We can use either ‘rkdeveloptool’ or ‘upgradetool’ to do this process. We saw in the boot process documentation that we first need that SPL loader so it initialisess the board far enough that we can flash the EMMC storage. SO lets first make sure they are installed. You can search online on how to install them for your distro/pakcage manager. If they are installed we can run either of the commands below which will show how you have to use the command. For simplicity lets use ‘rkdeveloptool’.
rkdeveloptoolThis command above shows the list below
---------------------Tool Usage ---------------------
Help: -h or --help
Version: -v or --version
ListDevice: ld
DownloadBoot: db <Loader>
UpgradeLoader: ul <Loader>
ReadLBA: rl <BeginSec> <SectorLen> <File>
WriteLBA: wl <BeginSec> <File>
WriteLBA: wlx <PartitionName> <File>
WriteGPT: gpt <gpt partition table>
WriteParameter: prm <parameter>
PrintPartition: ppt
EraseFlash: ef
TestDevice: td
ResetDevice: rd [subcode]
ReadFlashID: rid
ReadFlashInfo: rfi
ReadChipInfo: rci
ReadCapability: rcb
PackBootLoader: pack
UnpackBootLoader: unpack <boot loader>
TagSPL: tagspl <tag> <U-Boot SPL>
-------------------------------------------------------Now we first need to get the precompiled xxxxxx_spl_loader.bin. We can download it from radxa’s website which is the easiest option. So go to the Radxa website with the downloads on it and download the SPL Loader. You can also download it directly from my CDN if you dont want to go to the radxa website by clicking This button. Then you can copy the file to the directory you also cloned the uboot source and arm trusted firmware source in for easy access to all required files.
But first our board needs to be in bootrom mode. To do this, you need to plug the board in with a USB DATA cable (Be aware as some cables are power-only and wont work) into your computer. Before plugging it in you need to hold down the EMMC disable button (and if you have an SPI flash on your board also the SPI flash disable button) which will disable such boot storage devices and thus causes the SOC to hopefully find no suitable boot device and automatically fall back to bootrom mode. To know if your board is in bootrom mode, you can use the command lsusb. If you find a USB device with a similar name to down below, congratulations! Your board is now in bootrom mode and the rkdeveloptool can talk to it.
root@foobar# lsusb
Bus 001 Device 020: ID 2207:350a Fuzhou Rockchip Electronics Company here the name is the most important bit. That string before it is simply it’s ID and is not important.
After that, simply execute the commands below which will first upload the SPL Loader file you just downloaded and then writes idbloader.img and the u-boot fit to the right address/offset on the EMMC.
rkdeveloptool db rk356x_spl_loader_ddr1056_v1.10.111.binAfter this you should see a success message. If thats not the case, try it again.
So next flash the idbloader (SPL and DDR initialisation blob)
rkdeveloptool wl 0x40 idbloader.imgAnd here you should also get a success message in the form of this: Write LBA from file (100%)
and finally the full Uboot image.
rkdeveloptool wl 0x4000 u-boot.itbAnd here also check if it gives the same message.
And after this you can either reboot the board by unplugging it and replugging it or by executing the command below
rkdeveloptool rdAnd if everything has gone succesfull you can now hook up a UART to the board (see the pinout online) and see if you get boot messages at a baud rate of 1500000. If everything goes correct you should see the Uboot banner appear on UART terminal. Also if you left the led support enabled you should alos see a green led light up. SO we now succesfully booted into Uboot-mainline and now can end it here or continue and try and compile the custom uboot by the vendor… I decided to try that next.
Vendor Uboot #
So i decided to try the vendor’s Uboot tree next. Because i dont know if some packages will work on my bleeding edge fedora system, i set up a Virtual machine (with generous amounts of ram and cpu cores to speed up compilation process) with ubuntu and ssh’ed into it. Then installe dthe packages and cloned the radxa uboot source tree and copied it as an output folder named ‘Uboot-radxa’ so keep the same naming scheme.
git clone https://github.com/radxa/u-boot.git Uboot-radxaUboot next-dev-v2026.01 #
After cloning it and then moving into the Uboot-radxa folder i can see that it is of the ’next-dev-v2026.01’ branch and thus this is one of the newer ones. Lets ’ls’ it and see what files and folders it has.
mr309electronics@Musicbox-dev ~/build/Uboot-radxa (next-dev-v2026.01) $ ls
api common doc env Kbuild MAINTAINERS post snapshot.commit
arch config.mk Documentation examples Kconfig Makefile PREUPLOAD.cfg test
board configs drivers fs lib make.sh README tools
cmd disk dts include Licenses net scripts usb_update.txtAnd other than some standard Uboot folders we do indeed see some custom stuff added. That ‘make.sh’ script is new and there are some other minor differences but why is there a custom script and what does it do? lets run it.
mr309electronics@Musicbox-dev ~/build/Uboot-radxa (next-dev-v2026.01) $ ./make.sh
ERROR: No .config
Usage:
./make.sh [board|sub-command]
- board: board name of defconfig
- sub-command: elf*|loader|trust|uboot|--spl|--tpl|itb|map|sym|<addr>
- ini: ini file to pack trust/loader
Output:
When board built okay, there are uboot/trust/loader images in current directory
Example:
1. Build:
./make.sh evb-rk3399 --- build for evb-rk3399_defconfig
./make.sh firefly-rk3288 --- build for firefly-rk3288_defconfig
./make.sh EXT_DTB=rk-kernel.dtb --- build with exist .config and external dtb
./make.sh --- build with exist .config
./make.sh env --- build envtools
2. Pack:
./make.sh uboot --- pack uboot.img
./make.sh trust --- pack trust.img
./make.sh trust <ini> --- pack trust img with assigned ini file
./make.sh loader --- pack loader bin
./make.sh loader <ini> --- pack loader img with assigned ini file
./make.sh --spl --- pack loader with u-boot-spl.bin
./make.sh --tpl --- pack loader with u-boot-tpl.bin
./make.sh --tpl --spl --- pack loader with u-boot-tpl.bin and u-boot-spl.bin
3. Debug:
./make.sh elf --- dump elf file with -D(default)
./make.sh elf-S --- dump elf file with -S
./make.sh elf-d --- dump elf file with -d
./make.sh elf-* --- dump elf file with -*
./make.sh <no reloc_addr> --- unwind address(no relocated)
./make.sh <reloc_addr-reloc_off> --- unwind address(relocated)
./make.sh map --- cat u-boot.map
./make.sh sym --- cat u-boot.symAnd it seems that it actually is what compiles everything so instead of the regular makefiles and binman assembling the Uboot binary, this customised vendor uboot has a custom build script. Oh and ofcourse lets also clone rkbin as the script asks for it so i cloned it outside the Uboot-radxa folder. Lets try running the script.
./make.sh radxa-cm3-io-rk3366 CROSS_COMPILE=aarch64-linux-gnu-After running the script we get some output.
aarch64-linux-gnu-ld.bfd: warning: u-boot-spl has a LOAD segment with RWX permissions
OBJCOPY spl/u-boot-spl-nodtb.bin
CAT spl/u-boot-spl-dtb.bin
COPY spl/u-boot-spl.bin
CFGCHK u-boot.cfg
SEC=1
pack u-boot.itb okay! Input: /home/mr309electronics/build/rkbin/RKTRUST/RK3568TRUST.ini
FIT description: FIT Image with ATF/OP-TEE/U-Boot/MCU
Created: Sun May 10 21:34:55 2026
Image 0 (uboot)
Description: U-Boot
Created: Sun May 10 21:34:55 2026
Type: Standalone Program
Compression: uncompressed
Data Size: 1449832 Bytes = 1415.85 KiB = 1.38 MiB
Architecture: AArch64
Load Address: 0x00a00000
Entry Point: unavailable
Hash algo: sha256
Hash value: a0f48c3c931c38ba20fe5d85e72b79abcbcf43a6221486aefb8b0ca307b685c8
Image 1 (atf-1)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 106496 Bytes = 104.00 KiB = 0.10 MiB
Architecture: AArch64
Load Address: 0x00040000
Hash algo: sha256
Hash value: d7623f2738f84728805f7dab5cca6f7c62dcbf9a8f6f20a3c43d4fc16d055453
Image 2 (atf-2)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 40960 Bytes = 40.00 KiB = 0.04 MiB
Architecture: AArch64
Load Address: 0xfdcc1000
Hash algo: sha256
Hash value: 6cff1ca02dcff4dd7e04f84478f94058816d8386dcb82aef281173bd935f7954
Image 3 (atf-3)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 20739 Bytes = 20.25 KiB = 0.02 MiB
Architecture: AArch64
Load Address: 0x0005c000
Hash algo: sha256
Hash value: 1ceb7c563d845a782ab4d6924faa3c64ec400cda20b43e5b90a15207f60cf94f
Image 4 (atf-4)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 8192 Bytes = 8.00 KiB = 0.01 MiB
Architecture: AArch64
Load Address: 0xfdcce000
Hash algo: sha256
Hash value: 673d5b3eb110f9502c1324fa1c98635c49082435b178e2ef32c01570180962ed
Image 5 (atf-5)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 8192 Bytes = 8.00 KiB = 0.01 MiB
Architecture: AArch64
Load Address: 0xfdcd0000
Hash algo: sha256
Hash value: 59cdd7d1fad150be720f8a2a21e6bf7b5bfb3b73c5738135d5791516bf1510d1
Image 6 (atf-6)
Description: ARM Trusted Firmware
Created: Sun May 10 21:34:55 2026
Type: Firmware
Compression: uncompressed
Data Size: 7764 Bytes = 7.58 KiB = 0.01 MiB
Architecture: AArch64
Load Address: 0x0005a000
Hash algo: sha256
Hash value: 7a657e895ab58a9ec203231e2ed177f98e5bb27a91de993b45fe684f5c19dabf
Image 7 (fdt)
Description: U-Boot dtb
Created: Sun May 10 21:34:55 2026
Type: Flat Device Tree
Compression: uncompressed
Data Size: 23672 Bytes = 23.12 KiB = 0.02 MiB
Architecture: AArch64
Hash algo: sha256
Hash value: 4a91f425cd70485de0d050a3fd38e4294c1f5facb754a71127a84058bd014c3a
Default Configuration: 'conf'
Configuration 0 (conf)
Description: rk3566-radxa-cm3-io
Kernel: unavailable
Firmware: atf-1
FDT: fdt
Loadables: uboot
atf-2
atf-3
atf-4
atf-5
atf-6
********boot_merger ver 1.35********
Info:Pack loader ok.
pack loader okay! Input: /home/mr309electronics/build/rkbin/RKBOOT/RK3568MINIALL.ini
/home/mr309electronics/build/Uboot-radxa
Image(no-signed, version=0): uboot.img (FIT with uboot, trust...) is ready
Image(no-signed): rk356x_spl_loader_v1.23.114.bin (with spl, ddr...) is ready
pack uboot.img okay! Input: /home/mr309electronics/build/rkbin/RKTRUST/RK3568TRUST.ini
Platform RK3568 is build OK, with new .config(make radxa-cm3-io-rk3566_defconfig -j20)
aarch64-linux-gnu-
Sun May 10 09:34:55 PM CEST 2026Okay, so it seems this generated the images we need for boot method 1. uboot.img but no idbloader.img, but this did generate the rkxxxx_spl_loader_xxxx.bin which we’d normally use to initialise the board and be able to flash and interact with the EMMC…. Sadly no u-boot.itb or idbloader.img so what to flash? lets look at the rockchip documentation and navigating to the section which lists stuff about the idbloader.img we can see that it either allows packing some miniloader file from rkbin into idbloader with an ddr blob before it, but it also allows packing the u-boot-tpl.bin and u-boot-spl.bin files. We need to focus on boot flow method 1 so lets do that. First i have to see what files i need. Scrolling down we see we need rkxx_loader_vx.xx.bin which might be the spl loader and then we also need uboot.img (which we have and the make.sh script built for us) and trust.img which contains the trusted firmware and trusted execution stuff. But wait, remember the output above, it said it already pckaged the trusted firmware components for us so we only need to flash uboot.img and the spl loader.
So lets try it.
rkdeveloptool db rk356x_spl_loader_v1.23.114.binSo first the spl loader (preloader/replacement for the idbloader).
And then as last the uboot image itself which contains the trusted firmware and everything we need.
rkdeveloptool wl 0x4000 uboot.imgAnd yeah it works! Now to make changes to the configuration we can simply use the standard make menuconfig and ater that run the ‘make.sh’ without the ‘radxa-cm3-io-rk3566’ bit (beware as this is where i struggled a long time and it was just because when you specify a configuration s an argument with the make.sh script, it overwrites what you have set using menuconfig).
So i want to try the other versions and see how those are. The repository has multiple branches you can choose from. It seems the next-dev-v2026.01 is an older version as there are no commands like ‘cls’ (clearscreen) or other options and it is quite bare in terms of configureability. So i copied it to a backup folder and then used the command below to go to the other branch which i wanted to try, rk35xx-2024.01. Later i also want to try rk3xxx-2024.04.
Uboot rk3xxx-2024.04 #
So lets try the rk3xxx-2024.04 version next! So i fully cleaned the buildenv with distclean and then checked out with git the rk3xxx-2024.04 version. So This does not have a ‘make.sh’ script and its also newer as the next-dev versions are based on older 2017/2028 uboot trees i think. So here we should just be able to use our build_uboot-mainline script and edit it a tiny bit changing the uboot directory variable to Uboot-radxa and changing the name to build_uboot-radxa. So i ran the script and it worked and i opened the menuconfig configuration menu and then spent some time configuring it. After that i compiled it and in a few seconds i had a fresh idbloader.img and u-boot.itb file. I flashed it to the EMMC and it worked! Ethernet di not work but for that i had to enable the realtek nic option, designware phy, rockchip phy and also enable some settings related to rgmii and such. And ofcourse compile in the commands i wanted for network. And yeah it worked! Network interface was being detected and using the ‘dhcp’ command worked and i could tftp a testfile.txt from my host (which i set up as tftp server). And i enabled support for the status led and gpios.
Part2’er #
So i want to make a part2 to this dedicated to my experience with making a SPI flash compatible Uboot image and also having it boot. I wont spoil much but i can say, it took me a damn long time to get it to even boot! So click the button below to move to part2 or click This link