一、U-Boot概述:Bootloader、历史地位与启动流程

1.1 什么是Bootloader?

先问个问题:你按下嵌入式设备的电源键后,发生了什么?

CPU上电后,它的程序计数器(PC)会指向一个固定地址。这个地址里放的,就是Bootloader。说白了,Bootloader就是硬件上电后运行的第一段程序。它的任务很简单——把操作系统从存储介质里搬出来,放到内存里,然后跳转过去执行。

我刚开始做嵌入式时,总觉得Bootloader可有可无。直到有一次,我在一块新板子上直接烧了Linux内核,结果CPU根本不理我。嗯,后来才明白:没有Bootloader,CPU连内存都初始化不了,更别提加载内核了。

Bootloader的核心职责就三条:

  • 硬件初始化——设置时钟、配置内存控制器、初始化串口等外设
  • 加载操作系统——从Flash、SD卡、网络等介质读取内核镜像
  • 传递启动参数——把硬件信息、命令行参数交给内核

关键认知:Bootloader是硬件与操作系统之间的桥梁。没有它,操作系统就是空中楼阁。

1.2 U-Boot的历史与地位

U-Boot的全称是"Universal Boot Loader"。它最初是德国DENX软件工程中心的Wolfgang Denk在1999年发起的项目。说实话,当年Bootloader领域挺乱的——ppcboot、armboot各玩各的,互不兼容。

U-Boot的出现,说白了就是要把这些碎片统一起来。它合并了ppcboot和armboot,支持了PowerPC、ARM、MIPS、x86等几乎所有主流架构。我记得2010年左右,我接手一个基于MIPS架构的项目,当时找了一圈,发现只有U-Boot能稳定支持。从那以后,我对U-Boot就再也没怀疑过。

现在U-Boot的地位如何?

  • 它是嵌入式Linux生态中事实上的标准Bootloader
  • 几乎所有主流SoC厂商(TI、NXP、Rockchip、Allwinner等)都提供U-Boot支持
  • 社区活跃,代码质量高,文档齐全

个人经验:我建议新手从U-Boot入手学习Bootloader。为什么?因为它的代码结构清晰,文档丰富,而且你遇到的90%的问题,网上都能找到答案。相比之下,有些厂商自己魔改的Bootloader,文档不全,出了问题只能看源码硬啃。

1.3 U-Boot在嵌入式系统中的启动流程

U-Boot的启动流程,我把它拆成三个阶段。你想想看,一个Bootloader要完成那么多事,总得有个先后顺序吧?

第一阶段:汇编级初始化(start.S)

这个阶段全部用汇编语言写。为什么?因为这时候C语言运行时环境还没建立起来。具体做了啥:

  1. 设置CPU为SVC模式(超级用户模式)
  2. 关闭中断、MMU、Cache
  3. 初始化时钟和内存控制器
  4. 设置栈指针(SP),为C语言运行做准备
  5. 跳转到C语言的入口函数board_init_f()

避坑指南:我曾经在一个项目中,因为start.S里忘记关MMU,导致后面C代码访问的地址全是虚拟地址,而这时候页表还没建立,结果直接死机。嗯,查了两天才找到原因。所以,第一阶段一定要把MMU关干净。

第二阶段:C语言初始化(board_init_f / board_init_r)

进入C语言后,U-Boot会做两轮初始化:

  • board_init_f():早期初始化。这时候U-Boot还在Flash或ROM里运行。它会初始化串口、定时器,然后规划内存布局——比如把U-Boot自身从Flash拷贝到RAM的哪个位置。
  • board_init_r():后期初始化。这时候U-Boot已经重定位到RAM里了。它会初始化所有外设驱动、文件系统支持、网络协议栈等。

我个人习惯把board_init_f看作"生存模式"——只做最必要的事,保证能活下来。board_init_r才是"正常模式"——把所有功能都打开。

第三阶段:进入命令行或加载内核

初始化完成后,U-Boot会进入主循环:

  • 如果设置了bootdelay(默认2~5秒),等待用户按键进入命令行
  • 如果超时或没有按键,自动执行bootcmd环境变量中的命令
  • bootcmd通常包含:从Flash/SD卡读取内核镜像 → 设置启动参数 → 跳转到内核入口

这里有个细节:U-Boot通过bootm命令启动内核。它会检查镜像头部的魔数(0x27051956),验证CRC,然后把内核解压到指定地址,最后跳转过去。

核心流程总结:

上电 → start.S(汇编初始化)→ board_init_f(早期C初始化)→ 重定位到RAM → board_init_r(完整初始化)→ 进入命令行或自动启动内核

1.4 一张图看懂U-Boot启动流程

下面我用SVG画了一张流程图,把整个启动过程串起来。你对照着看,应该会更清楚。

U-Boot启动流程总览 第一阶段:汇编级初始化 start.S — 设置CPU模式、关中断/MMU、初始化时钟和内存 第二阶段:board_init_f() 早期C初始化 — 串口、定时器、内存布局规划 重定位:U-Boot从Flash拷贝到RAM 第二阶段:board_init_r() 完整初始化 — 外设驱动、文件系统、网络协议栈 第三阶段:命令行 / 自动启动 等待按键 → 执行bootcmd → 加载内核并跳转 用户按键 进入U-Boot命令行 超时/无按键 自动启动内核 阶段一 阶段二 阶段三

1.5 小结

U-Boot作为嵌入式Linux的"看门人",它的重要性怎么强调都不过分。我个人觉得,理解U-Boot的启动流程,是嵌入式工程师从"会用"到"懂原理"的关键一步。

这一章我们聊了:

  • Bootloader是什么,它解决了什么问题
  • U-Boot的历史——从碎片化到统一标准
  • 三阶段启动流程——汇编初始化、C语言初始化、加载内核

下一章,我们会深入U-Boot的源码结构,看看那些关键的目录和文件到底在干什么。嗯,到时候我会分享一些我踩过的坑,保证有用。

给新手的建议:如果你手头有开发板,现在就去编译一个U-Boot,烧进去,然后打开串口看启动日志。对照着这一章的流程图,一行一行地看。相信我,这样学比看十遍文档都管用。

可可精品资料库