3. 设备树编译与反编译:DTC工具使用、DTS编译为DTB、DTB反编译为DTS、设备树覆盖(overlay)
设备树源文件(.dts)是文本格式的硬件描述,但内核引导加载程序(如U-Boot)和内核本身无法直接解析文本。必须通过设备树编译器(DTC)将其转换为二进制设备树Blob(.dtb)。本章深入讲解DTC工具链、编译/反编译流程以及设备树覆盖(Overlay)机制,这是实现Platform驱动与设备树匹配的工程基础。
3.1 DTC工具与编译流程
DTC(Device Tree Compiler)是设备树生态的核心工具,通常集成在Linux内核源码的 scripts/dtc/ 目录下。系统安装后可通过 dtc 命令直接调用。
3.1.1 DTC工具安装与版本确认
| 发行版 | 安装命令 | 验证命令 |
|---|---|---|
| Ubuntu/Debian | sudo apt install device-tree-compiler |
dtc --version |
| Fedora/CentOS | sudo dnf install dtc |
dtc --version |
| Arch Linux | sudo pacman -S dtc |
dtc --version |
| Yocto/OpenEmbedded | 默认包含在 native SDK 中 |
dtc -v |
版本提示:推荐使用 DTC 1.6.0 及以上版本,对 Overlay 语法和
/plugin/ 支持更完善。低版本可能无法正确解析 &{label} 引用语法。
3.1.2 DTS → DTB 编译命令
将设备树源文件编译为二进制DTB,是设备树使用的最基本操作。标准命令格式如下:
# 基本编译:输出 DTB 文件
dtc -I dts -O dtb -o my_board.dtb my_board.dts
# 添加调试信息(保留符号)
dtc -I dts -O dtb -@ -o my_board.dtb my_board.dts
# 指定输出格式为 DTB,并显示编译进度
dtc -I dts -O dtb -o my_board.dtb -V 17 my_board.dts
| 参数 | 含义 | 说明 |
|---|---|---|
-I dts |
输入格式为 DTS 文本 | 默认值,可省略 |
-O dtb |
输出格式为 DTB 二进制 | 必须指定 |
-o |
输出文件名 | 通常以 .dtb 结尾 |
-@ |
保留符号节点 | 用于 Overlay 支持 |
-V 17 |
指定 DTB 版本 | 版本 17 支持更多特性 |
实战要点:在 Platform 驱动开发中,每次修改
.dts 后必须重新编译生成 .dtb,并将新 DTB 烧录到目标板或替换内核启动时加载的 DTB 文件。否则驱动 probe 函数无法匹配到更新后的设备节点。
3.1.3 DTS 编译为 DTB 的流程示意图
3.2 DTB 反编译为 DTS
反编译(Decompilation)是将二进制 DTB 还原为可读的 DTS 文本,常用于调试、逆向分析或验证编译结果。反编译后的 DTS 会丢失原始注释和部分格式,但节点结构和属性值完全保留。
3.2.1 反编译命令
# 基本反编译:DTB → DTS
dtc -I dtb -O dts -o my_board_decompiled.dts my_board.dtb
# 反编译并保留符号(用于 Overlay 分析)
dtc -I dtb -O dts -@ -o my_board_decompiled.dts my_board.dtb
# 直接输出到终端查看
dtc -I dtb -O dts my_board.dtb
3.2.2 反编译前后对比
| 对比项 | 原始 DTS | 反编译 DTS |
|---|---|---|
| 注释 | 保留 /* */ 和 // 注释 |
所有注释丢失 |
| 宏定义 | 支持 #include 和 #define |
宏已被展开为具体值 |
| 标签引用 | 使用 &label 语法 |
标签被解析为 phandle 数值 |
| 节点顺序 | 按编写顺序排列 | 按内存地址顺序排列 |
| 属性格式 | 可读性强 | 数值属性可能显示为十六进制数组 |
反编译陷阱:反编译后的 DTS 不能直接用于重新编译得到与原始 DTB 完全一致的二进制文件。因为标签引用被替换为
phandle 数值,重新编译时 DTC 会重新分配 phandle,导致二进制差异。反编译主要用于调试查看,而非生产编译。
3.2.3 DTB 反编译为 DTS 流程示意图
3.3 设备树覆盖(Device Tree Overlay)
设备树覆盖允许在不修改主设备树(Base DTB)的情况下,动态添加或修改硬件节点。这在可插拔硬件(如FPGA加速器、扩展板)、模块化系统以及快速原型开发中极为重要。Platform 驱动可以通过 Overlay 机制实现运行时设备匹配。
3.3.1 Overlay 文件结构
Overlay 文件使用 /plugin/ 标识,通过 &{label} 引用主设备树中的节点进行修改或追加。
// my_overlay.dts
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = &i2c0; // 引用主设备树中的 i2c0 节点
__overlay__ {
status = "okay";
my_sensor: sensor@48 {
compatible = "myvendor,my-sensor";
reg = <0x48>;
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;
};
};
};
};
3.3.2 Overlay 编译与加载
| 步骤 | 命令/操作 | 说明 |
|---|---|---|
| 1. 编译 Overlay | dtc -I dts -O dtb -@ -o my_overlay.dtbo my_overlay.dts |
输出 .dtbo 后缀,-@ 保留符号 |
| 2. 加载 Overlay | mkdir /sys/kernel/config/device-tree/overlays/my_overlay |
通过 configfs 接口动态加载 |
| 3. 验证加载 | ls /sys/kernel/config/device-tree/overlays/ |
查看已加载的 Overlay 列表 |
| 4. 卸载 Overlay | rmdir /sys/kernel/config/device-tree/overlays/my_overlay |
移除 Overlay,恢复原始设备树 |
Platform 驱动关联:当 Overlay 加载后,内核会重新扫描设备树,为新添加的节点创建
platform_device。如果驱动已注册且 compatible 匹配,probe 函数会被立即调用。这使得 Platform 驱动可以支持热插拔式硬件,无需重启系统。
3.3.3 Overlay 工作原理示意图
3.4 常见编译错误与调试技巧
3.4.1 典型错误对照表
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
ERROR: Input tree has errors |
DTS 语法错误,如缺少分号、括号不匹配 | 检查 DTS 文件语法,使用 dtc -I dts -O dts 先做语法检查 |
WARNING: Overlay fragment target not found |
Overlay 中 target 引用的标签在主设备树中不存在 |
确认主 DTB 包含该标签,或使用 target-path 替代 |
FATAL ERROR: Unable to parse input tree |
输入文件格式错误或损坏 | 确认文件是有效的 DTS 或 DTB 格式 |
Error: phandle 0x... not found |
节点引用了不存在的 phandle | 检查 interrupt-parent 或 clocks 等属性中的引用 |
WARNING: Property 'status' missing |
节点未设置 status 属性 | 添加 status = "okay" 或 "disabled" |
3.4.2 调试技巧
- 使用
-W和-E参数:dtc -W all -E all启用所有警告和错误检查,提前发现问题。 - 反编译验证:编译后立即反编译,对比关键节点是否与预期一致。
- 查看 DTB 内容:使用
fdtdump my_board.dtb快速查看 DTB 的原始结构。 - 内核日志:加载 Overlay 后执行
dmesg | tail -20,查看内核是否成功创建 platform_device。 - 设备树目录检查:
ls -l /sys/firmware/devicetree/base/查看当前生效的设备树节点。
工程建议:在 Platform 驱动开发中,建议将设备树源文件纳入版本控制(Git),每次修改后执行编译+反编译双重验证,确保 DTB 内容符合预期。对于 Overlay,先在开发板上手动加载测试,确认 probe 函数正常调用后再集成到产品代码中。