1. U-Boot概述:U-Boot的历史、作用与在嵌入式系统中的位置
各位同学,咱们今天聊聊U-Boot。说实话,很多做嵌入式开发的朋友,对U-Boot的态度就是“能用就行”。但真到了板子起不来、内核挂在那不动的时候,才发现自己对这位“看门大爷”几乎一无所知。
我刚开始做嵌入式那会儿,也踩过这个坑。记得有一次调试一块新板子,串口打印停在“Starting kernel ...”就没了下文。我折腾了三天,查内核、查设备树,最后发现是U-Boot传给内核的bootargs里console参数写错了。嗯,从那以后,我再也不敢小看U-Boot了。
1.1 U-Boot从哪里来?
U-Boot的全称是Universal Boot Loader,通用引导加载程序。它的前身是德国DENX软件工程中心的Wolfgang Denx在1999年发起的PPCBoot项目,专门给PowerPC架构用的。
后来ARM架构火了,PPCBoot在2002年改名为U-Boot,开始支持ARM、MIPS、x86等多种架构。到现在,U-Boot已经支持超过30种CPU架构、上千款开发板,是嵌入式领域事实上的标准引导程序。
核心要点:U-Boot不是Linux内核的一部分,它是一个独立的、开源的裸机程序。它的代码仓库在https://source.denx.de/u-boot/u-boot,你也可以从主线拉下来自己裁剪。
1.2 U-Boot到底在干什么?
说白了,U-Boot就干三件事:
- 初始化硬件——把CPU、内存、串口、网卡、存储控制器这些基础外设“唤醒”
- 加载内核——从Flash、SD卡、网络或USB里把内核镜像读到内存
- 传递控制权——设置好启动参数,跳转到内核入口点,把CPU交给Linux
你想想看,如果没有U-Boot,CPU上电后直接跑Linux内核行不行?理论上可以,但实际不行。因为Linux内核假设自己运行在一个已经初始化好的环境中——内存控制器要配好、MMU要关掉、栈指针要设对。这些脏活累活,U-Boot替你干了。
| 阶段 | U-Boot负责 | Linux内核负责 |
|---|---|---|
| 上电复位 | CPU模式设置、关中断、关MMU | — |
| 内存初始化 | DDR控制器时序配置、内存测试 | 接管页表管理 |
| 外设驱动 | 串口、网卡、存储控制器(轻量级) | 完整驱动框架 |
| 文件系统 | 支持ext4/FAT/UBIFS(只读加载) | 完整VFS |
| 网络协议 | TFTP/NFS(用于下载内核) | 完整TCP/IP栈 |
个人经验:我建议你在项目初期就把U-Boot的串口输出完整保存下来。很多启动故障,光看“Starting kernel ...”之前的那几行打印就能定位问题。我曾经靠一个“DRAM: 512 MiB”后面跟的校验警告,发现是DDR时序参数配错了。
1.3 U-Boot在嵌入式系统中的位置
咱们画一张图,把整个启动流程串起来。这样你就能看清U-Boot到底站在哪个位置。
从这张图你能看到:
- BootROM是芯片出厂固化的,用户改不了。它负责从指定介质加载SPL。
- SPL(Secondary Program Loader)是U-Boot的“迷你版”,负责初始化DDR,然后加载完整的U-Boot。
- U-Boot主程序才是我们平时说的“U-Boot”,它提供命令行、环境变量、网络下载等功能。
- U-Boot把内核加载到内存后,通过
bootm或bootz命令跳转到内核入口,从此U-Boot使命完成。
注意:U-Boot和内核之间通过启动参数(bootargs)和设备树(DTB)通信。如果这两个东西传错了,内核要么起不来,要么起来后找不到串口、网卡。这是调试中最常见的故障点。
1.4 为什么我们要深入学U-Boot?
可能有同学会问:“现在不是有ATF、UEFI、Coreboot这些新东西吗?U-Boot是不是过时了?”
我的回答是:U-Boot依然是嵌入式Linux的事实标准。原因有三:
- 硬件覆盖广——从Cortex-M的MCU到Cortex-A72的MPU,从老旧的AT91到最新的i.MX 8,U-Boot都能跑。
- 社区活跃——主线U-Boot几乎每周都有更新,新芯片的支持很快就能合入。
- 调试手段丰富——U-Boot的命令行本身就是最好的调试工具。你可以手动读内存、写寄存器、测试网卡、烧写Flash。
我个人习惯在项目初期,先把U-Boot的md(内存显示)、mw(内存写)、mm(内存修改)这几个命令玩熟。有一次调试DDR稳定性,我就是用md反复读同一段地址,发现数据偶尔会跳变,才定位到DDR供电纹波过大的硬件问题。
1.5 本章小结
咱们这一章把U-Boot的来龙去脉讲清楚了:
- 它从PPCBoot发展而来,现在支持几乎所有主流嵌入式CPU
- 它负责硬件初始化、加载内核、传递控制权
- 它在启动流程中处于BootROM和Linux内核之间,是承上启下的关键环节
- 掌握U-Boot调试,是嵌入式Linux开发的基本功