程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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官方提供的207.09版本的uboot代码下载下来,编译之后看看能不能运行,如果可以的话,再去参考Rockchip官方的uboot去移植最新版本的uboot。

这里有一点需要补充的是:Rockchip官方提供的uboot 207.09版本做了大量的改动,尤其是引导内核启动上,为了支持多种内核镜像加载方式,对uboot源码进行了大量修改,所以要求我们烧录的内核镜像也要按照官方指定的格式调整否则无法被uboot正确引导。

1.1 下载源码

我们可以在Rockchip的github上下载到芯片厂商提供的u-boot源码,如下图所示:

这里我们下载的是最新的next-dev分支的代码:

root@zhengyang:/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@zhengyang:/work/sambashare/rk3399/u-boot# cd ..
root@zhengyang:/work/sambashare/rk3399# cd u-boot/
root@zhengyang:/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
View Code

需要注意的是:尽量不要下载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
View Code

因此执行如下命令,生成.config文件:

root@zhengyang:/work/sambashare/rk3399/u-boot# make evb-rk3399_defconfig V=1

出如下(忽略编译器警告信息):

root@zhengyang:/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@zhengyang:/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@zhengyang:/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@zhengyang:/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@zhengyang:/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@zhengyang:/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-spl-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@zhengyang:/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@zhengyang:/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@zhengyang:/work/sambashare/rk3399/u-boot# cat spl/u-boot-spl.bin >> idbloader.img
root@zhengyang:/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这三个文件。在顶层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 file(DTS)的语法,生成的image文件也和dtb文件类似(称做itb)。

注意:这一小节只是补充关于FIT的相关知识,并不是编译u-boot.itb的步骤。

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) images节点

指定所要包含的二进制文件,可以指定多种类型的多个文件,例如u-boot.its中的包含了1个standalone image、5个firmware image、1个fdt image。每个文件都是images下的一个子node,例如:

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>;
};

可以包含如下的关键字:

  • description:描述,可以随便写;
  • data:二进制文件的路径,格式为/incbin/("path/to/data/file.bin");
  • type:二进制文件的类型,"standalone","firmware","kernel", "ramdisk", "flat_dt"等;
  • arch:平台类型,“arm”, "arm64", “i386”等;
  • os:操作系统类型,linux、vxworks等;
  • compression:二进制文件的压缩格式,SPL、或uboot会按照执行的格式解压;
  • load:二进制文件的加载位置,SPL、或uboot会把它copy对应的地址上;
  • entry:二进制文件入口地址,一般kernel image需要提供,uboot会跳转到该地址上执行;
  • hash:使用的数据校验算法。

具体可以参考:doc/uImage.FIT/source_file_format.txt

(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”指定,当然也可以在运行时指定。

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结构结束地址下一扇区起始地址开始计算的)、以及data-size(指定文件的大小)属性,而在data属性指向的二进制数据文件将会被追加到FIT结构的尾部(也是扇区对齐);下面我们分析的itb文件布局格式就是这种;

因此我们可以采用如下方式生成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@zhengyang:/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";
                        };
                };

        };


};
View Code

注意:这里我们补充一下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进制查看:

由于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属性比较多,这里我并没有截全:

其属性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@zhengyang:/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@zhengyang:/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:

这样我们可以得到一个文件内容布局表:

地址范围 偏移 大小 内容 加载到内存地址
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.2 编译ATF

因为rk3399是arm64,所以我们还需要编译ATF (Arm trust firmware), ATF 主要负责在启动uboot之前把CPU从安全的EL3 切换到 EL2,然后跳转到uboot,并且在内核启动后负责启动其他的CPU。

3.2.1 下载源码

下载arm-trusted-fireware到uboot同级目录:

root@zhengyang:/work/sambashare/rk3399# git clone https://github.com/ARM-software/arm-trusted-firmware.git --depth 1 

查看文件内容:

View Code
3.2.2 编译ATF

运行如下命令:

root@zhengyang:/work/sambashare/rk3399/arm-trusted-firmware# make CROSS_COMPILE=arm-linux- PLAT=rk3399

编译后报出一个缺少​​arm-none-eabi-gcc​​工具链的错误:

安装该工具链并重新编译:

root@zhengyang:/work/sambashare/rk3399/arm-trusted-firmware# sudo apt-get install gcc-arm-none-eabi
root@zhengyang:/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@zhengyang:/work/sambashare/rk3399/u-boot# cp /work/sambashare/rk3399/arm-trusted-firmware/build/rk3399/release/bl31/bl31.elf ./
3.3.2 编译

然后执行编译命令:

root@zhengyang:/work/sambashare/rk3399/u-boot# make u-boot.itb ARCH=arm CROSS_COMPILE=arm-linux-

这里提示我们缺少python依赖elffile,如下图所示:

我们直接使用如下命令安装python依赖包(需要注意自己的python版本,必须是2.7版本,使用pip2安装依赖):

root@zhengyang:/work/sambashare/rk3399/u-boot# pip2 install pyelftools

重新编译生成u-boot.itb文件:

root@zhengyang:/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@zhengyang:/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@zhengyang:/work/sambashare/rk3399# git clone https://github.com/rockchip-linux/rkdeveloptool.git --depth 1

4.2 配置

首先安装libusb与udev,例如对于ubuntu:

root@zhengyang:/work/sambashare/rk3399# sudo apt-get install libudev-dev libusb-1.0-0-dev dh-autoreconf

切换到rkdeveloptool/目录进行配置:

root@zhengyang:/work/sambashare/rk3399# cd rkdeveloptool/
root@zhengyang:/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@zhengyang:/work/sambashare/rk3399/rkdeveloptool# ./configure

4.3 编译安装

运行如下命令:

root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# make 
root@zhengyang:/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@zhengyang:/work/sambashare/rk3399/rkdeveloptool# sudo apt-get install pkg-config libusb-1.0

编译完成后,在当前路径下生成rkdeveloptool可执行文件:

root@zhengyang:/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@zhengyang:/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@zhengyang:/work/sambashare/rk3399# cd rkbin/
root@zhengyang:/work/sambashare/rk3399/rkbin# tools/boot_merger  /work/sambashare/rk3399/rkbin/RKBOOT/RK3399MINIALL.ini
********boot_merger ver 1.2********
Info:Pack loader ok.
root@zhengyang:/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@zhengyang:/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@zhengyang:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot/u-boot.itb ./
root@zhengyang:/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 烧录

使用下载引导命令去使目标机器初始化DDR与运行usbplug(初始化DDR的原因是由于升级需要很大的内存,所以需要使用到DDR);

root@zhengyang:/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
View Code

使用wl命令烧写镜像到目标机器的eMMC,需要注意的是访问DDR所需的所有其他命令都应在使用db命令之后才能使用;

root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40 idbloader.img
Write LBA from file (100%)
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x4000 u-boot.itb
Write LBA from file (100%)

在烧写镜像完成后使用rd命令重启目标机器:

root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool rd
Reset Device OK.

需要注意的是:如果这个时候你也烧录了内核程序,执行完rd命令后是无法进入uboot命令的的,这里会直接启动内核。

六、测试

6.1 串口连接

使用准备好的USB转串口适配器和连接线(需另购),连接开发板,我使用的开发板提供了DEG_UART的4个引脚,其使用的是RK3399的UART2;

引脚  开发板接口 USB转串口 
 1  GND DND 
VCC 5V  VCC 
UART2DBG_TX  RX
 UART2DBG_RX TX 

其电路原理图如下:

需要注意的是:这四根线都要接上,我只接TX、RX串口输出数据都是乱码。

6.2 MobaXterm

这里我使用的串口调试工具是MobaXterm,选择串口端口,设置波特率为1500000,8位数据位,1位停止位。

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
View Code

其中比较重要的环境变量有:

  • 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/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 is not stored)  --->    
           (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之后,导致初始化环境变量的时候eMMC还未初始化,修改方法,就是经将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@zhengyang:/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来确保代码质量。一旦您已经验证了程序的正确性,就可以将其关闭,并在开发过程中保持高水平的代码质量。

八、 脚本方式

由于我们每次修改程序后,重新编译步骤比较麻烦,这里我们可以将这步骤编写成一个sheell脚本,这样每次执行就比较容易了。

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@zhengyang:/work/sambashare/rk3399/u-boot# chmod +x build.sh
8.1.1 clean

执行如下命令进行清理:

root@zhengyang:/work/sambashare/rk3399/u-boot# ./build.sh clean 
8.1.2 配置

执行如下命令进行uboot配置:

root@zhengyang:/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@zhengyang:/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
View Code

在命令行输入:

root@zhengyang:/work/sambashare/rk3399/u-boot# ./make.sh help

查看具体编译指令,如下图:

root@zhengyang:/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@zhengyang:/work/sambashare/rk3399/u-boot# ./make.sh evb-rk3399

参考文章

[1]RK3399-Linux

[2]rk3399移植 u-boot

[3]Firefly-Rk339 U-Boot 使用

[4]__attribute_((packed))

[5]移植U-Boot思路和实践 | 基于RK3399

[6]RK3399——裸机大全

[7]U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明

[8]嵌入式ARM64 uboot 2022.01 移植

[9]深度探索uboot

[10] uboot 2021.10源码分析(启动流程)

[11]Uboot 2017.01 SPL中的image_loader

[12]imx8mq - u-boot-spl启动分析

[13]RK3399-SD卡linux系统制作(uboot,kernel内核,根文件)

posted @ 2023-05-11 01:02  大奥特曼打小怪兽  阅读(4054)  评论(9编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步