一、教程大纲

第一部分:硬件视角——从复位向量到第一条指令

1.1 复位向量与异常处理
  • ARMv7架构:复位向量固定地址为0x000000000xFFFF0000(通过SCTLR.V位控制)
  • ARMv8架构:四个异常级别(EL0-EL3),复位入口由配置寄存器(如RVBAR_EL3)定义
  • RISC-V架构:复位入口位于机器模式(Machine Mode),从固定ROM地址取第一条指令
  • 复位向量表结构:典型汇编代码示例
.section .vectors
b _reset          // 复位向量
b undefined_instruction
b software_interrupt
...
_reset:
    ldr sp, =0x8000              // 设置栈指针
    mrc p15, 0, r0, c1, c0, 0   // 读取CP15控制寄存器
    bic r0, r0, #0x1             // 禁用MMU
    bic r0, r0, #0x4             // 禁用数据缓存
    mcr p15, 0, r0, c1, c0, 0   // 写回控制寄存器
1.2 上电复位过程
  • 触发条件:芯片通电或复位信号(Reset Pin)触发
  • 硬件行为
    • CPU从固定地址取第一条指令(如ARMv7的0x00000000)
    • 所有寄存器和内存被重置为默认状态
    • 锁存Boot Mode引脚(决定从何种存储设备启动)
  • 关键任务
    • 配置PLL(锁相环),生成CPU、总线和外设的时钟信号
    • 初始化片内SRAM作为临时运行空间
    • 若支持XIP,则映射NOR Flash到内存地址空间
1.3 POR与电源管理单元协同
  • PMU执行ROM固化程序初始化系统电源域
  • 各电源域依序上电:VDD_CORE → VDD_CPU → VDD_RAM → 外设电源
  • 时钟树初始化与PLL锁定

第二部分:BootROM——芯片内“不可篡改的守护者”

2.1 BootROM设计原理
  • 架构定位:固化在芯片内部的只读存储器代码,由芯片厂商预烧录,用户不可修改
  • 核心任务
    • 最小化硬件初始化(CPU时钟、SRAM、启动引脚检测)
    • 从预定义介质加载下一阶段引导程序(SPL或直接U-Boot)
    • 可选的安全验证(签名校验)
  • 特点:代码体积小(通常几十KB),功能有限,不初始化DRAM
2.2 BootROM执行流程
上电复位
    ├─ 锁存Boot Mode引脚
    ├─ 初始化时钟/PLL
    ├─ 初始化SRAM/内存控制器
    ├─ 检测启动设备(SD/eMMC/NAND/NOR/USB)
    ├─ 加载SPL/U-Boot到SRAM或DDR
    └─ 跳转到SPL/U-Boot执行
2.3 启动介质检测机制
  • BootROM按固定顺序检测外部存储:SD/eMMC/NAND/NOR/USB等
  • 检测顺序由Boot Mode引脚的电平状态决定
  • 支持XIP(Execute In Place):NOR Flash可直接映射到内存地址空间执行
2.4 硬件初始化细节
  • 时钟初始化:配置PLL生成稳定时钟(i.MX6ULL BootROM初始化ARM内核时钟至396MHz)
  • 内存控制器初始化:初始化片内OCRAM作为临时运行空间
  • 基础外设:可选初始化调试串口用于输出启动日志;关闭MMU和Cache确保直接访问物理内存

第三部分:SPL——从SRAM到DRAM的桥梁

3.1 SPL设计动机
  • 核心问题:SoC上电后可用的SRAM极其有限,无法直接放下大体积的U-Boot
  • 典型数据:Cortex-A9/A53/A72 SoC的SRAM只有32KB/64KB/128KB,而完整U-Boot动辄数百KB甚至上MB
  • 解决方案:先用极小的SPL初始化DDR,再将完整版U-Boot从外部介质加载到DDR
3.2 SPL执行流程
  • SPL是一个精简版U-Boot
  • 初始化必要硬件(主要是时钟/DDR)
  • 包含极简驱动,仅包含板级初始化和加载U-Boot代码的功能
  • 加载完整U-Boot到内存,跳转到U-Boot入口
3.3 DDR初始化要点
  • 配置DDR控制器寄存器
  • 执行DRAM PHY训练(Training)优化内存时序
  • 验证内存可用性(读写测试)
  • 若DDR初始化失败,系统将静默挂死(需反复验证时序参数)
3.4 与BootROM的职责区别
阶段 存储位置 主要职责 特点
BootROM SoC内部ROM 最小硬件初始化,加载SPL 不可更改,由原厂烧录
SPL 外部介质→SRAM 初始化DDR,加载U-Boot 精简版U-Boot,约几十KB
U-Boot Proper DDR 完整功能,加载内核 具备命令行,功能完整

第四部分:U-Boot——功能完备的引导加载器

4.1 有限版本的三级结构
  • TPL (Tertiary Program Loader):可选,用于内存极端受限的平台(尺寸小于SPL)
  • SPL (Secondary Program Loader):标准二级加载器
  • U-Boot Proper:完整功能的引导加载器
4.2 U-Boot两阶段初始化
  • board_init_f():第一阶段初始化,有限栈、无BSS段可用的环境下执行
  • 重定位(Relocation):U-Boot将自身从加载地址复制到RAM中
  • board_init_r():第二阶段初始化,完整RAM和BSS可用
4.3 核心功能模块
  • 全局数据(gd结构体):U-Boot中的中央数据仓库
  • 环境变量系统:配置参数持久化存储(env_get(), env_set(), saveenv
  • 驱动模型:标准化设备访问接口(Uclasses、Drivers、Devices)
  • 命令行接口:交互式Shell,支持bootm/booti/bootz等启动命令
4.4 内核启动与参数传递
  • 加载组件:内核镜像(zImage/Image)、设备树Blob(.dtb)、initrd/initramfs
  • 启动方式
    • bootm ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
    • bootz(ARM平台,zImage)
    • booti(ARM64平台,Image)
  • Distro Boot框架bootcmddistro_bootcmdboot_targets迭代器
  • 支持从extlinux.confboot.scr等配置文件启动

第五部分:设备树——硬件描述的解耦艺术

5.1 为什么要设备树
  • 早年嵌入式Linux通过板级文件(board-file)将板级硬件硬编码进内核
  • 任何硬件调整(I²C外设地址变更、GPIO复用变化等)都需改内核、重编译
  • 设备树将非自发现硬件结构化的描述从内核代码中剥离,实现一个内核+多DTB适配多板卡的范式
5.2 DTS/DTB基本构成
  • 节点命名node-name@unit-address
  • 核心属性
    • compatible:驱动匹配标识,按“最具体→最通用”排序
    • #address-cells / #size-cells:地址/长度的cell数量
    • phandle / &label / /aliases:节点跨引用
    • /chosen:启动必需信息(bootargsstdout-pathinitrd-start等)
    • /memory / reserved-memory:物理内存与保留区
5.3 U-Boot中的DTB处理
  • 加载:将内核镜像 + DTB + initramfs搬到RAM
  • 修补:更新/chosen/bootargs/chosen/stdout-path、内存布局等
  • 传参:将DTB物理地址交给内核入口
5.4 架构间寄存器传递差异
架构 寄存器 传递内容
ARM32 r0 = 0, r1 = mach_id(旧), r2 FDT物理地址
ARM64 x0 FDT物理地址
RISC-V a1 FDT物理地址

第六部分:安全启动——信任链的构建

6.1 ARM TrustZone安全启动(TF-A)
  • 启动阶段划分(异常级别EL3→EL0):
    • BL1(AP Trusted ROM):上电后第一代码,执行基础硬件初始化
    • BL2(Trusted Boot Firmware):加载BL31、BL32、BL33到内存
    • BL31(Secure Monitor):运行在EL3,管理安全监控
    • BL32(Secure OS):可选,如OP-TEE(运行在Secure EL1)
    • BL33(Normal World):U-Boot(运行在Non-secure EL2)
  • 整个加载过程可配置为安全启动方式,每级镜像加载前验证电子签名合法性
6.2 RISC-V安全启动
  • OpenSBI:运行在M-Mode,为S-Mode操作系统提供二进制接口
  • RustSBI:Rust实现的开源SBI实现
  • PMP(Physical Memory Protection):物理内存保护机制
  • 多核启动:主核启动后唤醒从核(通过CLINT/PLIC)
6.3 安全启动链示例
BootROM验证SPL签名 → SPL验证U-Boot签名 → U-Boot验证内核签名

第七部分:Linux内核启动

7.1 内核入口分析
  • vmlinux.lds链接脚本找到内核第一条执行的代码
  • 入口汇编代码完成最小CPU/缓存/MMU准备,跳入start_kernel()之前必须读取DTB的关键内容
  • 创建一级页表,建立虚拟地址到物理地址的映射关系
  • 清除I-cache、D-cache
  • 使能MMU
7.2 体系结构相关的初始化
// 典型内核启动序列
start_kernel()              // init/main.c
    ├─ setup_arch()         // 架构特定初始化,解析DTB
    ├─ mm_init()            // 内存管理初始化
    ├─ sched_init()         // 调度器初始化
    ├─ rest_init()          // 其余初始化
        ├─ kernel_init()    // 内核线程
        └─ run_init_process()  // 执行init程序
7.3 根文件系统与用户空间
  • 内核挂载initramfs作为初始根文件系统
  • 执行/init脚本或二进制
  • init程序准备真正的根文件系统,执行/sbin/init
  • 用户空间正式进入可操作状态

第八部分:常见启动问题与调试方法

8.1 启动失败分类
  • 无任何输出:通常在BootROM之前的问题(电源/时钟/复位)
  • BootROM有输出但SPL无响应:启动介质或镜像格式问题
  • SPL有输出但U-Boot加载失败:DDR初始化或镜像加载地址错误
  • U-Boot启动但内核崩溃:设备树、内核配置或根文件系统问题
8.2 调试方法论
  • 串口初期调试是救命稻草:在所有可能位置添加printk()/printf()
  • 使用LED指示灯辅助定位
  • JTAG/SWD单步调试:设置断点在main/入口函数
  • QEMU/仿真器进行无硬件调试

第九部分:基于开源项目的SolG🧊冷启动Demo案例

9.1 Demo1:U-Boot全流程仿真(QEMU + ARM64)

目标平台:QEMU virt ARM64
覆盖阶段:BootROM(模拟)→ U-Boot → 最小Linux内核 → initramfs

硬件软件准备

  • QEMU 8.0+
  • ARM64交叉编译工具链(aarch64-linux-gnu-
  • U-Boot源码(master分支)
  • Linux内核源码(6.x)
  • Buildroot(构建initramfs)

实验步骤

1. Build U-Boot for QEMU arm64
   make qemu_arm64_defconfig
   make CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
   # 输出:u-boot.bin

2. Build Linux kernel
   make ARCH=arm64 defconfig
   make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
   # 输出:arch/arm64/boot/Image

3. 使用Buildroot构建initramfs
   make qemu_aarch64_virt_defconfig
   make

4. 运行完整的冷启动流程
   qemu-system-aarch64 \
       -machine virt \
       -cpu cortex-a57 \
       -nographic \
       -bios u-boot.bin \
       -kernel Image \
       -initrd rootfs.cpio.gz \
       -append "console=ttyAMA0"

预期启动日志

  • U-Boot启动信息(版本、DRAM大小、启动设备)
  • U-Boot命令行提示符
  • 自动执行bootcmd → 加载内核
  • Linux内核启动日志
  • initramfs执行,用户空间启动
9.2 Demo2:RVSoC RISC-V完整系统(FPGA硬件/Verilog仿真)

开源项目:RVSoC Project(东京工业大学Arch Lab)

RVSoC是一个使用Verilog HDL编写的便携式Linux capable RISC-V SoC,可在FPGA上实现,支持低资源FPGA。

架构组件

  • RVCoreM:12步多周期处理器(非流水线)
  • RVuc:4步多周期处理器(用于I/O操作)
  • MMU:Sv32地址转换单元和TLB

实验环境搭建

1. 下载RVSoC源码(MIT License)
   https://www.arch.cs.titech.ac.jp/rvsoc/

2. 构建RISC-V交叉编译器和Linux二进制文件
   参考项目文档的Binary构建步骤

3. Verilator仿真
   使用Verilator执行Verilog仿真

4. FPGA部署(支持板卡)
   - Nexys A7 FPGA board
   - Arty A7-35T board
   - 包含bitstream文件和内存初始化文件

5. 启动流程
   Berkeley Boot Loader → Device Tree → Linux Kernel → Disk Image

冷启动四个阶段在RVSoC中的对应

阶段 RVSoC实现
BootROM RVCoreM从固定地址取指,初始化最小硬件
SPL Berkeley Boot Loader(BBL)加载
U-Boot 可选或用BBL直接加载Linux
内核/根文件系统 Linux + 内存初始化文件包含的磁盘镜像
9.3 Demo3:Renode全系统仿真调试

开源框架:Renode(Antmicro开发的开源SoC仿真框架)

Renode支持完整的未修改软件在仿真平台上运行,包括I/O及传感器/执行器交互。

实验场景:仿真完整的CHERI-enabled RISC-V平台

1. 获取最新的Renode
   (预编译二进制或从源码构建)

2. 下载CHERIoT架构模拟器
   https://github.com/google/mpact-cheriot

3. 运行Demo
   renode renode_cheriot_demo.resc

4. 仿真启动
   - 观察完整冷启动日志
   - 单步调试每个加载阶段
   - 支持GDB远程调试
9.4 Demo4:Coreboot on RISC-V完整启动

开源项目:COREBOOT + Linuxboot for RISC-V Systems

Bootblock → Ramstage → ACPI Tables → Linuxboot → OpenSBI

实验平台:QEMU riscv virt machine

二、总结

本教程完整地涵盖了从SoC硬件上电复位到操作系统用户空间启动的全部环节,逐层深入的逐级讲解方式能够帮助开发者系统掌握嵌入式系统底层启动机制。理解冷启动全过程对于以下场景都极为宝贵:

  • BSP移植与系统Bring-up
  • RTOS/嵌入式Linux驱动开发
  • 安全启动链设计
  • 系统调试与性能优化

建议按照大纲顺序实践上述Demo案例作为辅助学习手段,将理论知识与实际操作相结合,才能真正吃透SoC冷启动的全貌。

一、BootROM 与 PCB 层面:为什么是 GPU 接管了第一棒?

树莓派4B的冷启动之所以如此特殊,核心在于BCM2711芯片内部的设计哲学——上电后,ARM Cortex-A72 四核全部处于休眠状态,真正第一个醒来的,是VideoCore VI GPU中的VPU核。这个VPU核不同于执行复杂图形计算的GPU着色器,它是一个小型的RISC-V或类似CPU的协处理器,专门负责最底层的系统初始化和启动序列控制。

PCIe/USB存储介质角度:与x86平台的NOR Flash Boot ROM不同,树莓派没有使用昂贵的NOR闪存来存储BootROM的下一阶段加载器。取而代之的是:全功能系统存储在内置USB控制器挂载的SD/eMMC卡上,第一阶段Bootloader(First Stage Bootloader)直接固化在不可修改的SoC片上ROM内部。这意味着没有外置BootROM芯片,没有额外的Flash IC,启动过程嵌入到SoC中。

BootROM执行流程

[上电/复位]
    │
    ▼ VideoCore VPU启动
BCM2711片上ROM执行
    ├─ 读取OTP(One-Time Programmable)熔丝位
    ├─ 根据BOOT_ORDER默认顺序尝试启动源
    │   ├─ 1. recovery.bin 在SD/eMMC卡(必须是FAT32格式)
    │   ├─ 2. SPI flash中有Tag标记的blob
    │   └─ 3. USB设备模式(类似Raspberry Pi 0的模式)
    ├─ 如果检测到有效镜像,将其载入GPU L2缓存
    └─ 执行第二阶段引导

└──→ 控制权自动传递给载入到GPU L2 Cache中的镜像,继续引导

BCM2711 ROM按以下顺序尝试三种启动源:首先检测uSD/eMMC卡上的recovery.bin,该分区必须为FAT32格式,MBR支持0x4/0x6/0x7/0xb/0xc/0xe类型码,GPT接受EFI系统分区或Microsoft Basic Data分区;其次检测SPI闪存中的Tag Blob,最后尝试USB设备模式。

OTP熔丝与一次性编程管理:OTP是SoC内部一个特殊区域,一旦熔丝位被编程,便不可撤销。用户可以利用未公开的OTP寄存器调整启动行为——例如强制跳过SD/eMMC启动或SPI启动,且GPIO电平控制在启动时需要进行检测。安全启动和安全链构建依赖这些OTP熔丝来锁定公用密钥或禁止某些调试模式。

二、第二阶段引导:板载EEPROM与bootcode4.bin

树莓派4B与早期型号最大不同:arm不再需要bootcode.bin这个第二级加载文件,但为了兼容性,依然保留了配套文件fixup.dat/fixup4.datstart.elf/start4.elf

第二阶段加载器的双重来源

  1. 板载SPI EEPROM(Pi 4特有——在官方U盘存储的新设计中引入了板载EEPROM,支持系统动态更新优化启动顺序)

    • 存储Second Stage Bootloader完整镜像(pieeprom.bin
    • 提供灵活的启动顺序配置(BOOT_ORDER
    • 支持从网络、USB、SD卡等多种设备启动
    • 可通过rpi-eeprom-update工具在线更新
  2. recovery.bin(也就是后来的bootcode4.bin

    • 在EEPROM被破坏时用于恢复
    • 可以从USB设备模式或直接SD卡加载
    • 支持重新烧写EEPROM镜像

BOOT_ORDER配置示例

# 查看当前EEPROM配置
$ rpi-eeprom-config

# 编辑EEPROM配置,设置启动顺序
# 0xf1表示顺序为:SD Card (0x1) → USB (0x2) → Network (0x4) →...(自定义)
$ sudo -E rpi-eeprom-config --edit

# 在配置文件中设置
BOOT_ORDER=0xf1   # 实际值根据文档定义

GPIO条件还可控制在特定电平下拉时改变启动行为,方便多启动模式切换。

三、第三阶段:start4.elf——GPU固件的核心密钥

start4.elf本质上是运行在VideoCore VI GPU上的闭源微程序。其核心使命远不止加载kernel.img,还负责以下几项关键工作:

3.1 DRAM训练——3200MHz LPDDR4的生死关

树莓派4B配备LPDDR4内存,频率达3200MHz。start4.elf在启动初期执行DRAM训练过程,动态调整读写延迟、相位校准等参数。如果这一步失败,后果是系统卡死,连任何输出都没有——也就是常说的"砖头"或无法启动。

3.2 解析config.txt

GPIO配置、内存分割、超频设置等都在ARM启动前由GPU读取执行的关键参数文件:

# 典型config.txt配置
gpu_mem=256          # GPU预分配256MB内存
arm_64bit=1          # 启用64位ARM模式
hdmi_force_hotplug=1 # 强制HDMI热插拔检测
core_freq=500        # GPU核心频率
disable_splash=1     # 禁用启动彩虹屏

# 显示用/dev/ttyAMA10串口控制台,无需设置ARM调试UART
enable_uart=1        # 启用UART串口(用于控制台)
uart_2ndstage=1      # 第二阶段启用UART输出(可选)

# 内核与设备树
kernel=kernel8.img   # 指定64位内核
device_tree=bcm2711-rpi-4-b.dtb

3.3 加载fixup4.dat

fixup4.datstart4.elf配对使用,包含关键的硬件配置数据和内存地址映射信息。它主要完成:调整ARM处理器与VideoCore GPU之间的内存布局、配置CPU/GPU之间的内存分割、设置外设地址和中断映射、修复硬件兼容性问题。

fixup4.dat本质上是start4.elf的重定位信息表,让固件能够根据实际内存布局正确调整指针和地址。GPIO配置和硬件初始化参数也在此阶段应用。

3.4 从SD卡读取有效载荷

start4.elf根据config.txt中的kernel=指令,从FAT32 boot分区读取对应的内核镜像(例如64位模式下是kernel8.img),然后将其载入内存地址0x80000处。

树莓派型号 SoC 架构 内核镜像
Pi 4 BCM2711 ARMv8 kernel8.img (64位),kernel7l.img (32位大物理地址)

四、第四阶段:唤醒ARM核与跳转到有效载荷

在GPU固件完成DDR训练、config.txt解析、fixup.dat应用和内核镜像加载后,最终一步由start4.elf释放ARM核心的复位信号,控制权从GPU侧的VideoCore内核转移到ARM侧的Cortex-A72核心。

BCM2711 PERIPHERALS寄存器映射细节:start.elf会将内核镜像加载到0x80000地址,设备树加载到0x2000000处,最后通过特定寄存器的MWAIT休眠唤醒机制来释放ARM核。有效载荷可以是裸机内核、U‑Boot或Arm Trusted Firmware****。

五、树莓派4B信任链与安全启动

树莓派4B的安全启动是一条从硬件熔丝到最终可信操作系统的完整信任链:

ROM Bootloader(内置,私有不公开)
    │硬件信任锚点(OTP熔丝)
    ▼
EEPROM中的bootmain(由RPi私钥签名)
    │
    ▼
boot.img(客户自有公钥签名)
    ├─ 自带的DTB已嵌入客户公钥hash
    └─ U-Boot或Linux内核
    │
    ▼
initramfs(继承用户信任)

关键细节:与ARM Cortex-A典型的三级加载不同,树莓派的安全启动核验起始点不是ARM Trusted Firmware(BL1/BL2/BL31),而是闭源的EEPROM二进制blob。另外设备树DTB并不包含在统一的FIT镜像中,而是单独存储在bootloader分区,因此在U‑Boot执行前必须经过鉴权。

启用安全启动的步骤:

# 第一步:构建并签名安全镜像(Yocto环境)
# 在local.conf中加上,构建boot.img
WELMA_SECURE_BOOT = "1"

# 第二步:生成BL2 bootloader并启用SIGNED_BOOT=1
# 修改boot.conf文件
SIGNED_BOOT=1

# 第三步:烧写OTP熔丝,锁定信任根(危险操作,不可逆)
# 使用usbboot工具中的rpiboot例程

六、U-Boot移植与设备树耦合

6.1 在树莓派4B上运行U-Boot

由于RPi 4可从EEPROM直接引导,使得常见方案包含U‑Boot或ATF BL31+U‑Boot:

# 构建适用于RPi 4的U-Boot(rpi_4_defconfig)
$ make rpi_4_defconfig
$ make CROSS_COMPILE=aarch64-linux-gnu-

# 将生成的u-boot.bin文件重命名为kernel8.img
$ cp u-boot.bin /boot/firmware/kernel8.img

# 而kernel真正的镜像改为其他命名(如Image.linux),由U‑Boot加载

6.2 设备树动态适配

树莓派将主设备树bcm2711-rpi-4-b.dtb存放在boot分区,与config.txt及各种overlay配合完成硬件配置。使用dtc工具可反编译查看:

# 反编译dtb文件查看设备树结构
$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts /boot/bcm2711-rpi-4-b.dtb

七、树莓派4B启动全过程图形总结

树莓派4B的GPU优先冷启动完整信号流与信任链、设备树和固件层次总结如图所示。

    上电/复位
        │
        ▼
┌───────────────────────────────────────────────────────────────────────┐
│  第一阶段:ROM Boot (GPU/VPU)                                           │
│  ├─ 从BCM2711片上ROM取第一条指令                                        │
│  ├─ 读取OTP熔丝(启动模式选择/安全锁定)                                 │
│  ├─ 尝试启动顺序:                                                      │
│  │   ├─ 1. 检查SD/eMMC 的 recovery.bin/bootcode4.bin                   │
│  │   ├─ 2. 检查SPI Flash的Tag blob                                     │
│  │   └─ 3. USB设备模式(rpiboot/otg)                                  │
│  └─ 将第二阶段加载器载入GPU L2缓存并执行                                 │
└───────────────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────────────┐
│  第二阶段:载入EEPROM或recovery.bin (GPU)                               │
│  ├─ 从SPI EEPROM加载pieeprom.bin(或在SD上加载bootcode4.bin)           │
│  ├─ 可选验证数字签名(安全启动链)                                       │
│  ├─ 初始化部分外设:SDRAM、SD卡控制器、USB等                            │
│  ├─ 读取bootconf.txt中的BOOT_ORDER,决定启动流                          │
│  └─ 加载第三阶段start4.elf到GPU                                         │
└───────────────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────────────┐
│  第三阶段:start4.elf + fixup4.dat (GPU固件)                            │
│  ├─ 执行DRAM训练,初始化LPDDR4内存(3200MHz时序调整)                    │
│  ├─ 解析config.txt配置参数(内存分割、超频、设备树选择、64bit使能等)      │
│  ├─ 应用fixup4.dat重定位表和硬件配置                                    │
│  ├─ 从SD卡读取内核镜像kernel8.img到0x80000                              │
│  ├─ 读取设备树bcm2711-rpi-4-b.dtb                                      │
│  └─ 释放ARM核复位信号                                                    │
└───────────────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────────────┐
│  第四阶段:ARM Cortex-A72接管                                           │
│  ├─ ARM从0x80000入口点开始执行(有效载荷:U-Boot / ATF / Linux内核)     │
│  ├─ 初始化MMU、异常向量表、中断控制器等                                  │
│  ├─ 解析传入的设备树blob                                                │
│  ├─ 挂载根文件系统                                                      │
│  └─ 执行/sbin/init进入用户空间                                          │
└───────────────────────────────────────────────────────────────────────┘

八、树莓派4B冷启动Demo实例

Demo 1:在真实树莓派4B上编译并引导最小裸机程序

# 1. 准备交叉编译工具链(Ubuntu/Debian)
$ sudo apt install gcc-aarch64-linux-gnu make

# 2. 编写最小裸机程序(kernel8.c)
$ cat > kernel8.c << 'EOF'
#define UART0_BASE 0xFE201000
#define UART_DR    (*(volatile unsigned int*)(UART0_BASE + 0x000))
#define UART_FR    (*(volatile unsigned int*)(UART0_BASE + 0x018))
#define UART_IBRD  (*(volatile unsigned int*)(UART0_BASE + 0x024))
#define UART_FBRD  (*(volatile unsigned int*)(UART0_BASE + 0x028))
#define UART_LCR_H (*(volatile unsigned int*)(UART0_BASE + 0x02C))
#define UART_CR    (*(volatile unsigned int*)(UART0_BASE + 0x030))
#define UART_IMSC  (*(volatile unsigned int*)(UART0_BASE + 0x038))

void uart_init(void) {
    UART_CR = 0x0;  // 禁用UART进行配置
    UART_IBRD = 0x1B;   // 系统时钟50MHz,波特率115200
    UART_FBRD = 0x8;
    UART_LCR_H = 0x70;  // 8n1, 启用FIFO
    UART_CR = 0x301;    // 启用Tx/Rx
}

void uart_send(char c) {
    while (UART_FR & (1 << 5));  // 等待TX FIFO有空位
    UART_DR = c;
}

void uart_puts(const char* s) {
    while (*s) uart_send(*s++);
}

void kernel_main(unsigned long dtb_addr) {
    uart_init();
    uart_puts("\n[Hello from Raspberry Pi 4 in Bare Metal Mode]\n");
    uart_puts("DTB address: 0x");
    // 这里可以添加十六进制打印函数
    uart_puts("\nBoot completed!\n");
    while (1);
}
EOF

# 3. 编写链接脚本(link.ld)
$ cat > link.ld << 'EOF'
SECTIONS {
    . = 0x80000;
    .text : { *(.text*) }
    .rodata : { *(.rodata*) }
    .data : { *(.data*) }
    .bss : { *(.bss*) }
}
EOF

# 4. 编写启动汇编(boot.S)
$ cat > boot.S << 'EOF'
.section .text
.globl _start
_start:
    ldr x0, =0x2000000          // DTB地址由GPU固件传入
    bl kernel_main              // 跳转到C函数
1:  wfi
    b 1b
EOF

# 5. 编译
$ aarch64-linux-gnu-gcc -c -o boot.o boot.S
$ aarch64-linux-gnu-gcc -c -o kernel8.o kernel8.c -O2 -ffreestanding -nostdlib
$ aarch64-linux-gnu-ld -T link.ld -o kernel8.elf boot.o kernel8.o
$ aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img

# 6. 将kernel8.img复制到SD卡boot分区,重命名覆盖或配合config.txt
# 在config.txt中添加 kernel=kernel8.img

Demo 2:QEMU仿真树莓派4B完整引导链

QEMU官方对树莓派4的仿真支持有限,但可接近真实场景搭建:

# 1. 下载Raspberry Pi OS lite镜像
$ wget https://downloads.raspberrypi.org/raspios_lite_arm64_latest
$ unzip raspios_lite_arm64_latest.zip

# 2. 提取内核和设备树
$ fdisk -l 2025-04-04-raspios-bookworm-arm64-lite.img
$ sudo losetup -P /dev/loop0 2025-04-04-raspios-bookworm-arm64-lite.img
$ sudo mount /dev/loop0p1 /mnt
$ cp /mnt/kernel8.img .
$ cp /mnt/bcm2711-rpi-4-b.dtb .
$ sudo umount /mnt
$ sudo losetup -d /dev/loop0

# 3. 修改设备树启用USB(QEMU中USB控制器默认禁用,需patch为"okay")
$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts bcm2711-rpi-4-b.dtb
$ sed -i 's/status = "disabled";/status = "okay";/g' bcm2711-rpi-4-b.dts
$ dtc -I dts -O dtb -o bcm2711-rpi-4-b.dtb bcm2711-rpi-4-b.dts

# 4. 运行QEMU仿真
$ qemu-system-aarch64 \
    -M raspi4b \
    -kernel kernel8.img \
    -dtb bcm2711-rpi-4-b.dtb \
    -sd 2025-04-04-raspios-bookworm-arm64-lite.img \
    -append "console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4" \
    -nographic

# 注意:QEMU对GPU模拟不完整,config.txt解析等部分行为不同于真机

基于RK35系列的实例分析

一、RK3566 vs RK3588 启动架构全景对比

在深入具体启动流程前,有必要先厘清这两款SoC与RPi4 BCM2711之间的根本性架构差异。这三条启动链的核心差异,不只是技术实现的不同,更反映了三家芯片厂商在处理"第一级加载"这一古老问题上的设计取舍:

环节 RPi 4 (BCM2711) RK3566 / RK3568 RK3588
第一级处理器 VideoCore GPU (VPU核) Cortex-A55 (CPU0, 运行在EL3) Cortex-A76 (CPU0, 运行在EL3)
上电入口地址 GPU私有的程序计数器 0xFFFF0000 → remap后可变为0x00000000 0x00000000(硬连线到Mask ROM)
BootROM种类 单一BootROM 单一BootROM 双BootROM(普通+安全BootROM)
SRAM容量 VideoCore L2 Cache复用 128KB256KB(PMU_SRAM + SYSTEM_SRAM) 更大容量SRAM(PMU_SRAM + SYSTEM_SRAM)
分阶段加载器 GPU固件链:start4.elf → fixup.dat idbloader.img → U-Boot → TRUST → Kernel TPL → SPL → ATF(BL31) → U-Boot → OP-TEE → Kernel
开源程度 GPU固件闭源 支持完全开源(U-Boot SPL/TPL)或闭源方案(ddr bin+miniloader) 上游ATF支持(TF-A v2.12+),开源社区活跃
安全启动 依赖OTP熔丝+EEPROM签名 BootROM验签idbloader → SPL验签U-Boot→内核 完整ATF信任链 + TrustZone + OP-TEE安全世界
多核启动 唤醒四核A72 通过PSCI启动其他三个Cortex-A55 big.LITTLE架构:唤醒A76簇→A55簇

二、BootROM——两代SoC的"第一推动力"深度剖析

2.1 上电复位与BootROM初始化

当26MHz或24MHz晶体振荡器稳定后,RK3566与RK3588的BootROM有细微的入口地址差异:

  • RK3566/3568:PMU首先建立CPU核心、DDR控制器、外设IO等电源域,24MHz晶振起振,通过PLL生成各模块所需时钟,复位信号释放后,CPU从固定地址0xFFFF0000开始执行BootROM代码。随后,BootROM可配置remap位将SRAM映射到同一地址,实现内存重映射。

  • RK3588:四核Cortex-A76中的CPU0(主核)从物理地址0x0000_0000开始取指执行——这一地址被硬连线到芯片内部的Mask ROM,即BL1。这也是TrustZone安全框架的信誉根。同时,RK3588配备了两个BootROM:非安全世界普通BootROM(CPU_S和CPU_NS均可访问)和安全世界安全BootROM(仅CPU_S可访问)。

2.2 BootROM的核心任务与启动介质检测

BootROM的核心任务高度一致:

  1. 初始化PLL:建立基本的系统时钟树
  2. 基础外设初始化:极少的SRAM空间、存储控制器的基本配置
  3. 检测硬件STRAP引脚电平:判断启动优先级顺序
  4. 尝试从介质加载下一级:若失败则进入MaskROM模式等待USB下载

RK3566的启动介质优先级

  • SPI NOR Flash是最优先检测的介质,其次是SPI NAND,接着是eMMC,最后是SD/MMC卡
  • 在U-Boot SPL阶段,默认启动顺序变为SD/MMC0 → eMMC → SPI。因此,如果eMMC和uSD卡上同时有可启动镜像,uSD卡中的镜像会先启动

RK3588的启动介质优先级

  • 支持从Serial Nor Flash(1/4bits数据宽度)、Serial Nand Flash(1bit数据宽度)、eMMC(8bits数据宽度)、SDMMC卡(4bits数据宽度)启动
  • BootROM按照固定预置顺序遍历上述设备,若有介质包含有效的IDBlock,则加载下一级引导程序

2.3 MaskROM模式——硬件的"最终安全网"

当所有启动介质都无法找到有效镜像时,RK3566/RK3588会自动进入MaskROM(Mask Read-Only Memory)模式。此模式下,BootROM会通过USB OTG端口启用Rockusb协议,上报PID:0x350B,等待主机通过rkdeveloptool或RKDevTool发送MiniLoader进行救援。实际上,在驱动层面,MaskROM模式对应的是PID:0x2207(VID保持不变),通过API层面完成权限转移和数据重放。

MaskROM模式是硬件级别的最底层恢复通道,与高通的9008模式、MTK的Preloader模式在概念上等同。进入方式包括:

  • 软件方式:当BootROM检测到所有外部介质都无有效IDBlock时自动进入
  • 硬件方式:短接eMMC时钟引脚与GND,彻底破坏存储访问,强制SoC切换到MaskROM状态。
  • 复合方式:或在Loader模式下用rkdeveloptool reboot-maskrom直接复位进入

三、两级Loader体系架构

Rockchip的启动链可以归纳为两类方案:

3.1 启动链对比

启动链 阶段1 阶段2 阶段3 阶段4 阶段5
闭源启动链 BOOTROM → ddr bin → Miniloader → TRUST → U-BOOT → KERNEL
开源启动链 BOOTROM → TPL → SPL → TRUST → U-BOOT → KERNEL
  • ddr bin:Rockchip官方预编译闭源文件(如rk3568_ddr_1560MHz_v1.08.bin),负责DRAM初始化,不开源。
  • Miniloader:Rockchip官方二级预加载器,同样闭源。
  • TPL(Tertiary Program Loader):开源替代方案,代替ddr bin完成DRAM初始化,隶属于U-Boot源码分支。
  • SPL(Secondary Program Loader):开源替代方案,代替Miniloader,负责加载后续镜像。

3.2 idbloader.img的生成逻辑

BootROM寻找的下一个程序实体被封装为idbloader.img。这个文件是一个Rockchip自定义格式的IDBlock镜像,通常位于存储介质偏移量0x40(64 Block)处。idbloader.img内部打包了两部分内容:

  • DRAM init程序:根据所选方案,可以是闭源的ddr bin或开源的TPL
  • 加载控制逻辑:可以是闭源的Miniloader或开源的SPL

闭源的RKLoader = ddr bin + usbplug + Miniloader,而其开源版本RKLoader = uboot TPL + usbplug + uboot SPL。在编译时,scripts/loader.sh调用boot_merger工具,读取RKBOOT/*MINIALL.ini文件,将上述组件合并为一个二进制的IDBlock块,最终写入存储介质的偏移0x40处。

3.3 DDR初始化——首道关键坎

DDR初始化是整个启动过程中最容易失败的地方,也是性能差距的主要来源。BootROM首先加载IDBlock中的DRAM初始化代码到SRAM并执行。这包括训练读/写每DQ通道的时序、调整ODT、优化频率以及最终的稳定性验证。

以TinkerBoard 3(RK3566)为例,DDR训练的四步递进过程是:

  1. LPDDR4X时序训练在324MHz启动
  2. 依次变频测试528MHz、780MHz
  3. 最终标称频率1056MHz完成校准
  4. 配置完4GB LPDDR4X参数:BW=32、Col=10、CS=2、Row=16

RK3588的DDR接口更加强大——支持LPDDR4X和LPDDR5两种内存类型,并具有宽格式位宽配置。一旦初始化失败,系统可能表现为任何串口输出都没有或死锁在"U-Boot TPL"阶段。


四、TRUST与ATF阶段——安全世界的基础

4.1 RK3566/3568的Trust

在RK3566/3568的非AARCH64启动路径中,Miniloader或SPL会在加载U-Boot主体前,额外加载TRUST镜像。该镜像包含关键安全组件:

  • OP-TEE OS:开源可信执行环境内核,作为TrustZone的安全世界操作系统
  • ATF BL31:运行在EL3的安全监视器,支持PSCI电源管理接口

当使用Rockchip闭源工具链时,需要用trustmerge工具将bl31.bin打包为Miniloader可加载的信任镜像格式。如果是AARCH64启动路径,启动链为:Bootrom → BL1/BL2 → BL31 → BL33 → Linux kernel。

4.2 RK3588的完整ATF支持

RK3588是瑞芯微首个在主线上游获得Arm Trusted Firmware完整支持的产品,TF-A从v2.12版本开始正式支持RK3588。在这一平台上,官方不再强制依赖闭源的rk3588_bl31_v1.45.elf,而是可以直接使用社区构建的TF-A。U-Boot会将BL31嵌入到u-boot.itb包中,形成完整的可信FIT映像。RK3588启动时直接进入EL3 aarch64模式,BL1/BL2负责:

  • 初始化PLL:多种PLL(NPLL/GPLL/V0PLL/AUPLL/CPLL/SPLL)接收24MHz振荡器作为参考时钟输入
  • GICv3中断控制器初始化:支持512个SPI中断和12个PPI中断
  • 解析FIP(Firmware Image Package)包:定位BL31和U-Boot
  • 注册PSCI服务:管理CPU hotplug和big.LITTLE大小核调度

4.3 OP-TEE与安全世界

TrustZone架构将硬件分为两个世界。安全相关操作均在EL3和Secure EL1中执行,防止非安全软件访问或篡改敏感数据。安全启动链中最常被忽略的是Secure Monitor切换模式(SMCCC)的适配,RK3588的bl31实现以这种方式确保了非安全世界(U-Boot/内核)无法突破安全墙。RK3588及RK356X系列均支持安全OTP功能,用于存储关键的安全信息,如加密密钥、设备身份标识等。


五、U-Boot与FIT镜像打包

5.1 三种启动路径的U-Boot角色

RK平台根据安全性要求和厂商支持路线,实际存在至少三条U-Boot启动的竞争路径:

  1. 路径A(全闭源,工业级信任链) :BootROM → idbloader.img(闭源ddr.bin+闭源miniloader) → trust.img(OP-TEE+bl31) → uboot.img (闭源为主,但加载器部分基于GPL开源)
  2. 路径B(混合启动,平衡信任) :ARM TF-A路线:BootROM → TPL(开源) → SPL(开源) → uboot.itb → Kernel
  3. 路径C(FIT完整镜像路径) :BootROM → idbloader.img → uboot.itb嵌入式BL31→u-boot proper

终端开发者最常用路径B,即用开源TPL+SPL+GPL U-Boot完整版,以方便代码调试与裁剪。

5.2 FIT(Flattened Image Tree)格式深度解构

RK3588及其较新平台普遍要求U-Boot以.ITB格式提供——这是一种由mkimage工具生成的多镜像打包容器。其内部视图不再单一依赖外部trust分区的二进制blob,而是将以下组件高效内嵌:

  • ATF BL31镜像:EL3安全监视器,负责SMC调用转发
  • U-Boot Proper:功能齐全的引导加载器
  • DTB文件:设备树硬件描述

这种集成就避免了多数据流加载和兼容性问题。一个典型的ITS源文件如下架构(伪视图):

/ {
    images {
        atf {
            description = "ARM Trusted Firmware";
            data = /incbin/("bl31.bin");
            type = "firmware";
            arch = "arm64";
            os = "arm-trusted-firmware";
            load = <0x00000000 0x00100000>;
            entry = <0x00000000 0x00100000>;
        };
        u-boot {
            description = "U-Boot (64-bit)";
            data = /incbin/("u-boot-nodtb.bin");
            type = "standalone";
            arch = "arm64";
            os = "u-boot";
            load = <0x00000000 0x00200000>;
        };
        fdt {
            description = "Flattened Device Tree";
            data = /incbin/("rk3588-evb1-v10.dtb");
            type = "flat_dt";
            arch = "arm64";
            load = <0x00000000 0x01f00000>;
        };
    };
    configurations {
        default = "conf";
        conf {
            description = "rk3588";
            firmware = "atf";
            loadables = "u-boot";
            fdt = "fdt";
        };
    };
};

其中firmware属性指向BL31镜像,loadables是该镜像加载后要执行的次级组件,最终系统启动时先执行ATF,再跳转至U-boot。


六、设备树与硬件耦合差异

RK平台的设备树结构与树莓派最大的区别在于:树莓派由VideoCore固件动态生成dtb,而RK是在U-Boot阶段直接从Flash加载外部平台的dtb文件。对于big.LITTLE架构的RK3588,dts会包含更为复杂的内容:CPU拓扑描述、GICv3的多个Distributor和Redistributor描述、PCIe/SATA/USB等高级外设描述。

由于RK3588需要引导M.2 NVMe或SATA固态硬盘时,BootROM无法直接初始化PCIe外设,必须在U-Boot阶段启用PCIe控制器,从固态加载内核和根文件系统,这实际需要修改dts并重新配置U-Boot。


七、安全启动链式信任验证机制

RK3566/3588均支持完整的安全启动机制,采用链式信任模型,从硬件信任根延续至最终用户系统:

  1. BootROM验证:作为硬件信任根,使用硬件密码引擎对idbloader.img进行RSA签名验证。公钥存储于芯片OTP熔丝中,破坏后可熔断一次性确认。
  2. SPL/Miniloader验证:初始化DDR完成后,验证ATF BL31和U-Boot镜像的数字签名,建立第二环信任。
  3. U-Boot或OP-TEE验证:最终环确保内核及initramfs未受篡改。

一次典型的安全启动失败,会触发串口调试输出签名校验错误的具体定位断点在"BL2 phase/SPL sig_check"退出代码。

如果在量产阶段使用了RK的安全启动功能烧录OTP熔丝,此操作不可逆转,必须提前确认公钥正确性,一旦出错则整片芯片锁死。


八、Demo实例

Demo 1x:RK3566从裸TPL手动构建最小启动镜像(开源全链路)

目标:完全避免使用Rockchip闭源ddr.bin,仅基于U-Boot源码编译完整的TPL+SPL+U-Boot映像。这是第一个关键且易出错的步骤。

# 1. 克隆U-Boot主线和rkbin辅助资源
$ git clone https://github.com/u-boot/u-boot.git
$ cd u-boot
$ git checkout v2024.01   # 稳定版本
$ git clone https://github.com/rockchip-linux/rkbin ../rkbin   # DDR init 工具包

# 2. 配置板级支持
$ make rk3568_defconfig   # RK3568通用配置
$ make menuconfig
# 确保以下关键项已启用:
# - CONFIG_ROCKCHIP_RK3568=y
# - CONFIG_SPL_DRIVERS_MISC=y   (SPL misc驱动)
# - CONFIG_TPL=y                (显式打开TPL)
# - CONFIG_TPL_DRIVERS_MISC=y   (TPL阶段 misc访问)
.  # 大部分会在配置文件自动选中,但需要开发者验证TPL是否会被正确编译

# 3. 从Rockchip rkbin中复制关键的DDR初始化二进制文件
# 注意:真正开源路线上的 ddr 初始化需要厂商配合,而rkbin 内的文件仍是闭源的。这里使用 rkbin 里的bin 做连接过渡
#   本文仍展示非依赖闭源DDL做自训练的潜力,实践中用户可以用已提供的loader bin,但也可反向用自己的TPL程序

$ cp ../rkbin/bin/rk35/rk3568_ddr_1560MHz_v1.08.bin tpl/ddr_init.bin  # 如果需要还是用闭源
$ export ROCKCHIP_TPL=../rkbin/bin/rk35/rk3568_ddr_1560MHz_v1.08.bin

# 4. 构建TPL(DRAM init)+SPL
$ make CROSS_COMPILE=aarch64-linux-gnu- BL31=../rkbin/bin/rk35/rk3568_bl31_v1.45.elf \
    ROCKCHIP_TPL=../rkbin/bin/rk35/rk3568_ddr_1560MHz_v1.08.bin
   -j$(nproc)

# 输出文件:u-boot-rockchip.bin, idbloader.img, u-boot.itb

# 5. 使用Rockchip boot_merger工具打包最终loader镜像(需要rkbin工具)
$ cd ../rkbin/tools
$ ./boot_merger ../RKBOOT/RK3568MINIALL.ini ./idbloader.img
$ ls   # 查看生成的 rk356x_loader_v1.xx.xxx.bin

# 6. 烧录至SD卡(偏移量按Rockchip规范计算,u-boot.itb位置需256KB之后)
$ sudo dd if=idbloader.img of=/dev/sdX seek=64 bs=512
$ sudo dd if=u-boot.itb of=/dev/sdX seek=16384 bs=512

# 注意:若seek值偏差将导致 BootROM 加载不到正确的 IDBlock

警示:上述过程中TPL的替代非完全开源,由于依赖ddr.bin实际上非公开真正的DRAM培训代码;用户可尝试ROCKCHIP_TPL=路径指向自己编写的裸机初始化程序,但风险高。

Demo 2x:RK3568从Linux SDK生成完全可启动镜像(野火/鲁班猫类)

用官方发布的鲁班猫(LubanCat)SDK进行系统、bootloader定制。鲁班猫为RK356x使用者提供了一个简易打包脚本:

# 1. 获取 SDK 源码
$ git clone https://github.com/LubanCat/lubancat_sdk_rk356x.git
$ cd lubancat_sdk_rk356x

# 2. 单独编译u-boot(有源码级的TPL/SPL控制)
$ cd u-boot
$ make lubancat2_defconfig   # 选择对应板卡配置,如Cat2
$ make CROSS_COMPILE=aarch64-linux-gnu- -j4
$ ls
# 输出: idbloader.img (8KB DRAM init + miniloader), u-boot.itb

# 3. 烧录完整系统
$ sudo dd if=idbloader.img of=/dev/sdX seek=64 bs=512
$ sudo dd if=u-boot.itb of=/dev/sdX seek=16384 bs=512
$ sudo dd if=boot.img of=/dev/sdX seek=32768 bs=512
$ sudo dd if=rootfs.ext4 of=/dev/sdX seek=262144 bs=512

# 4. 验证启动串口输出

Demo 3:RK3588自制Bootable Linux镜像(Buildroot集成)

Buildroot项目为RK3588等提供了预生成工具支持,极大简化了用户从源码开始构建自有Bootloader至根文件系统的全链路。以下是一个Minimal Buildroot配置的生成过程:

# 1. 克隆Buildroot
$ git clone https://github.com/buildroot/buildroot.git
$ cd buildroot
$ make rock5b_defconfig   # RADXA ROCK 5B (使用RK3588)
# 或 make rockchip_rk3588_evb_defconfig  标准参考板

# 2. 配置内核和U-Boot版本
$ make menuconfig
# 选择 Target options → AArch64
# Bootloaders → 启用U-Boot → U-Boot board name: 配置为 rock5b-rk3588
# Kernel → 选择 linux 版本 (6.6+)
# Target packages → 选择 Busybox 与核心工具

# 3. 关键配置:确保U-Boot编译时启用SPL_FIT,且BL31选择从ARM Trusted Firmware获取
# 在 Buildroot 全局配置中添加:
# BR2_TARGET_UBOOT_SPL=y
# BR2_TARGET_UBOOT_SPL_NAME="u-boot-spl.bin"
# BR2_TARGET_ARM_TRUSTED_FIRMWARE=y
# BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_GIT=y
# BR2_TARGET_ARM_TRUSTED_FIRMWARE_PLATFORM="rk3588"

# 4. 正式构建(耗时较长,约1-2小时)
$ make -j$(nproc)
# 生成文件位于 output/images/
# idbloader.img, u-boot.itb, boot.vfat, rootfs.ext4

# 5. 烧录到eMMC或SD卡
$ sudo dd if=idbloader.img of=/dev/sdX seek=64
$ sudo dd if=u-boot.itb of=/dev/sdX seek=16384
$ sudo dd if=boot.vfat of=/dev/sdX seek=32768
$ sudo dd if=rootfs.ext4 of=/dev/sdX seek=262144

Demo 4:利用rkdeveloptool进入MaskROM模式烧录RK3568(救援场景)

# 1. 安装rkdeveloptool
$ git clone https://github.com/rockchip-linux/rkdeveloptool
$ cd rkdeveloptool
$ mkdir build && cd build
$ cmake ..
$ make && sudo make install
# 或直接从Linux包管理器安装(部分发行版预置)

# 2. 硬件进入Maskrom模式方法
# 方法1:强制让SoC找不到有效IDBlock
#   - 移除SD卡
#   - 短接 eMMC Data0/CLK 到 GND 上电(高风险,请先参考原理图)
# 方法2:软件触发(前提是有Loader环境且UBoot已启动)
# 在UBoot命令行执行:
#   rk3568# rbrom  或 rockusb

# 3. 命令行下载及验证
$ sudo rkdeveloptool ld
# 显示设备列表,若处于MaskROM状态应出现类似 "DevNo=1 Vid=0x2207,Pid=0x350B,LocationID=xxx" 的条目

# 4. 下载MiniLoader并运行
$ sudo rkdeveloptool db ../rkbin/bin/rk35/rk3568_loader_v1.08.111.bin
# Downloading bootloader succeeded.

# 5. 擦除Flash分区
$ sudo rkdeveloptool ef
# Erasing flash complete.

# 6. 烧录完整镜像(如bootloader和分区)
$ sudo rkdeveloptool wl 0x40 idbloader.img
$ sudo rkdeveloptool wl 0x4000 u-boot.itb
$ sudo rkdeveloptool rd
# Reset Device OK。

Demo 5:RK3588从NVMe固态启动配置(修改U-Boot)

扩展RK3588的启动链到NVMe是高级玩法。由于BootROM不能直接识别NVMe SSD,需要在U-Boot阶段先初始化PCIe控制器,再加载内核和设备树。具体步骤如下:

# 1. 修改U-Boot设备树,使能PCIe控制器
$ cd u-boot/arch/arm/dts
$ vi rk3588-evb.dts

#   &pcie2x1 {
#       status = "okay";
#       num-lanes = <1>;
#   };
#   &pcie30phy {
#       status = "okay";
#   };
#   &combphy0_ps {
#       status = "okay";
#   };

# 2. 修改U-Boot配置,启用NVMe支持
$ make menuconfig
# Device Drivers → NVME Support → Enable
# Command Line Interface → nvme commands → enable 'nvme'

# 3. 重新编译U-Boot并烧录至SPI Flash或SD/eMMC
$ make CROSS_COMPILE=aarch64-linux-gnu- -j8
$ sudo dd if=idbloader.img of=/dev/sdX seek=64 bs=512
$ sudo dd if=u-boot.itb of=/dev/sdX seek=16384 bs=512

# 4. 在U-Boot命令行环境手动从NVMe引导
# U-Boot> nvme scan
# U-Boot> nvme info
# U-Boot> load nvme 0:1 $kernel_addr_r /boot/Image
# U-Boot> load nvme 0:1 $fdt_addr_r /boot/rk3588.dtb
# U-Boot> booti $kernel_addr_r - $fdt_addr_r

更进一步,可以通过修改bootcmd环境变量实现自动NVMe引导。

Demo 6:使用open-rk3588全开源镜像链构建(纯ATF及主线U-Boot)

最新的RK3588用户已经可以尝试完全摆脱Rockchip闭源预编译blob,直接使用上游社区开发的开源启动流程,项目组(open-rk3588)提供了以下资源:

组件 GitHub仓库 角色
arm-trusted-firmware open-rk3588/arm-trusted-firmware BL31.elf生成
u-boot-v2017 open-rk3588/u-boot-v2017 BL2 + BL33 (SPL and U-Boot)
optee_os open-rk3588/optee_os-4.2.0 BL32安全OS
linux-5.10 open-rk3588/kernel-5.10 Linux内核分支
# 1. 克隆open-rk3588环境,按README准备编译环境
$ git clone https://github.com/open-rk3588/u-boot-v2017.git
$ cd u-boot-v2017
$ git clone https://github.com/open-rk3588/arm-trusted-firmware.git ../arm-trusted-firmware

# 2. 编译ATF BL31
$ cd ../arm-trusted-firmware
$ make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3588
$ cp build/rk3588/release/bl31/bl31.elf ../u-boot-v2017/

# 3. 编译U-Boot
$ cd ../u-boot-v2017
$ make CROSS_COMPILE=aarch64-linux-gnu- rockchip_rk3588_defconfig
$ make CROSS_COMPILE=aarch64-linux-gnu- -j8 BL31=bl31.elf

# 4. 烧录并测试

这一线路是对想要真正掌握RK3588底层的开发者最为理想的途径,规避了所有黑匣子。


九、RK系列启动常见故障定位排查表

基于实际开发板bring-up中踩过的坑,总结以下快速排障对照表:

故障现象 可能原因 排查方法
串口完全无输出,板子无反应 SRAM/PMU未初始化;BootROM进入MaskROM但USB未连 检查电源域是否稳定;用rkdeveloptool检测设备是否处于MaskROM状态;测量PMU输出电压
仅有 U-Boot TPL 打印输出一行就停住 TPL阶段DDR init失败;未找到有效DRAM训练序列 检查DRAM电源/时钟/配置;尝试用官方ddr bin回滚测试;观察PHY训练时序参数
打印 U-Boot SPL 但无后续 SPL未正确加载Trust/U-Boot镜像 检查分区表的idbloader偏移位置是否正确;确认spl_boot_order环境变量;验证FIT镜像完整性
U-Boot启动但无法引导内核 设备树匹配错误;PCIe/NVMe扫描未完成 用fdt addr ${fdt_addr_r} + fdt list检查设备树节点是否enabled;检查bootcmd是否正确load了镜像文件
多次Reset或热启动卡死,冷启动正常 DRAM初始化时序部分丢失 检查DDR PHY的回写模式;测试RCONFIG寄存器非典型非默认值;用ddr扫频测试稳定性
Security Boot FAIL 签名错误提示 OTP公钥hash烧录错误,签名不匹配 检查OTP烧录过程(不可逆)确认使用的签名工具链一致性

总结

瑞芯微RK3566/RK3588的冷启动架构相对树莓派更有分层冗余性:

  • 灵活性:RK平台允许开发者选择两种主链路(闭源Miniloader或开源U-Boot SPL/TPL),这在嵌入式工业领域极为罕见。
  • 安全等级:RK3588完整支持ATF链式信任,OTP+硬件密码引擎比树莓派4的靠EEPROM签名防线更为坚固。
  • 复杂性成本:控制权落地时,BootROM加载idbloader.img的偏移量、DDR时序选择、TrustZone落地对工业品可靠性提出了更严苛的开发要求。
  • 性能:RK3588拥有更强大的big.LITTLE调度机制,大大改善了多核多场景的计算效率。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐