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 并不是简单的文本替换,而是一次完整的“解析—展开—校验—序列化”过程。其核心阶段如下:

  1. 预处理:DTS 通常包含 #include、宏定义和条件编译,需要先经过 C 预处理器(cpp)处理,生成展开后的中间文件。
  2. 语法解析:DTC 将文本解析为设备树节点树,识别节点、属性、单元地址、标签和引用。
  3. 引用与 phandle 解析:把 &label、phandle 等引用关系解析为具体数值,建立节点间连接。
  4. 合并与覆盖:处理 /include/、overlay 片段以及板级 DTS 对 SoC 公共 DTSI 的覆盖。
  5. 校验:检查节点名、属性格式、reg 与 #address-cells 是否匹配,发现错误则报错退出。
  6. 序列化:按 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。

DTC 工具链:DTS ⇄ DTB 编译与反编译 DTS 源文件 board.dts / .dtsi 文本、可读、可编辑 预处理 + 解析 cpp 展开宏与 include 节点树 / 引用 / phandle DTB 二进制 头部 + 结构块 字符串块 + 保留内存 dtc -I dts -O dtb 反编译输出 dtc -I dtb -O dts fdtdump / fdtget 反编译 常见编译错误与定位手段 • syntax error:节点/属性缺少分号或大括号不匹配 • reg 与 #address-cells 不匹配:地址单元数量错误 • phandle 引用失败:label 未定义或拼写错误 • 反编译核对:dtc -I dtb -O dts 检查最终生效内容

三、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,确认启动加载路径正确
调试建议:先反编译确认最终 DTB 内容,再回到 DTS 源头修改;每次修改后重新编译并用 fdtdump 或 dtc -I dtb -O dts 复核,避免“改了但没生效”的假象。
注意:DTS 中的 /include/ 与 C 预处理 #include 行为不同,前者用于设备树片段合并,后者用于宏与头文件展开。混用时若顺序不当,容易出现覆盖失效或重复定义。

掌握 DTC 工具链的编译与反编译能力,是设备树工程实践的基本功。它让 DTS 不再是“黑盒文本”,而是可以被验证、被追踪、被精确控制的板级资源描述。无论是新增外设、调整引脚复用,还是排查启动阶段的资源冲突,围绕 DTB 的反编译核对都能显著缩短调试路径。