4. 设备树编译与反编译:DTS、DTC、DTB的关系、dtc工具使用、编译命令、反编译命令、设备树覆盖(overlay)基础

4.1 DTS、DTC、DTB 三者的核心关系

在嵌入式 Linux 设备树开发流程中,DTS(Device Tree Source)、DTC(Device Tree Compiler) 和 DTB(Device Tree Blob) 构成了从人类可读的文本描述到内核可解析的二进制数据的完整链路。理解这三者的角色与转换关系,是进行设备树移植与适配的第一步。

核心流程: DTS(源文件) → DTC(编译器) → DTB(二进制目标文件) → Bootloader 加载 → 内核解析。
术语 全称 文件格式 角色与用途
DTS Device Tree Source 文本文件(.dts / .dtsi) 开发者编写的设备树源文件,描述硬件拓扑、寄存器地址、中断、时钟等。.dtsi 为公共头文件,通过 #include 包含。
DTC Device Tree Compiler 可执行工具(dtc) 将 DTS 编译为 DTB,或将 DTB 反编译为 DTS。支持语法检查、覆盖(overlay)处理、符号表生成。
DTB Device Tree Blob 二进制文件(.dtb) 内核启动时由 Bootloader(如 U-Boot)传递给内核的二进制数据块。内核通过 of_* 系列 API 解析 DTB 以获取硬件信息。
DTS → DTC → DTB 转换关系 DTS / DTSI 设备树源文件(文本) DTC 编译器 dtc 工具 DTB 设备树二进制(Blob) 编译 生成 反编译(-I dtb -O dts) Bootloader 加载 DTB → 内核解析硬件

4.2 dtc 工具使用与编译命令

dtc 是设备树开发的核心工具,通常集成在 Linux 内核源码的 scripts/dtc/ 目录下,也可单独安装。其基本语法为:

dtc [选项] -I <输入格式> -O <输出格式> -o <输出文件> <输入文件>

4.2.1 常用编译命令

场景 命令示例 说明
标准编译 dtc -I dts -O dtb -o myboard.dtb myboard.dts 将 DTS 源文件编译为 DTB 二进制文件。
保留符号(-@) dtc -@ -I dts -O dtb -o myboard.dtb myboard.dts 生成符号表,用于设备树覆盖(overlay)支持。
输出依赖文件 dtc -I dts -O dtb -o myboard.dtb myboard.dts -d myboard.d 生成 Makefile 风格的依赖文件,便于构建系统管理。
指定空格缩进 dtc -I dts -O dtb -o myboard.dtb myboard.dts -S 4 编译时指定输出 DTB 的缩进空格数(调试用)。
实战提示: 在内核源码根目录下,使用 make dtbs 命令可批量编译所有使能的设备树。内核构建系统会自动调用 dtc 并传递正确的 -@ 等参数。手动编译时,务必确认 dtc 版本 ≥ 1.4.7,以支持 overlay 特性。

4.2.2 编译过程中的常见选项

  • -I / -O: 指定输入/输出格式。常用格式:dts(源文件)、dtb(二进制)、fs(文件系统表示,调试用)。
  • -@: 生成 __symbols__ 节点,这是 overlay 能够定位基设备树节点的关键。
  • -H: 指定 phandle 格式,可选 legacy、epapr,默认 epapr。
  • -W / -E: 启用/禁用特定警告或错误检查,如 -W no-unit_address_vs_reg 可屏蔽地址格式警告。

4.3 反编译命令与调试技巧

反编译是将 DTB 二进制文件还原为 DTS 文本的过程,常用于分析现有 DTB、验证编译结果或从运行中的系统提取设备树。

4.3.1 基本反编译命令

dtc -I dtb -O dts -o decompiled.dts myboard.dtb

该命令将 myboard.dtb 反编译为 decompiled.dts。反编译输出的 DTS 会包含所有节点、属性、phandle 引用以及 __symbols__ 节点(如果原 DTB 由 -@ 编译)。

4.3.2 从运行中的系统提取 DTB

在目标板 Linux 系统运行时,可以通过以下方式获取当前内核使用的 DTB:

# 方法1:从 /sys/firmware/devicetree 直接复制(推荐)
dtc -I fs -O dts -o running.dts /sys/firmware/devicetree/base

# 方法2:从 /proc/device-tree 复制(旧内核)
dtc -I fs -O dts -o running.dts /proc/device-tree

# 方法3:使用 hexdump 抓取 DTB 内存区域(需知道加载地址)
# 通常由 Bootloader 在启动时告知内核,可通过 dmesg 查看
注意: 从 /sys/firmware/devicetree 反编译得到的 DTS 是内核解析后的“扁平化”表示,与原始 DTS 在格式上略有差异(例如所有 phandle 引用已被解析为实际值)。此方法适用于调试,但不适合直接用于重新编译。

4.3.3 反编译输出对比

对比项 原始 DTS 反编译 DTS
标签引用 &uart1 { ... }; uart1: serial@... { ... }; + phandle = <0x...>;
包含文件 #include "soc.dtsi" 所有内容已展开为扁平节点
宏定义 #define GPIO_ACTIVE_HIGH 0 宏已被预处理器展开为数值
注释 保留 C 风格注释 所有注释被移除

4.4 设备树覆盖(Overlay)基础

设备树覆盖(Device Tree Overlay)允许在系统运行时动态添加或修改设备树节点,而无需重新编译整个 DTB。这在 FPGA 动态重配置、外设热插拔、模块化硬件设计中尤为重要。

4.4.1 Overlay 的工作原理

Overlay 本质上是一个“部分设备树”,它包含 __overlay__ 和 __fixups__ 等特殊节点。内核的 of_overlay 子系统在加载 overlay 时,会将其中的节点与基设备树(Base DTB)进行合并或替换。

设备树覆盖(Overlay)合并流程 基设备树(Base DTB) / { uart1: serial@... { ... }; gpio0: gpio@... { ... }; }; Overlay DTSO &uart1 { status = "okay"; my-custom-prop; }; of_overlay_apply() 合并后的设备树 uart1 节点被修改/增强 其他节点保持不变

4.4.2 Overlay 文件格式与编译

Overlay 源文件通常以 .dtso 为扩展名(Device Tree Source Overlay),其语法与普通 DTS 类似,但使用 /dts-v1/; 和 /plugin/; 声明:

// my-overlay.dtso
/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target = &uart1;
        __overlay__ {
            status = "okay";
            my-custom-prop = <1>;
        };
    };
};

编译 overlay 的命令:

dtc -@ -I dts -O dtb -o my-overlay.dtbo my-overlay.dtso

输出文件 .dtbo 是 overlay 的二进制格式,可直接被内核的 configfs 接口加载。

4.4.3 Overlay 的加载与卸载

在 Linux 内核中,overlay 通过 configfs 文件系统进行管理:

# 挂载 configfs(如果未挂载)
mount -t configfs configfs /sys/kernel/config

# 创建 overlay 目录
mkdir /sys/kernel/config/device-tree/overlays/my-overlay

# 写入 overlay 二进制数据
cat my-overlay.dtbo > /sys/kernel/config/device-tree/overlays/my-overlay/dtbo

# 卸载 overlay
rmdir /sys/kernel/config/device-tree/overlays/my-overlay
关键点: 基设备树必须使用 -@ 选项编译,以生成 __symbols__ 节点。否则 overlay 中的 target = &uart1 无法解析为具体的 phandle,加载会失败。可通过 dtc -@ -I dts -O dtb -o base.dtb base.dts 确保符号表存在。

4.4.4 Overlay 的典型应用场景

场景 描述 Overlay 作用
FPGA 动态重配置 FPGA 加载不同 bitstream 后,外设接口(如 SPI、I2C)发生变化 动态添加/修改对应外设节点,无需重启系统
扩展板支持 同一主板上可插入不同功能的扩展板(如摄像头、显示、传感器) 插入扩展板时加载对应 overlay,移除时卸载
调试与开发 开发阶段频繁修改设备树参数(如时钟频率、引脚复用) 通过 overlay 快速验证,避免反复烧写 DTB
模块化硬件 SoM(系统级模块)+ 载板设计,载板外设可能不同 基设备树描述 SoM,overlay 描述载板差异

4.5 常见编译错误与排查方法

常见错误 1: WARNING: Avoid unneeded /plugin/ in non-overlay DTS
原因:在普通 DTS 中误加了 /plugin/; 声明。解决方案:移除 /plugin/;,仅 overlay 文件使用。
常见错误 2: ERROR: Input tree has errors ... aborting
原因:DTS 语法错误,如缺少分号、括号不匹配、属性值格式错误。解决方案:使用 dtc -I dts -O dts -o /dev/null myboard.dts 进行语法检查,定位错误行。
常见错误 3: FATAL ERROR: Unable to parse input tree
原因:输入文件不是有效的 DTS 或 DTB 格式。解决方案:确认文件扩展名与 -I 参数匹配,检查文件是否损坏。

排查工具推荐:

  • dtc -W / -E: 启用详细警告,如 dtc -W all -E all -I dts -O dtb -o /dev/null myboard.dts。
  • fdtdump: 直接以十六进制和文本混合方式查看 DTB 结构,适合快速验证 DTB 是否被正确加载。
  • dt-validate: 针对 YAML 格式的设备树绑定进行校验(需配合内核文档中的 schema)。