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 的流程示意图

DTS 编译为 DTB 流程 DTS → DTB 编译流程 DTS 源文件 my_board.dts DTC 编译器 dtc -I dts -O dtb DTB 二进制 my_board.dtb 编译过程:词法分析 → 语法解析 → 节点展开 → 二进制序列化 包含 include 文件展开、宏替换、标签解析 输出:my_board.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 流程示意图

DTB 反编译为 DTS 流程 DTB 反编译为 DTS 流程 DTB 二进制 my_board.dtb DTC 反编译器 dtc -I dtb -O dts DTS 文本 my_board_decompiled.dts 反编译过程:解析 DTB 结构 → 重建节点树 → 生成 DTS 文本 注意:注释丢失、标签变为 phandle、宏已展开

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
cat my_overlay.dtbo > /sys/kernel/config/device-tree/overlays/my_overlay/dtbo
通过 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 工作原理示意图

设备树覆盖(Overlay)工作原理 设备树覆盖(Overlay)工作原理 主设备树(Base DTB) /soc/i2c0 { ... } /soc/spi1 { ... } /soc/uart2 { ... } Overlay 文件(.dtbo) fragment@0 target = &i2c0 __overlay__ { sensor@48 } 合并后的设备树 /soc/i2c0 { sensor@48 { ... } } Overlay 加载流程:configfs 写入 → 内核解析 fragment → 定位 target 节点 → 合并节点/属性 触发 of_platform_populate() → 创建 platform_device → 匹配驱动 probe Platform 驱动 probe 函数被调用 compatible = "myvendor,my-sensor" 匹配成功

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 函数正常调用后再集成到产品代码中。