----------------------------------------------------------------------------------------------------------------------------
开发板 :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
需要注意的是:尽量不要下载release分支,最初我也下载了这个分支,这里分支默认配置有问题。其中配置项:CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x50000,表示SPL为ATF预留了0x50000大小的内存空间,这个地址范围是从0x00000008开始的,而0x500008之后是SPL代码。程序运行后,SPL从eMMC加载0x00040000.bin到内存地址0x40000时,由于文件也比较大,直接覆盖了原有的SPL代码,导致程序直接卡死。
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@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-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文件,然后跳转到uboor执行。
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)
3.1 FIT介绍
FIT是flattened image tree的简称,它采用了device tree source filse(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 -f u-boot.its u-boot.itb
其中 -f 指定需要编译的source文件,并在后面指定需要生成的image文件(一般以.itb为后缀,例如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"; }; }; }; };
编译生成的u-boot.itb文件,以16进制查看:
由于itb文件布局和dtb文件布局一样,所以我们按照dtb文件布局格式来解读。
其中地址范围0x00000000~0x00000027表示的是fdt_header 结构体的成员信息:
- 地址0x00000000:对应magic,表示设备树魔数,固定为0xd00dfeed;
- 地址0x00000004:对应totalsize,表示源u-boot.its文件打包后在u-boot.itb中所占的大小,需要注意的是不包含image data file文件的代销,从上图可知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-size属性的值是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
因为rk399是arm64,所以我们还需要编译ATF (Arm trust firmware), ATF 主要负责在启动uboot之前把CPU从安全的EL3 切换到 EL2,然后跳转到uboot,并且在内核启动后负责启动其他的CPU。
3.2.1 下载源码
root@zhengyang:/work/sambashare/rk3399# git clone https://github.com/ARM-software/arm-trusted-firmware.git --depth 1
查看文件内容:
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.bin,只不过这时候只是在内存中执行,如果不执行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
使用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转串口适配器和连接线(需另购),连接开发板:
引脚 | 开发板接口 | USB转串口 |
1 | GNC | - |
2 | VCC 5V | - |
3 | UART2DBG_TX | RX |
4 | UART2DBG_RX | TX |
需要注意的是:我这板子比较奇怪,必须还要连接上USB,不然串口输出数据都是乱码。
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
其中比较重要的环境变量有:
- 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内核分区加载到内存,然后直接boom <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
如果断电重启或者通过rest命令重启,你会发现环境变量并没有保存成功,这个问题等后面分析uboot源码再回过来看吧。
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,t命令用于来查看当前开发板一共有几个MMC设备:
=> mmc list dwmmc@fe320000: 1 sdhci@fe330000: 0 (eMMC)
我们看到既有sdmmc,也有sdhci,在我使用的开发板中,设备树描述信息中:sdmmc代表 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即可查看 EMMC 卡的信息,结果如图下所示:
=> 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, 84位宽的总线。
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 是要读取的块数量。
比如从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是要写入的块大小,一个块为 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 elfn" ${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@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
[6]RK3399——裸机大全
[7]U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明
[9]深度探索uboot
[11]Uboot 2017.01 SPL中的image_loader
[13]RK3399-SD卡linux系统制作(uboot,kernel内核,根文件)
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!