User:Brendlefly62/Rockchip RK3288 Asus Tinker Board S/Build-Install-U-Boot

From Gentoo Wiki
Jump to:navigation Jump to:search

There are a number of places you can find pre-built images to boot your TInkerBoard S. This guide describes how to set up Das U-Boot bootloader, using another arm device or on an amd64 crossdev platform, by building the bootloader firmware from mainline upstream source code.

Acknowledgement: This article is largely based on a similar work done for the Rock4c+


It really isn't necessary to do all the work outlined in the next section below, if the user has already performed the armbian/build process for producing an Armbian system for this board, as described in the previous section. Part of the output of that process is a debian package containing the u-boot files for the TinkerBoard and a script that explains how to install them --

user $tree ./armbian-build-output/
|-- debs
|   `-- linux-u-boot-tinkerboard-current_23.11.0-trunk_armhf__2022.04-Se4b6-P0e83-H0e2e-Vc2b8-Bd32f-R448a.deb

Extract the contents of the u-bood .deb package

user $cd armbian-build
user $dpkg-deb -x ./debs/linux-u-boot-tinkerboard-current_23.11.0-trunk_armhf__2022.04-Se4b6-P0e83-H0e2e-Vc2b8-Bd32f-R448a.deb u-boot
user $tree u-boot

u-boot/ `-- usr

   |-- lib
   |   |-- linux-u-boot-current-tinkerboard
   |   |   |-- rk3288_boot.bin
   |   |   `-- u-boot-rockchip-with-spl.bin
   |   `-- u-boot
   |       |-- LICENSE
   |       |--
   |       `-- tinker-rk3288_defconfig
   `-- share
       `-- doc
           `-- linux-u-boot-tinkerboard-current
`-- changelog.gz

Examine (but Do Not execute the "" script:

user $cd u-boot
user $cat usr/lib/u-boot/

write_uboot_platform () {

   dd if=/dev/zero of=$2 bs=1k count=1023 seek=1 status=noxfer > /dev/null 2>&1;
   dd if=$1/u-boot-rockchip-with-spl.bin of=$2 seek=64 conv=notrunc > /dev/null 2>&1

This script is basically the how-to for putting the secondary program loader (SPL) already built onto a micro SD card that will hold a Gentoo OS and custom kernel.

Use a blank micro SD card, or **Do Not** execute the first dd command in the "" script - that will erase the partition table and all data on the device will be lost
Only the "with-spl.bin" file is needed for this board
Also make sure to replace "sdX" below with the right device name

Use the structure of the second "dd" command in that script to write the "with-spl.bin" file to the device

root #dd if=lib/linux-u-boot-current-tinkerboard/u-boot-rockchip-with-spl.bin of=/dev/sdX seek=64 conv=notrunc > /dev/null 2>&1

The board should now be "bootable" and will display u-boot output on serial console when powered on. See

DIY Prerequisites

The outline below is a work in progress - attempt to document how to build a working u-boot bootloader from scratch. The documentation below is very rough draft and DOES NOT WORK. U-Boot can be built on an ARM device or cross-compiled on a PC. Regardless, we will need:

  • git, to check out the sources
  • dtc, to compile device trees
  • swig, to build U-Boot
  • crossdev, since a 32-bit ARM toolchain is required

Let's make sure these are installed:

root #emerge --ask --update dev-vcs/git sys-apps/dtc dev-lang/swig sys-devel/crossdev

Please review the crossdev article if you don't already have it set up.

You may need to install the 32-bit toolchain, which is required to build code for Cortex-M0 MCUs in BL31:

root #crossdev --target arm-none-eabi

If cross-compiling, install the 64-bit cross-compiler toolchain:

root #crossdev --target armv7a-unknown-linux-gnueabihf


To compile on ARM run commands like:

user $make -j$(nproc) PLAT=rk3288 <target>

If cross-compiling, run:

user $make -j$(nproc) PLAT=rk3288 CROSS_COMPILE=armv7a-unknown-linux-gnueabihf- <target>

If running distcc on the cross-compiling platform, it may be wise/necessary to disable that by temporarily removing '/usr/lib/distcc/bin' from your $PATH and turning off distcc in $FEATURES

user $export PATH=$(echo $PATH | sed 's|/usr/lib/distcc/bin:||')
user $MAKEOPTS="-j$(nproc) -l$(nproc)" FEATURES=$FEATURES" -distcc -distcc-pump" PLAT=rk3288 CROSS_COMPILE=armv7a-unknown-linux-gnueabihf- <target>

For Arm64 platforms, there is another pre-requisite step of building Arm Trusted Firmware "bl31" file, but as of Nov. 2023, the ATF Makefile says, "bl31 is not currently supported for aarch32.

Boot Loader alternatives

There are a number of ways to do this. This article describes one way to build a boot loader for TinkerBoard S using Arm Trusted Firmware (ATF or TF-A) and Das U-Boot. For detailed information on other methods, see link in the external resources and references secions, below.

Prepare to Build ATF

user $cd arm-trusted-firmware
user $git tag

Select the latest currently stable version:

user $git checkout v2.9.0

The repository contains a few binaries. If this is unacceptable, they may be deleted:

user $for x in $(find ./ -iname *.bin); do rm -v $x; done
user $git status
        deleted:    plat/arm/board/common/protpk/arm_protpk_rsa_sha256.bin
        deleted:    plat/arm/board/common/rotpk/arm_rotpk_ecdsa_sha256.bin
        deleted:    plat/arm/board/common/rotpk/arm_rotpk_rsa_sha256.bin
        deleted:    plat/arm/board/common/swd_rotpk/arm_swd_rotpk_rsa_sha256.bin
        deleted:    plat/rockchip/rk3368/drivers/ddr/rk3368_ddr_reg_resume_V1.05.bin
        deleted:    plat/rockchip/rk3399/drivers/dp/hdcp.bin

Per the ATF "How to Build" instructions,[1] compile (using an appropriately formatted command [see above]) the bl32 file that this board's u-boot will expect. According to the TF-A users guide, using the targets "all fip" will generate all of the bootloader binaries and a firmware information package (fip.bin) --

user $MAKEOPTS="-j4 -l4" FEATURES=" -userpriv -distcc -distcc-pump" make ARCH=aarch32 PLAT=rk3288 CROSS_COMPILE=armv7a-unknown-linux-gnueabihf- AARCH32_SP=sp_min all fip
Including bl32/sp_min/
  CC      lib/libfdt/fdt.c
  CC      lib/libfdt/fdt_addresses.c
  CC      lib/libfdt/fdt_empty_tree.c
  CC      lib/libfdt/fdt_ro.c
  CC      lib/libfdt/fdt_rw.c
  CC      lib/libfdt/fdt_strerror.c
  CC      lib/libfdt/fdt_sw.c
  CC      lib/libfdt/fdt_wip.c
  AR      /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/lib/libfdt.a
Building rk3288
  CC      lib/libc/abort.c
  CC      lib/libc/assert.c
  CC      lib/libc/exit.c
  CC      lib/libc/memchr.c
  CC      lib/libc/memcmp.c
  CC      lib/libc/memcpy.c
  CC      lib/libc/memmove.c
  CC      lib/libc/memrchr.c
  CC      lib/libc/memset.c
  CC      lib/libc/printf.c
  CC      lib/libc/putchar.c
  CC      lib/libc/puts.c
  CC      lib/libc/snprintf.c
  CC      lib/libc/strchr.c
  CC      lib/libc/strcmp.c
  CC      lib/libc/strlcat.c
  CC      lib/libc/strlcpy.c
  CC      lib/libc/strlen.c
  CC      lib/libc/strncmp.c
  CC      lib/libc/strnlen.c
  CC      lib/libc/strrchr.c
  CC      lib/libc/strtok.c
  CC      lib/libc/strtoul.c
  CC      lib/libc/strtoll.c
  CC      lib/libc/strtoull.c
  CC      lib/libc/strtol.c
  AR      /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/lib/libc.a
  CC      bl32/sp_min/sp_min_main.c
  CC      common/runtime_svc.c
  CC      drivers/arm/cci/cci.c
  CC      drivers/arm/gic/v2/gicdv2_helpers.c
  CC      drivers/arm/gic/v2/gicv2_helpers.c
  CC      drivers/arm/gic/v2/gicv2_main.c
  CC      drivers/delay_timer/delay_timer.c
  CC      drivers/delay_timer/generic_delay_timer.c
  CC      lib/cpus/errata_report.c
  CC      lib/el3_runtime/aarch32/context_mgmt.c
  CC      lib/el3_runtime/cpu_data_array.c
  CC      lib/locks/bakery/bakery_lock_coherent.c
  CC      lib/psci/psci_common.c
  CC      lib/psci/psci_main.c
  CC      lib/psci/psci_mem_protect.c
  CC      lib/psci/psci_off.c
  CC      lib/psci/psci_on.c
  CC      lib/psci/psci_setup.c
  CC      lib/psci/psci_suspend.c
  CC      lib/psci/psci_system_off.c
  CC      plat/common/aarch32/plat_sp_min_common.c
  CC      plat/common/plat_gicv2.c
  CC      plat/rockchip/common/aarch32/platform_common.c
  CC      plat/rockchip/common/params_setup.c
  CC      plat/rockchip/common/plat_pm.c
  CC      plat/rockchip/common/plat_topology.c
  CC      plat/rockchip/common/rockchip_gicv2.c
  CC      plat/rockchip/common/rockchip_sip_svc.c
  CC      plat/rockchip/common/sp_min_plat_setup.c
  CC      plat/rockchip/rk3288/drivers/pmu/pmu.c
  CC      plat/rockchip/rk3288/drivers/secure/secure.c
  CC      plat/rockchip/rk3288/drivers/soc/soc.c
  CC      plat/rockchip/rk3288/plat_sip_calls.c
  CC      services/std_svc/std_svc_setup.c
  CC      common/bl_common.c
  CC      common/tf_log.c
  CC      drivers/console/multi_console.c
  CC      plat/common/plat_bl_common.c
  CC      plat/common/plat_log_common.c
  CC      plat/common/aarch32/plat_common.c
  CC      lib/compiler-rt/builtins/popcountdi2.c
  CC      lib/compiler-rt/builtins/popcountsi2.c
  CC      lib/compiler-rt/builtins/ctzdi2.c
  CC      lib/compiler-rt/builtins/divdi3.c
  CC      lib/compiler-rt/builtins/divmoddi4.c
  CC      lib/compiler-rt/builtins/lshrdi3.c
  CC      lib/compiler-rt/builtins/udivmoddi4.c
  CC      common/desc_image_load.c
  CC      lib/bl_aux_params/bl_aux_params.c
  CC      plat/common/plat_psci_common.c
  CC      lib/xlat_tables/xlat_tables_common.c
  CC      lib/xlat_tables/aarch32/xlat_tables.c
  AS      bl32/sp_min/aarch32/entrypoint.S
  AS      bl32/sp_min/wa_cve_2017_5715_icache_inv.S
  AS      drivers/ti/uart/aarch32/16550_console.S
  AS      lib/cpus/aarch32/cortex_a12.S
  AS      lib/cpus/aarch32/cpu_helpers.S
  AS      lib/el3_runtime/aarch32/cpu_data.S
  AS      lib/locks/exclusive/aarch32/spinlock.S
  AS      lib/psci/aarch32/psci_helpers.S
  AS      plat/common/aarch32/platform_mp_stack.S
  AS      plat/rockchip/common/aarch32/plat_helpers.S
  AS      plat/rockchip/common/aarch32/pmu_sram_cpus_on.S
  AS      common/aarch32/debug.S
  AS      lib/aarch32/cache_helpers.S
  AS      lib/aarch32/misc_helpers.S
  AS      plat/common/aarch32/platform_helpers.S
  AS      lib/compiler-rt/builtins/arm/aeabi_ldivmod.S
  AS      lib/compiler-rt/builtins/arm/aeabi_uldivmod.S
  AS      lib/compiler-rt/builtins/arm/aeabi_memcpy.S
  AS      lib/compiler-rt/builtins/arm/aeabi_memset.S
  AS      plat/common/aarch32/crash_console_helpers.S
  PP      bl32/sp_min/sp_min.ld.S
  LD      /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/bl32/bl32.elf

Built /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/bl32/bl32.elf successfully

  OD      /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/bl32/bl32.dump
  BIN     /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/bl32.bin

Built /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/bl32.bin successfully

  HOSTCC  fiptool.c
  HOSTCC  tbbr_config.c
  HOSTLD  fiptool
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: skipping incompatible /usr/lib/ when searching for -lc
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: skipping incompatible /usr/lib/libc.a when searching for -lc

Built fiptool successfully

Secure Payload BL32 (Trusted OS): offset=0x60, size=0xFF6E0018, cmdline="--tos-fw"

Built /home/joe/MyTinkerboardFiles/arm-trusted-firmware/build/rk3288/release/fip.bin successfully

Locate the resulting bl32.elf --

user $find ./ -iname bl32.elf

Note that the output files are stored in a "release" directory within a platform directory *"rk3288" inside the "build" sub-directory. The sturcture should look something like this

user $tree build
`-- rk3288
    `-- release
        |-- bl32
        |   |-- 16550_console.d
        |   |-- 16550_console.o
        |   |-- aeabi_ldivmod.d
        |   |-- aeabi_ldivmod.o
        |   |-- aeabi_memcpy.d
        |   |-- aeabi_memcpy.o
        |   |-- aeabi_memset.d
        |   |-- aeabi_memset.o
        |   |-- aeabi_uldivmod.d
        |   |-- aeabi_uldivmod.o
        |   |-- bakery_lock_coherent.d
        |   |-- bakery_lock_coherent.o
        |   |-- bl32
        |   |   `-- sp_min
        |   |       |-- sp_min.ld
        |   |       `-- sp_min.ld.d
        |   |-- bl32.dump
        |   |-- bl32.elf
        |   |--
        |   |-- bl_aux_params.d
        |   |-- bl_aux_params.o
        |   |-- bl_common.d
        |   |-- bl_common.o
        |   |-- build_message.o
        |   |-- cache_helpers.d
        |   |-- cache_helpers.o
        |   |-- cci.d
        |   |-- cci.o
        |   |-- context_mgmt.d
        |   |-- context_mgmt.o
        |   |-- cortex_a12.d
        |   |-- cortex_a12.o
        |   |-- cpu_data.d
        |   |-- cpu_data.o
        |   |-- cpu_data_array.d
        |   |-- cpu_data_array.o
        |   |-- cpu_helpers.d
        |   |-- cpu_helpers.o
        |   |-- crash_console_helpers.d
        |   |-- crash_console_helpers.o
        |   |-- ctzdi2.d
        |   |-- ctzdi2.o
        |   |-- debug.d
        |   |-- debug.o
        |   |-- delay_timer.d
        |   |-- delay_timer.o
        |   |-- desc_image_load.d
        |   |-- desc_image_load.o
        |   |-- divdi3.d
        |   |-- divdi3.o
        |   |-- divmoddi4.d
        |   |-- divmoddi4.o
        |   |-- entrypoint.d
        |   |-- entrypoint.o
        |   |-- errata_report.d
        |   |-- errata_report.o
        |   |-- generic_delay_timer.d
        |   |-- generic_delay_timer.o
        |   |-- gicdv2_helpers.d
        |   |-- gicdv2_helpers.o
        |   |-- gicv2_helpers.d
        |   |-- gicv2_helpers.o
        |   |-- gicv2_main.d
        |   |-- gicv2_main.o
        |   |-- lshrdi3.d
        |   |-- lshrdi3.o
        |   |-- misc_helpers.d
        |   |-- misc_helpers.o
        |   |-- multi_console.d
        |   |-- multi_console.o
        |   |-- params_setup.d
        |   |-- params_setup.o
        |   |-- plat_bl_common.d
        |   |-- plat_bl_common.o
        |   |-- plat_common.d
        |   |-- plat_common.o
        |   |-- plat_gicv2.d
        |   |-- plat_gicv2.o
        |   |-- plat_helpers.d
        |   |-- plat_helpers.o
        |   |-- plat_log_common.d
        |   |-- plat_log_common.o
        |   |-- plat_pm.d
        |   |-- plat_pm.o
        |   |-- plat_psci_common.d
        |   |-- plat_psci_common.o
        |   |-- plat_sip_calls.d
        |   |-- plat_sip_calls.o
        |   |-- plat_sp_min_common.d
        |   |-- plat_sp_min_common.o
        |   |-- plat_topology.d
        |   |-- plat_topology.o
        |   |-- platform_common.d
        |   |-- platform_common.o
        |   |-- platform_helpers.d
        |   |-- platform_helpers.o
        |   |-- platform_mp_stack.d
        |   |-- platform_mp_stack.o
        |   |-- pmu.d
        |   |-- pmu.o
        |   |-- pmu_sram_cpus_on.d
        |   |-- pmu_sram_cpus_on.o
        |   |-- popcountdi2.d
        |   |-- popcountdi2.o
        |   |-- popcountsi2.d
        |   |-- popcountsi2.o
        |   |-- psci_common.d
        |   |-- psci_common.o
        |   |-- psci_helpers.d
        |   |-- psci_helpers.o
        |   |-- psci_main.d
        |   |-- psci_main.o
        |   |-- psci_mem_protect.d
        |   |-- psci_mem_protect.o
        |   |-- psci_off.d
        |   |-- psci_off.o
        |   |-- psci_on.d
        |   |-- psci_on.o
        |   |-- psci_setup.d
        |   |-- psci_setup.o
        |   |-- psci_suspend.d
        |   |-- psci_suspend.o
        |   |-- psci_system_off.d
        |   |-- psci_system_off.o
        |   |-- rockchip_gicv2.d
        |   |-- rockchip_gicv2.o
        |   |-- rockchip_sip_svc.d
        |   |-- rockchip_sip_svc.o
        |   |-- runtime_svc.d
        |   |-- runtime_svc.o
        |   |-- secure.d
        |   |-- secure.o
        |   |-- soc.d
        |   |-- soc.o
        |   |-- sp_min_main.d
        |   |-- sp_min_main.o
        |   |-- sp_min_plat_setup.d
        |   |-- sp_min_plat_setup.o
        |   |-- spinlock.d
        |   |-- spinlock.o
        |   |-- std_svc_setup.d
        |   |-- std_svc_setup.o
        |   |-- tf_log.d
        |   |-- tf_log.o
        |   |-- udivmoddi4.d
        |   |-- udivmoddi4.o
        |   |-- wa_cve_2017_5715_icache_inv.d
        |   |-- wa_cve_2017_5715_icache_inv.o
        |   |-- xlat_tables.d
        |   |-- xlat_tables.o
        |   |-- xlat_tables_common.d
        |   `-- xlat_tables_common.o
        |-- bl32.bin
        |-- fip.bin
        |-- lib
        |   |-- libc.a
        |   `-- libfdt.a
        |-- libc
        |   |-- abort.d
        |   |-- abort.o
        |   |-- assert.d
        |   |-- assert.o
        |   |-- exit.d
        |   |-- exit.o
        |   |-- memchr.d
        |   |-- memchr.o
        |   |-- memcmp.d
        |   |-- memcmp.o
        |   |-- memcpy.d
        |   |-- memcpy.o
        |   |-- memmove.d
        |   |-- memmove.o
        |   |-- memrchr.d
        |   |-- memrchr.o
        |   |-- memset.d
        |   |-- memset.o
        |   |-- printf.d
        |   |-- printf.o
        |   |-- putchar.d
        |   |-- putchar.o
        |   |-- puts.d
        |   |-- puts.o
        |   |-- snprintf.d
        |   |-- snprintf.o
        |   |-- strchr.d
        |   |-- strchr.o
        |   |-- strcmp.d
        |   |-- strcmp.o
        |   |-- strlcat.d
        |   |-- strlcat.o
        |   |-- strlcpy.d
        |   |-- strlcpy.o
        |   |-- strlen.d
        |   |-- strlen.o
        |   |-- strncmp.d
        |   |-- strncmp.o
        |   |-- strnlen.d
        |   |-- strnlen.o
        |   |-- strrchr.d
        |   |-- strrchr.o
        |   |-- strtok.d
        |   |-- strtok.o
        |   |-- strtol.d
        |   |-- strtol.o
        |   |-- strtoll.d
        |   |-- strtoll.o
        |   |-- strtoul.d
        |   |-- strtoul.o
        |   |-- strtoull.d
        |   `-- strtoull.o
        |-- libfdt
        |   |-- fdt.d
        |   |-- fdt.o
        |   |-- fdt_addresses.d
        |   |-- fdt_addresses.o
        |   |-- fdt_empty_tree.d
        |   |-- fdt_empty_tree.o
        |   |-- fdt_ro.d
        |   |-- fdt_ro.o
        |   |-- fdt_rw.d
        |   |-- fdt_rw.o
        |   |-- fdt_strerror.d
        |   |-- fdt_strerror.o
        |   |-- fdt_sw.d
        |   |-- fdt_sw.o
        |   |-- fdt_wip.d
        |   `-- fdt_wip.o
        |-- libwrapper
        `-- romlib

11 directories, 222 files

Save a copy somewhere safe --

user $cp -av ./build/rk3288 ../

Prepare to Build U-Boot

This example will build and use peer-level directories for u-boot and rkbin, within a common project directory, to simplify relative addressing of command line parameters

Return to the project directory and get u-boot sources. The u-boot sub-directory will be created in the process:

user $cd ..
user $git clone
user $cd u-boot
user $git tag

Check out latest currently stable version:

user $git checkout v2023.10

Instead of cloning the complete u-boot repository as shown in the steps above, you may save some time and disc space by cloning branch v2023.10 only:

user $git clone --depth 1 --branch v2023.10

Identify and load board-specific default configuration

user $find ./ -iname *rk3288*_defconfig | grep tinker

U-Boot boot-flow(s)

The Rockchip u-boot README[2] indicates that there are two possible boot-flows involving the use of either the rockchip "miniloader" or a TPL/SPL build with ATF. See much more detail at In particular, note the details illustrated in the graphic at Source --

Build u-boot

user $MAKEOPTS="-j$(nproc) -l$(nproc)" FEATURES=$FEATURES" -distcc -distcc-pump" PLAT=rk3288 CROSS_COMPILE=armv7a-unknown-linux-gnueabihf- make O=tinker-s-rk3288_output BL32=../rk3288/release/bl32/bl32.elf tinker-s-rk3288_defconfig all
make[1]: Entering directory '/home/joe/MyTinkerboardFiles/u-boot/tinker-s-rk3288_output'
  HOSTCC  scripts/basic/fixdep
  GEN     Makefile
  HOSTCC  scripts/kconfig/conf.o
  YACC    scripts/kconfig/
  LEX     scripts/kconfig/zconf.lex.c
  HOSTCC  scripts/kconfig/
  HOSTLD  scripts/kconfig/conf
# configuration written to .config
  GEN     Makefile
scripts/kconfig/conf  --syncconfig Kconfig
  CFG     u-boot.cfg
  GEN     include/
  GEN     include/
  CFG     spl/u-boot.cfg
  GEN     spl/include/
  CC      tpl/fs/fs_internal.o
  AR      tpl/fs/built-in.o
  LDS     tpl/
  LD      tpl/u-boot-tpl
  OBJCOPY tpl/u-boot-tpl-nodtb.bin
  COPY    tpl/u-boot-tpl.bin
  SYM     tpl/u-boot-tpl.sym
  COPY    u-boot.dtb
  MKIMAGE u-boot-dtb.img
  BINMAN  .binman_stamp
  OFCHK   .config
make[1]: Leaving directory '/home/joe/MyTinkerboardFiles/u-boot/tinker-s-rk3288_output'

Similar to the Linux kernel, menuconfig can be used to make changes to the configuration. This is optional; there is no real need to make any changes.

If the serial console employs old hardware or is "noisy," the u-boot serial console baud rate setting can be reduced. Newer systems default to 1500000 baud, but the tinker-s-rk3288_defconfig evidently sets its default at 115200. If this is still too fast, it can be changed to e.g. 9600.
user $grep -i baudrate .config
user $make menuconfig
You probably won't need to make any other changes.

Get rkbin and create trust.img[3]

From u-boot directory, cd .. to move to the parent project directory, then get rkbin sources (peer level to the project's u-boot directory) --

user $cd rkbin/
user $./tools/trust_merger RKTRUST/RK3399TRUST.ini
out:trust.img merge success(trust.img)

Create uboot.img

If not already there, cd to /path/to/rkbin, then --

user $./tools/loaderimage --pack --uboot ../u-boot/tinker-s-rk3288_output/u-boot-dtb.bin uboot.img
load addr is 0x60000000!

pack input ../u-boot/tinker-s-rk3288_output/u-boot-dtb.bin pack file size: 553000(540 KB) crc = 0x746c261e uboot version: U-Boot 2017.09-gc64e256218-200416 #yzc (Apr 29 2020 - 16:22:05)

pack uboot.img success!

Installing u-boot

When powered on, the TinkerBoard S will attempt to find a bootloader in the following order:

  • Removable eMMC - empty connector, not populated by default on 4c Plus
  • microSD card - i.e. this is the only real option for where to put u-boot

Note that if the maskrom/eMMC Recovery jumper header is set in the "default/disable/park" position, the board will try to boot from the eMMC; if it is set in the "eMMC Recovery/MASKROM" position, it will try to boot from the SD card.

Once U-Boot is loaded on one of these devices, it can then be used to load the kernel from MMC/SD or USB external storage, or via the network. (There is no M.2 nor PCIe connector on the TinkerBoard/TInkerBoard S. By default, after exhausting other options, u-boot will connect to the local network's dhpc server, looking for tftp service from which to retrieve a bootable image (this might be considered a security concern).

Installing on microSD/eMMC

If writing to the eMMC, connect the board (microUSB) connector to a PC (USB-A).

If writing to a micro SD Card, put the card in a card reader and connect that to a PC/workstation USB port.

Note the device connected, and as root, enter the u-boot build directory, and write the images to the card.

For miniloader boot option, the Rockchip community ( instructs loading these files built above at the associated sectors of the eMMC/SD Card --
dd if=idbloader.img of=<device> seek=64
dd if=uboot.img of=<device> seek=16384
dd if=trust.img of=<device> seek=24576
dd if=boot.img of=<device> seek=32768
dd if=rootfs.img of=<device> seek=262144

However, it also notes that "boot.img" may also be the boot partition/folder of the rootfs, wherein the extlinux/extlinux.conf, zImage, and tinker-s-rk3288.dtb file will reside (with optional initrd and/or optional grub.efi. The example below assumes no initrd, no grub.efi, and will append the rootfs information to kernel cli, etc.

There are separate instructions at the link above for loading files pertaining to TPL/SPL boot option)
* Note that uboot.img is written starting at sector 16384, trust.img is written at sector 24576, and the scenario expects the boot.img (or the boot directory/partition) to be at sector 32768. Make sure your card is partitioned such that 32768 is the start address for the boot partition (FAT 32), and there is no other file system installed below that sector.
  • Be sure to replace /dev/sdd with the correct device!

Write output files to boot media

root #dmesg | tail ### note information about device just connected
root #cd /path/to/u-boot/
root #dd if=tinker-s-rk3288_output/idbloader.img of=/dev/sdd seek=64
140+0 records in
140+0 records out
71680 bytes (72 kB, 70 KiB) copied, 0.0350433 s, 2.0 MB/s
root #cd ../rkbin/
root #dd if=uboot.img of=/dev/sdd seek=16384
8192+0 records in
8192+0 records out
4194304 bytes (4.2 MB, 4.0 MiB) copied, 0.681352 s, 6.2 MB/s
root #dd if=trust.img of=/dev/sdd seek=24576
8192+0 records in
8192+0 records out
4194304 bytes (4.2 MB, 4.0 MiB) copied, 0.683164 s, 6.1 MB/s
Serial console support established with USB-FTDI connection to GND and UART3 Tx/Rx (pins 34, 36, 37)

Observing boot message with serial console

Before installing a Linux operating system, check to ensure that U-Boot is booting correctly. It may be possible to use a USB-to-TTL connector (such as this one or this one), though the author has found that those are not supported by Windows 11. Instead, the author used a mini-usb FTDI board such as are commonly used to program AVR microcontrollers that lack on-board USB support (see photo) and used PuTTy software on a Windows 11 desktop PC to monitor the serial console.

Connect the USB-TTL converter device ground pin to pin 34 (GND), its TX pin to pin in 36 (UART3_RX), and its RX pin to pin 37 (UART3_TX) See also the TInkerBoard Developer Guide for reference) to see the early boot up messages:

root #screen /dev/ttyUSB0 115200
  • Make sure RX and TX are not backwards

Optional: Adapting baud rate

Serial communication usually requires both sides of the communication to agree on a common baud rate. Though the tinker-s-rk3288_defconfig default seems to be set to 115200 baud, Rockchip otherwise normally defaults to 1500000 bauds, which may be too fast for common USB-to-serial adapters. This may garble or prevent visibility of boot-messages from u-boot. A much more common and widely supported, but slower baud rate is 115200 bauds. U-Boot can be configured to use this slower baud rate.

Use menuconfig as described in #Build_U-Boot and reconfigure the symbol BAUDRATE to 115200. Rebuild u-boot and copy it to the SD-card. Now start your program of choice for serial communication

Linux PC as external serial console

root #screen /dev/ttyUSB0 115200

Window s PC for external serial console

Using PuTTy as serial console - configure serial port, baud rate, etc
Use DeviceManager to confirm the serial comm port number

Expected boot messages

Now put the card into the device and power on. If successful, you should be able to see output like this (click to expand):

U-Boot TPL 2023.07.02 (Sep 24 2023 - 23:39:53)
lpddr4_set_rate: change freq to 400MHz 0, 1
Channel 0: LPDDR4, 400MHz
BW=32 Col=10 Bk=8 CS0 Row=16 CS=1 Die BW=16 Size=2048MB
Channel 1: LPDDR4, 400MHz
BW=32 Col=10 Bk=8 CS0 Row=16 CS=1 Die BW=16 Size=2048MB
256B stride
lpddr4_set_rate: change freq to 800MHz 1, 0
Trying to boot from BOOTROM
Returning to boot ROM...

U-Boot SPL 2023.07.02 (Sep 24 2023 - 23:39:53 -0400)
Trying to boot from MMC1
spl_load_fit_image: Skip load 'atf-5': image size is 0!
NOTICE:  BL31: v2.9(release):v2.9.0-dirty
NOTICE:  BL31: Built : 21:42:04, Sep 24 2023

U-Boot 2023.07.02 (Sep 24 2023 - 23:39:53 -0400)

SoC: Rockchip rk3399
Reset cause: POR
Model: Radxa ROCK Pi 4C
DRAM:  4 GiB (effective 3.9 GiB)
Core:  291 devices, 29 uclasses, devicetree: separate
MMC:   mmc@fe310000: 2, mmc@fe320000: 1, mmc@fe330000: 0
Loading Environment from MMC... Card did not respond to voltage select! : -110
*** Warning - No block device, using default environment

In:    serial
Out:   vidconsole
Err:   vidconsole
Model: Radxa ROCK Pi 4C
can't get vref-supply: -121
rockchip_dnl_key_pressed: adc_channel_single_shot fail!
Net:   eth0: ethernet@fe300000
Hit any key to stop autoboot:  0
** Booting bootflow 'mmc@fe320000.bootdev.part_1' with script
Loaded and running boot.scr!

  • Early output from U-Boot TPL/SPL won't be shown on HDMI, only on serial
  • You can press a key to interrupt the boot process and enter U-Boot's interactive shell
  • You can find the list of commands and their syntax if you run help

Booting a Linux system

Now that U-Boot installed on the SD card, the next step is to use it to boot a Linux system.

By default, U-Boot scans the partitions of each device (except SATA, see next section) to find something it knows how to load. The default supported file systems are ext2, ext4 and FAT, but there are a few others you can enable in U-Boot's menuconfig. You will want your /boot partition to be formatted with one of those filesystems.

For TinkerBoard S using the miniloader boot flow described above, the boot directory should contain the kernel image, dtb file, and optional initrd and/or optional grub.efi. The extlinux/extlinux.conf file will identify the kernel (optionally initrd), and append text to the kernel command line, identifying the location of the rootfs. That should be all that is needed to boot this system. However, it is also possible to boot with a u-boot hush shell script compiled with the mkimage tool provided with u-boot sources or from the Gentoo dev-embedded/u-boot-tools package. Information about how to use that option follows in sections further below.

Verify extlinux.conf and /etc/fstab

root #mount /dev/sdd2 /mnt/gentoo/
root #mount /dev/sdd1 /mnt/gentoo/boot/
root #cd /mnt/gentoo/boot/
root #nano extlinux/extlinux.conf
label kernel-6.4.12
   kernel /zImage
   fdt /rk3288-tinker-s.dtb
append net.ifnames=0 earlyprintk quiet splash plymouth.ignore-serial-consoles console=tty1 root=/dev/mmcblk1p2 rw init=/sbin/init
root #nano /mnt/gentoo/etc/fstab
UUID="aaaabbbb-cccc-dddd-1234-eeeeffff2222" / ext4 noatime 0 1
UUID="DDDD-CCCC" /boot vfat defaults 0 2
/var/cache/swap/swap1 none swap sw 0 0
/dev/mapper/vg_demo-srv /srv ext4 noatime 0 0
root #cd
root #umount -R /mnt/gentoo
root #sync

Now remove the SD card from the reader, put it into the reader on the TinkerBoard S, and boot...

NO GO :-( ...

Creating boot.scr [work in progress - draft copied from Rock4c+ page]

One thing it can load boot.scr, which will contain commands for U-Boot to execute. This will be in your /boot partition along with your kernel and device tree binary.

First, create a file boot.cmd. The example below includes a lot of extra stuff to facilitate exploration and debugging, but it can be significantly simplified --

CODE boot.cmd
echo "Loaded and running boot.scr!"
#setenv userEnv "joetooEnv.txt"

# defaults
setenv load_addr "0x9000000"
setenv overlay_error "false"
setenv rootdev "/dev/mmcblk0p1"
setenv verbosity "1"
setenv console "both"
setenv bootlogo "false"
setenv rootfstype "ext4"
if test -z "$bootargs"; then
    setenv bootargs root=${rootdev} rootdelay=5

# print some debugging info
echo "-----[ Initial State ]-----------------------------------"
#echo "userEnv..........: "${userEnv}
echo "load_addr........: "${load_addr}
echo "filesize.........: "${filesize}
echo "rootdev..........: "${rootdev}
echo "devtype..........: "${devtype}
echo "devnum...........: "${devnum}
echo "prefix...........: "${prefix}
echo "distro_bootpart..: "${distro_bootpart}
echo "verbosity........: "${verbosity}
echo "console..........: "${console}
echo "consoleargs......: "${consoleargs}
echo "bootlogo.........: "${bootlogo}
echo "rootfstype.......: "${rootfstype}
echo "earlycon.........: "${earlycon}
echo "overlays.........: "${overlays}
echo "overlay_prefix...: "${overlay_prefix}
echo "fdtfile..........: "${fdtfile}
echo "fdt_addr_r.......: "${fdt_addr_r}
echo "imagefile........: "${imagefile}
echo "kernel_addr_r....: "${kernel_addr_r}
echo "initrdfile.......: "${initrdfile}
echo "ramdisk_addr_r...: "${ramdisk_addr_r}
echo "partuuid.........: "${partuuid}
echo "usbstoragequirks.: "${usbstoragequirks}
echo "extraargs........: "${extraargs}
echo "extraboardargs...: "${extraboardargs}
echo "bootargs.........: "${bootargs}
echo "--------------------------------------------------------"

# Override/overwrite defaults per user's environment selections
echo "Ok - I am about to try reading joetooEnv.txt..."
if test -e ${devtype} ${devnum} ${prefix}joetooEnv.txt; then
        load ${devtype} ${devnum} ${load_addr} ${prefix}joetooEnv.txt
        env import -t ${load_addr} ${filesize}
	echo "Did not find or load joetooEnv.txt"

# Manage console settings
if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=tty1"; fi
if test "${console}" = "serial" || test "${console}" = "both"; then setenv consoleargs "console=ttyS2,115200 ${consoleargs}"; fi
if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; fi
if test "${bootlogo}" = "true"; then
        setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}"
        setenv consoleargs "splash=verbose ${consoleargs}"

# get PARTUUID of first partition on SD/eMMC the boot script was loaded from
if test "${devtype}" = "mmc"; then part uuid mmc ${devnum}:1 partuuid; fi

# update bootargs
#setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}"
setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}"

echo "-----[ Potentially Updated State ]-----------------------"
#echo "userEnv..........: "${userEnv}
echo "load_addr........: "${load_addr}
echo "filesize.........: "${filesize}
echo "rootdev..........: "${rootdev}
echo "devtype..........: "${devtype}
echo "devnum...........: "${devnum}
echo "prefix...........: "${prefix}
echo "distro_bootpart..: "${distro_bootpart}
echo "verbosity........: "${verbosity}
echo "console..........: "${console}
echo "consoleargs......: "${consoleargs}
echo "bootlogo.........: "${bootlogo}
echo "rootfstype.......: "${rootfstype}
echo "earlycon.........: "${earlycon}
echo "overlays.........: "${overlays}
echo "overlay_prefix...: "${overlay_prefix}
echo "fdtfile..........: "${fdtfile}
echo "fdt_addr_r.......: "${fdt_addr_r}
echo "imagefile........: "${imagefile}
echo "kernel_addr_r....: "${kernel_addr_r}
echo "initrdfile.......: "${initrdfile}
echo "ramdisk_addr_r...: "${ramdisk_addr_r}
echo "partuuid.........: "${partuuid}
echo "usbstoragequirks.: "${usbstoragequirks}
echo "extraargs........: "${extraargs}
echo "extraboardargs...: "${extraboardargs}
echo "bootargs.........: "${bootargs}
echo "-----[ now env print -a ]---------------------------------"
env print -a

# Load device tree file and kernel (to do: initramfs option)

# device tree - note that it finds the file in the "dtb" directory
echo "Loading device tree from ${devtype} ${devnum}:${distro_bootpart}..."
load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}dtb/${overlay_prefix}/${fdtfile}

echo "Loading initrd from ${devtype} ${devnum}:${distro_bootpart}..."
load ${devtype} ${devnum} ${ramdisk_addr_r} ${prefix}${initrdfile}

echo "Loading kernel from ${devtype} ${devnum}:${distro_bootpart}..."
load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} ${prefix}${imagefile}
# Load overlays (to do, if needed)

# Boot!
echo "Booting with arguments: ${bootargs}"
#booti ${kernel_addr_r} - ${fdt_addr_r}
booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}

# Recompile instruction 
# Cross-compiling:
# cd to u-boot directory holding boot.cmd and run
# mkimage -C none -A arm64 -T script -d boot.cmd boot.scr
# then copy boot.scr, boot.cmd, and joetooEnv.txt to the /boot partition
# of your target system eMMC or microSD card
# If modifying an existing system (from said system)
# mkimage -C none -A arm64 -T script -d /boot/boot.cmd /boot/boot.scr
CODE joetooEnv.txt
Be sure to change rootdev= to point to your actual root partition! This can by done by UUID, as in this example, more portably than by path to block device, like "/dev/mmcblk0p1" -- but either way is valid

Then, compile it to boot.scr:

root #mkimage -C none -A arm64 -T script -d boot.cmd boot.scr
  • mkimage can be used from either the bin directory in your U-Boot build directory or from dev-embedded/u-boot-tools
  • Create a Makefile or a shell script so you don't have to look up the command above next time you need to edit

If the /boot partition is on microSD, it should work without additional steps.

External resources