第二章 BootROM与Bootloader:芯片级启动流程
2.1 从按下电源键说起
手机按下电源键的那一刻,发生了什么?
很多人以为第一个跑起来的是Linux内核。其实不是。在CPU真正执行任何代码之前,芯片内部有一个硬编码的微型程序——这就是BootROM。它固化在芯片的只读存储器里,你改不了,也删不掉。
我刚开始研究启动流程时,一直有个疑惑:为什么不能直接把Bootloader放在BootROM里?后来踩了坑才明白——BootROM太小了,通常只有几十KB。它只够做一件事:初始化最基本的硬件,然后加载Bootloader。
核心要点:BootROM是芯片上电后执行的第一个程序。它不可修改,负责初始化时钟、堆栈、存储器控制器,然后从指定存储介质加载Bootloader。
2.2 BootROM到底干了什么
说白了,BootROM就是个"启动引导器的小引导器"。它的工作流程非常固定:
- 上电复位——CPU从复位向量地址取指,这个地址指向BootROM
- 基础硬件初始化——设置时钟频率、关闭看门狗、初始化堆栈指针
- 检测启动介质——检查eMMC、UFS、SD卡等设备是否存在
- 加载Bootloader——从启动介质读取Bootloader到内部SRAM
- 跳转执行——验证签名后,跳转到Bootloader入口
嗯,这里要注意:BootROM阶段还没有DDR内存可用。它只能用芯片内部那点可怜的SRAM。所以Bootloader必须足够小,能塞进SRAM里。我记得在某个低端平台上,SRAM只有64KB,Bootloader稍微大一点就装不下,折腾了好几天才搞定。
2.3 Secure Boot:为什么需要它
你想想看,如果Bootloader可以被随意替换,那手机的安全性就形同虚设。恶意软件只要改掉Bootloader,就能在系统启动前植入后门。
Secure Boot就是为了解决这个问题。它的核心思想是:每一级启动程序都要验证下一级的签名。
具体流程是这样的:
- BootROM验证Bootloader的签名
- Bootloader验证Boot.img的签名
- Boot.img验证System分区的签名
- ...以此类推,形成一条完整的信任链
个人经验:我在做某个平板项目时,Secure Boot验证一直失败。查了两天才发现,是Bootloader的签名密钥和BootROM里烧录的公钥不匹配。生产线上烧录密钥时出了差错。从那以后,我每次都会在项目初期就确认密钥对的匹配关系。
2.4 Bootloader的两阶段设计
Android的Bootloader通常分为两个阶段:
| 阶段 | 名称 | 存储位置 | 主要任务 |
|---|---|---|---|
| 第一阶段 | SPL (Secondary Program Loader) | eMMC的特定分区 | 初始化DDR、加载第二阶段 |
| 第二阶段 | U-Boot / Little Kernel | eMMC的Boot分区 | 初始化外设、加载内核 |
为什么搞这么复杂?因为BootROM能加载的程序大小有限。第一阶段SPL非常精简,它的唯一使命就是初始化DDR内存,然后把完整的U-Boot加载到DDR中。一旦U-Boot跑起来,整个系统就活了。
我曾经在一个项目里遇到过SPL加载U-Boot时CRC校验失败的问题。排查下来是eMMC的时序配置不对,导致读取数据出错。调整了eMMC的时钟频率后问题解决。这种问题在硬件调试阶段特别常见。
2.5 AB分区策略
你有没有遇到过系统升级到一半断电,手机变砖的情况?AB分区就是为了解决这个问题。
AB分区的核心思想很简单:准备两套系统分区。一套是当前运行的(Slot A),另一套是备用的(Slot B)。升级时只更新备用分区,升级完成后切换启动槽位。
关键机制:Bootloader会读取misc分区中的槽位信息,决定从A还是B启动。如果当前槽位启动失败,Bootloader会自动切换到另一个槽位。这就是"回滚保护"。
AB分区的典型布局:
- boot_a / boot_b——内核镜像
- system_a / system_b——系统文件
- vendor_a / vendor_b——厂商驱动
- misc——存储槽位信息和启动计数
避坑指南:我曾经在实现AB分区时犯过一个低级错误——两个槽位的fstab文件配置不一致。结果从Slot A切换到Slot B后,系统因为挂载不了data分区直接卡在开机动画。记住:两个槽位的分区配置必须完全对称。
2.6 Bootloader的完整启动流程
把上面这些串起来,完整的芯片级启动流程是这样的:
- CPU上电,从BootROM开始执行
- BootROM初始化基础硬件,检测启动介质
- BootROM从eMMC的Boot分区加载SPL到SRAM
- BootROM验证SPL的签名(Secure Boot)
- SPL初始化DDR内存
- SPL从eMMC加载U-Boot到DDR
- SPL验证U-Boot的签名
- U-Boot读取misc分区,确定启动槽位
- U-Boot从选定槽位加载内核和dtb
- U-Boot验证内核签名,然后跳转到内核入口
你看,每一步都有验证,每一步都有备份。这就是Android系统启动的第一道防线。
2.7 调试Bootloader的实用技巧
最后分享几个我在调试Bootloader时常用的方法:
- 串口输出——Bootloader阶段还没有显示驱动,串口是唯一的调试手段。我习惯在关键节点加打印,比如"Loading SPL... OK"、"Verifying signature... OK"
- GPIO点灯——如果串口都不好使,就用GPIO控制LED。不同颜色代表不同阶段,简单粗暴但有效
- dump内存——在U-Boot命令行下,用md命令查看内存内容,确认数据是否正确加载
- 模拟器测试——用QEMU模拟硬件环境,可以快速验证Bootloader逻辑,不用每次都烧录真机
我的习惯:每次拿到新板子,第一件事就是确认BootROM能不能正常加载SPL。如果这一步都过不了,后面全是白搭。先跑通最小系统,再逐步添加功能——这个原则在Bootloader开发中特别适用。
好了,芯片级启动流程就讲到这里。下一章我们会深入Linux内核的启动过程,看看内核是怎么接管系统的。到时候再聊。