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 / .dtsi 源文件 节点、属性、#include 人类可读文本 board.dts + soc.dtsi dtc 编译器 预处理 → 解析 → 检查 -I dts -O dtb -@ 支持符号引用 .dtb 二进制 魔数 0xd00dfeed 结构块 / 字符串块 内核可解析 编译 生成 反编译 还原 dtc -I dtb -O dts 用于分析厂商固件 验证实际生效配置 $ dtc -I dts -O dtb -o board.dtb board.dts # DTS 编译为 DTB $ dtc -I dtb -O dts -o board.dts board.dtb # DTB 反编译为 DTS

二、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
反编译得到的 DTS 会丢失原始注释、宏定义和 #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 的正向编译与反向反编译,等于掌握了设备树的“读写能力”。编译用于交付,反编译用于取证,两者结合才能在驱动适配中快速定位配置问题。