4. 设备树编译与反编译
设备树源文件(DTS)是给人阅读和编辑的文本描述,而内核在启动阶段真正解析的是二进制设备树块(DTB)。两者之间的桥梁就是 DTC(Device Tree Compiler)工具链。理解 DTC 的编译与反编译机制,不仅能帮助我们把板级资源正确交给内核,还能在出现“设备树不生效”“节点找不到”“地址映射错误”等问题时快速定位根因。本章围绕 DTC 工具链、DTS 到 DTB 的编译流程、DTB 反编译以及常见错误调试方法展开。
一、DTC 工具链的组成与职责
DTC 是设备树的核心工具,源码位于内核源码树的 scripts/dtc/ 目录,也可独立安装为 device-tree-compiler 软件包。它并不是单一程序,而是一组围绕设备树语法处理、二进制生成与反解析的工具集合。工程中最常用的命令包括:
dtc:主程序,负责 DTS 与 DTB 之间的双向转换,也支持 DTS 到 DTS 的格式化输出。fdtdump:快速查看 DTB 头部与结构块内容,适合初步确认魔数、版本和大小。fdtget/fdtset:读取或修改 DTB 中指定节点的属性值,常用于产线参数注入。dtc -I dts -O dtb:最典型的编译调用形式,输入 DTS,输出 DTB。
DTC 的输入输出格式由 -I(输入格式)和 -O(输出格式)指定,常见格式有 dts、dtb、fs(/proc/device-tree 风格)。编译时还会用到 -o 指定输出文件、-@ 生成符号信息以支持运行时 overlay、-H 指定 phandle 格式等参数。
二、DTS 到 DTB 的编译过程
从 DTS 到 DTB 并不是简单的文本替换,而是一次完整的“解析—展开—校验—序列化”过程。其核心阶段如下:
- 预处理:DTS 通常包含
#include、宏定义和条件编译,需要先经过 C 预处理器(cpp)处理,生成展开后的中间文件。 - 语法解析:DTC 将文本解析为设备树节点树,识别节点、属性、单元地址、标签和引用。
- 引用与 phandle 解析:把
&label、phandle等引用关系解析为具体数值,建立节点间连接。 - 合并与覆盖:处理
/include/、overlay 片段以及板级 DTS 对 SoC 公共 DTSI 的覆盖。 - 校验:检查节点名、属性格式、
reg与#address-cells是否匹配,发现错误则报错退出。 - 序列化:按 DTB 规范生成头部、结构块、字符串块和内存保留块,输出二进制文件。
dtc -I dts -O dtb -o board.dtb board.dts若需支持 overlay:
dtc -@ -I dts -O dtb -o board.dtb board.dts
在内核构建体系中,DTS 通常由 Makefile 自动调用 DTC 编译,例如 make ARCH=arm dtbs 会批量生成 arch/arm/boot/dts/ 下的 DTB 文件。理解这一流程后,就能明白为什么“改了 DTS 但设备没变化”——很可能是没有重新编译 DTB,或者启动时加载的仍是旧 DTB。
三、DTB 反编译与内容核对
当系统启动后设备树行为与预期不符时,第一步不是盲目改 DTS,而是确认“内核实际拿到的 DTB 是什么”。反编译就是把 DTB 还原为可读 DTS,常用命令如下:
# 将 DTB 反编译为 DTS
dtc -I dtb -O dts -o dump.dts board.dtb
# 快速查看 DTB 头部与结构
fdtdump board.dtb | head -n 40
# 读取指定节点属性
fdtget board.dtb /soc/uart@10000000 status
反编译结果可用于核对三类关键信息:节点是否真的存在、属性值是否被正确覆盖、reg 与中断等资源是否与硬件手册一致。尤其在多级 DTSI 包含和板级覆盖场景中,反编译是验证“最终合并结果”的最可靠手段。
四、常见编译错误与调试方法
DTC 报错信息通常比较直接,但需要结合设备树语法理解。下表汇总了工程中最常见的错误类型、典型现象与处理方式:
| 错误类型 | 典型报错/现象 | 定位与处理方法 |
|---|---|---|
| 语法错误 | syntax error, expected ";" | 检查节点末尾分号、大括号配对、属性名拼写 |
| 地址单元不匹配 | reg 长度与 #address-cells 不符 | 核对父节点 cells 定义,调整 reg 单元数量 |
| 引用失败 | Label or path not found | 确认 label 已定义、include 顺序正确 |
| 覆盖未生效 | 反编译后属性仍是旧值 | 检查 DTS 包含顺序与 status 覆盖位置 |
| DTB 未更新 | 设备行为与修改前一致 | 重新编译 DTB,确认启动加载路径正确 |
fdtdump 或 dtc -I dtb -O dts 复核,避免“改了但没生效”的假象。
/include/ 与 C 预处理 #include 行为不同,前者用于设备树片段合并,后者用于宏与头文件展开。混用时若顺序不当,容易出现覆盖失效或重复定义。
掌握 DTC 工具链的编译与反编译能力,是设备树工程实践的基本功。它让 DTS 不再是“黑盒文本”,而是可以被验证、被追踪、被精确控制的板级资源描述。无论是新增外设、调整引脚复用,还是排查启动阶段的资源冲突,围绕 DTB 的反编译核对都能显著缩短调试路径。