1 RK3568框图
本文从程序在不同存储其中的运行跳转,来理解RK3568启动的全过程。
- 访问地址
- 框图:https://www.rock-chips.com/uploads/pdf/2022.8.26/191/RK3568%20Brief%20Datasheet.pdf
- TRM Part1:https://opensource.rock-chips.com/images/2/26/Rockchip_RK3568_TRM_Part1_V1.3-20220930P.PDF
- 上电boot流程
- BootROM映射到物理地址0xFFFF0000,CPU从0xFFFF0000读取第一条指令并执行
- 可执行程序角度:BootROM -> TPL(ddr.bin,DDR初始化) -> SPL(spl.bin,加载后续镜像) -> Trust(ARM Trust Firmware + OP-TEE,安全) -> U-Boot(加载内核) -> Kernel(运行内核)
- 存储角度:BootROM -> SRAM(TPL) -> SRAM(SPL) + DDR(SPL) -> DDR(Trust) -> DDR(U-Boot)
- 镜像
- 本质:二进制文件的一种组织形式
- 作用:CPU从存储设备中读取镜像文件,获取二进制可执行程序入口,跳转执行
- 种类:TPL,SPL,UBoot,Trust,Kernel
- 目录
- 2 boot流程:帮助理解boot的启动流程
- 3 RK3568物理地址空间设计:帮助理解CPU如何从BootROM读取指令并执行;帮助理解CPU如何访问SRAM;帮助理解CPU如何访问DDR内存
- 4 存储分区:帮助理解镜像数据在存储设备中的分布
- 5 BootROM:帮助立即存储在MaskROM存储设备中的程序的执行流程
- 6 SRAM:帮助理解运行在SRAM中的TPL和SPL(如果支持重定位,SPL会把代码搬移到DDR中执行)的执行流程
- 7 DDR:帮助理解运行在DDR内存中的UBoot的执行流程
2 boot流程
- RK3568开源方案:BootROM -> TPL -> SPL -> Trust -> UBoot -> Kernel
- RK3568闭源方案:MaskROM -> ddr.bin -> miniloader.bin -> Trust -> UBoot -> Kernel
- boot执行流程
- CPU从0xFFFF0000获取第一条指令,并执行BootROM代码
- 依次检查SPI Nor Flash,SPI NAND Flash,NAND Flash,eMMC Flash,SD卡,USB OTAG
- 从存储器中读取ID BLOCK,并进行检查和校验
- 加载DDR(TPL)初始化代码到SRAM中并执行
- 加载SPL代码到DDR中并执行
- 加载UBoot到DDR中并执行
- 加载Kernel,跳转到Kernel执行
3 RK3568物理地址空间设计
- 物理地址分布
- DDR内存区域
- 外设寄存器
- SRAM区域
- BooROM区域
- boot地址映射
- remap[1:0]=2’b00
- BootRom(20KB) : 0xFFFF0000
- PMU_SRAM(8KB) : 0xFDCD0000
- SYSTEM_SRAM (64KB) : 0xFDCC0000
- remap[1:0]=2’b11
- BootRom(20KB) : 不可访问
- PMU_SRAM(8KB) :0xFFFF0000
- PMU_SRAM(8KB) :0xFDCD0000
- SYSTEM_SRAM (64KB) :0xFDCC0000
- remap[1:0]=2’b10
- BootRom(20KB) :不可访问
- SYSTEM_SRAM (64KB) :0xFFFF0000
- PMU_SRAM(8KB) :0xFDCD0000
- SYSTEM_SRAM (64KB) :0xFDCC0000
- remap[1:0]=2’b00
- 外设空间映射:TRM Part1的Addresss Mapping章节
- PCIe:超过4G空间的地址
- PCIe内存空间: 这是“数据窗口”,CPU 读写这个窗口,就是在读写 PCIe 设备. CPU 直接访问 PCIe 设备上的 BAR 空间(如显卡的显存)
- PCIe2x1_S: 0x300000000
- PCIe3x1_S: 0x340000000
- PCIe3x2_S: 0x380000000 - -PCIe配置空间: 这是“控制面板”,用来配置控制器本身和发现/配置 PCIe 设备. CPU 访问 PCIe 控制器自身的配置寄存器,或通过控制器间接访问 PCIe 设备的配置空间
- PCIe2x1_DBI: 0x3C0000000
- PCIe3x1_DBI: 0x3C0400000
- PCIe3x2_DBI: 0x3C0800000
- PCIe内存空间: 这是“数据窗口”,CPU 读写这个窗口,就是在读写 PCIe 设备. CPU 直接访问 PCIe 设备上的 BAR 空间(如显卡的显存)
4 存储分区
- 默认分区映射表:https://opensource.rock-chips.com/wiki_Partitions
- 注意事项
- 一级引导是MiniLoader,则loader2可存放uboot.img,trust分区可存放trust.img
- 一级引导是不支持安全可信固件的SPL,则loader2分区可存放u-boot.bin,无可用trust分区
- 一级引导式支持安全可信固件的SPL(搭载ATF或OPTEE),则loader2分区用于存放u-boot.itb(内含u-boot.bin与可信固件二进制文件),无可用trust分区
- RK使用GPT分区表:https://github.com/rockchip-linux/u-boot/blob/android/doc/README.gpt
4.1 不同存储介质的ID BLOCK偏移
- eMMC芯片
- 首先需要了解eMMC芯片的结构
- 从boot扇区加载, 也就是从0扇区加载
- 扇区 64 = 0x40(同 SD,eMMC 的 user data area) - SD卡
- 前置空间需要放置MBR
- 扇区 64 = 0x40(32KB 偏移) - SPI Nor Flash
- 起始扇区 0(0x0 偏移), SPI 无"扇区"概念,按字节/页读
- rkspi 每 4KB 只用前 2KB 做 spread(镜像翻倍)
- rknand 按 pagesize/skippages 做 spread - SPI NAND Flash
- 存在坏块管理, 从逻辑0扇区加载, 可设置备份分区
- 起始页 0(0x0 偏移,但有跳页要求): NAND page(pagesize,如2KB/4KB) - NAND Flash
- 起始页 0(同 SPI NAND,跳页)
4.2 不同介质的本质区别
- SD/eMMC 用扇区 64(0x40)
- SD/eMMC 的前 64 个扇区(32KB)是 MBR/GPT 分区表区域,BootROM 跳过分区表,从扇区 64 开始读 ID Block
- doc/README.rockchip:296 明确:“rksd.c produces an image consisting of 32KB of empty space, a header and u-boot-spl-dtb.bin”——这 32KB 空白就是留给分区表的
- SD/eMMC 是块设备,有分区表(MBR/GPT),BootROM 必须跳过分区表才能放引导代码,所以固定从扇区 64 开始
- SPI Nor / NAND 从起始(0x0)读
- SPI/NAND 没有分区表的概念,BootROM 直接从介质最起始处读 ID Block
- doc/README.rockchip:323:“The image should be written to the start of SPI flash.”
- SPI Nor 是线性字节设备,没有分区表,从 0 开始即可
- NAND 的"位置"还要考虑坏块和跳页
- NAND 有坏块,BootROM 读取时会跳过坏块
- NAND 的 page 结构要求 spread
- SPI NAND / 并行 NAND 是页设备,没有分区表,从 page 0 开始,但要处理坏块和页对齐
4.3 镜像打包相关代码
- 关键文件
- 不同介质的封装差异:uboot/tools/rksd.c、rkspi.c、rknand.c
- 扇区偏移文档:uboo/doc/README.rockchip
- 镜像打包逻辑:uboot/make.sh 的打包逻辑
5 BootROM
- BootROM执行流程
- BootROM被映射到物理地址0xFFFF0000
- CPU从0xFFFF0000读取指令并执行
- CPU从存储设备中读取ID BLOCK并校验
- 校验成功,加载TPL
- 前置分析
- BootROM不可修改,由于它首先要加载ID BLOCK,而其支持的存储设备有很多:SPI Nor Flash,SPI NAND Flash,NAND Flash,eMMC Flash,SD卡,所以它只能从固定位置访问ID BLOCK
- 对于不同的存储器,我们要掌握这个固定位置的设计,这是确保系统能正常启动的第一步
- ID BLOCK是校验镜像合法性的第一步,ID BLOCK的组成也是镜像启动的关键一步
- 校验成功后,开始加载TPL。BootROM如何知道从存储器的什么位置加载TPL?在ID BLOCK的位置不变的情况下,扩展性的最好的设计就是通过在镜像头部记录不同镜像的offset,依此来定位镜像的位置。所以镜像的头部信息非常重要
5.1 BootROM加载TPL/SPL
- 处理流程
- 从可用的存储其中读取ID BLOCK
- 校验ID BLOCK通过, 则加载DDR初始化代码到SRAM中执行
5.2 ID BLOCK
- 本质: 给tpl/spl加头部信息
- 通过mkimage工具给tpl+spl加一个头部信息
- 这个头部信息中包含了魔数, tpl哈希值, spl哈希值, tpl入口地址, spl入口地址等信息
- make.sh
- mkimage -T rksd -d tpl:spl: idblock.bin / idbloader.img
- 如何管理存储的块信息: uboot/tools/rkcommon.h
enum { RK_BLK_SIZE = 512, RK_SIZE_ALIGN = 2048, RK_INIT_OFFSET = 4, RK_MAX_BOOT_SIZE = 512 << 10, RK_SPL_HDR_START = RK_INIT_OFFSET * RK_BLK_SIZE, RK_SPL_HDR_SIZE = 4, };- 不同芯片版本对应的ID BLOCK不同: uboot/tools/rkcommon.c
struct spl_info { const char *imagename; const char *spl_hdr; const uint32_t spl_size; const bool spl_rc4; const uint32_t header_ver; }; static struct spl_info spl_infos[] = { { "rk3066", "RK30", 0x8000, true, RK_HEADER_V1 }, ... { "rk1808", "RK18", 0x200000 - 0x2000, false, RK_HEADER_V1 }, ... { "rk3568", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 }, { "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2 }, { "rk3588", "RK35", 0x100000 - 0x1000, false, RK_HEADER_V2 }, };- 不同版本使用的头部数据
struct header0_info_v2 { uint32_t magic; // 偏移0x00: RK_MAGIC_V2 = 0x534E4B52 ("RKSN") uint8_t reserved[4]; // 偏移0x04 uint32_t size_and_nimage; // 偏移0x08: [31:16]镜像数; [15:0]hash字段偏移(4B单位) uint32_t boot_flag; // 偏移0x0C: [3:0]hash类型(0=none,1=sha256,2=sha512) uint8_t reserved1[104]; // 偏移0x10 struct image_entry images[4]; // 偏移0x78: 最多4个镜像(TPL+SPL) uint8_t reserved2[1064]; uint8_t hash[512]; // header 自身的 SHA256 }; struct image_entry { // rkcommon.c:45-52 uint32_t size_and_off; // [31:16]镜像大小(扇区数); [15:0]镜像扇区偏移 uint32_t address; // 加载地址, 默认 0xFFFFFFFF uint32_t flag; uint32_t counter; // 镜像序号(1,2,...) uint8_t reserved[8]; uint8_t hash[64]; // 该镜像的 hash };- ID BLOCK二进制布局
存储介质扇区 64 (0x40) 起: ┌─────────────────────────────────────────────────────┐ 0x000 │ header0 (4KB = 4扇区) │ │ header0_info_v2 结构 + 填充 + hash[512] │ ← BootROM 先读这里 │ images[0]: TPL — size_and_off=[大小|偏移4] │ │ images[1]: SPL — size_and_off=[大小|偏移4+TPL大小] │ ← BootROM 据此找 SPL ├─────────────────────────────────────────────────────┤ 0x800 (扇区4) │ "RK35" (4字节 spl_name) + TPL 代码 (init_file) │ ← TPL 数据, 2KB对齐 ├─────────────────────────────────────────────────────┤ 0x800 + TPL_size │ SPL 代码 (boot_file) │ ← SPL 数据, 2KB对齐 └─────────────────────────────────────────────────────┘6 SRAM:TPL + SPL
6.1 TPL内存布局: DDR相关, 根据SoC特化
- 配置
- u-boot-next-dev\arch\arm\mach-rockchip\Kconfig
- RK3568
- TPL_LDSCRIPT: default “arch/arm/mach-rockchip/u-boot-tpl-v8.lds”
- TPL_TEXT_BASE:default 0xfdcc1000 - remap[1:0]=2’b00: BootRom = 0xFFFF0000, SYS_SRAM = 0xFDCC0000
- SRAM是64KB, 从0x1000开始, 说明TPL要小于60KB
- TPL_MAX_SIZE:default 61440
- ROCKCHIP_IRAM_START_ADDR: 0xfdcc0000
- RK3568
- u-boot-next-dev\arch\arm\mach-rockchip\Kconfig
- 链接脚本
- u-boot-next-dev\arch\arm\mach-rockchip\u-boot-tpl-v8.lds
- TPL_TEXT_BASE: text -> rodata -> data -> u_boot_list -> image_copy_end -> bss
- 条件判断
- CONFIG_TPL_MAX_SIZE
- CONFIG_TPL_BSS_MAX_SIZE
- CONFIG_TPL_MAX_FOOTPRINT
- CONFIG_TPL_TINY_FRAMEWORK: 极简TPL框架内存布局
0xfdcc0000 ┌─────────────────────────────┐ IRAM 基地址 │ BROM 保留区 (0xfdcc0000~0xfdcc1000)│ 含 BROM 启动源ID @ 0xfdcc0010 0xfdcc1000 ├─────────────────────────────┤ TPL_TEXT_BASE (TPL 加载/运行地址) │ .text (代码段, start.o 在最前) │ │ .rodata (只读数据) │ │ .data (已初始化数据) │ ← TPL_MAX_SIZE=60KB 上限 │ .u_boot_list (链表) │ │ .image_copy_end / .end │ 0xfdcc1000 │ .bss (NOLOAD, 运行时清零) │ ← TPL 的 BSS 紧跟在代码后(同一段SRAM) +60KB └─────────────────────────────┘ 0xfdcc1000 + 0xF000 = 0xfdcd0000 │ (SRAM 剩余空间留给 BROM 回调) │6.2 SPL内存布局
- 配置
- include/configs/rk3568_common.h
- #define CONFIG_SPL_TEXT_BASE 0x00000000 // 行15:代码段链接地址(DDR起始)
- #define CONFIG_SPL_MAX_SIZE 0x00040000 // 行16:256KB
- #define CONFIG_SPL_BSS_START_ADDR 0x03fe0000 // 行17:BSS在DDR的0x03fe0000
- #define CONFIG_SPL_BSS_MAX_SIZE 0x00010000 // 行18:64KB
- #define CONFIG_SPL_STACK 0x03fe0000 // 行19:栈在0x03fe0000(与BSS同址)
- #define CONFIG_SPL_LOAD_FIT_ADDRESS 0x10000000 // 行23:FIT临时加载点
- include/configs/rk3568_common.h
- 链接脚本
- u-boot-next-dev\arch\arm\cpu\armv8\u-boot-spl.lds(ARM64通用SPL脚本)
- text -> rodata -> data -> u_boot_list -> image_copy_end -> end ->
- CONFIG_SPL_SKIP_RELOCATE(无重定位): _image_binary_end -> bss
- 重定位:rel_dyn_start -> rela.dyn -> .rel_dyn_end -> _image_binary_end -> bss
- 内存布局
DDR 0x00000000 ┌─────────────────────────────┐ .sram 区域 (ORIGIN=SPL_TEXT_BASE=0, LEN=256KB) │ .text (代码, start.o 最前) │ ← SPL 被加载到这里运行 │ .rodata │ │ .data │ │ .u_boot_list │ │ .rela.dyn (重定位信息) │ 0x00040000 ├─────────────────────────────┤ SPL_MAX_SIZE 上限 (256KB) │ ... DDR 可用空间 ... │ │ (FIT 镜像临时加载到 0x10000000) │ │ ... │ 0x03fe0000 ├─────────────────────────────┤ .sdram 区域 (ORIGIN=SPL_BSS_START_ADDR, LEN=64KB) │ SPL 栈 (CONFIG_SPL_STACK) │ ← 栈顶 0x03fe0000,向下生长 │ ↓ gd 结构 (栈顶预留) │ board_init_f_alloc_reserve 在此预留 gd │ ↓ │ │ .bss (NOLOAD, 64KB) │ ← BSS 段,crt0_64.S:144 清零 0x03ff0000 ├─────────────────────────────┤ BSS_MAX_SIZE 上限 │ ... DDR 可用空间 ... │7 DDR内存:SPL + UBoot + Kernel
- 内存物理地址空间
- 32位CPU
- 0x00000000 - 0xEFFFFFFF:3.75G内存
- 0xF0000000 - 0xFFFFFFFF:外设寄存器
- 64位CPU
- 0x00000000 - 0xEFFFFFFF:3.75G内存
- 0xF0000000 - 0xFFFFFFFF:外设寄存器
- 超过4G的位置依然可以随意访问,也就是说DDR可以扩展到8G,16G,设置更多
- 对于ARM64来说,支持48位的扩展地址
- 32位CPU
7.1 DDR内存地址布局:uboot/include/configs/rk3568_common.h
- SPL
- CONFIG_SPL_TEXT_BASE:0x00000000
- CONFIG_SPL_MAX_SIZE:0x40000,限制256KB。对应 MiniLoaderAll.bin 的代码容量上限
- CONFIG_SPL_STACK:0x03fe0000,SPL 运行栈地址
- CONFIG_SPL_BSS_START_ADDR:0x03fe0000,SPL BSS 段起始地址
- CONFIG_SPL_BSS_MAX_SIZE:0x10000,64KB。SPL 全局变量(BSS 段)最大空间
- CONFIG_SPL_LOAD_FIT_ADDRESS:0x10000000,SPL 加载 FIT 镜像 (u-boot.itb) 的内存目标地址
- UBoot
- CONFIG_SYS_TEXT_BASE:U-Boot 主程序运行基地址。正常启动U-Boot 加载至 0x00a00000 运行;USB烧录调试模式,重定位到 0x00000000。
- CONFIG_SYS_INIT_SP_ADDR:U-Boot 启动初期栈指针地址
- CONFIG_SYS_LOAD_ADDR:默认下载地址,loadb/loadx 等命令默认把文件读到这个内存地址
- CONFIG_SYS_SDRAM_BASE:0。DDR内存基地址为0
- SDRAM_MAX_SIZE:0xf0000000。可用DDR上限,上方保留给外设寄存器
- Kernel
#define ENV_MEM_LAYOUT_SETTINGS \ "scriptaddr=0x00c00000\0" \ "pxefile_addr_r=0x00e00000\0" \ "fdt_addr_r=0x08300000\0" \ "kernel_addr_r=0x00280000\0" \ "kernel_addr_c=0x04080000\0" \ "ramdisk_addr_r=0x0a200000\0"7.2 DDR内存地址布局:来源于uboot手册
【DDR内存地址布局】 ATF Start Addr:0x00000000 Size:1M Usage:ARM Trusted Firmware Secure:Yes SHM Start Addr:0x00100000 Size:1M Usage:SHM, Pstore Secure:No OP-TEE Start Addr:0x08400000 Size:2M~30M Usage:参考TEE开发手册 Secure:Yes FDT Start Addr:fdt_addr_r Size:- Usage:kernel dtb Secure:No KERNEL Start Addr:kernel_addr_r Size:- Usage:kernel镜像 Secure:No RAMDISK Start Addr:ramdisk_addr_r Size:- Usage:ramdisk镜像 Secure:No …… Start Addr:- Size:- Usage:- Secure:- FASTBOOT Start Addr:- Size:- Usage:Fastboot buffer Secure:No …… Start Addr:- Size:- Usage:- Secure:- SP Start Addr:- Size:- Usage:stack Secure:No FDT Start Addr:- Size:sizeof(dtb) Usage:U-Boot dtb Secure:No GD Start Addr:- Size:sizeof(gd) Usage:- Secure:No Board Start Addr:- Size:sizeof(bd_t) Usage:- Secure:No MALLOC Start Addr:- Size:CONFIG_SYS_MALLOC_LEN Usage:系统的堆空间 Secure:No U-Boot Start Addr:- Size:sizeof(mon) Usage:u-boot镜像 Secure:No Video FB Start Addr:- Size:fb size(32M) Usage:显示帧缓存 Secure:No TLB Table Start Addr:RAM_TOP-64K Size:32K Usage:MMU页表 Secure:No 【PS】 64位平台: ATF是ARMv8必需的, OP-TEE是可选的; 32位平台, 只有OP-TEE kernel fdt/kernel/ramdisk是U-Boot需要加载的固件地址, 由ENV_MEM_LAYOUT_SETTINGS定义 Fastboot功能需要的buffer地址和大小在deconfig中定义 OP-TEE 占据的空间需要根据实际需求而定7.3 SPL加载UBoot
- SPL加载FIT镜像: uboot.itb
- 配置: CONFIG_ROCKCHIP_FIT_IMAGE_PACK
- FIT 本质是 FDT(设备树格式)blob,magic = FDT_MAGIC(0xd00dfeed)
- FIT 由 make_fit_atf.sh 生成 u-boot.its,再 mkimage -f u-boot.its -E uboot.itb(-E 外部数据)打包。结构(arch/arm/mach-rockchip/fit_nodes.sh)
- FIT打包
FIT 包含的关键信息(属性定义在 include/image.h:923-953): - load(FIT_LOAD_PROP):镜像加载地址 - entry(FIT_ENTRY_PROP):镜像入口地址(uboot 节点无此属性) - os(FIT_OS_PROP):OS 类型——"arm-trusted-firmware"/"U-Boot"/"op-tee",SPL 据此决定跳转方式 - data/data-position/data-offset/data-size:镜像数据(-E 外部数据) - hash:每段 SHA256,SPL 校验完整性 - firmware/loadables/fdt:配置节点指定加载顺序- 从启动到FI镜像加载
SPL 入口 start.S:21 _start → reset → save_boot_params(setjmp 保存 BROM 上下文)→ 设 EL3 → master_cpu: bl _main(start.S:237)→ crt0_64.S:68 _main: - crt0_64.S:75-76:ldr x0, =(CONFIG_SPL_STACK=0x03fe0000) 设栈; - crt0_64.S:82-86:board_init_f_alloc_reserve/board_init_f_init_reserve(栈顶预留 gd); - crt0_64.S:89:bl board_init_f(spl.c:204)—— spl_early_init、RAM init、console; - crt0_64.S:125-138:spl_relocate_stack_gd(RK3568 返回0,不重定位栈); - crt0_64.S:144-149:清 BSS(0x03fe0000~0x03ff0000); - crt0_64.S:152-154:b board_init_r(common/spl/spl.c:507)。 board_init_r(spl.c:507-650)SPL 主循环: - spl.c:560-561:entry_point_bl32=-1; entry_point_bl33=CONFIG_SYS_TEXT_BASE(默认值); - spl.c:573:board_boot_order → spl_boot_device(返回 BOOT_DEVICE_MMC1); - spl.c:575:boot_from_devices → spl_mmc_load_image(spl_mmc.c:282)。- 加载FIT流程
① 检测 FIT(spl_mmc.c:57-105 mmc_load_image_raw_sector): header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - sizeof(struct image_header)); blk_dread(..., sector, 1, header); // 读1扇区头部 if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) // line 76-82 ★ spl_load_simple_fit(spl_image, &load, sector, header); // line 91 → FIT 路径 ② 加载 FIT blob 到内存(spl_fit.c:929 spl_load_simple_fit → spl_fit.c:702 spl_internal_load_simple_fit): - spl_fit_load_blob(spl_fit.c:489-538):把整个 FIT DTB 放在 CONFIG_SYS_TEXT_BASE - size 之前(≈0x009fxxxx),base_offset 供外部>镜像加载地址 ┌───────────────┬───────────┬──────────────────────────────┬─────────────────────────┬────────────────────────────────────────────────────┐ │ 镜像 │ FIT 节点 │ 加载地址 │ 地址来源 │ 文件:行号 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ U-Boot │ uboot │ 0x00a00000 │ CONFIG_SYS_TEXT_BASE │ rk3568_common.h:32; fit_args.sh:120; spl_fit.c:888 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ ATF bl31 段1 │ atf-1 │ bl31.elf 段1 p_paddr │ decode_bl31.py:31 │ fit_nodes.sh:129; spl_fit.c:837 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ ATF bl31 段2+ │ atf-2.. │ 各段 p_paddr │ 同上 │ fit_nodes.sh:166 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ OP-TEE │ optee │ 0x08400000 │ DRAM_BASE(0)+0x08400000 │ fit_args.sh:44; spl_fit.c:914 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ U-Boot dtb │ fdt │ 紧跟 firmware 之后 │ spl_fit_append_fdt │ spl_fit.c:417 │ ├───────────────┼───────────┼──────────────────────────────┼─────────────────────────┼────────────────────────────────────────────────────┤ │ FIT 临时 DTB │ 整个 blob │ 0x009fxxxx(TEXT_BASE 之前) │ spl_fit_load_blob │ spl_fit.c:525 │ └───────────────┴───────────┴──────────────────────────────┴─────────────────────────┴────────────────────────────────────────────────────┘
- 镜像调用
spl_fit.c:888:spl_image->entry_point_bl33 = image_info.load_addr = 0x00a00000。 跳转分发(spl.c:590-604) switch (spl_image.os) { case IH_OS_ARM_TRUSTED_FIRMWARE: // line 596 printf("Jumping to %s(0x%08lx) via ARM Trusted Firmware(0x%08lx)\n", ..., (ulong)spl_image.entry_point_bl33, (ulong)spl_image.entry_point); spl_invoke_atf(&spl_image); // line 602 ★ break; } 跳转前的准备工作 ① 从 FDT /fit-images 查找最终入口(spl_atf.c:171-220 spl_invoke_atf): ② 清理(spl_atf.c:212 spl_cleanup_before_jump,定义在 spl.c:725-757): ③ 构造 bl31_params 并跳转(spl_atf.c:123-133 bl31_entry): 跳转后:ATF(bl31) 在 EL3 初始化安全世界 → 启动 OP-TEE@EL1 Secure → ERET 到 U-Boot@EL2 Non-secure(0x00a00000)。 为什么 SPL 不直接跳 U-Boot 而要先跳 ATF 1. EL 切换必须经 ERET:SPL 在 EL3 Secure,U-Boot 要在 EL2 Non-secure,从 EL3 降到 EL2 同时切安全态只能靠 ERET(目标 EL 由 SPSR_EL3/ELR_EL3 决定),SPL 直接跳做不到; 2. 常驻 EL3 服务:kernel 运行后需要 PSCI(多核上下电)、SMC 路由(访问 OP-TEE/加密/OTP),没有 bl31 常驻 EL3,kernel 发 SMC 无人响应; 3. Secure/Normal 世界分离:OP-TEE 需先在 EL1 Secure 启动,ATF 负责调度 BL31→BL32→BL33。
7.4 UBoot加载Kernel
- Kernel镜像格式
- Android boot.img: uboot/include/android_image.h
- uboot/common/image-android.c
- 镜像布局:header(1页) | kernel(n) | ramdisk(m) | second(o) | [dtb]
- ARM64 Image header
- Documentation/arm64/booting.txt in the Linux kernel
- uboot/cmd/booti.c:20-31
- RK私有镜像
- bootrkp.c
- 8 字节 header(tag=TAG_KERNEL + size)+ data + CRC32
- FIT镜像
- FDT 格式,内含 kernel/ramdisk/fdt 子节点,fit_get_blob(fit.c:139-215)从 boot/recovery 分区读取
- 镜像处理路径
┌──────────────┬────────────────┬────────────────────┬────────────────────────┬──────────────────────────────────────────────────┐ │ 路径 │ 命令 │ Kernel 来源 │ 镜像格式 │ 文件:行号 │ ├──────────────┼────────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┤ │ bootrkp │ bootrkp │ kernel 分区 │ RK 私有 rockchip_image │ cmd/bootrkp.c:215-300 │ ├──────────────┼────────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┤ │ boot_android │ boot_android │ boot/recovery 分区 │ Android boot.img │ cmd/boot_android.c:21; android_bootloader.c:1217 │ ├──────────────┼────────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┤ │ boot_fit │ boot_fit │ boot/recovery 分区 │ FIT │ cmd/bootfit.c:59; fit.c:422 │ ├──────────────┼────────────────┼────────────────────┼────────────────────────┼──────────────────────────────────────────────────┤ │ distro │ distro_bootcmd │ 任意设备 │ extlinux.conf/boot.scr │ config_distro_bootcmd.h:383 │ └──────────────┴────────────────┴────────────────────┴────────────────────────┴──────────────────────────────────────────────────┘
- 进入uboot, 倒计时无按键则执行bootcmd:按顺序执行
#define RKIMG_BOOTCOMMAND \ "boot_android ${devtype} ${devnum};" \ "boot_fit;" \ "bootrkp;" \ "run distro_bootcmd;"
- 加载位置
#define ENV_MEM_LAYOUT_SETTINGS \ "scriptaddr=0x00c00000\0" \ "pxefile_addr_r=0x00e00000\0" \ "fdt_addr_r=0x08300000\0" \ "kernel_addr_r=0x00280000\0" \ "kernel_addr_c=0x04080000\0" \ "ramdisk_addr_r=0x0a200000\0"