第4章 U-Boot移植基础
好,咱们开始聊U-Boot。说实话,做嵌入式Linux开发,U-Boot是绕不开的第一道坎。很多新手觉得它就是个开机引导程序,随便配配就能跑。嗯,我当年也这么想,结果被RK3288的板子折腾了整整一周。
U-Boot全称是Universal Boot Loader,说白了就是一段在操作系统启动前运行的代码。它的任务很简单:初始化硬件、加载内核、启动系统。但简单背后藏着不少门道。
U-Boot简介
U-Boot支持ARM、x86、RISC-V等几乎所有主流架构。瑞芯微的芯片用的是ARM架构,所以咱们重点看ARM这一支。
我个人习惯把U-Boot分成三个阶段:
- SPL(Secondary Program Loader):第一级引导,负责初始化DDR和基本外设
- TPL(Tertiary Program Loader):有些芯片需要,比如RK3399,用来做更精细的初始化
- U-Boot proper:完整的U-Boot,提供命令行、网络、文件系统支持
你可能会问:为什么搞这么复杂?直接一个U-Boot不行吗?
原因很简单:芯片内部SRAM太小了。RK3288的SRAM只有8KB,连个完整的U-Boot都塞不下。所以必须分阶段加载。
核心要点:SPL负责把U-Boot proper从存储介质(eMMC、SD卡、SPI Flash)加载到DDR中,然后跳转执行。
瑞芯微U-Boot源码结构
瑞芯微的U-Boot是基于主线U-Boot维护的,但加了不少私货。你从Rockchip官方GitHub拉下来的代码,目录结构大概是这样的:
u-boot/
├── arch/
│ └── arm/
│ ├── mach-rockchip/ # 瑞芯微平台相关代码
│ ├── dts/ # 设备树文件
│ └── include/ # 头文件
├── board/
│ └── rockchip/ # 板级代码
│ ├── evb_rk3288/ # 开发板
│ ├── evb_rk3399/
│ └── ...
├── configs/ # defconfig配置文件
├── dts/ # 设备树源文件
└── drivers/ # 驱动代码
我记得第一次看这个目录结构时,最困惑的是board/rockchip/和arch/arm/mach-rockchip/的区别。后来才搞明白:
mach-rockchip/:放的是SoC级别的代码,比如时钟、复位、DDR初始化board/rockchip/:放的是板级代码,比如GPIO配置、外设初始化
说白了,一个管芯片内部,一个管板子外部。
板级配置(defconfig)与设备树(dts)的关系
这是很多新手容易搞混的地方。defconfig和设备树到底谁管谁?
我打个比方:
- defconfig:像是一份菜单,告诉U-Boot「我要吃哪些菜」(启用哪些功能)
- 设备树:像是菜谱,告诉U-Boot「这些菜怎么做」(硬件怎么配置)
举个例子,RK3288的defconfig里有一行:
CONFIG_DM_MMC=y
这表示启用MMC驱动框架。但具体MMC控制器挂在哪根总线上、时钟频率多少、用的是哪个GPIO做检测,这些信息都在设备树里:
&dwmmc@ff0f0000 {
compatible = "rockchip,rk3288-dw-mshc";
reg = <0x0 0xff0f0000 0x0 0x4000>;
clocks = <&cru HCLK_MMC>, <&cru SCLK_MMC>;
bus-width = <8>;
status = "okay";
};
你看,defconfig只管「要不要」,设备树管「怎么要」。两者缺一不可。
我的经验:调试时先确认defconfig里有没有打开对应的驱动选项。我曾经花了两天查一个网卡不工作的问题,最后发现是defconfig里忘了配CONFIG_DM_ETH。嗯,这种低级错误犯一次就够了。
编译U-Boot
编译U-Boot其实不复杂,但有几个坑要注意。以RK3288为例,完整流程如下:
# 1. 设置交叉编译工具链
export CROSS_COMPILE=arm-linux-gnueabihf-
# 2. 清理之前的编译产物
make distclean
# 3. 加载板级配置
make evb-rk3288_defconfig
# 4. 编译
make -j4
编译完成后,你会得到几个关键文件:
| 文件 | 说明 |
|---|---|
| u-boot.bin | 完整的U-Boot镜像 |
| u-boot-dtb.bin | U-Boot + 设备树合并后的镜像 |
| spl/u-boot-spl.bin | SPL镜像 |
| u-boot.itb | FIT格式镜像(用于较新芯片) |
避坑指南:我曾经在编译RK3399时,直接用make -j8,结果编译到一半报错。后来发现是并行编译导致依赖关系没处理好。建议第一次编译先用make -j1,确认没问题再开多线程。
编译完成后,怎么烧录到板子上?常见方式有两种:
- SD卡启动:用
dd命令把镜像写到SD卡指定偏移位置 - USB烧录:瑞芯微的板子支持RockUSB模式,用
rkdeveloptool工具烧录
我个人更推荐先用SD卡调试。原因很简单:改错了换张卡就行,不用反复擦写eMMC。
SD卡烧录命令示例:
# 假设SD卡设备是/dev/sdb
sudo dd if=idbloader.img of=/dev/sdb seek=64
sudo dd if=u-boot.itb of=/dev/sdb seek=16384
这里的seek参数对应的是SD卡的扇区偏移量。不同芯片的偏移量不一样,RK3288和RK3399就不同。具体数值可以查芯片的TRM文档。
最后说一句:U-Boot移植说白了就是三板斧——配defconfig、改设备树、调驱动。把这章的内容吃透了,后面讲内核移植时你会轻松很多。