第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、改设备树、调驱动。把这章的内容吃透了,后面讲内核移植时你会轻松很多。