3、U-Boot源码结构分析:掌握U-Boot的目录与核心文件
拿到一份U-Boot源码,第一反应往往是——这么多文件夹,从哪看起?
我刚开始接触U-Boot时也懵过。那时候领导丢给我一块新板子,说“把U-Boot移植上去”。我盯着源码树发呆了好一阵子。后来摸爬滚打多了,才慢慢理清这套结构的设计思路。
说白了,U-Boot的目录布局是有章可循的。它把“架构相关”、“板级相关”、“通用功能”这三层剥得清清楚楚。你只要抓住这个主线,源码就不再是一团乱麻。
3.1 顶层目录:一眼看穿全局
先看顶层。U-Boot源码根目录下大概有二十几个文件夹。我习惯把它们分成四类:
| 类别 | 目录名 | 一句话说明 |
|---|---|---|
| 架构核心 | arch/ | CPU架构相关代码,ARM、RISC-V、x86都在这里 |
| 板级支持 | board/ | 各家开发板、产品的板级初始化代码 |
| 通用驱动 | drivers/ | 串口、网卡、MMC、Flash等外设驱动 |
| 工具脚本 | tools/ scripts/ | 编译工具、打包脚本、环境配置 |
嗯,这四类基本覆盖了U-Boot的全部功能。你想想看,一个Bootloader要做什么?初始化硬件、加载内核、传递参数。这些事分别对应到arch、board、drivers里。
我的习惯:拿到一个新板子,先看board/和arch/下的代码。这两个目录决定了“这板子能不能跑起来”。drivers/可以后面再细看。
3.2 arch/目录:CPU的“身份证”
arch/是架构相关代码的大本营。以ARM为例,路径是arch/arm/。里面又按CPU系列分了子目录:
- cpu/ —— 各种ARM核的启动代码,比如cortex-a、cortex-m
- include/ —— 架构相关的头文件,寄存器定义、内存映射
- lib/ —— 架构公用的库函数,比如cache操作、MMU设置
- mach-*/ —— SoC厂商相关的代码,比如mach-sunxi(全志)、mach-imx(NXP)
我在项目中遇到过一个问题:同一颗ARM Cortex-A7核,在不同SoC上启动流程居然不一样。原因就在mach-*目录里。SoC厂商会在这里加上自己的“私货”——比如特殊的时钟配置、安全启动检查。
避坑指南:我曾经以为换了CPU核就要重写arch/下的代码。后来发现,大部分时候只需要改mach-*目录里的板级初始化函数。CPU核的通用启动代码(比如cpu/目录下的文件)基本不用动。
3.3 board/目录:板子的“个性”
board/目录存放的是板级代码。每个板子一个子目录,比如board/ti/am335x/。这里面最重要的文件是:
- board.c —— 板级初始化入口,调用各种外设的初始化函数
- Kconfig —— 板子的配置选项,决定哪些功能被编译进去
- MAINTAINERS —— 这个板子谁维护的,出问题找谁
你可能会问:board/和arch/mach-*/有什么区别?
简单说:arch/mach-*/管的是SoC内部的东西(时钟、中断控制器、DDR控制器)。board/管的是板子上的东西(LED、按键、网口PHY地址)。
注意:board/目录不是必须的。如果你的板子和某个参考板完全一样,可以直接用那个板子的配置。但实际项目中,99%的板子都有自己的board/目录。
3.4 核心文件:U-Boot的“骨架”
除了arch/和board/,还有几个文件是U-Boot的灵魂。我列个清单:
| 文件 | 路径 | 作用 |
|---|---|---|
| 链接脚本 | arch/arm/cpu/u-boot.lds | 决定代码段的布局,入口点在哪 |
| 配置文件 | configs/xxx_defconfig | 板子的默认配置,编译时用 |
| 设备树 | arch/arm/dts/xxx.dts | 描述硬件资源,驱动通过它找外设 |
| Makefile | 顶层Makefile | 整个工程的编译入口 |
这里我想重点说说链接脚本。我记得有一次调试U-Boot,死活跑不到第二行代码。后来发现是链接脚本里把.text段的起始地址写错了。嗯,链接脚本决定了U-Boot在内存里的“长相”,搞错了连第一条指令都执行不了。
3.5 整体结构图:一张图理清关系
说了这么多,不如一张图来得直观。下面这张SVG图展示了U-Boot源码的核心层次关系:
3.6 编译系统:Kconfig与Makefile的配合
U-Boot使用Kconfig管理配置。你运行make menuconfig时,看到的就是Kconfig生成的菜单。每个目录下的Kconfig文件定义了该目录有哪些配置选项。
举个例子,你想让U-Boot支持FAT文件系统。在fs/目录下的Kconfig里,会有CONFIG_FS_FAT这个选项。选中后,Makefile就会把fs/fat/下的源文件编译进去。
小技巧:我习惯在configs/目录下找最接近的defconfig文件,复制一份再改。比如我的板子用全志H3芯片,我就拿orangepi_zero_defconfig做模板。这样省去了从头配置的麻烦。
3.7 设备树:硬件描述的“通用语言”
设备树(Device Tree)是U-Boot和Linux共享的硬件描述文件。它告诉U-Boot:内存多大、串口在哪个地址、GPIO怎么映射。
设备树文件放在arch/arm/dts/目录下。以.dts结尾的是源文件,.dtb是编译后的二进制文件。U-Boot启动时会解析dtb,然后根据里面的信息初始化外设。
我曾经踩过一个坑:设备树里把串口时钟配错了,结果U-Boot打印出来的全是乱码。查了两天才发现是clock-frequency属性写错了。嗯,设备树这东西,差一个数字都不行。
3.8 小结:源码结构就是“分层”二字
回顾一下,U-Boot源码结构的核心思想就是分层:
- arch/ —— 管CPU和SoC,跟具体板子无关
- board/ —— 管板子上的外设,跟CPU无关
- drivers/ —— 管驱动实现,跟架构和板子都无关
- Kconfig + Makefile —— 管编译,把上面三层串起来