说明
系统:Android10.0
设备: FireFly RK3399 (ROC-RK3399-PC-PLUS)
前言
上一篇讲到了u-boot编译完成之后,会生成各种镜像, 本章节重点介绍u-boot各个镜像生成的基本流程, 通过了解文件, 来辅助理解rk3399 u-boot启动基本过程。
一,编译u-boot的一般步骤
如果不考虑现有源码中的各种脚本, 比如不管make.sh, 编译u-boot的源码的一般步骤有以下通用步骤:
1,设置交叉工具链,一般修改Makefile
ARCH=arm
CROSS_COMPILE=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
或者在make命令后面直接传递参数。
2, 选择芯片配置文件, 如rk3399, 其实是将configs/xxx_defconfig拷贝到顶层目录.config
make rk3399_defconfig
3, make编译
二, u-boot.bin文件生成
可以通过编译的调试信息中发现如下结果:
/mnt/ext-disk2/RK/rk3399_Android10.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-objcopy --gap-fill=0xff -j .text -j .secure_text -j .secure_data -j .rodata -j .data -j .u_boot_list -j .rela.dyn -j .got -j .got.plt -j .dtb.init.rodata -j .efi_runtime -j .efi_runtime_rel -O binary u-boot u-boot-nodtb.bin
cat u-boot-nodtb.bin dts/dt.dtb > u-boot-dtb.bin
cp u-boot-dtb.bin u-boot.bin
truncate -s "%8" u-boot.bin #直接设置成一定大小文件
最终发现,Makefile中通过objcopy, 将elf格式的u-boot转换成u-boot-nodtb.bin。
三, uboot.img文件生成
注意, 这个镜像叫做uboot.img 而在同一级目录下有个叫做u-boot.img , 镜像是由如下脚本生成的:
/mnt/ext-disk2/RK/rk3399_Android10.0/u-boot/scripts/uboot.sh --load 0x00200000
load addr is 0x200000!
pack input u-boot.bin
pack file size: 1028816(1004 KB)
crc = 0xdbf2d111
uboot version: U-Boot 2017.09 (Nov 15 2021 - 17:03:41)
pack uboot.img success!
pack uboot okay! Input: u-boot.bin
在uboot.sh脚本中,实际是调用了:
./tools/loaderimage --pack --uboot u-boot.bin uboot.img {LOAD_ADDR} {SIZE}
rk3399平台执行的脚本为:
./tools/loaderimage --pack --uboot u-boot.bin uboot.img 0x00200000
通过名字大体可以猜测到,应该就是在u-boot.bin上增加一个入口地址0x00200000 , 这个地址就是u-boot.bin被加载到内存的地址。
其他镜像如下:
./tools/mkimage -f auto -A arm -T firmware -C none -O u-boot -a 0x00200000 -e 0 -n "U-Boot 2017.09"" for evb_rk3399 board" -E -b arch/arm/dts/rk3399-evb.dtb -d u-boot-nodtb.bin u-boot-dtb.img
./tools/mkimage -f auto -A arm -T firmware -C none -O u-boot -a 0x00200000 -e 0 -n "U-Boot 2017.09"" for evb_rk3399 board" -E -b arch/arm/dts/rk3399-evb.dtb -d u-boot-nodtb.bin u-boot.img
这么看 u-boot-dtb.img和u-boot.img 是一样的, 都是通过在u-boot-nodtb.bin加上一个头部信息, 类似内核zImage和uImage的差别。
四, rk3399_loader_v1.24.126.bin生成
打包loader镜像是通过make.sh loader来完成的, 最终执行如下:
rk3399对应的脚本为:
u-boot/scripts/loader.sh --ini ../rkbin/RKBOOT/RK3399MINIALL.ini
而../rkbin/RKBOOT/RK3399MINIALL.ini配置文件是:
[CHIP_NAME]
NAME=RK330C
[VERSION]
MAJOR=1
MINOR=26
[CODE471_OPTION]
NUM=1
Path1=bin/rk33/rk3399_ddr_800MHz_v1.24.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.24.bin
FlashBoot=bin/rk33/rk3399_miniloader_v1.26.bin
[OUTPUT]
PATH=rk3399_loader_v1.24.126.bin
可以得知依赖的文件有:DDR相关的bin/rk33/rk3399_ddr_800MHz_v1.24.bin、USB相关的bin/rk33/rk3399_usbplug_v1.26.bin、miniloader(瑞芯微自己写的不开源的bootloader)相关的bin/rk33/rk3399_miniloader_v1.26.bin。
boot_merger将这三个bin文件最后合并成rk3399_loader_v1.24.126.bin。
五, trust.img生成
make.sh trust: 打包trust.img镜像, 实际调用:
{SCRIPT_ATF} --ini {INI_TRUST} {PLAT_SHA} {PLAT_RSA} ${PLAT_TRUST_SIZE}
rk3399对应的脚本为:
u-boot/scripts/atf.sh --ini ../rkbin/RKTRUST/RK3399TRUST.ini
atf 是arm trust firmware缩写, atf.sh脚本负责将几个和trust zone相关的二进制合并在一起:
[VERSION]
MAJOR=1
MINOR=0
[BL30_OPTION]
SEC=0
[BL31_OPTION]
SEC=1
PATH=bin/rk33/rk3399_bl31_v1.35.elf
ADDR=0x00040000
[BL32_OPTION]
SEC=1
PATH=bin/rk33/rk3399_bl32_v2.01.bin
ADDR=0x08400000
[BL33_OPTION]
SEC=0
[OUTPUT]
PATH=trust.img
六, rk3399上电启动u-boot逻辑
1、板卡上电,cpu从片内0xffff0000(bootrom)启动,将外部存储(spi flash, emmc, sd/tf等)中读取的MiniLoaderAll.bin复制到内部SRAM中,完成对关键部分进行初始化, 如时钟,中断, 外部内存,usb设备等的初始化.(u-boot代码也可能做同样的事情)
2、MiniLoaderAll从外部存储(spi flash, emmc, sd/tf等)中读取parameter.txt,并进行解析,解析出来各个分区信息;
3、根据分区信息在外部存储的指定位置寻找trust.img镜像,并执行trust zone相关安全认证代码, 认证完毕就加载u-boot.img到指定的地址(0x00200000)运行
4, u-boot在内存中执行, 并解析bootcmd代码,启动内核
七, make.sh脚本主要执行步骤
了解了上面的过程, 大家也可以对make.sh脚本进行分析, 这里将主要的结果步骤大体列举一下, 总共会经历12条指令, 大家可以通过set -x 和 set +x对脚本进行调试查看。
process_args $*
prepare
select_toolchain
select_chip_info
fixup_platform_configure
select_ini_file
handle_args_late
sub_commands
clean_files
make V=1 CROSS_COMPILE={TOOLCHAIN_GCC} all #--jobs={JOB}
pack_images
finish
第一条指令:process_args $*
#实际就是process_args rk3399, 结果就是分析各种参数, 同时执行make rk3399_defconfig -j${JOB}
第二条指令:prepare 获取rkbin工具路径, 并且根据是否64位设置对应的ARM64_TRUSTZONE="y" (是有的), 以及PLAT_TYPE="FIT"(没有)
第三条指令:select_toolchain 设置交叉工具链
,64位和32位分分别在:
TOOLCHAIN_ARM32=../prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin
TOOLCHAIN_ARM64=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
第四条指令:select_chip_info 从.config文件获取芯片信息
根据.config中的芯片名字, 在表格CHIP_TYPE_FIXUP_TABLE中找loader, trust, label的名字,对于rk399而言, 表格中并没有对应单项
第五条指令:fixup_platform_configure 在表格中通过芯片名字选择平台配置
比如配置rsa加密算法和sha签名算法版本, 32位和64位对应的uboot, trust镜像大小和个数
而表格中并没有rk3399的配置, 所以都是用默认的-, 使用默认的uboot镜像大小, trust镜像大小
第六条指令:select_ini_file:选择ini配置文件
minibootloader的配置文件:../rkbin/RKBOOT/RK3399MINIALL.ini
trust镜像配置文件: ../rkbin/RKTRUST/RK3399TRUST.ini
第七条指令:补充部分编译参数
第八条指令:处理子命令, 如make.sh uboot, make.sh trust等
第9条指令:clean_files, 删除目标文件, 如 u-boot, tpl/u-boot-tpl.dtb等我呢间
第10条指令:直接make 编译
第11条指令: pack_images 打包
主要是打包uboot.img, trust.img , rk3399_loader_v1.24.126.bin
第12调指令finish :结束, 只是打印信息
六,总结
u-boot编译的相关镜像是如何生成的, 以及上电之后, RK3399做了什么事情,这块大家需要心里有个大体的概念, 此处并没有涉及到代码层面, 所以更多的是梳理流程和理解逻辑, 我们在了解整个RK3399 启动Android 的过程的时候, 就应该从上电开始, 这样才能形成一个整体。
原作者:旗浩QH