3. 设备树编译与反编译:DTC工具使用、dtb生成、fdtdump反编译、设备树覆盖(overlay)简介

设备树源文件(.dts / .dtsi)是文本格式的描述文件,必须经过编译生成二进制设备树 Blob(.dtb),才能被内核或引导加载程序解析。本章深入讲解设备树的编译与反编译工具链、二进制格式、以及动态修改设备树的覆盖(Overlay)机制。

3.1 DTC 工具与设备树编译流程

DTC(Device Tree Compiler)是设备树源文件的官方编译器,负责将 .dts 文本转换为 .dtb 二进制,并支持反编译与输出汇编格式。

3.1.1 DTC 工具安装与版本

平台 安装命令 验证版本
Ubuntu/Debian sudo apt install device-tree-compiler dtc --version
Fedora/RHEL sudo dnf install dtc dtc --version
Buildroot/Yocto 默认包含在 host-dtc 包中 内核源码 scripts/dtc/ 内置
内核内置 DTC: Linux 内核源码 scripts/dtc/ 目录下包含 DTC 源码,编译内核时会自动构建。使用 make dtbs 可直接编译内核中的设备树。

3.1.2 编译命令与参数

基本编译命令格式:

dtc -I dts -O dtb -o output.dtb input.dts

常用参数说明:

  • -I:输入格式,dts(源文件)、fs(文件系统)、dtb(二进制)
  • -O:输出格式,dtb(二进制)、dts(源文件)、asm(汇编)
  • -o:输出文件名
  • -@:生成符号节点,支持 Overlay 动态加载
  • -H:指定 phandle 格式(epapr/linux)
  • -W / -E:启用/禁用特定警告或错误检查
生产环境建议: 编译时始终添加 -@ 参数,即使当前不使用 Overlay,也能保留未来扩展能力。同时使用 -W no-unit_address_vs_reg 避免某些 SoC 特有的误报。

3.1.3 编译流程示意图

设备树编译流程 输入源文件 .dts(板级描述) .dtsi(SoC 包含) C 预处理器 #include / #define 展开宏与头文件 DTC 编译器 语法检查 / 解析 生成扁平化结构 输出文件 .dtb(二进制) .dtbo(Overlay) 关键编译选项 • -@ : 生成 __symbols__ 节点,支持 Overlay • -W no-unit_address_vs_reg : 忽略地址单元警告

3.2 DTB 二进制格式与结构

DTB 是设备树的二进制表示,采用扁平化结构(Flattened Device Tree, FDT),由头部(Header)、内存预留块(Memory Reservation Block)、结构块(Structure Block)和字符串块(Strings Block)四部分组成。

3.2.1 DTB 头部字段

偏移 字段名 大小 说明
0x00 magic 4 字节 魔数,固定为 0xD00DFEED
0x04 totalsize 4 字节 DTB 文件总大小(大端序)
0x08 off_dt_struct 4 字节 结构块偏移
0x0C off_dt_strings 4 字节 字符串块偏移
0x10 off_mem_rsvmap 4 字节 内存预留块偏移
0x14 version 4 字节 版本号,当前为 17
0x18 last_comp_version 4 字节 向后兼容最低版本,通常为 16
0x1C boot_cpuid_phys 4 字节 引导 CPU 的物理 ID
0x20 size_dt_strings 4 字节 字符串块大小
0x24 size_dt_struct 4 字节 结构块大小

3.2.2 结构块节点类型

结构块使用令牌(Token)标记节点边界,每个令牌 4 字节对齐:

  • FDT_BEGIN_NODE (0x00000001):节点开始,后跟节点名(含 '\0' 填充对齐)
  • FDT_END_NODE (0x00000002):节点结束
  • FDT_PROP (0x00000003):属性定义,后跟属性值长度、属性名偏移、属性值
  • FDT_NOP (0x00000004):空操作,用于填充对齐
  • FDT_END (0x00000009):结构块结束
字节序注意: DTB 所有多字节字段均使用大端序(Big-Endian)。在 x86 等小端序平台上解析时,必须进行字节序转换。内核使用 be32_to_cpu() 宏处理。

3.3 fdtdump 反编译与调试

fdtdump 是 DTC 工具包提供的 DTB 反编译工具,可将二进制 DTB 转换为可读的文本格式,用于调试和逆向分析。

3.3.1 基本用法

# 反编译 DTB 到标准输出
fdtdump board.dtb

# 保存到文件
fdtdump board.dtb > board_debug.dts

# 使用 dtc 反编译(更规范的输出)
dtc -I dtb -O dts -o board_reverse.dts board.dtb

3.3.2 fdtdump 与 dtc 反编译对比

特性 fdtdump dtc -I dtb -O dts
输出格式 原始十六进制 + 文本混合 标准 DTS 语法
可读性 较低,适合底层调试 高,可直接修改后重新编译
保留注释 不保留 不保留
地址显示 显示原始偏移 不显示偏移
适用场景 验证 DTB 结构完整性 逆向工程、恢复源文件

3.3.3 反编译输出解读

// 使用 fdtdump 查看 DTB 头部
$ fdtdump board.dtb | head -20
/dts-v1/;
// magic:        0xd00dfeed
// totalsize:    0x1000 (4096)
// off_dt_struct: 0x48
// off_dt_strings: 0x9c0
// off_mem_rsvmap: 0x28
// version:      17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x40
// size_dt_struct: 0x978

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    ...
};
调试技巧: 当内核启动时设备树解析失败,可使用 fdtdump 检查 DTB 的 totalsize 是否与文件实际大小一致,以及结构块偏移是否在文件范围内。常见错误是编译时 #include 路径错误导致节点缺失。

3.4 设备树覆盖(Overlay)简介

设备树覆盖(Device Tree Overlay)允许在系统运行时动态添加或修改设备树节点,无需重新编译整个 DTB。Overlay 在 FPGA 动态重配置、外设热插拔、内核模块加载等场景中广泛应用。

3.4.1 Overlay 工作原理

设备树覆盖(Overlay)工作原理 基础 DTB 系统启动时加载 包含所有固定外设 Overlay .dtbo 运行时动态加载 新增/修改/禁用节点 内核 OF 子系统 动态合并解析 更新设备模型 最终设备树视图 = 基础 DTB + Overlay 增量 通过 __symbols__ 和 __fixups__ 机制实现符号解析 Overlay 关键机制 __symbols__ 节点导出全局标签 | __fixups__ 记录外部引用 | 内核 configfs 接口加载/卸载

3.4.2 Overlay 源文件编写

Overlay 源文件使用 /dts-v1/; 和 /plugin/; 标记,通过 &label 引用基础 DTB 中的节点:

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target = &am33xx_pinmux;
        __overlay__ {
            my_device_pins: my_device_pins {
                pinctrl-single,pins = <0x078 0x07 0x038 0x07>;
            };
        };
    };

    fragment@1 {
        target = &ocp;
        __overlay__ {
            my_device {
                compatible = "my,driver";
                pinctrl-0 = <&my_device_pins>;
                reg = <0x48000000 0x1000>;
                interrupts = <56>;
                status = "okay";
            };
        };
    };
};

3.4.3 Overlay 编译与加载

编译 Overlay 时必须添加 -@ 参数生成符号表:

# 编译 Overlay
dtc -@ -I dts -O dtb -o my_overlay.dtbo my_overlay.dts

# 通过 configfs 加载(内核需配置 CONFIG_OF_OVERLAY)
mkdir /sys/kernel/config/device-tree/overlays/my_overlay
cat my_overlay.dtbo > /sys/kernel/config/device-tree/overlays/my_overlay/dtbo

# 卸载 Overlay
rmdir /sys/kernel/config/device-tree/overlays/my_overlay

3.4.4 Overlay 与传统 DTB 对比

特性 传统 DTB Overlay (.dtbo)
加载时机 系统启动时一次性加载 系统运行时动态加载/卸载
修改范围 定义完整硬件拓扑 增量修改,可新增/覆盖/禁用节点
符号解析 无需外部符号 依赖基础 DTB 的 __symbols__
编译参数 无需特殊参数 必须使用 -@
典型应用 固定硬件平台 FPGA 重配置、扩展板、热插拔
Overlay 使用限制: 内核需启用 CONFIG_OF_OVERLAY 和 CONFIG_CONFIGFS_FS。Overlay 不能删除已有节点,只能通过设置 status = "disabled" 禁用。多个 Overlay 加载顺序可能影响最终设备树视图,需谨慎管理依赖关系。

3.5 本章小结

  • DTC 工具是设备树编译的核心,支持 dts → dtb 编译和反向操作,-@ 参数为 Overlay 提供符号支持。
  • DTB 格式采用大端序扁平化结构,包含头部、内存预留块、结构块和字符串块,内核通过 OF API 逐令牌解析。
  • fdtdump 提供底层 DTB 结构查看能力,dtc -I dtb -O dts 则生成可重新编译的标准 DTS。
  • 设备树覆盖通过 /plugin/ 和 fragment 机制实现运行时动态修改,是 FPGA 和可扩展平台的关键技术。