第二章:U-Boot源码结构解析

拿到U-Boot源码包,第一件事是什么?

别急着编译。我个人习惯是先逛一圈目录结构。就像搬进新家,总得先看看厨房在哪、卧室多大。源码结构搞清楚了,后面移植、调试、裁剪,心里才有底。

U-Boot的顶层目录,乍一看挺唬人,目录很多。但别慌,核心就几个:board、arch、common、drivers。我当年刚接触时,也是一个个翻过来的。今天带你走一遍,哪些是重点,哪些可以暂时忽略。

核心观点:U-Boot源码是分层设计的。硬件相关代码在arch和board,通用逻辑在common,驱动在drivers。移植时,你改得最多的就是board和arch。

U-Boot 源码核心目录结构 U-Boot 根目录 arch/ board/ common/ arch/arm/ arch/arm64/ arch/x86/ 等 board/厂商/ board/ti/am335x/ board/freescale/ common/ main.c, cli.c command/ 等 drivers/ 移植核心:board/ + arch/ + drivers/

2.1 顶层目录:先看门牌号

解压U-Boot源码后,ls一下,你会看到一堆目录。我挑几个必须知道的说说。

  • arch/ —— 架构相关代码。ARM、ARM64、x86、RISC-V都在这里。你是什么CPU,就进哪个子目录。
  • board/ —— 板级代码。每个厂商、每个板子都有自己的文件夹。移植时,这里是你主要动刀的地方。
  • common/ —— 通用功能。命令行、环境变量、启动流程,都在这里。
  • drivers/ —— 驱动代码。串口、网卡、MMC、NAND Flash……各种外设驱动。
  • include/ —— 头文件。全局的配置、数据结构定义。
  • configs/ —— 默认配置文件。每个板子一个defconfig文件。
  • tools/ —— 工具链。打包、签名、镜像生成工具。
  • doc/ —— 文档。嗯,有时候有用,有时候过时了。

我的习惯:拿到一个新板子,先看configs/里有没有相近的配置,再看board/里有没有同系列的参考板。这样可以少走很多弯路。

2.2 arch/目录:CPU架构的根据地

说白了,arch/就是U-Boot里跟CPU架构绑定的那部分代码。你用的是ARM Cortex-A7,那就进arch/arm/。如果是Cortex-A53,那就是arch/arm64/。

进去之后,你会发现还有子目录:

  • cpu/ —— CPU初始化代码。比如设置异常向量表、使能MMU、初始化Cache。
  • lib/ —— 架构相关的库函数。比如内存拷贝、字符串操作,有些会针对架构优化。
  • include/ —— 架构相关的头文件。寄存器地址、CPU特殊寄存器定义。
  • mach-xxx/ —— 具体SoC厂商的代码。比如mach-sunxi是全志的,mach-imx是NXP的。

我记得有一次移植,板子启动到一半就挂了。查了半天,发现是arch/arm/cpu/里某个Cache初始化宏没定义。嗯,这种问题最坑,因为编译不会报错,但跑起来就崩。

避坑指南:千万不要随意修改arch/下的通用代码。你改了一个文件,可能影响所有同架构的板子。我曾经为了修一个bug,改了arch/arm/lib/下的代码,结果其他同事的板子全挂了……从那以后,我改arch/之前都会先确认有没有更局部的修改方式。

2.3 board/目录:你的板子,你的地盘

这是移植工作的主战场。board/下面按厂商分类,比如board/ti/、board/freescale/、board/allwinner/。每个厂商下再按板子分。

一个典型的板级目录包含:

  • Kconfig —— 板级的配置选项。
  • Makefile —— 编译规则。
  • board.c —— 板级初始化代码。比如DDR初始化、GPIO配置、时钟设置。
  • dts/ —— 设备树文件。现在新版的U-Boot都推荐用设备树来描述硬件。

你想想看,为什么U-Boot要把板级代码单独放?因为不同板子,即使CPU一样,外设、引脚、DDR参数都可能不同。把这些差异隔离在board/里,核心代码就不用动了。

我个人习惯是,新建一个板子时,先复制一份最接近的参考板,然后改DDR参数、改引脚复用、改时钟配置。这样效率最高。

2.4 common/目录:U-Boot的“大脑”

common/里装的是U-Boot的核心逻辑。说白了,就是那些跟具体硬件无关的功能。

几个关键文件:

文件 作用
main.c U-Boot的主入口。启动流程、命令行循环都在这里。
cli.c 命令行解释器。你输入printenv、bootm,就是它解析的。
command/ 各种命令的实现。比如cmd_bootm.c、cmd_nand.c。
env/ 环境变量管理。从哪里加载、保存环境变量。
autoboot.c 自动启动逻辑。倒计时、默认启动命令。

这里有个点要注意:common/里的代码虽然通用,但很多地方用了条件编译。比如CONFIG_CMD_NAND定义了,才会编译NAND命令。所以配置裁剪时,common/是重点优化对象。

2.5 drivers/目录:外设驱动的仓库

U-Boot的驱动模型(Driver Model,简称DM)现在越来越成熟。drivers/下面按设备类型分类:

  • serial/ —— 串口驱动。调试输出全靠它。
  • net/ —— 网卡驱动。TFTP下载、网络启动。
  • mmc/ —— SD卡、eMMC驱动。
  • mtd/ —— NAND Flash、SPI Flash驱动。
  • gpio/ —— GPIO驱动。
  • i2c/、spi/ —— 总线驱动。

每个驱动目录下,又按厂商或芯片型号分文件。比如drivers/net/下面有dwmac.c(DesignWare MAC)、fec.c(Freescale MAC)等等。

移植时,如果板子上的网卡是DesignWare的,你只需要在设备树里配好,U-Boot的DM框架会自动匹配驱动。嗯,这里要注意,设备树里的compatible字符串必须跟驱动里的一模一样,大小写都不能错。我曾经因为一个下划线没对上,折腾了两天。

2.6 其他重要目录

include/ —— 全局头文件。最核心的是include/configs/下的板级配置头文件。老版本的U-Boot用.h文件配置,新版本逐渐迁移到Kconfig + defconfig了。

configs/ —— 默认配置。每个板子一个xxx_defconfig文件。编译时make xxx_defconfig就是读取这里。

dts/ —— 设备树源文件。现在U-Boot和Linux内核共用设备树,这是个好趋势。移植时,设备树写对了,驱动基本就通了一半。

scripts/ —— 编译脚本、链接脚本。比如u-boot.lds,定义了镜像的链接布局。

2.7 我的移植经验总结

说了这么多,总结几条实战经验:

  1. 先看board/和configs/ —— 找最接近的参考板,复制修改。
  2. 设备树优先 —— 能用设备树描述的,就别写死代码。后期维护方便得多。
  3. 驱动别从头写 —— drivers/里大概率有现成的,改改寄存器地址、中断号就行。
  4. 别动common/和arch/的核心代码 —— 除非你很清楚后果。
  5. 每次只改一个模块 —— 改完DDR就编译测试,再改网卡。别一次改一堆,出了问题都不知道是哪里的锅。

一句话总结:U-Boot源码结构,说白了就是“架构分离、板级隔离、驱动模块化”。理解了这个,移植就是填空游戏。

可可精品资料库