View on GitHub

富乎 · 地问


avatar
辗转探寻为富乎?《天问》无解向地问!

<<< 返回主页

U-Boot移植进阶版——基于香橙派5RK3588S

1、背景

2、原理简述

3、实现细节

首先要强调的是,现实中的情形并非是先理论后实践、且一气呵成的过程,因为即使有前面的参考材料, 也要费一番心思去阅读、梳理,更别提在对很多新概念和知识点一空二白的情况下,还要先费心找资料, 并在一堆沙子里找黄金,这就很考验一个人的探索和筛选能力了。下面将尽量还原此次经历的来龙去脉, 供各位参考(参考就好,不要照搬)。

第一个且极其重要的步骤,就是看用户手册,即使有些用户手册很烂。不看文档的人踩坑完全不冤! 得益于用户手册,才能获知这些信息:源码出处、构建工具是什么、产出的形式是DEB包、 开发板已自带烧录脚本等等。接下来要做的只是逐个击破即可。

回顾文章开头提到的绕开笨重的官方SDK而手动编译U-Boot,首先要做的当然是下载源码包。 这一步很简单,唯一需要注意的是,最好按散列码commit hash)或标签tag)下载, 而不是按分支branch),例如:

$ wget -c -O u-boot-orangepi-752ac3f2fdcfe9427ca8868d95025aacd48fc00b.tar.gz \
    'https://github.com/orangepi-xunlong/u-boot-orangepi/archive/752ac3f2fdcfe9427ca8868d95025aacd48fc00b.tar.gz'

而不是:

$ wget -c -O u-boot-orangepi-v2017.09-rk3588.tar.gz \
    'https://github.com/orangepi-xunlong/u-boot-orangepi/archive/refs/heads/v2017.09-rk3588.tar.gz'

拿到源码之后,先不急着编译,而是先搞清楚需要编译出什么产物。由于用户手册已说明产物是DEB包, 且提供了解包命令,因而可获知结果文件的安装路径,然后直接在开发板上查看:

$ ls /usr/lib/u-boot
LICENSE  orangepi_5_defconfig  platform_install.sh
$
$ ls /usr/lib/linux-u-boot-*
idbloader.img  rkspi_loader.img  u-boot.itb

其中,/usr/lib/u-boot目录下的orangepi_5_defconfig可直接拿来覆盖源码包里的同名配置文件, 并用于make menuconfigplatform_install.sh是烧录脚本的其中一个组件,此时可先不理会。 至于/usr/lib/linux-u-boot-*(末尾的星号对应的是官方SDK定义的分支和发行版本号, 在此不关心)目录下的文件,则与前一章节列举的待烧录清单对应上了,接下来只需搞清楚如何把它们编译出来就行了。

可以肯定的是,直接在U-Boot源码根目录执行make大概率是得不到这些文件的, 因为仅从文件名就能看出它们多少有些特殊。要解决这个问题,还是要研读官方SDK的脚本逻辑, 不能仅靠前面的参考材料——原因也很简单,一来不同芯片有差异,二来代码和文章也有时效性, 所以即使很麻烦,官方SDK也是最接近正确答案的方案。不过,前面的参考材料并不算白读, 因为基础理论也同等重要,不可或缺。

现在稍微分析一下官方SDK的脚本逻辑。香橙派官方SDKorangepi-build, 在GitHub有开源。 为了避免脚本内容更新而导致与本文解说对应不上的情况,特意复刻了一份以冻结住相关状态, 读者若感兴趣,查阅复刻的这个仓库即可。 由于篇幅的限制以及不是本文重点,详细的分析就不在这里展开,仅列举部分核心逻辑。 首先,找出编译U-Boot的顶层函数,在scripts/compilation.sh文件, 剔除不重要以及与RK3588(S)无关的内容,其内部核心逻辑大致如下:

compile_uboot()
    |
    |-- ……
    |
    |-- while read -r target do ... done <<< $UBOOT_TARGET_MAP
    |       |
    |       |-- UBOOT_TARGET_MAP:定义在external/config/sources/families/include/rockchip64_common.inc,
    |       |       且取值由若干变量影响,这些变量定义在external/config/boards/orangepi5.conf,
    |       |       此处仅列举重要变量取值如下:
    |       |           BOARDFAMILY:rockchip-rk3588
    |       |           BOOTCONFIG:orangepi_5_defconfig
    |       |           BOOT_FDT_FILE:rockchip/rk3588s-orangepi-5.dtb
    |       |           BOOT_SCENARIO:spl-blobs
    |       |       因此,UBOOT_TARGET_MAP的值为:BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb
    |       |       很容易在同一文件内找到BL31_BLOB的取值应为:rk35/rk3588_bl31_v1.42.elf
    |       |       这对于后面下载瑞芯微闭源固件很重要。
    |       |
    |       |-- target_make=$(cut -d';' -f1 <<< "${target}"),即UBOOT_TARGET_MAP的值以分号分隔的第一部分内容:
    |       |       BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb
    |       |
    |       |-- target_files=$(cut -d';' -f3 <<< "${target}"),逻辑与上相似,是第三部分:
    |       |       idbloader.img u-boot.itb
    |       |
    |       |-- make $BOOTCONFIG:根据orangepi_5_defconfig生成“.config”
    |       |
    |       |-- 微调“.config”里面部分编译选项
    |       |
    |       |-- make $target_make:即编译出spl/u-boot-spl.bin、u-boot.dtb、u-boot.itb
    |       |
    |       |-- uboot_custom_postprocess():定义在external/config/sources/families/include/rockchip64_common.inc,
    |       |       作用是针对部分型号的板进行特定的后处理,例如若想从香橙派5的SPI NOR Flash启动,
    |       |       还需生成rkspi_loader.img
    |       |
    |       `-- 复制$target_files到待打包目录
    |
    |-- 生成platform_install.sh:主要作用是提供write_uboot_platform()、write_uboot_platform_mtd()
    |       等函数供烧录脚本/usr/sbin/nand-sata-install调用,
    |       它们均是定义在external/config/sources/families/include/rockchip64_common.inc
    |
    `-- 打DEB包:略

从上面可以看出,除了有一个rk3588_bl31_v*.elf固件是闭源、需要手动获取之外,其余内容都是开源的, 而且也不复杂。还应注意到有若干个关键函数定义在external/config/sources/families/include/rockchip64_common.inc, 可以简单看看其实现:

uboot_custom_postprocess()
{
    if [[ $BOOT_SUPPORT_SPI == yes ]]; then
        if [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then
            tools/mkimage -n rk3588 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
            dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
            /sbin/parted -s rkspi_loader.img mklabel gpt
            /sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
            /sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
            dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
            dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc
        elif [[ $BOARDFAMILY == "rockchip-rk356x" ]]; then
            ...
        fi
    fi
}

#
# 用法:write_uboot_platform <镜像所在目录> <储存卡对应的设备节点>
# 示例:write_uboot_platform /usr/lib/linux-u-boot-current-orangepi5_1.1.8_arm64 /dev/mmcblk1
#
write_uboot_platform()
{
    if [[ -f $1/rksd_loader.img ]]; then # legacy rk3399 loader
        ...
    elif [[ -f $1/u-boot.itb ]]; then # $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes
        dd if=$1/idbloader.img of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
        dd if=$1/u-boot.itb of=$2 seek=16384 conv=notrunc status=none >/dev/null 2>&1
    elif [[ -f $1/uboot.img ]]; then # $BOOT_USE_BLOBS == yes
        ...
    else
        echo "[write_uboot_platform]: Unsupported u-boot processing configuration!"
        exit 1
    fi
}

#
# 用法同write_uboot_platform()
#
write_uboot_platform_mtd()
{
    if [[ -f $1/rkspi_loader.img ]]; then
        dd if=$1/rkspi_loader.img of=$2 conv=notrunc status=none >/dev/null 2>&1
    else
        echo "SPI u-boot image not found!"
        exit 1
    fi
}

当然,还会有一些边边角角的小逻辑,读者感兴趣的话可自行查看其余脚本,但最重要最核心的逻辑已在上面列出, 看明白之后就可以开始编译了。编译环境方面,使用电脑或香橙派5开发板均可, 但最好是Ubuntu 22.04,参考命令如下(先解压前述U-Boot源码压缩包并进入其根目录):

$ sudo apt install gcc-11-aarch64-linux-gnu # 若使用电脑,则需要先安装交叉编译器
$
$ wget -c -O bl31.elf 'https://github.com/armbian/rkbin/raw/master/rk35/rk3588_bl31_v1.42.elf'
$ sed -i '1s/\(python\)2/\1/' arch/arm/mach-rockchip/decode_bl31.py
$ make all u-boot.dtb u-boot.itb ARCH=arm CROSS_COMPILE=aarch64-linux-gnu-
$
$ make tpl/u-boot-tpl.bin spl/u-boot-spl.bin ARCH=arm CROSS_COMPILE=aarch64-linux-gnu-
$ tools/mkimage -n rk3588s -T rksd -d tpl/u-boot-tpl.bin idbloader.img
$ cat spl/u-boot-spl.bin >> idbloader.img

当然,若要作为一个项目,肯定要为其增加版本管理,具体可通过本文开头的基础版文章链接去进一步了解, 在这里就不继续重复述说,至于代码仓库,详见U-Boot移植项目orange-pi-5子目录。

成功编译后,便可以烧录了,只需在开发板执行以下命令:

$ source /usr/lib/u-boot/platform_install.sh # 为了导入一个镜像目录变量DIR
$ sudo cp u-boot.itb idbloader.img ${DIR}/ # 要先拷贝镜像到特定目录才能烧录;若之前在电脑编译,此处就用scp命令
$ sudo nand-sata-install && sudo reboot # 烧录并重启

至此,本文到了收尾阶段。至于开头提到的移植网络功能的需求,与本文的U-Boot核心逻辑关系不大, 而且还未实现,所以就不作为本文内容了,感兴趣的可继续关注前述的U-Boot移植项目, 但由于日常繁忙,完工的日期无法预料。