4. 设备树编译与反编译
设备树从源码到内核可用,必须经过编译工具链的转换。Linux 内核源码树中的 scripts/dtc/ 目录提供了设备树编译器 dtc(Device Tree Compiler),它负责把人类可读的 .dts 源文件编译成内核启动阶段可解析的二进制 .dtb 文件,也支持反向把 .dtb 还原为 .dts。理解这条“源码 ↔ 二进制”的双向通路,是驱动适配工程师定位设备树问题的基本功。
一、dtc 工具链与编译流程
设备树编译并不是单一命令,而是一条包含预处理、语法解析、语义检查、二进制生成的流水线。内核构建系统通常通过 make dtbs 触发,其底层调用的正是 dtc。手工编译时,我们直接调用 dtc 即可完成同样的工作。
编译过程中,dtc 会先处理 #include 头文件(如 skeleton.dtsi、SoC 级 .dtsi),再解析节点与属性,最后按 DTB 规范生成扁平化设备树(Flattened Device Tree)。DTB 的头部包含魔数、总大小、结构块偏移、字符串块偏移等元信息,内核 early_init_dt_scan 阶段会据此逐块解析。
二、DTS 编译为 DTB 的实操方法
手工编译设备树时,最常用的命令形式如下。其中 -I 指定输入格式,-O 指定输出格式,-o 指定输出文件名,-@ 用于生成 __symbols__ 节点以支持运行时 overlay 引用。
# 基础编译:dts → dtb
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 带符号信息,便于 overlay 使用
dtc -@ -I dts -O dtb -o myboard.dtb myboard.dts
# 内核构建系统中编译全部 dtb
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
# 指定单个目标
make ARCH=arm64 rockchip/rk3568-evb.dtb
编译成功后,可用 fdtdump 或 dtc -I dtb -O dts 快速检查生成结果。若编译报错,dtc 会输出带行号的错误信息,这是排查语法问题的第一手线索。
三、DTB 反编译为 DTS 的典型场景
反编译在驱动适配中非常实用。厂商提供的固件往往只附带 .dtb,而我们需要知道其中真实的节点、地址、中断号和时钟配置。此时用 dtc 反编译即可还原出可读文本。
# 反编译 dtb 为 dts
dtc -I dtb -O dts -o recovered.dts vendor.dtb
# 查看反编译结果中的关键节点
grep -n "ethernet\|mdio\|phy" recovered.dts
# 使用 fdtdump 快速浏览结构
fdtdump vendor.dtb | head -50
#include 结构,属性顺序也可能变化。它适合用于“读取事实”,不适合直接作为长期维护的源码。正确做法是把反编译结果作为参考,回写到规范的板级 DTS 中。
四、常见编译错误与处理清单
设备树编译错误大致分为语法错误、引用错误、语义错误和工具链错误四类。下表汇总了高频问题、典型日志与处理方式。
| 错误类型 | 典型日志 | 原因 | 处理方式 |
|---|---|---|---|
| 语法错误 | ERROR (syntax): ... expected ; |
缺少分号、括号不匹配 | 按行号定位,补齐 ; 或 } |
| 引用错误 | ERROR: Label or path not found |
引用了未定义的 label 或节点 | 检查 &label 是否在包含的 dtsi 中定义 |
| 属性类型错误 | Warning: ... cells |
reg、interrupts 单元数不匹配 | 核对 #address-cells、#size-cells |
| 重复定义 | ERROR: Duplicate node name |
同一层级出现同名节点 | 合并节点或重命名 |
| 工具链错误 | dtc: command not found |
未安装或未指定路径 | 安装 device-tree-compiler 或使用内核自带 dtc |
五、案例复盘:一次 PHY 节点编译失败
某项目中,工程师在板级 DTS 中新增以太网 PHY 节点后,make dtbs 报出 Label or path ethernet0 not found。排查发现,该 label 定义在 SoC 级 .dtsi 中,但板级 DTS 未包含对应头文件。补上 #include "rk3568.dtsi" 后编译通过。这个案例说明:设备树编译错误往往不是“写错了”,而是“引用链断了”。
dtc 的 Warning。很多 Warning 在启动阶段会演变为设备探测失败,例如 reg 单元数不匹配会导致内核无法正确映射寄存器地址。建议在 CI 中把 -Werror 打开,让警告直接阻断构建。
掌握 dtc 的正向编译与反向反编译,等于掌握了设备树的“读写能力”。编译用于交付,反编译用于取证,两者结合才能在驱动适配中快速定位配置问题。