1. 课程导入
大家好,我是你们这门课的老朋友。做嵌入式这么多年,我见过太多工程师在启动流程上栽跟头。说白了,搞懂从按下电源到系统跑起来这整个过程,是咱们移植 U-Boot 和调试启动问题的基本功。今天这第一讲,咱们就把这条启动链从头到尾捋一遍。
1.1 按下电源键之后,芯片在干嘛?
你按下电源键,芯片上电。这时候内存还没初始化,CPU 也处于最原始的状态。芯片内部固化了一段只读代码,叫 BootROM。这段代码是芯片出厂就写死的,你改不了它。
BootROM 的任务很简单:
- 初始化最基本的硬件(比如时钟、片内 SRAM)
- 从预定义的启动介质读取下一级代码
- 把代码加载到片内 SRAM 并跳转执行
我当年调试一块新板子,死活起不来。查了三天,最后发现是 BootROM 的启动介质选择引脚电平没拉对。嗯,这种坑踩过一次就记住了。
1.2 SPL:第一级引导加载程序
BootROM 加载的下一级代码,通常叫 SPL(Secondary Program Loader)。为什么需要 SPL?因为 U-Boot 本体太大了,片内 SRAM 装不下。SPL 就是个小号的加载器,负责初始化 DDR 内存,然后把完整的 U-Boot 从存储介质搬到内存里。
SPL 的特点:
- 体积小,通常几十 KB
- 运行在片内 SRAM 中
- 只做必要的初始化:时钟、DDR、存储控制器
- 加载 U-Boot 到 DDR 后跳转
我记得有一次移植新平台,SPL 加载 U-Boot 时总报校验错误。查到最后,是 DDR 时序参数配错了,导致数据读出来是乱的。所以我说,DDR 初始化是 SPL 里最关键的环节,没有之一。
1.3 U-Boot:真正的引导加载程序
U-Boot 被 SPL 加载到 DDR 后,就接管了控制权。它比 SPL 复杂得多,功能也丰富得多。U-Boot 主要做这几件事:
- 完整硬件初始化:串口、网卡、存储设备、USB 等
- 加载内核:从 Flash、SD 卡、网络等介质读取内核镜像
- 传递启动参数:通过设备树(DTB)和内核命令行参数告诉内核硬件信息
- 跳转到内核:准备好环境后,把控制权交给内核
关键点:U-Boot 和内核之间的接口是设备树(Device Tree)。设备树描述了硬件信息,U-Boot 把它加载到内存,把地址告诉内核。如果设备树不对,内核要么认不出硬件,要么直接崩溃。
1.4 内核启动与根文件系统挂载
U-Boot 跳转到内核后,内核开始执行。内核会:
- 解压自身(如果是压缩镜像)
- 初始化内存管理、进程调度、中断系统
- 枚举并初始化所有硬件设备
- 挂载根文件系统
- 启动第一个用户空间进程(通常是 init)
根文件系统是操作系统运行的基础。它包含了所有用户空间的程序、库、配置文件。没有根文件系统,内核启动到一半就会 panic,因为找不到 init 进程。
你想想看,整个启动链就像接力赛:BootROM 传给 SPL,SPL 传给 U-Boot,U-Boot 传给内核,内核挂载根文件系统。每一棒都不能掉。
1.5 完整启动链全景图
下面这张图把整个流程串起来了。我建议你把它存下来,后面调试的时候对照着看。
1.6 各阶段对比表
为了让你更清楚地看到每个阶段的特点,我整理了一张对比表。这张表在调试时特别有用,能帮你快速定位问题出在哪个环节。
| 阶段 | 运行位置 | 代码大小 | 主要任务 | 可修改性 |
|---|---|---|---|---|
| BootROM | 片内 ROM | 几 KB | 初始化时钟、加载 SPL | 不可修改 |
| SPL | 片内 SRAM | 几十 KB | 初始化 DDR、加载 U-Boot | 可修改(源码级) |
| U-Boot | DDR 内存 | 几百 KB ~ 几 MB | 加载内核、设备树、传递参数 | 可修改(源码级) |
| Linux 内核 | DDR 内存 | 几 MB ~ 几十 MB | 硬件初始化、进程管理、挂载根文件系统 | 可修改(源码级) |
| 根文件系统 | 存储介质 | 几 MB ~ 几百 MB | 提供用户空间程序、库、配置文件 | 可修改(文件级) |
1.7 调试启动问题的基本思路
做 U-Boot 移植,说白了就是跟这条启动链打交道。问题可能出在任何一环。我个人习惯的调试思路是这样的:
- 先确认硬件:电源、时钟、复位信号对不对?串口能不能输出?
- 看 BootROM 阶段:启动介质选对了没有?SPL 有没有被正确加载?
- 看 SPL 阶段:DDR 初始化成功了吗?U-Boot 加载到内存后校验通过了吗?
- 看 U-Boot 阶段:U-Boot 能正常启动吗?能识别存储设备吗?能加载内核吗?
- 看内核阶段:内核解压成功了吗?设备树解析正确吗?根文件系统挂上了吗?
1.8 为什么选择 Buildroot + U-Boot?
你可能要问,市面上这么多构建工具,为什么咱们这门课选 Buildroot?
- 简单直接:Buildroot 的配置体系清晰,不像 Yocto 那么庞大复杂
- 全链路覆盖:从交叉工具链、U-Boot、内核到根文件系统,一条命令全搞定
- 适合调试:修改配置后重新编译很快,迭代效率高
- 社区活跃:遇到问题容易找到解决方案
我个人做项目时,原型阶段基本都用 Buildroot。等产品定型了,再考虑换更复杂的构建系统。你想想看,调试阶段最重要的是什么?是快速验证想法。Buildroot 在这方面做得很好。