1. Bootloader 基础概念

做嵌入式开发这些年,我经常遇到刚入行的朋友问一个问题:「板子上电后,程序是怎么跑起来的?」

嗯,这个问题其实就引出了我们今天的主角——Bootloader。

什么是 Bootloader?

说白了,Bootloader 就是一块芯片上电后最先执行的程序。它不像你的应用程序那样功能丰富,它的任务很纯粹:初始化硬件,加载操作系统,然后把控制权交出去。

我习惯把它比作「看门大爷」。你想想看,一栋大楼(嵌入式系统)早上开门,看门大爷先检查水电(硬件初始化),确认电梯能跑(内存正常),然后通知各个部门的人来上班(加载内核)。等人都到齐了,大爷就退到一边,不再插手日常工作。

Bootloader 也是这个道理。它完成使命后,就把 CPU 的控制权交给操作系统内核,自己「退休」了。

核心要点:Bootloader 是硬件与操作系统之间的桥梁,它负责从「上电复位」到「内核启动」之间的所有准备工作。

Bootloader 在嵌入式系统中的作用

我在项目中见过不少因为 Bootloader 没写好导致整机无法启动的案例。它的作用,比你想象的要重要得多。

  • 硬件初始化:设置时钟频率、配置内存控制器、初始化串口等外设。没有这一步,后面的代码连运行环境都没有。
  • 内存布局规划:告诉系统哪些地址是 RAM,哪些是 ROM,哪些是外设寄存器。这个搞错了,程序跑飞是迟早的事。
  • 加载操作系统:从 Flash、SD 卡、网络等介质中读取内核镜像,放到内存指定位置,然后跳转过去执行。
  • 提供调试与恢复手段:大多数 Bootloader 都支持命令行交互,方便你烧写固件、修改参数、诊断问题。
  • 传递启动参数:给内核传递设备树、根文件系统位置、控制台参数等信息。

我的经验:曾经有一块板子,上电后串口死活没输出。查了两天,最后发现是 Bootloader 里串口时钟配置错了。从那以后,我每次写 Bootloader 都会先确认串口能不能正常工作——这是你调试的「眼睛」。

常见的 Bootloader 类型

嵌入式世界里 Bootloader 种类不少,但真正广泛使用的就那么几个。我整理了一个表格,方便你对比:

名称 适用架构 特点 我的评价
U-Boot ARM、x86、RISC-V、MIPS 等 功能最全,社区活跃,支持大量板级配置 嵌入式界的「Linux」,几乎成了事实标准
Barebox ARM、x86、MIPS 代码更简洁,设计更现代,POSIX 风格 API 我个人比较喜欢,适合想深入理解的人
GRUB x86、x86_64 PC 标准 Bootloader,支持多系统启动 嵌入式用得少,但 x86 平台绕不开它
RedBoot ARM、MIPS、PowerPC 基于 eCos,支持网络启动和调试 现在基本被 U-Boot 取代了,老项目里还能见到
SPL/TPL ARM(主要是 TI、Freescale 平台) 分阶段加载,解决片上 SRAM 太小的问题 U-Boot 的衍生方案,很多 SoC 默认就用它

Bootloader 的启动流程

不管用哪种 Bootloader,它们的启动流程都大同小异。我画了一张图,帮你理清这个逻辑:

Bootloader 典型启动流程 上电复位 CPU 从复位向量取指 Stage 1:汇编级初始化 关中断 · 设置栈指针 · 初始化时钟 Stage 2:C 语言环境初始化 BSS 清零 · 内存控制器 · 串口初始化 从存储介质加载内核 跳转到内核入口 说明 Stage 1 通常只有几百字节 用汇编写,放在片上 SRAM 中 Stage 2 用 C 写,功能更丰富

U-Boot 为什么是主流?

你可能要问:这么多 Bootloader,为什么偏偏 U-Boot 成了主流?

我个人的看法是,U-Boot 赢在「生态」二字。它支持的板子太多了,从低端的 Cortex-M 到高端的 Cortex-A72,从老旧的 PowerPC 到新兴的 RISC-V,几乎都能跑。你遇到的大多数问题,网上都能找到答案。

另外,U-Boot 的命令行非常强大。我记得有一次调试一块新板子,内存参数没配好,系统总是随机崩溃。我就在 U-Boot 命令行里用 md 和 mw 命令反复读写内存,最终定位到是时序问题。这种调试手段,其他 Bootloader 很难替代。

注意:U-Boot 虽然功能强大,但代码量也大,编译时间较长。如果你的项目对启动速度要求极高,或者片上 SRAM 非常有限,可以考虑 Barebox 或者自己写精简的 SPL。

Barebox 的特点

Barebox 是 U-Boot 的一个「现代派」替代品。它的代码结构更清晰,API 设计更接近 Linux 内核风格。如果你熟悉 Linux 驱动开发,上手 Barebox 会非常快。

我曾在一个小型物联网项目里用过 Barebox。当时选它的原因很简单:U-Boot 的配置太复杂了,而 Barebox 的 Kconfig 和设备树支持做得更干净。项目后期维护起来确实省心不少。

不过也要说实话,Barebox 的社区规模和板级支持远不如 U-Boot。如果你用的是比较冷门的 SoC,很可能找不到现成的配置,得自己从头移植。

Bootloader 与 Buildroot 的关系

讲到这里,你可能已经明白了:Bootloader 是系统启动的第一步。而在 Buildroot 中,我们可以很方便地选择、配置和编译 Bootloader,把它和内核、根文件系统打包在一起,生成完整的固件镜像。

这也是本课程的核心——用 Buildroot 来管理你的 Bootloader 分区。后面的章节里,我会带你一步步配置 U-Boot,调整分区表,处理环境变量,以及应对各种实际项目中遇到的坑。

一个小建议:刚开始学的时候,别急着改代码。先用 Buildroot 默认配置编译一遍,烧到板子上看看串口输出。亲眼看到 Bootloader 跑起来,比看十遍文档都管用。