Rockchip RK3399 - TPL/SPL方式加载uboot
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2017.09
----------------------------------------------------------------------------------------------------------------------------
NanoPC-T4
开发板,主控芯片是Rockchip RK3399
,big.LITTLE
大小核架构,双Cortex-A72
大核(up to 2.0GHz
) + 四Cortex-A53
小核结构(up to 1.5GHz
);Cortex-A72
处理器是Armv8-A
架构下的一款高性能、低功耗的处理器。
我们接着上一节,介绍《Rockchip
处理器启动支持的两种引导方式》:
TPL/SPL
加载:使用Rockchip
官方提供的TPL/SPL
U-boot(就是我们上面说的小的uboot
),该方式完全开源;- 官方固件加载:使用
Rockchip idbLoader
,来自Rockchip rkbin
项目的Rockchip DDR
初始化bin
和miniloader bin
,该方式不开源;
这一节我们将介绍采用TPL/SPL
方式,如何编译源码以及烧录程序到eMMC
,从而完成uboot
的启动。
一、uboot
uboot
通常有三种:
uboot
官方源码:https://github.com/u-boot/u-boot
,uboot
官方源码是由uboot
官方维护,支持非常全面的芯片,但对具体某款开发板支持情况一般;- 半导体厂商瑞芯微官方源码:
https://github.com/rockchip-linux/u-boot
,半导体厂商基于uboot
官方源码进行修改,对自家的芯片进行完善的支持,针对某款处理器支持情况较好; - 开发板友善之家官方源码:
https://github.com/friendlyarm/uboot-rockchip
,开发板厂商基于半导体厂商维护的uboot
,对自家的开发板进行板级支持,针对某款开发板支持情况较好;
我们不要上来就去移植uboot
官方的源码,一般来说uboot
官方的代码不做任何改动,是无法在我们板子上直接运行的。我们先去把Rockchip
官方提供的2017.09
版本的uboot
代码下载下来,编译之后看看能不能运行,如果可以的话,再去参考Rockchip
官方的uboot
去移植最新版本的uboot
。
这里有一点需要补充的是:Rockchip
官方提供的uboot 2017.09
版本做了大量的改动,尤其是引导内核启动上,为了支持多种内核镜像加载方式,对uboot
源码进行了大量修改,所以要求我们烧录的内核镜像也要按照官方指定的格式调整否则无法被uboot
正确引导。
1.1 下载源码
我们可以在Rockchip
的github
上下载到芯片厂商提供的u-boot
源码,如下图所示:
这里我们下载的是最新的next-dev
分支的代码:
root@ubuntu:/work/sambashare/rk3399# git clone https://github.com/rockchip-linux/u-boot.git --depth 1 -b next-dev
这里我是下载到/work/sambashare/rk3399
路径下的,这个路径后面专门存放与rk3399
相关的内容。
进入到u-boot
文件夹里,这就是我们需要的uboot
的源码了,后面就可以进行二次开发了;
root@ubuntu:/work/sambashare/rk3399/u-boot# cd ..
root@ubuntu:/work/sambashare/rk3399# cd u-boot/
root@ubuntu:/work/sambashare/rk3399/u-boot# ls -l
总用量 488
drwxr-xr-x 2 root root 4096 5月 7 20:00 api
drwxr-xr-x 14 root root 4096 5月 7 20:00 arch
drwxr-xr-x 181 root root 4096 5月 7 20:00 board
drwxr-xr-x 6 root root 4096 5月 7 20:00 cmd
drwxr-xr-x 5 root root 4096 5月 7 20:00 common
-rw-r--r-- 1 root root 2260 5月 7 20:00 config.mk
drwxr-xr-x 2 root root 69632 5月 7 20:00 configs
drwxr-xr-x 2 root root 4096 5月 7 20:00 disk
drwxr-xr-x 10 root root 12288 5月 7 20:00 doc
drwxr-xr-x 3 root root 4096 5月 7 20:00 Documentation
drwxr-xr-x 56 root root 4096 5月 7 20:00 drivers
drwxr-xr-x 2 root root 4096 5月 7 20:00 dts
drwxr-xr-x 2 root root 4096 5月 7 20:00 env
drwxr-xr-x 4 root root 4096 5月 7 20:00 examples
drwxr-xr-x 12 root root 4096 5月 7 20:00 fs
drwxr-xr-x 32 root root 16384 5月 7 20:00 include
-rw-r--r-- 1 root root 1863 5月 7 20:00 Kbuild
-rw-r--r-- 1 root root 14162 5月 7 20:00 Kconfig
drwxr-xr-x 14 root root 4096 5月 7 20:00 lib
drwxr-xr-x 2 root root 4096 5月 7 20:00 Licenses
-rw-r--r-- 1 root root 12587 5月 7 20:00 MAINTAINERS
-rw-r--r-- 1 root root 56469 5月 7 20:00 Makefile
-rwxr-xr-x 1 root root 19845 5月 7 20:00 make.sh
drwxr-xr-x 2 root root 4096 5月 7 20:00 net
-rwxr-xr-x 1 root root 1640 5月 7 20:00 pack_resource.sh
drwxr-xr-x 5 root root 4096 5月 7 20:00 post
-rw-r--r-- 1 root root 34 5月 7 20:00 PREUPLOAD.cfg
-rw-r--r-- 1 root root 189024 5月 7 20:00 README
drwxr-xr-x 6 root root 4096 5月 7 20:00 scripts
-rw-r--r-- 1 root root 17 5月 7 20:00 snapshot.commit
drwxr-xr-x 12 root root 4096 5月 7 20:00 test
drwxr-xr-x 16 root root 4096 5月 7 20:00 tools
需要注意的是:尽量不要下载release
分支,最初我也下载了这个分支,这里分支默认配置有问题。其中配置项:CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x50000
,表示SPL为ATF
预留了0x50000
大小的内存空间,这个地址范围是从0x00000008
开始的,而0x50008
之后是SPL
代码。程序运行后,SPL
从eMMC
加载bl31_0x00040000.bin
到内存地址0x40000
时,由于文件也比较大,直接覆盖了原有的SPL
代码,导致程序直接卡死。当然修复这个问题也很简单,配置CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0
即可。
1.2 配置uboot
uboot
的编译分为两步:配置、编译。单板的默认配置在configs
目录下,这里我们直接选择configs/evb-rk3399_defconfig
,这是Rockchip
评估板的配置:
展开查看详情
CONFIG_ARM=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_SPL_LIBCOMMON_SUPPORT=y
CONFIG_SPL_LIBGENERIC_SUPPORT=y
CONFIG_SYS_MALLOC_F_LEN=0x4000
CONFIG_ROCKCHIP_RK3399=y
CONFIG_RKIMG_BOOTLOADER=y
# CONFIG_USING_KERNEL_DTB is not set
CONFIG_DEFAULT_DEVICE_TREE="rk3399-evb"
CONFIG_DEBUG_UART=y
CONFIG_FIT=y
CONFIG_SPL_LOAD_FIT=y
CONFIG_SPL_FIT_GENERATOR="arch/arm/mach-rockchip/make_fit_atf.py"
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_ANDROID_BOOTLOADER=y
CONFIG_SPL_STACK_R=y
CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x10000
CONFIG_SPL_ATF=y
CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y
CONFIG_FASTBOOT_BUF_ADDR=0x00800800
CONFIG_FASTBOOT_BUF_SIZE=0x04000000
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_CMD_BOOTZ=y
# CONFIG_CMD_IMLS is not set
CONFIG_CMD_GPT=y
CONFIG_CMD_LOAD_ANDROID=y
CONFIG_CMD_BOOT_ANDROID=y
CONFIG_CMD_BOOT_ROCKCHIP=y
CONFIG_CMD_MMC=y
CONFIG_CMD_SF=y
CONFIG_CMD_USB=y
CONFIG_CMD_USB_MASS_STORAGE=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_TIME=y
CONFIG_RKPARM_PARTITION=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_LIVE=y
CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_REGMAP=y
CONFIG_SPL_REGMAP=y
CONFIG_SYSCON=y
CONFIG_SPL_SYSCON=y
CONFIG_CLK=y
CONFIG_SPL_CLK=y
CONFIG_ROCKCHIP_GPIO=y
CONFIG_SYS_I2C_ROCKCHIP=y
CONFIG_MISC=y
CONFIG_ROCKCHIP_EFUSE=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_ROCKCHIP=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ROCKCHIP=y
CONFIG_DM_ETH=y
CONFIG_ETH_DESIGNWARE=y
CONFIG_GMAC_ROCKCHIP=y
CONFIG_PINCTRL=y
CONFIG_SPL_PINCTRL=y
CONFIG_DM_PMIC=y
CONFIG_PMIC_RK8XX=y
CONFIG_REGULATOR_PWM=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_REGULATOR_RK8XX=y
CONFIG_PWM_ROCKCHIP=y
CONFIG_RAM=y
CONFIG_SPL_RAM=y
CONFIG_ROCKCHIP_SDRAM_COMMON=y
CONFIG_DM_RESET=y
CONFIG_BAUDRATE=1500000
CONFIG_DEBUG_UART_BASE=0xFF1A0000
CONFIG_DEBUG_UART_CLOCK=24000000
CONFIG_DEBUG_UART_SHIFT=2
CONFIG_SYSRESET=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_DWC3=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_GENERIC=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_GENERIC=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_GADGET=y
CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DOWNLOAD=y
CONFIG_USB_GADGET_MANUFACTURER="Rockchip"
CONFIG_USB_GADGET_VENDOR_NUM=0x2207
CONFIG_USB_GADGET_PRODUCT_NUM=0x330a
CONFIG_USB_HOST_ETHER=y
CONFIG_USB_ETHER_ASIX=y
CONFIG_USB_ETHER_ASIX88179=y
CONFIG_USB_ETHER_MCS7830=y
CONFIG_USB_ETHER_RTL8152=y
CONFIG_USB_ETHER_SMSC95XX=y
CONFIG_DM_VIDEO=y
CONFIG_DISPLAY=y
CONFIG_DRM_ROCKCHIP=y
CONFIG_DRM_ROCKCHIP_DW_MIPI_DSI=y
CONFIG_DRM_ROCKCHIP_ANALOGIX_DP=y
CONFIG_LCD=y
CONFIG_USE_TINY_PRINTF=y
CONFIG_SPL_TINY_MEMSET=y
CONFIG_ERRNO_STR=y
因此执行如下命令,生成.config
文件:
root@ubuntu:/work/sambashare/rk3399/u-boot# make evb-rk3399_defconfig V=1
输出如下(忽略编译器警告信息):
root@ubuntu:/work/sambashare/rk3399/u-boot# make evb-rk3399_defconfig V=1
make -f ./scripts/Makefile.build obj=scripts/basic
cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c
rm -f .tmp_quiet_recordmcount
make -f ./scripts/Makefile.build obj=scripts/kconfig evb-rk3399_defconfig
cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D_GNU_SOURCE -I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D_GNU_SOURCE -I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
scripts/kconfig/conf --defconfig=arch/../configs/evb-rk3399_defconfig Kconfig
#
# configuration written to .config
#
配置主要分为三个步骤:
- 第一步:执行
make -f ./scripts/Makefile.build obj=scripts/basic
,编译生成scripts/basic/fixdep
工具; - 第二步:执行
make -f ./scripts/Makefile.build obj=scripts/kconfig evb-rk3399_defconfig
,编译生成scripts/kcofig/conf
工具; - 第三步:执行
scripts/kconfig/conf --defconfig=arch/../configs/evb-rk3399_defconfig Kconfig
,scripts/kconfig/conf
根据evb-rk3399_defconfig
生成.config
配置文件;
这里会在当前路径下生成.config
文件,这实际上是一个配置文件,这个文件是怎么生成的呢,实际上就是根据configs/evb-rk3399_defconfig
文件以及我们make menuconfig
看到的那些默认配置(或者说是各个目录下的Kconfig
文件中有效的default
项)生成的。
在进行make
编译的时候,会根据这个文件生成include/config/auto.conf
文件,同时在顶层Makefile
会引入auto.conf
文件:
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
这样在执行make
编译过程中,就可以根据include/config/auto.conf
中的宏的定义编译不同的库文件。
1.2.1 配置串口波特率
uboot
中默认的调试串口波特率是1500000
,有很多的调试终端不支持1.5M
的波特率,我们可以把波特率重新配置下,在u-boot
文件夹下输入命令:make menuconfig
配置;
Device Drivers --->
Serial drivers --->
(150000) Default baudrate
注意: 波特率数值如果无法删除,按CTRL+
回车键尝试。
也可以直接编辑.config
配置项:
CONFIG_BAUDRATE=1500000
1.2.2 配置uboot
启动倒计时
如果在uboot
启动倒计时结束之前,没有按下任何键,将会执行那么将执行也就是bootcmd
中配置中的命令,bootcmd
中保存着默认的启动命令。
(5) delay in seconds before automatically booting
也可以直接编辑.config
配置项:
CONFIG_BOOTDELAY=5
保存文件,输入文件名为evb-rk3399_defconfig
,在当前路径下生成evb-rk3399_defconfig
:存档:
root@ubuntu:/work/sambashare/rk3399/u-boot# mv evb-rk3399_defconfig ./configs/
注意:如果需要配置生效,需要使用make distclean
清除之前的配置,重新执行配置命令。
更多内容可以参考我之前写的有关s3c2440 uboot
配置的文章:《make smdk2410_defconfig
配置分析》,虽然SoC
不同,但是make
配置流程是一样的。
1.2.3 开启调试信息
在uboot
启动时,如果我们想打印更加详细的信息,可以在include/configs/evb_rk3399.h
中加入如下宏:
#define DEBUG
后面测试发现,设置了这个虽然可以输出一些调试信息,但是确无法进入uboot
命令行,因此这个非特殊场景,尽量不要设置。
1.3 编译uboot
执行make
命令,生成u-boot
文件:
root@ubuntu:/work/sambashare/rk3399/u-boot# make ARCH=arm CROSS_COMPILE=arm-linux-
编译过程我们会发现有一些错误,这些错误的处理我们在文章的最后单独介绍。
更多内容可以参考我之前写的有关s3c2440 uboot
编译的文章:《make
编译正向分析之顶层目标依赖》,虽然SoC
不同,但是make
编译流程是一样的。
1.4 image
镜像
成功编译之后,就会在uboot
源码的根目录下产生多个可执行二进制文件以及编译过程文件,这些文件都是u-bootxxx
的命名方式。这些文件由一些列名为.xxx.cmd
的文件生成,.xxx.cmd
这些文件都是由编译系统产生的用于处理最终的可执行程序的。
在uboot
根录下生成文件有:
root@ubuntu:/work/sambashare/rk3399/u-boot# ll u-boot* Sys*
-rw-r--r-- 1 root root 153740 5月 14 10:30 System.map
-rwxr-xr-x 1 root root 6872736 5月 14 10:30 u-boot*
-rw-r--r-- 1 root root 931504 5月 14 10:30 u-boot.bin
-rw-r--r-- 1 root root 15808 5月 14 10:30 u-boot.cfg
-rw-r--r-- 1 root root 9996 5月 14 10:30 u-boot.cfg.configs
-rw-r--r-- 1 root root 51685 5月 14 10:30 u-boot.dtb # 设备树
-rw-r--r-- 1 root root 931501 5月 14 10:30 u-boot-dtb.bin # 等同u-boot.bin
-rw-r--r-- 1 root root 932864 5月 14 10:30 u-boot-dtb.img # 等同u-boot.img
-rw-r--r-- 1 root root 932864 5月 14 10:30 u-boot.img
-rw-r--r-- 1 root root 1304 5月 14 10:30 u-boot.lds
-rw-r--r-- 1 root root 800454 5月 14 10:30 u-boot.map
-rwxr-xr-x 1 root root 879816 5月 14 10:30 u-boot-nodtb.bin*
-rwxr-xr-x 1 root root 2529568 5月 14 10:30 u-boot.srec*
-rw-r--r-- 1 root root 300850 5月 14 10:30 u-boot.sym
其中:
u-boot
: 这个文件是编译后产生的ELF
格式的最原始的uboot
镜像文件,后续的文件都是由它产生的.u-boot.cmd
这个命令脚本描述了如何产生;u-boot-nodtb.bin
: 这文件是使用编译工具链的objcopy
工具从u-boot
这个文件中提取来的,它只包含可执行的二进制代码。就是把u-boot
这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。由.u-boot-nodtb.bin.cmd
这个命令脚本产生;u-boot-dtb.bin
: 在u-boot-nodtb.bin
尾部拼接上设备树后形成的文件。由.u-boot-dtb.bin.cmd
这个命令脚本产生;u-boot.bin
: 就是把u-boot-dtb.bin
重命名得到的。由.u-boot.bin.cmd
这个命令脚本产生;u-boot.img
: 在u-boot.bin
开头拼接一些信息后形成的文件。由.u-boot.img.cmd
这个命令脚本产生;u-boot-dtb.img
: 在u-boot.bin
开头拼接一些信息后形成的文件。由.u-boot-dtb.img.cmd
这个命令脚本产生;u-boot.srec
:S-Record
格式的镜像文件。由.u-boot.srec.cmd
这个命令脚本产生;u-boot.sym
: 这个是从u-boot
中导出的符号表文件。由.u-boot.sym.cmd
这个命令脚本产生;u-boot.lds
: 编译使用的链接脚本文件。由.u-boot.lds.cmd
这个命令脚本产生;u-boot.map
: 编译的内存映射文件。该文件是有编译工具链的连接器输出的;System.map
: 记录u-boot
中各个符号在内核中位置,但是这个文件是使用了nm
和grep
工具来手动生成的;
由于开启了SPL
和TPL
的,因此,在编译uboot
时会额外单独编译SPL
、TPL
,编译产生的镜像文件就存放在 ./spl
、./tpl
目录下。这下面的镜像生成方式与uboot
基本是一模一样的。
root@ubuntu:/work/sambashare/rk3399/u-boot# ls tpl
arch cmd drivers env include u-boot.cfg u-boot-tpl u-boot-tpl.dtb u-boot-tpl.map u-boot-tpl.sym
board common dts fs lib u-boot-spl.lds u-boot-tpl.bin u-boot-tpl-dtb.bin u-boot-tpl-nodtb.bin
root@ubuntu:/work/sambashare/rk3399/u-boot# ls spl
arch cmd drivers env include u-boot.cfg u-boot-spl.bin u-boot-spl-dtb.bin u-boot-spl.map u-boot-spl.sym
board common dts fs lib u-boot-spl u-boot-spl.dtb u-boot-spl.lds u-boot-spl-nodtb.bin
以SPL
为例:
u-boot-spl
: 这个文件是编译后产生的ELF
格式的SPL
镜像文件,后续的文件都是由它产生的.u-boot-spl.cmd
这个命令脚本描述了如何产生;u-boot-spl-nodtb.bin
: 这文件是使用编译工具链的objcopy
工具从u-boot-spl
这个文件中提取来的,它只包含可执行的二进制代码。就是把u-boot-spl
这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。由.u-boot-spl-nodtb.bin.cmd
这个命令脚本产生;u-boot-spl-dtb.bin
: 在u-boot-nodtb.bin
尾部依次拼接上u-boot-spl-pad.bin
和u-boot-spl.dtb
后形成的文件。由.u-boot-spl-dtb.bin.cmd
这个命令脚本产生;u-boot-spl.bin
: 就是把u-boot-dtb.bin
重命名得到的。由.u-boot-spl.bin.cmd
这个命令脚本产生;u-boot-spl.lds
: 编译使用的链接脚本文件。由.u-boot-spl.lds.cmd
这个命令脚本产生;u-boot-spl.map
: 编译SPL
的内存映射文件;u-boot-spl.dtb
: 这个是编译好的设备树二进制文件。就是./dts/dt.dtb
重命名得到的。./dts/dt.dtb
来自于arch/arm/dts/stm32f769-eval.dtb
重命名;
二、idbloader.img
我们基于uboot
源码编译出TPL/SPL
,其中TPL
负责实现DDR
初始化,TPL
初始化结束之后会回跳到BootROM
程序,BootROM
程序继续加载SPL
,SPL
加载u-boot.itb
文件,然后跳转到uboot
执行。
idbloader.img
是由tpl/u-boot-tpl.bin
和spl/u-boot-spl.bin
文件生成,这里我们需要使用到tools
目录下的mkimage
工具。
2.1 tpl/u-boot-tpl.bin
在u-boot
目录下执行:
root@ubuntu:/work/sambashare/rk3399/u-boot# tools/mkimage -n rk3399 -T rksd -d tpl/u-boot-tpl.bin idbloader.img
Image Type: Rockchip RK33 (SD/MMC) boot image
Init Data Size: 81920 bytes
其中:
-n rk3399
将镜像文件的名称设置为rk3399
;-T rksd
将映像类型指定为Rockchip SD
卡启动映像;-d tpl/u-boot-tpl.bin
将生成的TPL
镜像文件tpl/u-boot-tpl.bin
指定为输入文件,而idbloader.img
则指定为输出文件。
生成idbloader.img
文件:
root@ubuntu:/work/sambashare/rk3399/u-boot# ll idbloader.img
-rw-r--r-- 1 root root 83968 5月 14 10:38 idbloader.img
2.2 spl/u-boot-spl.bin
将spl/u-boot-spl.bin
合并到idbloader.img
:
root@ubuntu:/work/sambashare/rk3399/u-boot# cat spl/u-boot-spl.bin >> idbloader.img
root@ubuntu:/work/sambashare/rk3399/u-boot# ll idbloader.img
-rw-r--r-- 1 root root 210675 5月 14 10:39 idbloader.img
三、u-boot.idb
u-boot.itb
实际上是u-boot.img
的另一个变种,也是通过mkimage
构建出来的,依赖于u-boot.its u-boot.dtb u-boot-nodtb.bin
这三个文件。
mkimage
工具在u-boot
源码目录下的tools
目录中,不过由于u-boot
官方原本的FIT
功能无法满足实际产品需要,所以RK
平台对FIT
功能进行了适配和优化,所以自然对mkimage
工具的源代码进行了修改、优化;所以对于RK
平台硬件,如果使用FIT
格式镜像,必须使用RK u-boot
源码编译生成的mkimage
工具,不可使用u-boot
原版的mkimage
工具。
在顶层Makefile
文件中:
u-boot.itb: u-boot-nodtb.bin dts/dt.dtb $(U_BOOT_ITS) FORCE
$(call if_changed,mkfitimage)
这里的mkfitimage
被设置为了:
cmd_mkfitimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -f $(U_BOOT_ITS) -E $@ \
$(if $(KBUILD_VERBOSE:1=), >$(MKIMAGEOUTPUT))
3.1 FIT
介绍
FIT
是flattened image tree
的简称,它采用了device tree source filse(DTS)
的语法,生成的image
文件也和dtb
文件类似(称做itb
)。
注意:这一小节只是补充关于FIT
的相关知识,并不是编译u-boot.itb
的步骤。
具体可以参考:
- 《
doc/uImage.FIT/source_file_format.txt
》; - 《
Flattened Image Tree (FIT) Format
》: - 《
RK3568
开发笔记整理之FIT Image
》。
3.1.1 生成步骤
其中image source file(.its)
和device tree source file(.dts)
类似,负责描述要生成的image file
的信息。mkimage
和dtc
工具,可以将.its
文件以及对应的image data file
,打包成一个image file
。
3.1.2 image source file
语法
image source file
的语法和device tree source file
完全一样,只不过自定义了一些特有的节点,包括images
、configurations
等。1个FIT
根节点应该包含如下布局:
/ o image-tree
|- description = "image description" # 对本镜像的描述
|- timestamp = <12399321> # 镜像修改时间
|- #address-cells = <1>
|
o images # 镜像节点,必须包含一个子节点
| |
| o image-1 {...} #子节点
| o image-2 {...} #子节点
| ...
|
o configurations # 提供对上述镜像的组合,有默认配置和多种配置可选
|- default = "conf-1" # 默认配置,bootm未指定特殊配置时使用
|
o conf-1 {...} # 配置1
o conf-2 {...} # 配置2
...
其中:
description
:描述信息;timestamp
:修改镜像的时间,由mkimage
工具自动生成;images
:这个节点包含一组子节点,每个子节点代表一个单独的子镜像(如kernel
、ramdisk
等),至少需要一个子镜像;configurations
:配置项节点,可以将不同类型的二进制文件,根据不同的场景,组合起来,形成一个个的配置项。u-boot
在boot
的时候,以配置项为单位加载、执行,这样就可以根据不同的场景,方便的选择不同的配置;
3.1.2.1 /images
节点
该节点中描述了Image
镜像必要的信息,/images
节点的每个子节点应该具有以下布局:
o image-1
|- description = "component sub-image description"
|- data = /incbin/("path/to/data/file.bin")
|- type = "sub-image type name"
|- arch = "ARCH name"
|- os = "OS name"
|- compression = "compression name"
|- load = <00000000>
|- entry = <00000000>
|
o hash-1 {...}
o hash-2 {...}
...
可以包含如下的关键字:
description
:子镜像的描述,可以随便写;data
:二进制文件的路径,格式为/incbin/("path/to/data/file.bin")
;type
:子镜像的类型,standalone
,firmware
,kernel
,ramdisk
,flat_dt
等;arch
:平台类型,arm
,arm64
,i386
等;os
:操作系统类型,linux
、vxworks
等;compression
:二进制文件的压缩格式,none
、bzip2
、gzip
、lz4
等;load
:二进制文件的加载位置,SPL
或uboot
会把它copy
对应的地址上;entry
:二进制文件入口地址,一般kernel image
需要提供,uboot
会跳转到该地址上执行;hash
:镜像使用的校验算法,如sha256
,crc32
等。
这里我们节点说一下hash
节点;
o hash@1
|- algo = "hash or checksum algorithm name"
|- value = [hash or checksum value]
其中:
algo
:算法名称,如crc32
,md5
,sha256
等;value
:算法校验值,即algo
计算后的数值。
例如u-boot.its
中的包含了1个standalone image
、5个firmware image
、1个fdt image
。以atf@1
节点为例:
atf@1 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0x00040000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0x00040000>;
entry = <0x00040000>;
};
3.1.2.2 /configurations
节点
可以将不同类型的二进制文件,根据不同的场景,组合起来,形成一个个的配置项,u-boot
在boot
的时候,以配置项为单位加载、执行,这样就可以根据不同的场景,方便的选择不同的配置。
下面是u-boot.its
中的配置节点:
configurations {
default = "config";
config {
description = "Rockchip armv8 with ATF";
rollback-index = <0x0>;
firmware = "atf@1";
loadables = "uboot", "atf@2" , "atf@3" , "atf@4" , "atf@5" , "atf@6" ;
fdt = "fdt";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "firmware", "loadables";
};
};
};
这里只包含了1种配置,默认配置项由default
指定,当然也可以在运行时指定。
config
节点指定该配置具体使用那些kernel Image
,ramdisk Image
等。
config
节点中支持的属性:
description
:该配置的名称;kernel
:镜像类型为kernel
的单元的名称;ramdisk
:镜像类型为ramdisk
的单元的名称;fdt
:镜像类型为fdt
的单元的名称;loadables
:额外的可加载的二进制文件的列表,u-boot
将在给定的起始地址加载每个二进制文件。
3.1.3 编译
FIT image
文件的编译过程很简单,根据实际情况,编写image source file
之后(假设名称为u-boot.its
),在命令行使用mkimage
工具编译即可:
./tools/mkimage [-D dtc_options] [-f fit-image.its|-f auto|-f auto-conf|-F] [-b <dtb> [-b <dtb>]] [-E] [-B size] [-i <ramdisk.cpio.gz>] fit-image
<dtb> file is used with -f auto, it may occur multiple times.
-D => set all options for device tree compiler
-f => input filename for FIT source
-i => input filename for ramdisk file
-E => place data outside of the FIT structure
-B => align size in hex for FIT structure and header
-b => append the device tree binary to the FIT
-t => update the timestamp in the FIT
其中:
-D
:指定DTC(Device Tree Compiler)
编译器的选项;-f
:指定需要编译的image source file
,并在后面指定需要生成的image
文件(一般以.itb
为后缀,例如u-boot.itb
);-i
:指定用于创建FIT
镜像的RAM disk
文件名;-E
:将image data file
放置在FIT结构外的选项;-B
:指定FIT
结构和头的对齐大小;-b
:支持将一个或多个设备树二进制文件附加到FIT
文件中(可使用-b <dtb>
多次指定);-t
:更新 FIT 文件中的时间戳;
其中-E
这个字段比较重要,它会影响生成的itb
的文件布局;
- 如果没有指定该选项,其生成的
itb
文件格式和dts
文件编译生成的dtb
文件布局一样,包括data
属性指定的/incbin/("bl31_0x00040000.bin")
文件也会以二进制数据格式的形式放到FIT
结构内; - 如果指定了该选项,会为
data
属性指向的文件扩充data-offset
(指定文件的偏移,这个偏移是以FIT
结构结束地址下一扇区起始地址开始计算的,即相对于fdt_blob
末尾的位置偏移量)、以及data-size
(指定文件的大小)属性,而在data
属性指向的二进制数据文件将会被追加到FIT
结构的尾部(也是扇区对齐);下文我们分析的itb
文件布局格式就是这种;
有关这两种布局效果具体如下图所示:
![](https://gitee.com/zyly2033/blog-pic/raw/master/202408041652488.png)
我们可以采用如下方式生成u-boot.itb
文件:
./tools/mkimage -f u-boot.its -E u-boot.itb
u-boot.itb
生成后,也可以使用mkimage
命令查看它的信息:
tools/mkimage -l u-boot.itb
3.1.4 itb
文件布局
编译生成u-boot.itb
文件的u-boot.its
源文件如下:
展开查看详情
root@ubuntu:/work/sambashare/rk3399/u-boot# cat u-boot.its
/*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
*
* Minimal dts for a SPL FIT image payload.
*
* SPDX-License-Identifier: GPL-2.0+ X11
*/
/dts-v1/;
/ {
description = "Configuration to load ATF before U-Boot";
#address-cells = <1>;
images {
uboot {
description = "U-Boot (64-bit)";
data = /incbin/("u-boot-nodtb.bin");
type = "standalone";
os = "U-Boot";
arch = "arm64";
compression = "none";
load = <0x00200000>;
hash {
algo = "sha256";
};
};
atf@1 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0x00040000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0x00040000>;
entry = <0x00040000>;
hash {
algo = "sha256";
};
};
atf@2 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0xff3b0000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xff3b0000>;
hash {
algo = "sha256";
};
};
atf@3 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0xff8c0000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xff8c0000>;
hash {
algo = "sha256";
};
};
atf@4 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0xff8c1000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xff8c1000>;
hash {
algo = "sha256";
};
};
atf@5 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0xff8c2000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xff8c2000>;
hash {
algo = "sha256";
};
};
fdt {
description = "U-Boot device tree blob";
data = /incbin/("u-boot.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
hash {
algo = "sha256";
};
};
};
configurations {
default = "config";
config {
description = "Rockchip armv8 with ATF";
rollback-index = <0x0>;
firmware = "atf@1";
loadables = "uboot", "atf@2" , "atf@3" , "atf@4" , "atf@5" , "atf@6" ;
fdt = "fdt";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "firmware", "loadables";
};
};
};
};
注意:这里我们补充一下u-boot.its
文件的来源,在Makefile
中有如下配置:
# Boards with more complex image requirments can provide an .its source file
# or a generator script
ifneq ($(CONFIG_SPL_FIT_SOURCE),"") # 指定了u-boot.its文件
U_BOOT_ITS = $(subst ",,$(CONFIG_SPL_FIT_SOURCE))
else
ifneq ($(CONFIG_SPL_FIT_GENERATOR),"") # 走这里
U_BOOT_ITS := u-boot.its
$(U_BOOT_ITS): FORCE
$(srctree)/$(CONFIG_SPL_FIT_GENERATOR) \
$(patsubst %,arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) > $@ # 利用python脚本生成u-boot.its
endif
endif
由于configs/evb-rk3399_defconfig
中配置了CONFIG_SPL_FIT_GENERATOR
,因此这里会通过python
脚本生成u-boot.its
;
CONFIG_SPL_FIT_GENERATOR="arch/arm/mach-rockchip/make_fit_atf.py"
编译生成的u-boot.itb
文件,以16
进制查看:
![](https://gitee.com/zyly2033/blog-pic/raw/master/RockchipRK3399-TPLSPL方式加载uboot.md.assets/1328274-20230514184021187-713570085.png)
由于itb
文件布局和dtb
文件布局一样,所以我们按照dtb
文件布局格式来解读。
其中地址范围0x00000000~0x00000027
表示的是fdt_header
结构体的成员信息:
- 地址
0x00000000
:对应magic
,表示设备树魔数,固定为0xd00dfeed
; - 地址
0x00000004
:对应totalsize
,表示源u-boot.its
文件打包后在u-boot.itb
中所占的大小,由于我们编译指定了-E
属性,因此这里计算的是不包含image data file
文件的大小,更准确的说应该是FIT
结构的大小。从上图可知这个值为0x00000a39
; - 地址
0x00000008
:对应off_dt_struct
,表示structure block
的偏移地址,为0x00000038
; - 地址
0x0000000c
:对应off_dt_strings
,表示strings block
的偏移地址,为0x00000968
; - 地址
0x00000010
:对应off_mem_rsvmap
:表示memory reservation block
的偏移地址,为0x00000028
; - 地址
0x00000014
:对应version
,设备树版本的版本号为0x11
; - 地址
0x00000018
:对应last_comp_version
:向下兼容版本号0x10
; - 地址
0x0000001C
:对应boot_cpuid_phys
:在多核处理器中用于启动的主cpu
的物理id
,为0x0
; - 地址
0x00000020
:对应size_dt_strings
,strings block
的大小为0xd1
; - 地址
0x00000024
: 对应size_dt_struct
,structure block
的大小为0x00000930
;
其中地址范围0x00000028~0x00000037
表示的是fdt_reserve_entry
结构体的成员信息:
- 对应结构体
fdt_reserve_entry
:它所在的地址为0x28
,u-boot.its
设备树文件没有设置/memreserve/
,所以address = 0x0
,size = 0x0
;
其中地址范围0x00000038~0x00000967
表示的是structure block
信息:
- 地址
0x00000038
:值0x00000001
表示的是设备节点的开始; - 地址
0x0000003c
:表示设备节点的名字,这里是根节点,所以为0x00000000
; - 地址
0x00000040
:值0x00000003
表示的是开始描述设备节点的一个属性; - 地址
0x00000044
:表示这个属性值的长度为0x04
; - 地址
0x00000048
:表示这个属性的名字在strings block
的偏移量是0xad
,找到strings block
的地址0x00000968+0xad=0xA15
的地方,可知这个属性的名字是version
; - 地址
0x0000004c
:这个version
属性的值是0;
我们来看一下uboot
节点,由于uboot
属性比较多,这里我并没有截全:
![](https://gitee.com/zyly2033/blog-pic/raw/master/RockchipRK3399-TPLSPL方式加载uboot.md.assets/1328274-20230514192111733-900416182.png)
其属性data-size
地址范围在0x000000c0~0x000000db
:
- 地址
0x000000c0
:值0x00000001
表示的是设备节点的开始; - 地址
0x000000c4
:表示设备节点的名字,这里是uboot
节点,所以为uboot
,占用8个字节; - 地址
0x000000cc
:值0x00000003
表示的是开始描述设备节点的一个属性; - 地址
0x000000d0
:表示这个属性值的长度为0x04
; - 地址
0x000000d4
:表示这个属性的名字在strings block
的偏移量是0xc7
,找到strings block
的地址0x00000968+0xc7=0xa2f
的地方,可知这个属性的名字是data-size
; - 地址
0x000000d8
:这个data-size
属性的值是0x0d72e0
;
属性data-offset
地址范围在0x000000dc~0x000000eb
:
- 地址
0x000000dc
:值0x00000003
表示的是开始描述设备节点的一个属性; - 地址
0x000000e0
:表示这个属性值的长度为0x04
; - 地址
0x000000e4
:表示这个属性的名字在strings block
的偏移量是0xbb
,找到strings block
的地址0x00000968+0xbb=0xa23
的地方,可知这个属性的名字是data-offset
; - 地址
0x000000e8
:这个data-offset
属性的值是0x00
;需要注意的这个描述的就是uboot
节点data
属性指定的u-boot-nodtb.bin
文件的偏移,偏移0x00
是从FIT
文件结束下一扇区地址开始计算;也就是0xa3f
取下一扇区起始地址,即0x0c00
;
需要注意的是:data-offset
、data-size
这两个属性在u-boot.its
文件uboot
节点中是没有的,这俩应该是自动扩充的属性的属性,用来描述data
属性指向的u-boot-nodtb.bin
文件的信息。
我们验证一下u-boot-nodtb.bin
文件大小0xd72e0 =881376
,和命令看到的完全匹配:
root@ubuntu:/work/sambashare/rk3399/u-boot# ll u-boot-nodtb.bin
-rwxr-xr-x 1 root root 881376 5月 14 18:27 u-boot-nodtb.bin*
我们验证一下文件内容,u-boot-nodtb.bin
源文件内容:
root@ubuntu:/work/sambashare/rk3399/u-boot# hexdump u-boot-nodtb.bin -n 16
0000000 000a 1400 201f d503 0000 0020 0000 0000
定位到u-boot.itb
文件地址0x0c00
:
![](https://gitee.com/zyly2033/blog-pic/raw/master/RockchipRK3399-TPLSPL方式加载uboot.md.assets/1328274-20230514193008837-138420131.png)
这样我们可以得到一个文件内容布局表:
地址范围 | 偏移 | 大小 | 内容 | 加载到内存地址 |
---|---|---|---|---|
0x0000 0000 ~ 0x0000 0a39 | 0x0a39 | FIT:存放u-boot.its文件信息 | ||
0x0000 0c00~ 0x000d 7ee0 | 0x00(按扇区大小对齐) | 0xd72e0 | uboot:u-boot-nodtb.bin | 0x00200000 |
0x000d 8000~ 0x000f b04e | 0xd7400(按扇区大小对齐) | 0x2304e | atf@1:bl31_0x00040000.bin | 0x00040000 |
自己计算 | 0xfa600(按扇区大小对齐) | 0x1f58 | atf@2:bl31_0xff3b0000.bin | 0xff3b0000 |
自己计算 | 0xfc600(按扇区大小对齐) | 0x1000 | at3@3:bl31_0xff8c0000.bin | 0xff8c0000 |
自己计算 | 0xfd600(按扇区大小对齐) | 0x1000 | atf@4:bl31_0xff8c1000.bin | 0xff8c1000 |
自己计算 | 0xfe600(按扇区大小对齐) | 0x00 | atf@5:bl31_0xff8c2000.bin | 0xff8c2000 |
自己计算 | 0xfe600(按扇区大小对齐) | 0xc9e5 | fdt:u-boot.dtb |
这里我只列出了一部分地址范围,有兴趣自己去搞吧,太费劲了。
3.1.5 fdtdump
fdtdump
命令可以查看itb
文件内容:
fdtdump uboot.itb
3.2 编译ATF
因为rk399
是arm64
,所以我们还需要编译ATF (Arm trust firmware)
, ATF
主要负责在启动uboot
之前把CPU
从安全的EL3
切换到 EL2
,然后跳转到uboot
,并且在内核启动后负责启动其他的CPU
。
3.2.1 下载源码
下载《arm-trusted-fireware
》到uboot
同级目录:
root@ubuntu:/work/sambashare/rk3399# git clone https://github.com/ARM-software/arm-trusted-firmware.git --depth 1
3.2.2 编译ATF
运行如下命令:
root@ubuntu:/work/sambashare/rk3399/arm-trusted-firmware# make CROSS_COMPILE=arm-linux- PLAT=rk3399
编译后报出一个缺少arm-none-eabi-gcc
工具链的错误:
安装该工具链并重新编译:
root@ubuntu:/work/sambashare/rk3399/arm-trusted-firmware# sudo apt-get install gcc-arm-none-eabi
root@ubuntu:/work/sambashare/rk3399/arm-trusted-firmware# make CROSS_COMPILE=arm-linux- PLAT=rk3399
最终编译出来的目标文件为:build/rk3399/release/bl31/bl31.elf
, 这个文件需要和编译出来的uboot
一起打包成fit
格式的镜像才能被SPL
加载。
3.3 生成u-boot.itb
3.3.1 拷贝bl31.elf
将bl31.elf
拷贝到uboot
根目录下:
root@ubuntu:/work/sambashare/rk3399/u-boot# cp /work/sambashare/rk3399/arm-trusted-firmware/build/rk3399/release/bl31/bl31.elf ./
3.3.2 编译
然后执行编译命令:
root@ubuntu:/work/sambashare/rk3399/u-boot# make u-boot.itb ARCH=arm CROSS_COMPILE=arm-linux-
这里提示我们缺少python
依赖elffile
,如下图所示:
我们直接使用如下命令安装python
依赖包(需要注意自己的python
版,必须是2.7
版本,使用pip2
安装依赖):
root@ubuntu:/work/sambashare/rk3399/u-boot# pip2 install pyelftools
重新编译生成u-boot.itb
文件:
root@ubuntu:/work/sambashare/rk3399/u-boot# ls -l u-boot.itb
-rw-r--r-- 1 root root 1095168 5月 14 10:40 u-boot.itb
如果出现1:dtc: not found make
错误,如下:
tc
是device-tree-compiler
的缩写,即设备树编译器,说明系统中没有安装这个编译器,安装设备树编译器重新编译即可:
root@ubuntu:/work/sambashare/rk3399/u-boot# sudo apt-get install device-tree-compiler
四、rkdeveloptool
rkdeveloptool
是Rockchip
提供的一个与Rockusb
设备进行通信的工具,通过该工具我们可以将镜像文件下载到开发板的eMMC
。它被认为是upgrade_tool
的一个开源版本,只有很少区别。
要使用rkdeveloptool
进行升级,首先要知道rkdeveloptool
是基于什么情况下才会起作用的,是在SoC
进入MASKROM
模式后而且跟主机通过USB
连接,因为这个时候主板的DDR
并没有初始化,而升级过程是需要很大的内存空间的,所以升级之前第一步要做的就是执行rkdeveloptool db rkxx_loader_vx.xx.bin
(这个固件本质上也是idbloader.img
),只不过这时候只是在内存中执行,如果不执行db命令的话其他的命令则无法执行因为没有做内存初始化工作。
4.1 下载源码
在/work/sambashare/rk3399
目录下执行如下命令:
root@ubuntu:/work/sambashare/rk3399# git clone https://github.com/rockchip-linux/rkdeveloptool.git --depth 1
4.2 配置
首先安装libusb
与udev
,例如对于ubuntu
:
root@ubuntu:/work/sambashare/rk3399# sudo apt-get install libudev-dev libusb-1.0-0-dev dh-autoreconf
切换到rkdeveloptool/
目录进行配置:
root@ubuntu:/work/sambashare/rk3399# cd rkdeveloptool/
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# autoreconf -i
configure.ac:12: installing 'cfg/compile'
configure.ac:19: installing 'cfg/config.guess'
configure.ac:19: installing 'cfg/config.sub'
configure.ac:7: installing 'cfg/install-sh'
configure.ac:7: installing 'cfg/missing'
Makefile.am: installing 'cfg/depcomp'
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# ./configure
4.3 编译安装
运行如下命令:
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# make
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# make install
如果遇到如下编译错误:
./configure: line 4269: syntax error near unexpected token `LIBUSB1,libusb-1.0'
./configure: line 4269: `PKG_CHECK_MODULES(LIBUSB1,libusb-1.0)'
还需要安装pkg-config
与libusb-1.0
:
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# sudo apt-get install pkg-config libusb-1.0
编译完成后,在当前路径下生成rkdeveloptool
可执行文件:
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# ll rkdeveloptool
-rwxr-xr-x 1 root root 1059720 5月 11 19:56 rkdeveloptool*
4.4 rk3399_loader_v1.27.126.bin
由于SoC
进入到MASKROM
模式后,目标板子会运行Rockusb
驱动程序。在MASKROM
模式下,需要使用到DDR
,因此需要下载固件进行DDR
的初始化。
《Rockchip rkbin
项目》提供了ddr.bin
、usbplug.bin
、miniloader.bin
这三个包:
ddr.bin
:等价于我们之前说的TPL
,用于初始化DDR
;usbplug.bin
:Rockusb
驱动程序,用于将程序通过usb
下载到eMMC
;miniloader.bin
:Rockchip
修改的一个bootloader
,等价于我们之前说的SPL
,用于加载uboot
;
4.4.1 下载rkbin
我们可以在Rockchip
的github
上下载到Rockchip rkbin
项目,如下所示:
root@ubuntu:/work/sambashare/rk3399# git clone https://github.com/rockchip-linux/rkbin.git --depth 1
4.4.2 合并
在rkbin
目录下执行如下命令,可以将ddr.bin
、usbplug.bin
、miniloader.bin
这三个包合并:
root@ubuntu:/work/sambashare/rk3399# cd rkbin/
root@ubuntu:/work/sambashare/rk3399/rkbin# tools/boot_merger /work/sambashare/rk3399/rkbin/RKBOOT/RK3399MINIALL.ini
********boot_merger ver 1.2********
Info:Pack loader ok.
root@ubuntu:/work/sambashare/rk3399/rkbin# ll rk3399_loader_v1.27.126.bin
-rw-r--r-- 1 root root 465230 5月 11 20:06 rk3399_loader_v1.27.126.bin
可以根据自己的需求可以在./RKBOOT/RK3399MINIALL.ini
修改ddr
、usbplug
、miniloader
:
[CHIP_NAME]
NAME=RK330C
[VERSION]
MAJOR=1
MINOR=26
[CODE471_OPTION]
NUM=1
Path1=bin/rk33/rk3399_ddr_800MHz_v1.27.bin
Sleep=1
[CODE472_OPTION]
NUM=1
Path1=bin/rk33/rk3399_usbplug_v1.26.bin
[LOADER_OPTION]
NUM=2
LOADER1=FlashData
LOADER2=FlashBoot
FlashData=bin/rk33/rk3399_ddr_800MHz_v1.27.bin
FlashBoot=bin/rk33/rk3399_miniloader_v1.26.bin
[OUTPUT]
PATH=rk3399_loader_v1.27.126.bin
将rk3399_loader_v1.27.126.bin
拷贝到rkdeveloptool
路径下:
root@ubuntu:/work/sambashare/rk3399/rkbin# cp rk3399_loader_v1.27.126.bin ../rkdeveloptool/
五、烧录程序
烧录方法有两种:
- 一种是基于
Rockchip
的官方烧录工具RKDevTool
;官方RKDevTool
是基于recovery
模式实现的,如果板子带有recovery
按键,可以使用这种方式; - 另外一种是在开发板上使用
rkdeveloptool
工具直接烧写eMMC
;这里我们采用rkdeveloptool
烧录的方式;
5.1 准备镜像
我们需按照之前的流程得到了idbloader.img
、u-boot.itb
文件,由于我们这里不进行内核和根文件系统的烧录,所以暂时不需要准备这俩。
按照Rockchip
官方要求将idbloader.img
烧录到eMMC
的0x40
扇区,u-boot.itb
烧录到0x4000
扇区。
我们需要将idbloader.img
、u-boot.itb
拷贝到rkdeveloptool
路径下:
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot/u-boot.itb ./
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot/idbloader.img ./
5.2 进入MASKROM
升级模式
NanoPC-T4
开发板如需进入MASKROM
升级模式,需要进入如下操作:
- 将开发板连接上电源,并且连接
Type-C
数据线到PC
; - 按住
BOOT
键再长按Power
键开机(保持按下BOOT
键5秒以上),将强制进入MASKROM
模式。
一般电脑识别到USB
连接,都会发出声音。或者观察虚拟机右下角是否突然多个USB
设备:右键点击链接;
5.3 烧录
使用下载引导命令去使目标机器初始化DD
R与运行usbplug
(初始化DDR
的原因是由于升级需要很大的内存,所以需要使用到DDR
);
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool db rk3399_loader_v1.27.126.bin
Downloading bootloader succeeded.
由于BootROM
启动会将rk3399_loader_v1.27.126.bin
将在到内部SRAM
中,然后跳转到ddr.bin
代码进行DDR
的初始化,ddr.bin
执行之后会回跳到BootROM
程序,BootROM
程序继续加载usbplug.bin
,由usbplug.bin
完成程序的下载以及烧录到eMMC
。
如果接上串口的话,执行完这一步可以看到如下输出信息:
展开查看详情
DDR Version 1.27 20211018
In
Channel 0: LPDDR3, 800MHz
CS = 0
MR0=0x58
MR1=0x58
MR2=0x58
MR3=0x58
MR4=0x2
MR5=0x1
MR6=0x5
MR7=0x0
MR8=0x1F
MR9=0x1F
MR10=0x1F
MR11=0x1F
MR12=0x1F
MR13=0x1F
MR14=0x1F
MR15=0x1F
MR16=0x1F
CS = 1
MR0=0x58
MR1=0x58
MR2=0x58
MR3=0x58
MR4=0x2
MR5=0x1
MR6=0x5
MR7=0x0
MR8=0x1F
MR9=0x1F
MR10=0x1F
MR11=0x1F
MR12=0x1F
MR13=0x1F
MR14=0x1F
MR15=0x1F
MR16=0x1F
Bus Width=32 Col=10 Bank=8 Row=15/15 CS=2 Die Bus-Width=32 Size=2048MB
Channel 1: LPDDR3, 800MHz
CS = 0
MR0=0x58
MR1=0x58
MR2=0x58
MR3=0x58
MR4=0x2
MR5=0x1
MR6=0x5
MR7=0x0
MR8=0x1F
MR9=0x1F
MR10=0x1F
MR11=0x1F
MR12=0x1F
MR13=0x1F
MR14=0x1F
MR15=0x1F
MR16=0x1F
CS = 1
MR0=0x58
MR1=0x58
MR2=0x58
MR3=0x58
MR4=0x2
MR5=0x1
MR6=0x5
MR7=0x0
MR8=0x1F
MR9=0x1F
MR10=0x1F
MR11=0x1F
MR12=0x1F
MR13=0x1F
MR14=0x1F
MR15=0x1F
MR16=0x1F
Bus Width=32 Col=10 Bank=8 Row=15/15 CS=2 Die Bus-Width=32 Size=2048MB
256B stride
ch 0 ddrconfig = 0x101, ddrsize = 0x2020
ch 1 ddrconfig = 0x101, ddrsize = 0x2020
pmugrf_os_reg[2] = 0x3AA0DAA0, stride = 0xD
OUT
Boot1 Release Time: Jun 2 2020 15:02:17, version: 1.26
CPUId = 0x0
SdmmcInit=2 0
BootCapSize=100000
UserCapSize=14910MB
FwPartOffset=2000 , 100000
UsbBoot ...73858
powerOn 86071
使用wl
命令烧写镜像到目标机器的eMMC
,需要注意的是访问DDR
所需的所有其他命令都应在使用db
命令之后才能使用;
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40 idbloader.img
Write LBA from file (100%)
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x4000 u-boot.itb
Write LBA from file (100%)
在烧写镜像完成后使用rd
命令重启目标机器:
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool rd
Reset Device OK.
需要注意的是:如果这个时候你也烧录了内核程序,执行完rd
命令后是无法进入uboot
命令的的,这里会直接启动内核。
六、测试
6.1 串口连接
使用准备好的USB
转串口适配器和连接线(需另购),连接开发板,我使用的开发板提供了DEG_UART
的4个引脚,其使用的是RK3399
的UART2
;
引脚 | 开发板接口 | USB转串口 |
---|---|---|
1 | GND | GND |
2 | VCC 5V | VCC |
3 | UART2DBG_TX | RX |
4 | UART2DBG_RX | TX |
其电路原理图如下:
![](https://gitee.com/zyly2033/blog-pic/raw/master/RockchipRK3399-TPLSPL方式加载uboot.md.assets/1328274-20230525225434148-861994554.png)
需要注意的是:这四根线都要接上,我只接TX
、RX
串口输出数据都是乱码。
6.2 MobaXterm
这里我使用的串口调试工具是MobaXterm
,选择串口端口,设置波特率为1500000
,8位数据位,1位停止位。
![](https://gitee.com/zyly2033/blog-pic/raw/master/RockchipRK3399-TPLSPL方式加载uboot.md.assets/1328274-20230514110944091-1828310319.png)
6.3 上电
重启开发板,通过串口打印输出:
展开查看详情
U-Boot TPL 2017.09-gef1dd65-dirty #root (May 14 2023 - 12:08:25)
Channel 0: LPDDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=32 Size=2048MB
Channel 1: LPDDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=32 Size=2048MB
256B stride
Returning to boot ROM...
U-Boot SPL board init
U-Boot SPL 2017.09-gef1dd65-dirty #root (May 14 2023 - 12:08:25)
Trying to boot from MMC1
Trying fit image at 0x4000 sector
## Verified-boot: 0
## Checking atf@1 0x00040000 ... sha256(7de676d49d...) + OK
## Checking uboot 0x00200000 ... sha256(d1dc81ced6...) + OK
## Checking fdt 0x002d6d58 ... sha256(5416082316...) + OK
## Checking atf@2 0xff3b0000 ... sha256(5e781f41af...) + OK
## Checking atf@3 0xff8c0000 ... sha256(97b03d23aa...) + OK
## Checking atf@4 0xff8c1000 ... sha256(50a7da66c2...) + OK
## Checking atf@5 0xff8c2000 ... sha256(e3b0c44298...) + OK
Jumping to U-Boot(0x00200000) via ARM Trusted Firmware(0x00040000)
Total: 905.962 ms
NOTICE: BL31: v2.8(release):c194aa0
NOTICE: BL31: Built : 19:26:54, May 11 2023
U-Boot 2017.09-gef1dd65-dirty #root (May 14 2023 - 12:08:32 +0800)
Model: Rockchip RK3399 Evaluation Board
Serial: raw, 0xff1a0000
DRAM: 3.9 GiB
Sysmem: init
Relocation Offset: f5ba0000
Relocation fdt: f3d87a20 - f3d96cc8
CR: M/C/I
I2c0 speed: 400000Hz
PMIC: RK808
vdd_center init 950000 uV
Using default environment
MMC: dwmmc@fe320000: 1, sdhci@fe330000: 0
Card did not respond to voltage select!
mmc_init: -95, time 12
switch to partitions #0, OK
mmc0(part 0) is current device
Bootdev(scan): mmc 0
MMC0: HS400, 150Mhz
PartType: RKPARM
Could not find baseparameter partition
In: serial
Out: serial
Err: serial
Model: Rockchip RK3399 Evaluation Board
boot mode: None
Found DTB in resource part
Rockchip UBOOT DRM driver version: v1.0.1
AUX CH command reply failed!
AUX CH error happens: 2
AUX CH error happens: 2
failed to read link rate: -121
edp@ff970000 disconnected
CLK: (uboot. arml: enter 816000 KHz, init 816000 KHz, kernel 0N/A)
CLK: (uboot. armb: enter 816000 KHz, init 816000 KHz, kernel 0N/A)
aplll 816000 KHz
apllb 816000 KHz
dpll 800000 KHz
cpll 24000 KHz
gpll 800000 KHz
npll 600000 KHz
vpll 24000 KHz
aclk_perihp 133333 KHz
hclk_perihp 66666 KHz
pclk_perihp 33333 KHz
aclk_perilp0 266666 KHz
hclk_perilp0 88888 KHz
pclk_perilp0 44444 KHz
hclk_perilp1 100000 KHz
pclk_perilp1 50000 KHz
Net: eth0: ethernet@fe300000
### main_loop entered: bootdelay=5
### main_loop: bootcmd="boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;"
Hit key to stop autoboot('CTRL+C'): 0
=>
在倒计时执行完之前,按CTRL+C
即可进入uboot
命令行。
需要注意的是:有时候按CTRL+C
并没有进入uboot
命令行,可能串口有问题,没有接收到输入内容么?试着将串口重新连接,切换端口号试试。如果实在不行,可以尝试修改common/autoboot.c
函数__abortboot
,将if (ctrlc())
修改为if (tstc())
,这样按下任何键都可以进入uboot
命令行。或者干脆函数返回1。
6.3.1 查看环境变量
在命令行输入print
,查看所有环境变量:
展开查看详情
=> print
arch=arm
autoload=no
baudrate=150000
board=evb_rk3399
board_name=evb_rk3399
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootaa64.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_net_usb_start=usb start
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc1 mmc0 usb0 pxe dhcp
bootargs= root=/dev/mmcblk1p7 rw rootfstype=ext4 data=/dev/mmcblk1p8 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1 mtdparts=rk29xxnand:0x00002000@0x00002000(uboot),0x00002000@0x00004000(trust),0x00002000@0x00006000(misc),0x00006000@0x00008000(resource),0x00010000@0x0000e000(kernel),0x00010000@0x0001e000(boot),0x00c80000@0x00030000(rootfs),-@0x00cb0000(userdata) storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal
bootcmd=boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;
bootcmd_dhcp=run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00011:UNDI:003000;setenv bootp_arch 0xb;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_usb0=setenv devnum 0; run usb_boot
bootdelay=5
cpu=armv8
cpuid#=544d533632342e303000000000050607
devnum=0
devtype=mmc
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
eth1addr=ce:ff:bf:e6:3b:7d
ethaddr=ca:ff:bf:e6:3b:7d
fdt_addr_r=0x08300000
fdtcontroladdr=0xf3d87a20
kernel_addr_r=0x00280000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
partitions=uuid_disk=${uuid_gpt_disk};name=loader1,start=32K,size=4000K,uuid=${uuid_gpt_loader1};name=loader2,start=8MB,size=4MB,uuid=${uuid_gpt_loader2};name=trust,size=4M,uuid=${uuid_gpt_atf};name=boot,size=112M,bootable,uuid=${uuid_gpt_boot};name=rootfs,size=-,uuid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE;
pxefile_addr_r=0x00600000
ramdisk_addr_r=0x0a200000
rkimg_bootdev=if mmc dev 1 && rkimgtest mmc 1; then setenv devtype mmc; setenv devnum 1; echo Boot from SDcard;elif mmc dev 0; then setenv devtype mmc; setenv devnum 0;elif mtd_blk dev 0; then setenv devtype mtd; setenv devnum 0;elif mtd_blk dev 1; then setenv devtype mtd; setenv devnum 1;elif mtd_blk dev 2; then setenv devtype mtd; setenv devnum 2;elif rknand dev 0; then setenv devtype rknand; setenv devnum 0;elif rksfc dev 0; then setenv devtype spinand; setenv devnum 0;elif rksfc dev 1; then setenv devtype spinor; setenv devnum 1;elsesetenv devtype ramdisk; setenv devnum 0;fi;
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootaa64.efi; then echo Found EFI removable media binary efi/boot/bootaa64.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x00500000
serial#=137825cca4f2db8f
soc=rockchip
stderr=serial,vidconsole
stdout=serial,vidconsole
usb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fi
vendor=rockchip
Environment size: 5132/32764 bytes
其中比较重要的环境变量有:
bootcmd
:通过CONFIG_BOOTCOMMAND
设置,用来启动内核的命令;bootdelay
:通过CONFIG_BOOTDELAY
设置,uboot
启动倒计时,默认值为5s
,只有设置了bootcmd
,它才有用;baudrate
:通过CONFIG_BAUDRATE
设置,波特率默认为1500000
;ipaddr
:通过CONFIG_IPADDR
设置,IP
地址;可以不设置,使用dhcp
命令来从路由器获取IP
地址;serverip
:通过CONFIG_SERVERIP
设置,服务器IP
地址;也就是ubuntu
主机IP
地址,用于调试代码;netmask
:通过CONFIG_NETMASK
设置,子网掩码;gatewayip
:通过CONFIG_GATEWAYIP
设置,网关地址;ethaddr
:通过CONFIG_ETHADDR
设置,网卡地址; 如果设置了CONFIG_NET_RANDOM_ETHADDR
此宏的话就会随机分配网卡物理地址;bootargs
:通过CONFIG_BOOTARGS
设置,启动参数;
这些配置信息大部分配置在include/configs/evb_rk3399.h
、include/configs/rk3399_common.h
、include/configs/rockchip-common.h
、.config
文件中。
6.3.2 内核启动命令
这里我们重点看一下启动内核的命令:
bootcmd=boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;
bootcmd_dhcp=run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00011:UNDI:003000;setenv bootp_arch 0xb;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_usb0=setenv devnum 0; run usb_boot
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
咦,看到这里是不是很奇怪,我记得我们在学习Mini2440
的时候,bootcmd
配置的很简单:
nand read 0x30000000 kernel; bootm 0x30000000
只是把uImage从Nand Flash
内核分区加载到内存,然后直接bootm <Legacy uImage addr>
,即可启动内核。这里咋搞了一大堆启动相关的命令呢?
实际上Rockchip
为了支持从各种外部设备启动,同时也为了支持各种启动镜像格式,采用了《Distro Bootcmd
启动机制》。更多细节可以参考《树莓派4B(rpi4b)
引导ubuntu
分析.md》。
6.3.3 查看板子信息
在命令行输入bdinfo
,查看板子信息;
=> bdinfo
arch_number = 0x00000000
boot_params = 0x00000000
DRAM bank = 0x00000000
-> start = 0x00200000
-> size = 0xF7E00000
baudrate = 115200 bps
TLB addr = 0xF7FF0000
relocaddr = 0xF5DA0000
reloc off = 0xF5BA0000
irq_sp = 0xF3D87A10
sp start = 0xF3D87A10
FB base = 0x00000000
Early malloc usage: 14b0 / 4000
fdt_blob = 00000000f3d87a20
6.3.4 查看版本信息
在命令行输入version
,查看板子信息;
=> version
U-Boot 2017.09-gef1dd65-dirty #root (May 14 2023 - 12:08:32 +0800)
arm-linux-gcc (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205
GNU ld (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 2.39.0.20221210
6.4 设置环境变量
通常环境变量是存放在外部存储设备中,uboot
启动的时候会将环境变量读取DDR
中。所以使用命令setenv
修改的DDR
中的环境变量值,修改以后要使用save
命令将修改后的环境变量保存到eMMC
中,否则的话uboot
下一次重启会继续使用以前的环境变量值。
6.4.1 设置ip
我们设置以下环境变量:
=> set ipaddr 192.168.0.105
Unknown command 'set' - try 'help'
=> setenv ipaddr 192.168.0.105
=> setenv serverip 192.168.0.200
=> setenv netmask 255.255.255.0
=> setenv gatewayip 192.168.0.1
这里192.168.0.200
是我主机ubuntu
的ip
地址,而192.168.0.105
是我为开发板设置的ip
地址。
6.4.2 保存
进行保存:
=> saveenv
Saving Environment to nowhere...
看这意思,应该是我们没有设置环境变量的保存位置,参考之前写的这篇文章:《设置环境变量保存位置》,我们执行make menuconfig
,配置:
Environment --->
Select the location of the environment (Environment in an MMC device) --->
(X) Environment in an MMC device
需要注意的是环境变量默认存储在eMMC
地址0x3f8000
,大小:0x8000
;需要注意的是不要覆盖了其他分区。
CONFIG_ENV_SIZE=0x8000
CONFIG_ENV_OFFSET=0x3f8000
按照之前的步骤,重新编译,烧录程序即可。再次设置环境变量,保存:
=> setenv ipaddr 192.168.0.105
=> setenv serverip 192.168.0.200
=> setenv netmask 255.255.255.0
=> setenv gatewayip 192.168.0.1
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
如果断电重启或者通过reset
命令重启,发现环境变量没有保存成功的话。可以尝试修改common/board_r.c
文件中init_sequence_r
数组,将initr_mmc
方法放到initr_env
方法之前:
#ifdef CONFIG_MMC
initr_mmc,
#endif
#ifndef CONFIG_USING_KERNEL_DTB
/* init before storage(for: devtype, devnum, ...) */
initr_env,
#endif
6.4.3 测试
接着我们测试一下开发板网络是否能用,这里我们直接ping
服务器地址:
=> ping 192.168.0.200
ethernet@fe300000 Waiting for PHY auto negotiation to complete. done
Speed: 100, full duplex
Using ethernet@fe300000 device
host 192.168.0.200 is alive
我们通过tftp
服务器向开发板内存写入数据测试,比如我们向内存0x30001000
写入一个s3c2440-smdk2440.dtb
文件:
=> tftp 0x30001000 s3c2440-smdk2440.dtb
ethernet@fe300000 Waiting for PHY auto negotiation to complete. done
Speed: 100, full duplex
Using ethernet@fe300000 device
TFTP from server 192.168.0.200; our IP address is 192.168.0.105
Filename 's3c2440-smdk2440.dtb'.
Load address: 0x30001000
Loading: #
1.4 MiB/s
done
Bytes transferred = 7328 (1ca0 hex)
当然如果你有内核镜像,可以直接烧录内核,并通过bootm
命令启动内核。
6.5 mmc
操作指令
6.5.1 mmc list
在命令行输入mmc list
命令用于来查看当前开发板一共有几个MMC
设备:
=> mmc list
dwmmc@fe320000: 1
sdhci@fe330000: 0 (eMMC)
我们看到既有dwmmc
,也有sdhci
,在我使用的开发板中,设备树描述信息中:dwmmc
代表 SD/MMC
、sdhci
代表 eMMC
,无论是SD还
是eMMC
都是MMC
的一种。后面的数字代表设备号,比如sdhci
设备号为0。
6.5.2 mmc info
默认会将eMMC
设置为当前设备,如果需要查看eMMC
信息,运行如下命令:
=> mmc info
Device: sdhci@fe330000
Manufacturer ID: 15
OEM: 100
Name: AJNB4
Timing Interface: HS400
Tran Speed: 150000000
Rd Block Len: 512
MMC version 5.1
High Capacity: Yes
Capacity: 14.6 GiB
Bus Width: 8-bit DDR
Erase Group Size: 512 KiB
HC WP Group Size: 8 MiB
User Capacity: 14.6 GiB WRREL
Boot Capacity: 4 MiB ENH
RPMB Capacity: 4 MiB ENH
从上图中可以看到MMC
设备版本为5.1
, 14.6GiB
(eMMC
为16GB
),速度为150000000Hz=150MHz
, 8 位宽的总线。
6.5.3 mmc dev
如果我们需要切换到SD
,则可以通过如下命令:
mmc dev [dev] [part]
[dev
] 用来设置要切换的MMC
设备号, [part
]是分区号。如果不写分区号的话默认为分区0。
使用如下命令切换到SD
卡:
=> mmc dev 1
CMD_SEND:0
switch to partitions #0, OK
mmc1 is current device
从上图可以看出,切换到SD
卡成功, mmc1
为当前的MMC
设备,输入命令mmc info
即可查看SD
卡的信息,结果如图下所示:
=> mmc info
Device: dwmmc@fe320000
Manufacturer ID: 9f
OEM: 5449
Name: 00000
Timing Interface: Legacy
Tran Speed: 52000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 14.9 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes
从上图可以看出SD
卡版本为3.0
, 容量为14.9GiB
,速度为52000000Hz=52MHz
, 4位宽的总线。
6.5.4 mmc rescan
mmc rescan
命令用于扫描当前开发板上所有的MMC
设备,包括eMMC
和SD
卡,输入mmc rescan
即可。
6.5.5 mmc part
有时候SD
卡或者eMMC
会有多个分区,可以使用命令mmc part
来查看其分区,比如查看eMMC
的分区情况,输入如下命令:
=> mmc dev 0
switch to partitions #0, OK
mmc0(part 0) is current device
=> mmc part
Partition Map for MMC device 0 -- Partition Type: EFI
Part Start LBA End LBA Name
Attributes
Type GUID
Partition GUID
1 0x00000040 0x00001f7f "loader1"
attrs: 0x0000000000000000
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
guid: 8ef917d1-5bd0-2c4f-9a33-b225b21cf919
2 0x00004000 0x00005fff "loader2"
attrs: 0x0000000000000000
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
guid: 77877125-2374-ad40-8b02-609e37971c59
3 0x00006000 0x00007fff "trust"
attrs: 0x0000000000000000
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
guid: b4b84b8a-f8ae-0443-b5af-3605b195c4c9
4 0x00008000 0x0003ffff "boot"
attrs: 0x0000000000000004
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
guid: 35219908-db08-c643-9133-2213191e57ff
5 0x00040000 0x01d1efde "rootfs"
attrs: 0x0000000000000000
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
guid: b921b045-1df0-41c3-af44-4c6f280d3fae
从上图中可以看出,此时eMMC
有5个分区:
- 扇区
0x00000040 ~ 0x00001f7f
为第一个分区;存放的是idbloader.img
; - 扇区
0x00004000 ~ 0x00005fff
为第二个分区;存放的是u-boot.itb
; - 扇区
0x00006000~ 0x00007fff
为第三个分区;存放的是trust.img
; - 扇区
0x00008000~ 0x0003ffff
为第四个分区;存放的是boot.img
; - 扇区
0x00040000~ 0x01d1efde
为第五个分区;存放的是rootfs.img
;
6.5.6 mmc read
mmc read
命令用于读取mmc
设备的数据,命令格式如下:
mmc read addr blk# cnt
addr
是数据读取到DDR
中的地址, blk
是要读取的块起始地址,一个块是512
字节,这里的块和扇区是一个意思,在MMC
设备中我们通常说扇区,cnt
是要读取的块数量(注意这里默认是16进制)。
比如从eMMC
的第0x8000
个扇区开始,读取1个扇区的数据到DRAM
的0x10000000
地址处,命令如下:
=> mmc read 0x10000000 0x8000 1
MMC read: dev # 0, block # 32768, count 1 ...
1 blocks read: OK
6.5.7 mmc write
要将数据写到MMC
设备里面,可以使用命令mmc write
,格式如下:
mmc write addr blk# cnt
addr
是要写入MMC
中的数据在DDR
中的起始地址, blk
是要写入MMC
的块起始地址,cnt
是要写入的块大小(注意这里默认是16
进制),一个块为512
字节。
我们可以使用命令mmc write
来升级uboot
,也就是在uboot
中更新uboot
。这里要用到tftp
命令,通过tftp
命令将新的u-boot.itb
下载到开发板的DDR
中,然后再使用命令mmc write
将其写入到MMC
设备中。
我们就来更新一下eMMC
中的uboot
,输入命令:
=> setenv ipaddr 192.168.0.105
=> setenv serverip 192.168.0.200
=> tftp 0x10000000 u-boot.itb
=> mmc erase 0x4000 0x2000
=> mmc write 0x10000000 0x4000 0x2000
=> mmc read 0x10000000 0x4000 1
MMC read: dev # 0, block # 16384, count 1 ... 1 blocks read: OK
=> md.b 0x10000000 0x40 # 查看写入的数据
10000000: d0 0d fe ed 00 00 0a 39 00 00 00 38 00 00 09 68 .......9...8...h
10000010: 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 ...(............
10000020: 00 00 00 d1 00 00 09 30 00 00 00 00 00 00 00 00 .......0........
10000030: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
6.5.8 mmc erase
如果要擦除MMC
设备的指定块就是用命令mmc erase
,命令格式如下:
mmc erase blk# cnt
七、uboot
编译错误处理
7.1 -Werror
由于Makefile
在编译的时候配置 -Werror
,将所有warning
视为error
,直接搜索代码:
root@ubuntu:/work/sambashare/rk3399/u-boot# grep "\-Werror" * -nR
Makefile:363:KBUILD_CFLAGS += -fshort-wchar -Werror
Makefile:618:KBUILD_CFLAGS += $(call cc-option,-Werror=date-time)
scripts/Kbuild.include:117: $(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))
scripts/Kbuild.include:122: $(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",y,n)
scripts/gcc-stack-usage.sh:10:cat <<END | $@ -Werror -fstack-usage -x c - -c -o $TMP >/dev/null 2>&1 \
tools/buildman/builderthread.py:429: # We could avoid this by using -Werror everywhere...
KBUILD_CFLAGS
是CC
编译选项:
KBUILD_CFLAGS += $(call cc-option,-Werror=date-time) # 618行
在scripts/Kbuild.include
定义了cc-option
函数,函数用于检测$(CC)
是否支持给定的选项。比如注释中的例子的意思:如果交叉编译工具$(CC)
支持cc-optionl
函数的参数一表示的选项(也就是指-marm
),那么cc-option
函数的返回就是该选项(指-marm
),否则返回的是call
函数的参数二表示的选项。
# cc-option
# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
cc-option = $(call try-run,\
$(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))
由于编译选项配置了-Werror
,因此在编译源代码的时候,会将所有warning
视为error
。
因此我们可以通过修改Makefile
、以及scripts/Kbuild.include
文件 ,将-Werror
修改为-Wno-error
从而达到禁用-Werror
的目的。
关闭 -Werror
可能会导致程序中隐藏的问题,因此建议在调试和测试期间使用-Werror
来确保代码质量。一旦您已经验证了程序的正确性,就可以将其关闭,并在开发过程中保持高水平的代码质量。
八、 脚本方式
由于我们每次修改程序后,重新编译步骤比较麻烦,这里我们可以将这步骤编写成一个shell
脚本,这样每次执行就比较容易了。
8.1 自动构建脚本
在uboot
根目录下创建一个build.sh
文件:
#!/bin/bash
# 接收第一个参数 支持 ''|'config'|'clean'
step=$1
# 接收 V=1 支持编译输出详细信息
V=$2
cmd=${V%=*}
if [[ ${cmd} = 'V' ]]; then
V=${V#*=}
fi
if [[ ${step} == "config" ]];then
echo '----------------config evb-rk3399_defconfig------- -------------'
make evb-rk3399_defconfig V=${V}
fi
if [[ -z ${step} ]];then
echo "---------------1. compile uboot-------------------------------- "
make ARCH=arm CROSS_COMPILE=arm-linux- V=${V}
echo "---------------2. mkimahe idbloader----------------------------"
tools/mkimage -n rk3399 -T rksd -d tpl/u-boot-tpl.bin idbloader.img
cat spl/u-boot-spl.bin >> idbloader.img
echo "---------------3. make itb-------------------------------------"
make u-boot.itb ARCH=arm CROSS_COMPILE=arm-linux-
cp ./u-boot.itb ../rkdeveloptool
cp ./idbloader.img ../rkdeveloptool
fi
if [[ ${step} == "clean" ]];then
echo "-----------------clean-----------------------------------------"
make clean
make distclean
fi
然后给文件赋予可执行权限:
root@ubuntu:/work/sambashare/rk3399/u-boot# chmod +x build.sh
8.1.1 clean
执行如下命令进行清理:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./build.sh clean
8.1.2 配置
执行如下命令进行uboot
配置:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./build.sh config
----------------config evb-rk3399_defconfig------- -------------
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
8.1.2 构建
执行如下命令进行uboot
编译、生成idbloader.img
、 u-boot.itb
文件,并拷贝到rkdeveloptool
目录下:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./build.sh
如果需要输出编译详情信息,追加V=1
参数即可。
8.2 官方构建脚本
实际上u-boot
文件夹下有个make.sh
,这是官方提供的一个自动构建的脚本,这个脚本代码比较多,毕竟官方考虑的比较全嘛,这里我们先不去解读,等后面有时间了再来解读。
展开查看详情
#!/bin/bash
#
# Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
#
# SPDX-License-Identifier: GPL-2.0
#
set -e
JOB=`sed -n "N;/processor/p" /proc/cpuinfo|wc -l`
SUPPORT_LIST=`ls configs/*[r,p][x,v,k][0-9][0-9]*_defconfig`
CMD_ARGS=$1
########################################### User can modify #############################################
RKBIN_TOOLS=../rkbin/tools
CROSS_COMPILE_ARM32=../prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
CROSS_COMPILE_ARM64=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
########################################### User not touch #############################################
# Declare global INI file searching index name for every chip, update in select_chip_info()
RKCHIP=
RKCHIP_LABEL=
RKCHIP_LOADER=
RKCHIP_TRUST=
INI_TRUST=
INI_LOADER=
# Declare rkbin repository path, updated in prepare()
RKBIN=
# Declare global toolchain path for CROSS_COMPILE, updated in select_toolchain()
TOOLCHAIN=
TOOLCHAIN_NM=
TOOLCHAIN_OBJDUMP=
TOOLCHAIN_ADDR2LINE=
# Declare global plaform configure, updated in fixup_platform_configure()
PLAT_RSA=
PLAT_SHA=
PLAT_UBOOT_SIZE=
PLAT_TRUST_SIZE=
PLAT_TYPE="RKFW" # default
SRCTREE=`pwd`
SCRIPT_FIT="${SRCTREE}/scripts/fit.sh"
SCRIPT_ATF="${SRCTREE}/scripts/atf.sh"
SCRIPT_TOS="${SRCTREE}/scripts/tos.sh"
SCRIPT_SPL="${SRCTREE}/scripts/spl.sh"
SCRIPT_UBOOT="${SRCTREE}/scripts/uboot.sh"
SCRIPT_LOADER="${SRCTREE}/scripts/loader.sh"
SCRIPT_DECOMP="${SRCTREE}/scripts/decomp.sh"
CC_FILE=".cc"
REP_DIR="./rep"
#########################################################################################################
function help()
{
echo
echo "Usage:"
echo " ./make.sh [board|sub-command]"
echo
echo " - board: board name of defconfig"
echo " - sub-command: elf*|loader|trust|uboot|--spl|--tpl|itb|map|sym|<addr>"
echo " - ini: ini file to pack trust/loader"
echo
echo "Output:"
echo " When board built okay, there are uboot/trust/loader images in current directory"
echo
echo "Example:"
echo
echo "1. Build:"
echo " ./make.sh evb-rk3399 --- build for evb-rk3399_defconfig"
echo " ./make.sh firefly-rk3288 --- build for firefly-rk3288_defconfig"
echo " ./make.sh EXT_DTB=rk-kernel.dtb --- build with exist .config and external dtb"
echo " ./make.sh --- build with exist .config"
echo " ./make.sh env --- build envtools"
echo
echo "2. Pack:"
echo " ./make.sh uboot --- pack uboot.img"
echo " ./make.sh trust --- pack trust.img"
echo " ./make.sh trust <ini> --- pack trust img with assigned ini file"
echo " ./make.sh loader --- pack loader bin"
echo " ./make.sh loader <ini> --- pack loader img with assigned ini file"
echo " ./make.sh --spl --- pack loader with u-boot-spl.bin"
echo " ./make.sh --tpl --- pack loader with u-boot-tpl.bin"
echo " ./make.sh --tpl --spl --- pack loader with u-boot-tpl.bin and u-boot-spl.bin"
echo
echo "3. Debug:"
echo " ./make.sh elf --- dump elf file with -D(default)"
echo " ./make.sh elf-S --- dump elf file with -S"
echo " ./make.sh elf-d --- dump elf file with -d"
echo " ./make.sh elf-* --- dump elf file with -*"
echo " ./make.sh <no reloc_addr> --- unwind address(no relocated)"
echo " ./make.sh <reloc_addr-reloc_off> --- unwind address(relocated)"
echo " ./make.sh map --- cat u-boot.map"
echo " ./make.sh sym --- cat u-boot.sym"
}
function filt_val()
{
sed -n "/${1}=/s/${1}=//p" $2 | tr -d '\r' | tr -d '"'
}
function prepare()
{
if [ -d ${RKBIN_TOOLS} ]; then
absolute_path=$(cd `dirname ${RKBIN_TOOLS}`; pwd)
RKBIN=${absolute_path}
else
echo "ERROR: No ../rkbin repository"
exit 1
fi
if grep -Eq ''^CONFIG_ARM64=y'|'^CONFIG_ARM64_BOOT_AARCH32=y'' .config ; then
ARM64_TRUSTZONE="y"
fi
if grep -q '^CONFIG_ROCKCHIP_FIT_IMAGE_PACK=y' .config ; then
PLAT_TYPE="FIT"
elif grep -q '^CONFIG_SPL_DECOMP_HEADER=y' .config ; then
PLAT_TYPE="DECOMP"
fi
}
function process_args()
{
while [ $# -gt 0 ]; do
case $1 in
*help|--h|-h)
help
exit 0
;;
CROSS_COMPILE=*) # set CROSS_COMPILE
ARG_COMPILE="y"
CROSS_COMPILE_ARM32=${1#*=}
CROSS_COMPILE_ARM64=${1#*=}
if [ ${CMD_ARGS} == $1 ]; then
shift 1
CMD_ARGS=$1
else
shift 1
fi
;;
# '': build with exist .config
# loader|trust|uboot: pack image
# debug*|map|sym|elf*|nm: debug command
# env: build env tool
# itb: pack itb image
# fit: pack non-secure uboot.img && boot.img
''|loader|trust|uboot|debug*|itb|env|fit|map|sym|elf*|nm)
if [ "$2" == "spl" -o "$2" == "tpl" ]; then
ARG_TSPL=$2
shift 1
fi
shift 1
;;
# trust/loader ini files for packing trust.img/loader.img
*.ini|*.INI)
if [ ! -f $1 ]; then
echo "ERROR: No $1"
fi
if grep -q 'CODE471_OPTION' $1 ; then
ARG_INI_LOADER=$1
elif grep -Eq ''BL31_OPTION'|'TOS'' $1 ; then
ARG_INI_TRUST=$1
fi
shift 1
;;
--sz-trust) # set trust size
ARG_TRUST_SIZE="--size $2 $3"
shift 3
;;
--sz-uboot) # set uboot size
ARG_UBOOT_SIZE="--size $2 $3"
shift 3
;;
--raw-compile) # FIT: build but not pack image
ARG_RAW_COMPILE="y"
shift 1
;;
--no-uboot) # FIT: pack uboot.img without u-boot
ARG_NO_UBOOT="y"
shift 1
;;
--idblock) # pack idblock.bin
shift 1
;;
--tpl|tpl) # use tpl file
ARG_TPL_BIN="tpl/u-boot-tpl.bin"
shift 1
;;
--spl|spl*) # use spl file
ARG_SPL_BIN="spl/u-boot-spl.bin"
shift 1
;;
--uboot|--fdt|--optee|--mcu|--bl31) # uboot.img components
mkdir -p ${REP_DIR}
if [ ! -f $2 ]; then
echo "ERROR: No $2"
exit 1
fi
if [ "$1" == "--uboot" ]; then
cp $2 ${REP_DIR}/u-boot-nodtb.bin
elif [ "$1" == "--fdt" ]; then
cp $2 ${REP_DIR}/u-boot.dtb
elif [ "$1" == "--optee" ]; then
cp $2 ${REP_DIR}/tee.bin
elif [ "$1" == "--mcu" ]; then
cp $2 ${REP_DIR}/mcu.bin
elif [ "$1" == "--bl31" ]; then
if ! file $2 | grep 'ELF ' >/dev/null 2>&1 ; then
echo "ERROR: $2 is not a bl31.elf file"
exit 1
fi
cp $2 ${REP_DIR}/bl31.elf
fi
shift 2
;;
*)
#1. FIT scripts args
NUM=$(${SCRIPT_FIT} --args $1)
if [ ${NUM} -ne 0 ]; then
[ ${NUM} -eq 1 ] && ARG_LIST_FIT="${ARG_LIST_FIT} $1"
[ ${NUM} -eq 2 ] && ARG_LIST_FIT="${ARG_LIST_FIT} $1 $2"
shift ${NUM}
continue
#2. unwind function address
elif [ -z $(echo $1 | sed 's/[0-9,a-f,A-F,x,X,-]//g') ]; then
ARG_FUNCADDR=$1
#3. make defconfig
else
ARG_BOARD=$1
if [ ! -f configs/${ARG_BOARD}_defconfig -a ! -f configs/${ARG_BOARD}.config ]; then
echo -e "\n${SUPPORT_LIST}\n"
echo "ERROR: No configs/${ARG_BOARD}_defconfig"
exit 1
elif [ -f configs/${ARG_BOARD}.config ]; then
BASE1_DEFCONFIG=`filt_val "CONFIG_BASE_DEFCONFIG" configs/${ARG_BOARD}.config`
BASE0_DEFCONFIG=`filt_val "CONFIG_BASE_DEFCONFIG" configs/${BASE1_DEFCONFIG}`
MAKE_CMD="make ${BASE0_DEFCONFIG} ${BASE1_DEFCONFIG} ${ARG_BOARD}.config -j${JOB}"
echo "## ${MAKE_CMD}"
make ${BASE0_DEFCONFIG} ${BASE1_DEFCONFIG} ${ARG_BOARD}.config ${OPTION}
rm -f ${CC_FILE}
else
MAKE_CMD="make ${ARG_BOARD}_defconfig -j${JOB}"
echo "## ${MAKE_CMD}"
make ${ARG_BOARD}_defconfig ${OPTION}
rm -f ${CC_FILE}
fi
fi
shift 1
;;
esac
done
if [ ! -f .config ]; then
echo
echo "ERROR: No .config"
help
exit 1
fi
}
function select_toolchain()
{
# If no outer CROSS_COMPILE, look for it from CC_FILE.
if [ "${ARG_COMPILE}" != "y" ]; then
if [ -f ${CC_FILE} ]; then
CROSS_COMPILE_ARM32=`cat ${CC_FILE}`
CROSS_COMPILE_ARM64=`cat ${CC_FILE}`
else
if grep -q '^CONFIG_ARM64=y' .config ; then
CROSS_COMPILE_ARM64=$(cd `dirname ${CROSS_COMPILE_ARM64}`; pwd)"/aarch64-linux-gnu-"
else
CROSS_COMPILE_ARM32=$(cd `dirname ${CROSS_COMPILE_ARM32}`; pwd)"/arm-linux-gnueabihf-"
fi
fi
fi
if grep -q '^CONFIG_ARM64=y' .config ; then
TOOLCHAIN=${CROSS_COMPILE_ARM64}
TOOLCHAIN_NM=${CROSS_COMPILE_ARM64}nm
TOOLCHAIN_OBJDUMP=${CROSS_COMPILE_ARM64}objdump
TOOLCHAIN_ADDR2LINE=${CROSS_COMPILE_ARM64}addr2line
else
TOOLCHAIN=${CROSS_COMPILE_ARM32}
TOOLCHAIN_NM=${CROSS_COMPILE_ARM32}nm
TOOLCHAIN_OBJDUMP=${CROSS_COMPILE_ARM32}objdump
TOOLCHAIN_ADDR2LINE=${CROSS_COMPILE_ARM32}addr2line
fi
if [ ! `which ${TOOLCHAIN}gcc` ]; then
echo "ERROR: No find ${TOOLCHAIN}gcc"
exit 1
fi
# save to CC_FILE
if [ "${ARG_COMPILE}" == "y" ]; then
echo "${TOOLCHAIN}" > ${CC_FILE}
fi
}
#
# We select chip info to do:
# 1. RKCHIP: fixup platform configure
# 2. RKCHIP_LOADER: search ini file to pack loader
# 3. RKCHIP_TRUST: search ini file to pack trust
# 4. RKCHIP_LABEL: show build message
#
function select_chip_info()
{
# Read RKCHIP firstly from .config
# The regular expression that matching:
# - PX30, PX3SE
# - RK????, RK????X
# - RV????
CHIP_PATTERN='^CONFIG_ROCKCHIP_[R,P][X,V,K][0-9ESX]{1,5}'
RKCHIP=`egrep -o ${CHIP_PATTERN} .config`
# default
RKCHIP=${RKCHIP##*_}
RKCHIP_LOADER=${RKCHIP}
RKCHIP_TRUST=${RKCHIP}
RKCHIP_LABEL=`filt_val "CONFIG_CHIP_NAME" .config`
if [ -z "${RKCHIP_LABEL}" ]; then
RKCHIP_LABEL=${RKCHIP}
fi
}
# Priority: default < CHIP_CFG_FIXUP_TABLE() < make.sh args
function fixup_platform_configure()
{
U_KB=`filt_val "CONFIG_UBOOT_SIZE_KB" .config`
U_NUM=`filt_val "CONFIG_UBOOT_NUM" .config`
T_KB=`filt_val "CONFIG_TRUST_SIZE_KB" .config`
T_NUM=`filt_val "CONFIG_TRUST_NUM" .config`
SHA=`filt_val "CONFIG_TRUST_SHA_MODE" .config`
RSA=`filt_val "CONFIG_TRUST_RSA_MODE" .config`
# .config
PLAT_UBOOT_SIZE="--size ${U_KB} ${U_NUM}"
PLAT_TRUST_SIZE="--size ${T_KB} ${T_NUM}"
PLAT_SHA="--sha ${SHA}"
PLAT_RSA="--rsa ${RSA}"
# ./make.sh args
if [ ! -z "${ARG_UBOOT_SIZE}" ]; then
PLAT_UBOOT_SIZE=${ARG_UBOOT_SIZE}
fi
if [ ! -z "${ARG_TRUST_SIZE}" ]; then
PLAT_TRUST_SIZE=${ARG_TRUST_SIZE}
fi
}
function select_ini_file()
{
# default
INI_LOADER=${RKBIN}/RKBOOT/${RKCHIP_LOADER}MINIALL.ini
if [ "${ARM64_TRUSTZONE}" == "y" ]; then
INI_TRUST=${RKBIN}/RKTRUST/${RKCHIP_TRUST}TRUST.ini
else
INI_TRUST=${RKBIN}/RKTRUST/${RKCHIP_TRUST}TOS.ini
fi
# defconfig
NAME=`filt_val "CONFIG_LOADER_INI" .config`
if [ ! -z "${NAME}" ]; then
INI_LOADER=${RKBIN}/RKBOOT/${NAME}
fi
NAME=`filt_val "CONFIG_TRUST_INI" .config`
if [ ! -z "${NAME}" ]; then
INI_TRUST=${RKBIN}/RKTRUST/${NAME}
fi
# args
if [ ! -z "${ARG_INI_TRUST}" ]; then
INI_TRUST=${ARG_INI_TRUST}
fi
if [ ! -z "${ARG_INI_LOADER}" ]; then
INI_LOADER=${ARG_INI_LOADER}
fi
}
function sub_commands()
{
# skip "--" parameter, such as "--rollback-index-..."
if [[ ${CMD_ARGS} != --* ]]; then
CMD=${CMD_ARGS%-*}
ARG=${CMD_ARGS#*-}
else
CMD=${CMD_ARGS}
fi
if [ "${ARG_TSPL}" == "tpl" -o "${ARG_TSPL}" == "spl" ]; then
ELF=`find -name u-boot-${ARG_TSPL}`
MAP=`find -name u-boot-${ARG_TSPL}.map`
SYM=`find -name u-boot-${ARG_TSPL}.sym`
else
ELF=u-boot
MAP=u-boot.map
SYM=u-boot.sym
fi
case ${CMD} in
elf|nm)
if [ "${CMD}" == "nm" ]; then
echo -e "\n${ELF}: file format elf\n"
${TOOLCHAIN_NM} -r --size ${ELF} | grep -iv 'b' | less
else
if [ "${CMD}" == "elf" -a "${ARG}" == "elf" ]; then
ARG=D # default
fi
${TOOLCHAIN_OBJDUMP} -${ARG} ${ELF} | less
fi
exit 0
;;
map|sym)
if [ ${CMD} == "map" ]; then
cat ${MAP} | less
else
cat ${SYM} | less
fi
exit 0
;;
debug)
./scripts/rkpatch.sh ${ARG}
exit 0
;;
fit)
# Non-secure
${SCRIPT_FIT} --boot_img_dir images/ ${ARG_LIST_FIT}
exit 0
;;
uboot)
pack_uboot_image
exit 0
;;
trust)
pack_trust_image
exit 0
;;
loader)
pack_loader_image
exit 0
;;
itb)
pack_uboot_itb_image
exit 0
;;
env)
make CROSS_COMPILE=${TOOLCHAIN} envtools
exit 0
;;
--idblock)
pack_idblock
exit 0
;;
--tpl|--spl|tpl|spl)
pack_spl_loader_image
exit 0
;;
*)
unwind_addr_or_continue
;;
esac
}
function unwind_addr_or_continue()
{
FUNCADDR=${ARG_FUNCADDR}
RELOCOFF=${FUNCADDR#*-}
FUNCADDR=${FUNCADDR%-*}
if [ -z $(echo ${FUNCADDR} | sed 's/[0-9,a-f,A-F,x,X,-]//g') ] && [ ${FUNCADDR} ]; then
# With prefix: '0x' or '0X'
if [ `echo ${FUNCADDR} | sed -n "/0[x,X]/p" | wc -l` -ne 0 ]; then
FUNCADDR=`echo ${FUNCADDR} | awk '{ print strtonum($0) }'`
FUNCADDR=`echo "obase=16;${FUNCADDR}"|bc | tr '[A-Z]' '[a-z]'`
fi
if [ `echo ${RELOCOFF} | sed -n "/0[x,X]/p" | wc -l` -ne 0 ] && [ ${RELOCOFF} ]; then
RELOCOFF=`echo ${RELOCOFF} | awk '{ print strtonum($0) }'`
RELOCOFF=`echo "obase=16;${RELOCOFF}"|bc | tr '[A-Z]' '[a-z]'`
fi
# If reloc address is assigned, do sub
if [ "${FUNCADDR}" != "${RELOCOFF}" ]; then
# Hex -> Dec -> SUB -> Hex
FUNCADDR=`echo $((16#${FUNCADDR}))`
RELOCOFF=`echo $((16#${RELOCOFF}))`
FUNCADDR=$((FUNCADDR-RELOCOFF))
FUNCADDR=$(echo "obase=16;${FUNCADDR}"|bc | tr '[A-Z]' '[a-z]')
fi
echo
sed -n "/${FUNCADDR}/p" ${SYM}
${TOOLCHAIN_ADDR2LINE} -e ${ELF} ${FUNCADDR}
exit 0
fi
}
function pack_idblock()
{
INI=${INI_LOADER}
if [ ! -f ${INI} ]; then
echo "ERROR: No ${INI}"
exit 1
fi
# chip
COMMON_H=`grep "_common.h:" include/autoconf.mk.dep | awk -F "/" '{ printf $3 }'`
PLAT=${COMMON_H%_*}
# file
SPL_BIN=${RKBIN}/`filt_val "FlashBoot" ${INI}`
TPL_BIN=${RKBIN}/`filt_val "FlashData" ${INI}`
if [ ! -z "${ARG_SPL_BIN}" ]; then
SPL_BIN=${ARG_SPL_BIN}
fi
if [ ! -z "${ARG_TPL_BIN}" ]; then
TPL_BIN=${ARG_TPL_BIN}
fi
# pack
rm idblock.bin -f
./tools/mkimage -n ${PLAT} -T rksd -d ${TPL_BIN}:${SPL_BIN} idblock.bin
echo "Input:"
echo " ${INI}"
echo " ${TPL_BIN}"
echo " ${SPL_BIN}"
echo
echo "Pack ${PLAT} idblock.bin okay!"
echo
}
function pack_uboot_itb_image()
{
INI=${INI_TRUST}
if [ ! -f ${INI} ]; then
echo "ERROR: No ${INI}"
exit 1
fi
if [ "${ARM64_TRUSTZONE}" == "y" ]; then
BL31_ELF=`sed -n '/_bl31_/s/PATH=//p' ${INI} | tr -d '\r'`
BL32_BIN=`sed -n '/_bl32_/s/PATH=//p' ${INI} | tr -d '\r'`
rm bl31.elf tee.bin -rf
cp ${RKBIN}/${BL31_ELF} bl31.elf
if grep BL32_OPTION -A 1 ${INI} | grep SEC=1 ; then
cp ${RKBIN}/${BL32_BIN} tee.bin
TEE_OFFSET=`grep BL32_OPTION -A 3 ${INI} | grep ADDR= | awk -F "=" '{ printf $2 }' | tr -d '\r'`
TEE_ARG="-t ${TEE_OFFSET}"
fi
else
# TOS
TOS=`filt_val "TOS" ${INI}`
TOSTA=`filt_val "TOSTA" ${INI}`
if [ ! -z "${TOSTA}" ]; then
cp ${RKBIN}/${TOSTA} tee.bin
elif [ ! -z "${TOS}" ]; then
cp ${RKBIN}/${TOS} tee.bin
else
echo "WARN: No tee bin"
fi
if [ ! -z "${TOSTA}" -o ! -z "${TOS}" ]; then
TEE_OFFSET=`filt_val "ADDR" ${INI}`
if [ "${TEE_OFFSET}" == "" ]; then
TEE_OFFSET=0x8400000
fi
TEE_ARG="-t ${TEE_OFFSET}"
fi
fi
# MCUs
for ((i=0; i<5; i++))
do
MCU_BIN="mcu${i}.bin"
MCU_IDX="MCU${i}"
# compatible: use "MCU" to replace "MCU0" if "MCU" is present.
ENABLED=`awk -F"," '/MCU=/ { printf $3 }' ${INI} | tr -d ' '`
if [ ${i} -eq 0 ]; then
ENABLED=`awk -F"," '/MCU=/ { printf $3 }' ${INI} | tr -d ' '`
if [ ! -z ${ENABLED} ]; then
MCU_IDX="MCU"
fi
fi
ENABLED=`awk -F "," '/'${MCU_IDX}'=/ { printf $3 }' ${INI} | tr -d ' '`
if [ "${ENABLED}" == "enabled" -o "${ENABLED}" == "okay" ]; then
NAME=`awk -F "," '/'${MCU_IDX}'=/ { printf $1 }' ${INI} | tr -d ' ' | awk -F "=" '{ print $2 }'`
OFFS=`awk -F "," '/'${MCU_IDX}'=/ { printf $2 }' ${INI} | tr -d ' '`
cp ${RKBIN}/${NAME} ${MCU_BIN}
if [ -z ${OFFS} ]; then
echo "ERROR: No ${MCU_BIN} address in ${INI}"
exit 1
fi
MCU_ARG=${MCU_ARG}" -m${i} ${OFFS}"
fi
done
# Loadables
for ((i=0; i<5; i++))
do
LOAD_BIN="load${i}.bin"
LOAD_IDX="LOAD${i}"
ENABLED=`awk -F "," '/'${LOAD_IDX}'=/ { printf $3 }' ${INI} | tr -d ' '`
if [ "${ENABLED}" == "enabled" -o "${ENABLED}" == "okay" ]; then
NAME=`awk -F "," '/'${LOAD_IDX}'=/ { printf $1 }' ${INI} | tr -d ' ' | awk -F "=" '{ print $2 }'`
OFFS=`awk -F "," '/'${LOAD_IDX}'=/ { printf $2 }' ${INI} | tr -d ' '`
cp ${RKBIN}/${NAME} ${LOAD_BIN}
if [ -z ${OFFS} ]; then
echo "ERROR: No ${LOAD_BIN} address in ${INI}"
exit 1
fi
LOAD_ARG=${LOAD_ARG}" -l${i} ${OFFS}"
fi
done
# COMPRESSION
COMPRESSION=`awk -F"," '/COMPRESSION=/ { printf $1 }' ${INI} | tr -d ' ' | cut -c 13-`
if [ ! -z "${COMPRESSION}" -a "${COMPRESSION}" != "none" ]; then
COMPRESSION_ARG="-c ${COMPRESSION}"
fi
if [ -d ${REP_DIR} ]; then
mv ${REP_DIR}/* ./
fi
SPL_FIT_SOURCE=`filt_val "CONFIG_SPL_FIT_SOURCE" .config`
if [ ! -z ${SPL_FIT_SOURCE} ]; then
cp ${SPL_FIT_SOURCE} u-boot.its
else
SPL_FIT_GENERATOR=`filt_val "CONFIG_SPL_FIT_GENERATOR" .config`
# *.py is the legacy one.
if [[ ${SPL_FIT_GENERATOR} == *.py ]]; then
${SPL_FIT_GENERATOR} u-boot.dtb > u-boot.its
else
${SPL_FIT_GENERATOR} ${TEE_ARG} ${COMPRESSION_ARG} ${MCU_ARG} ${LOAD_ARG} > u-boot.its
fi
fi
./tools/mkimage -f u-boot.its -E u-boot.itb >/dev/null 2>&1
echo "pack u-boot.itb okay! Input: ${INI}"
echo
}
function pack_spl_loader_image()
{
rm -f *loader*.bin *download*.bin *idblock*.img
cd ${RKBIN}
DEF_PATH=${RKBIN}/`filt_val "^PATH" ${INI_LOADER}`
IDB_PATH=${RKBIN}/`filt_val "IDB_PATH" ${INI_LOADER}`
if [ ! -z "${ARG_SPL_BIN}" -a ! -z "${ARG_TPL_BIN}" ]; then
${SCRIPT_SPL} --ini ${INI_LOADER} --tpl ${SRCTREE}/${ARG_TPL_BIN} --spl ${SRCTREE}/${ARG_SPL_BIN}
elif [ ! -z "${ARG_TPL_BIN}" ]; then
${SCRIPT_SPL} --ini ${INI_LOADER} --tpl ${SRCTREE}/${ARG_TPL_BIN}
else
${SCRIPT_SPL} --ini ${INI_LOADER} --spl ${SRCTREE}/${ARG_SPL_BIN}
fi
cd -
if [ -f ${DEF_PATH} ]; then
mv ${DEF_PATH} ./
fi
if [ -f ${IDB_PATH} ]; then
mv ${IDB_PATH} ./
fi
}
function pack_uboot_image()
{
rm u-boot.img u-boot-dtb.img -f
LOAD_ADDR=`sed -n "/CONFIG_SYS_TEXT_BASE=/s/CONFIG_SYS_TEXT_BASE=//p" include/autoconf.mk|tr -d '\r'`
if [ -z "${LOAD_ADDR}" ]; then
# upstream U-Boot
LOAD_ADDR=`grep CONFIG_SYS_TEXT_BASE include/generated/autoconf.h | awk '{ print $3 }' | tr -d '\r'`
fi
if [ -z "${LOAD_ADDR}" ]; then
echo "ERROR: No CONFIG_SYS_TEXT_BASE for u-boot";
exit 1
fi
${SCRIPT_UBOOT} --load ${LOAD_ADDR} ${PLAT_UBOOT_SIZE}
}
function pack_loader_image()
{
rm -f *loader*.bin *download*.bin *idblock*.img
cd ${RKBIN}
DEF_PATH=${RKBIN}/`filt_val "^PATH" ${INI_LOADER}`
IDB_PATH=${RKBIN}/`filt_val "IDB_PATH" ${INI_LOADER}`
${SCRIPT_LOADER} --ini ${INI_LOADER}
cd -
if [ -f ${DEF_PATH} ]; then
mv ${DEF_PATH} ./
fi
if [ -f ${IDB_PATH} ]; then
mv ${IDB_PATH} ./
fi
}
function pack_trust_image()
{
DRAM_BASE=`filt_val "CONFIG_SYS_SDRAM_BASE" include/autoconf.mk`
rm trust*.img -f
cd ${RKBIN}
if [ "${ARM64_TRUSTZONE}" == "y" ]; then
${SCRIPT_ATF} --ini ${INI_TRUST} ${PLAT_SHA} ${PLAT_RSA} ${PLAT_TRUST_SIZE}
else
${SCRIPT_TOS} --ini ${INI_TRUST} --base ${DRAM_BASE} ${PLAT_TRUST_SIZE}
fi
cd -
if [ -f ${RKBIN}/trust*.img ]; then
mv ${RKBIN}/trust*.img ./
fi
}
function pack_fit_image()
{
# check host tools
if ! which dtc >/dev/null 2>&1 ; then
echo "ERROR: No 'dtc', please: apt-get install device-tree-compiler"
exit 1
elif [ "${ARM64_TRUSTZONE}" == "y" ]; then
if ! which python2 >/dev/null 2>&1 ; then
echo "ERROR: No python2"
exit 1
fi
fi
# If we don't plan to have uboot in uboot.img in case of: SPL => Trust => Kernel, creating empty files.
if [ "${ARG_NO_UBOOT}" == "y" ]; then
rm u-boot-nodtb.bin u-boot.dtb -f
touch u-boot-nodtb.bin u-boot.dtb
fi
rm uboot.img trust*.img -rf
${SCRIPT_FIT} ${ARG_LIST_FIT} --chip ${RKCHIP_LABEL}
rm ${REP_DIR} -rf
echo "pack uboot.img okay! Input: ${INI_TRUST}"
}
function handle_args_late()
{
ARG_LIST_FIT="${ARG_LIST_FIT} --ini-trust ${INI_TRUST} --ini-loader ${INI_LOADER}"
}
function clean_files()
{
rm spl/u-boot-spl.dtb tpl/u-boot-tpl.dtb u-boot.dtb -f
rm spl/u-boot-spl tpl/u-boot-tpl u-boot -f
}
function pack_images()
{
if [ "${ARG_RAW_COMPILE}" != "y" ]; then
if [ "${PLAT_TYPE}" == "FIT" ]; then
pack_fit_image ${ARG_LIST_FIT}
elif [ "${PLAT_TYPE}" == "DECOMP" ]; then
${SCRIPT_DECOMP} ${ARG_LIST_FIT} --chip ${RKCHIP_LABEL}
else
pack_uboot_image
pack_trust_image
pack_loader_image
fi
fi
}
function finish()
{
echo
if [ "${ARG_BOARD}" == "" ]; then
echo "Platform ${RKCHIP_LABEL} is build OK, with exist .config"
else
echo "Platform ${RKCHIP_LABEL} is build OK, with new .config(${MAKE_CMD})"
fi
}
process_args $*
prepare
select_toolchain
select_chip_info
fixup_platform_configure
select_ini_file
handle_args_late
sub_commands
clean_files
make PYTHON=python2 CROSS_COMPILE=${TOOLCHAIN} all --jobs=${JOB}
pack_images
finish
echo ${TOOLCHAIN}
date
在命令行输入:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./make.sh help
查看具体编译指令,如下图:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./make.sh help
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.sym
修改make.sh
设置交叉编译工具路径:
RKBIN_TOOLS=../rkbin/tools
CROSS_COMPILE_ARM32=/usr/local/arm/12.2.1/bin/arm-none-linux-gnueabihf-
CROSS_COMPILE_ARM64=/usr/local/arm/12.2.1/bin/aarch64-none-linux-gnu-
同时将select_toolchain
函数以下代码:
CROSS_COMPILE_ARM64=$(cd `dirname ${CROSS_COMPILE_ARM64}`; pwd)"/aarch64-linux-gnu-"
修改为:
CROSS_COMPILE_ARM64=$(cd `dirname ${CROSS_COMPILE_ARM64}`; pwd)"/aarch64-none-linux-gnu-"
第一次编译的时候,uboot
目录下并没有.config
配置文件,需要指定默认的配置文件。
如果编译使用的defonfig
文件为configs/evb-rk3399_defconfig
,则可以直接使用如下命令来编译:
root@ubuntu:/work/sambashare/rk3399/u-boot# ./make.sh evb-rk3399
参考文章
[1] RK3399-Linux
[2] rk3399
移植 u-boot
[6] RK3399
——裸机大全
[7] U-Boot
之一 零基础编译 U-Boot
过程详解、Image
镜像介绍及使用说明、DTB
文件使用说明
[9] 深度探索uboot
[11] Uboot 2017.01 SPL
中的image_loader