启动流程概览:从芯片上电到操作系统启动的完整链路
芯片上电那一刻,到底发生了什么?
很多人觉得启动就是「按一下电源键,然后屏幕亮了」。但在嵌入式世界里,这背后是一套精密的分级接力。我做了十几年底层固件,每次看到示波器上复位信号拉高、PC指针跳转到固定地址的那一刻,还是觉得挺有意思的——一个死气沉沉的芯片,就这样活过来了。
这一章,我们把整条链路拆开来看。从晶振起振,到操作系统开始调度第一个进程,中间到底经历了哪些阶段。说白了,就是回答一个问题:CPU 是怎么从「什么都不会」变成「什么都能干」的?
启动链路的五个核心阶段
我把整个启动过程分成五个阶段。你想想看,这就像接力赛——每一棒只干自己该干的事,干完了就交给下一棒。
| 阶段 | 执行者 | 主要任务 | 典型时间 |
|---|---|---|---|
| 阶段一:硬件初始化 | 硬件逻辑 + BootROM | 时钟稳定、电源就绪、复位释放 | 微秒级 |
| 阶段二:BootROM 执行 | 片上 ROM 固件 | 基础外设初始化、加载下一级启动代码 | 毫秒级 |
| 阶段三:SPL/预加载器 | SRAM 中的小代码 | 初始化 DDR、加载完整 U-Boot | 数十毫秒 |
| 阶段四:U-Boot 主程序 | DDR 中的 U-Boot | 板级初始化、加载内核 | 百毫秒级 |
| 阶段五:操作系统启动 | Linux 内核 | 驱动加载、文件系统挂载、init 进程 | 秒级 |
嗯,这里要注意——不同芯片的划分可能略有差异。比如有些 SoC 把阶段二和阶段三合并了,有些则拆得更细。但核心逻辑不变:每一级都比上一级更复杂,也更有「智能」。
阶段一:硬件初始化——芯片的「本能反应」
上电瞬间,芯片内部发生了什么?
我习惯把这段时间叫做「混沌期」。电源还没稳定,时钟还在振荡,复位信号还拉着低电平。这时候芯片什么都做不了,只能等。
- 电源就绪:PMIC(电源管理芯片)按顺序输出各路电压。核心电压先上,IO 电压后上。顺序错了,芯片可能锁死。
- 时钟稳定:晶振起振需要时间。PLL 锁定也需要时间。我记得有一次调试,板子死活起不来,最后发现是晶振负载电容配错了,起振时间长了 10 倍。
- 复位释放:所有条件满足后,复位信号拉高。CPU 从复位向量地址取第一条指令。
关键点:复位向量地址是芯片出厂时硬编码的。对 ARM Cortex-A 系列来说,通常是 0x00000000 或 0xFFFF0000。对某些 RISC-V 芯片,可能是 0x80000000。这个地址指向的就是 BootROM。
阶段二:BootROM 执行——芯片的「出厂设置」
BootROM 是芯片上电后执行的第一段软件。它固化在芯片内部,你改不了。我刚开始做这行的时候,总觉得这玩意儿很神秘——后来发现,它其实就是一个非常精简的引导程序。
BootROM 的任务很明确:
- 初始化最基础的外设:通常是 SPI、NAND、SD/MMC、USB 或 UART。注意,它不会初始化 DDR——DDR 初始化需要复杂的时序训练,BootROM 那点空间放不下。
- 检测启动介质:根据 boot pin 或 OTP 配置,决定从哪个设备加载下一级代码。比如从 SD 卡启动,就从 SD 卡读取固定偏移处的数据。
- 加载并校验:把下一级代码读到芯片内部的 SRAM 中。SRAM 很小,通常只有几十 KB 到几百 KB。所以下一级代码也必须很小。
- 跳转执行:校验通过后,跳转到 SRAM 中的入口地址。
个人经验:我曾经在一个项目里遇到 BootROM 加载失败的问题。查了两天,最后发现是 SD 卡的分区表格式不对。BootROM 只认裸设备偏移,不认文件系统。你把它格式化成 FAT32,它反而找不到启动代码了。
阶段三:SPL/预加载器——承上启下的「小个子」
SPL(Secondary Program Loader)是 U-Boot 体系里的概念。有些芯片叫它 MLO、IBL 或者 pre-loader。名字不同,干的活一样。
为什么需要 SPL?因为 BootROM 的 SRAM 太小了,装不下完整的 U-Boot。SPL 就是那个「先挤进来,然后把门撑开」的角色。
SPL 的核心工作:
- 初始化 DDR 控制器:这是最关键的步骤。DDR 初始化涉及时序参数、阻抗匹配、ZQ 校准。参数错了,系统直接挂掉。
- 加载完整 U-Boot:从启动介质(SD 卡、eMMC、NAND Flash)把 U-Boot 主程序读到 DDR 中。
- 可选的校验和压缩解压:有些方案会对 U-Boot 进行签名校验或 LZMA 压缩,SPL 负责解压和验证。
避坑指南:我曾经在一个项目里把 DDR 时序参数配错了。SPL 加载 U-Boot 时一切正常,但 U-Boot 一运行就随机崩溃。查了整整一周,最后用示波器量 DQS 信号才发现——时序偏差了 0.3ns。0.3ns,就这么点,系统就不稳定。
阶段四:U-Boot 主程序——真正的「系统管家」
U-Boot 主程序运行在 DDR 中,空间充裕了,能干的事也多起来。它不再像 BootROM 和 SPL 那样「缩手缩脚」。
U-Boot 的主要职责:
- 板级初始化:初始化网卡、USB、存储控制器、显示控制器等。这些外设的驱动代码都在 U-Boot 里。
- 环境变量加载:从存储介质读取 bootargs、bootcmd 等环境变量。这些变量决定了内核怎么启动。
- 内核加载与启动:从文件系统(FAT、ext4、UBIFS)或网络(TFTP)读取内核镜像,解析设备树,然后跳转到内核入口。
这里有个细节——U-Boot 启动内核时,会做一件很重要的事:关闭 MMU 和 Cache。因为内核启动时有自己的页表管理方式,U-Boot 必须把硬件状态恢复到「干净」的状态。
核心命令示例:
# 从 SD 卡加载内核到内存地址 0x82000000
fatload mmc 0:1 0x82000000 zImage
# 加载设备树
fatload mmc 0:1 0x83000000 imx6ull.dtb
# 设置启动参数
setenv bootargs console=ttymxc0,115200 root=/dev/mmcblk0p2 rootwait
# 启动内核
bootz 0x82000000 - 0x83000000
阶段五:操作系统启动——从「裸机」到「系统」
U-Boot 跳转到内核入口后,它的使命就完成了。内核接手后,会做以下几件事:
- 解压自身:如果内核是压缩的(zImage),先自解压。
- 初始化内核核心子系统:调度器、内存管理、中断系统。
- 设备驱动加载:根据设备树,逐个初始化外设驱动。
- 挂载根文件系统:从指定的存储设备挂载根文件系统。
- 启动 init 进程:通常是 /sbin/init,然后由 init 启动所有用户态服务。
到这一步,整个启动链路就走完了。从芯片上电到用户看到登录提示符,中间经历了五级接力。
BootROM 与 U-Boot 的分工协作
很多人搞不清楚 BootROM 和 U-Boot 到底谁该干什么。我打个比方:
- BootROM 是「保安」:它只认固定的流程,不会变通。它的代码是只读的,你改不了。它的任务就是确保系统能安全地启动起来。
- U-Boot 是「管家」:它灵活、可配置、可扩展。你可以通过环境变量改变启动方式,可以通过命令交互调试,可以支持多种启动介质。
它们的分工边界很清晰:
| 维度 | BootROM | U-Boot |
|---|---|---|
| 存储位置 | 芯片内部 ROM(只读) | 外部 Flash(可读写) |
| 可修改性 | 不可修改(出厂固化) | 可修改、可升级 |
| 运行环境 | SRAM(极小,通常 < 256KB) | DDR(大,通常 > 64MB) |
| 功能复杂度 | 极简(仅加载下一级) | 丰富(网络、文件系统、命令行) |
| 安全机制 | 基础签名校验 | 完整的安全启动链 |
我个人习惯把 BootROM 看作「硬件的一部分」。它虽然是一段软件,但它的行为逻辑和硬件一样——固定、可靠、不可变。而 U-Boot 才是真正的「软件」,你可以随意定制。
一个常见的误解:很多人以为 BootROM 里跑的是 U-Boot。不是的。BootROM 是芯片厂商写的,U-Boot 是板级厂商或开发者写的。它们之间通过「启动协议」对接——BootROM 把控制权交给 U-Boot 的 SPL,SPL 再加载完整的 U-Boot。
整条链路的可视化
下面这张图把整个启动流程串起来了。我建议你多看几遍,把每个阶段的「输入-处理-输出」理清楚。
几个需要记住的关键点
- BootROM 是起点,不是终点:它只负责把下一级代码拉起来,然后就退出了。你没法在 BootROM 里加功能。
- SPL 是瓶颈:它必须在 SRAM 里完成 DDR 初始化。SRAM 大小决定了 SPL 的复杂度上限。我见过有些芯片的 SRAM 只有 32KB,SPL 开发者得精打细算到每个字节。
- U-Boot 是灵活性所在:几乎所有板级定制都在 U-Boot 里完成。换 Flash 型号、改 DDR 参数、加网络协议栈——都在 U-Boot 层面改。
- 安全启动链:从 BootROM 到 U-Boot 到内核,每一级都要校验下一级的签名。只要有一级被篡改,系统就拒绝启动。这是防篡改的核心机制。
一个血的教训:我曾经在一个量产项目里,为了省成本换了颗便宜的 Flash 芯片。结果 BootROM 读 SPL 时总是超时——因为新 Flash 的读速度比原来的慢了一倍。BootROM 的读超时阈值是固定的,改不了。最后只能换回原来的 Flash。所以,换存储介质前,一定要确认 BootROM 的时序兼容性。