4. 设备树编译与反编译
设备树源文件(DTS)是工程师可读的文本描述,而内核在启动阶段真正解析的是二进制设备树(DTB)。两者之间的桥梁就是 DTC(Device Tree Compiler)工具链。理解 DTC 的安装、编译、反编译以及错误定位方法,是板级移植中最基础也最高频的工程能力。本章围绕“DTS → DTB → DTS”的闭环,讲清工具链使用与调试思路。
一、核心原理:DTC 在构建链中的位置
DTC 是一个独立的开源工具,源码位于内核源码树的 scripts/dtc/ 目录,也可以单独从 devicetree.org 获取。它的核心职责是把人类可读的 DTS 文本,经过词法分析、语法分析、语义检查后,生成扁平化设备树 Blob(Flattened Device Tree,FDT),即 DTB。内核启动时由 bootloader 把 DTB 地址传给内核,内核用 unflatten_device_tree() 将其展开为运行时结构。
DTC 支持三种主要操作:编译(-I dts -O dtb)、反编译(-I dtb -O dts)以及格式转换。编译过程会处理 #include 头文件、/include/ 包含文件、标签引用(&label)、/delete-node/、/delete-property/ 等语法,最终输出带结构块和字符串块的二进制。反编译则是把 DTB 还原成可读 DTS,常用于分析厂商提供的闭源 DTB。
二、DTC 工具链安装
在 Ubuntu/Debian 上最直接的方式是安装发行版包:
sudo apt update
sudo apt install device-tree-compiler
dtc --version
若需要与内核版本严格匹配的 DTC,建议从内核源码构建:
cd linux-6.x/scripts/dtc
make
sudo cp dtc /usr/local/bin/
# 或在内核根目录执行
make dtbs_check
交叉编译场景下,内核构建系统会自动调用 scripts/dtc/dtc,无需手动安装。若使用 Yocto/Buildroot,DTC 由构建系统作为 host 工具自动准备,工程中只需关注 DTS 本身。
三、DTS 编译为 DTB
最常用的命令形式如下,其中 -@ 表示生成 __symbols__ 节点以支持运行时 overlay:
dtc -I dts -O dtb -o board.dtb board.dts
dtc -@ -I dts -O dtb -o board.dtb board.dts
# 内核构建系统中
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- dtbs
编译时若 DTS 引用了 .dtsi,需保证 -i 包含路径正确:
dtc -I dts -O dtb -i include/ -i arch/riscv/boot/dts/ \
-o board.dtb board.dts
-Wno-unit_address_vs_reg 等选项按需抑制警告,但不要盲目关闭所有警告,很多警告正是地址与 reg 不匹配的真实问题。四、DTB 反编译为 DTS
拿到厂商提供的 DTB 或排查启动问题时,反编译是第一步:
dtc -I dtb -O dts -o decompiled.dts board.dtb
# 查看结构而不落盘
dtc -I dtb -O dts board.dtb | less
# 查看头部信息
fdtdump board.dtb | head -40
反编译结果会丢失注释、宏和 #include 展开前的原始写法,但节点、属性、phandle 关系完整保留,适合做差异对比:
diff -u vendor.dts decompiled.dts
五、常见编译错误与调试方法
| 错误/警告 | 典型原因 | 处理方法 |
|---|---|---|
| syntax error, expected ... | 缺少分号、括号不匹配、属性值格式错误 | 按行号定位,检查上一行结尾 |
| Label or path not found | &label 引用的标签未定义或拼写错误 | 确认 .dtsi 已包含且标签存在 |
| unit_address_vs_reg 警告 | 节点名含地址但无 reg,或反之 | 补齐 reg 或修正节点名 |
| Duplicate node name | 同一父节点下节点名重复 | 重命名或使用 /delete-node/ |
| ERROR: Input tree has errors | 语义检查失败,如 phandle 悬空 | 用 -f 强制输出后反编译核对 |
| FDT_ERR_BADMAGIC | DTB 未正确加载或地址错误 | 检查 bootloader 传参和 DTB 路径 |
-f 强制输出掩盖错误。强制生成的 DTB 可能结构不完整,内核启动时表现为“设备树解析失败”或外设全部丢失,反而增加排查难度。六、案例与复盘:一次 I2C 节点丢失的排查
某 RISC-V 板卡启动后 I2C 控制器不可用。排查步骤:先用 dtc -I dtb -O dts 反编译实际加载的 DTB,发现 i2c0 节点存在但 status 为 "disabled";再回到 DTS 源文件,发现板级 DTS 中 &i2c0 { status = "okay"; }; 被写在 #include 之前,导致覆盖顺序错误。调整包含顺序后重新编译,问题解决。
复盘要点:一是编译通过不等于语义正确,必须反编译核对最终 DTB;二是覆盖顺序、标签引用、/delete-property/ 的作用域要清晰;三是把 dtc -I dtb -O dts 作为板级调试的常规动作,而不是最后手段。