2. 设备树编译与反编译:DTC工具的使用、DTS编译为DTB、DTB反编译为DTS、反编译实战

设备树源文件(.dts)是人类可读的文本描述,而内核引导时实际使用的是二进制格式的设备树二进制文件(.dtb)。DTC(Device Tree Compiler)是完成这一转换的核心工具。掌握DTC的编译与反编译操作,是进行设备树调试、驱动移植和覆盖(Overlay)开发的基础。

2.1 DTC工具概述

DTC是Linux内核源码树中自带的设备树编译器,通常位于 scripts/dtc/ 目录下。编译内核时会自动生成DTC可执行文件,也可单独编译安装。

文件类型 扩展名 说明
设备树源文件 .dts 文本格式,人类可读写,包含节点、属性和值
设备树二进制文件 .dtb 二进制格式,内核引导加载程序直接使用
设备树源包含文件 .dtsi 公共设备树片段,通过 #include 引入
设备树覆盖源文件 .dtso 用于运行时覆盖的源文件(Linux 5.x+ 推荐)
设备树覆盖二进制文件 .dtbo 编译后的覆盖文件
提示:在较新的内核版本(5.x+)中,DTC 对覆盖源文件推荐使用 .dtso 扩展名,以与普通 .dts 区分。但传统上 .dts 仍可编译为 .dtbo。

2.2 DTS 编译为 DTB

将 .dts 源文件编译为 .dtb 二进制文件是设备树开发中最常用的操作。基本命令格式如下:

# 基本编译
dtc -I dts -O dtb -o output.dtb input.dts

# 常用参数说明
# -I dts    : 指定输入格式为 dts
# -O dtb    : 指定输出格式为 dtb
# -o        : 指定输出文件名
# -@        : 生成 __symbols__ 节点(用于覆盖支持)
# -H epapr  : 生成 EPAPR 标准头部(PowerPC 等架构使用)
# -W no-unit_address_vs_reg : 忽略某些警告
实战示例:编译一个简单的设备树文件,并启用覆盖支持。
# 1. 查看源文件内容
cat myboard.dts

# 2. 编译为 dtb(带符号表,支持覆盖)
dtc -I dts -O dtb -@ -o myboard.dtb myboard.dts

# 3. 验证编译结果
dtc -I dtb -O dts myboard.dtb | head -20

编译过程中常见的警告与处理方法:

警告信息 含义 处理建议
unit_address_vs_reg 节点地址与 reg 属性不匹配 检查节点名称中的 @ 地址是否与 reg 一致
avoid_unnecessary_addr_size 不必要的地址/大小单元 若总线只有单设备,可省略 #address-cells 和 #size-cells
graph_child_address 图形子节点地址问题 检查 ports/port 节点的 reg 属性
interrupt_provider 中断控制器属性缺失 确认 interrupt-controller 和 #interrupt-cells 已添加

2.3 DTB 反编译为 DTS

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

# 基本反编译
dtc -I dtb -O dts -o output.dts input.dtb

# 反编译并保留所有信息(包括空节点、phandle 等)
dtc -I dtb -O dts -f -o output.dts input.dtb

# 从 /sys/firmware/devicetree 反编译运行中的设备树
dtc -I fs -O dts -o running.dts /sys/firmware/devicetree/base
注意:反编译得到的 .dts 文件与原始源文件在格式上可能略有差异。例如,phandle 属性会被显式写出,而原始源文件中可能使用 &label 引用。反编译结果主要用于分析和调试,不建议直接作为修改后的源文件重新编译。

2.4 反编译实战:从运行系统提取设备树

在实际嵌入式开发中,经常需要从正在运行的目标板上提取设备树,以分析当前硬件配置或作为移植的起点。

步骤一:确认设备树接口

# 检查 /sys/firmware/devicetree 是否存在
ls -l /sys/firmware/devicetree/base

# 若存在,说明内核通过设备树启动
# 若不存在,可能是 ACPI 或旧式板级文件

步骤二:使用 DTC 反编译

# 方法1:直接从 /sys 文件系统反编译
dtc -I fs -O dts -o extracted.dts /sys/firmware/devicetree/base

# 方法2:先复制 dtb,再反编译
cp /sys/firmware/fdt /tmp/board.dtb
dtc -I dtb -O dts -o extracted.dts /tmp/board.dtb

步骤三:分析反编译结果

# 查看设备树结构概览
grep -E "^[[:space:]]*[a-zA-Z]" extracted.dts | head -30

# 查找特定外设节点
grep -A 10 "uart" extracted.dts

# 查看内存配置
grep -A 5 "memory" extracted.dts
实战要点:
  • /sys/firmware/fdt 是原始 DTB 的完整拷贝,大小与 DTB 文件一致。
  • /sys/firmware/devicetree/base 是内核解析后的文件系统视图,以目录结构呈现。
  • 反编译得到的 .dts 中所有节点地址和大小都以十六进制显示,与原始源文件可能不同。
  • 若目标板使用 U-Boot 引导,可在 U-Boot 命令行使用 fdt list 查看设备树。

2.5 设备树编译与反编译流程

设备树编译与反编译流程 DTS 源文件 myboard.dts DTSI 包含文件 soc.dtsi DTC 编译器 -I dts -O dtb -@ (符号表) -W (警告控制) -H (头部格式) DTB 二进制 myboard.dtb DTB 二进制 board.dtb DTC 反编译器 -I dtb -O dts 反编译 DTS extracted.dts 运行中系统 /sys/firmware/ DTC fs 反编译 -I fs -O dts 运行系统 DTS running.dts 说明: 编译方向:DTS → DTC → DTB(用于引导和内核解析) 反编译方向:DTB/运行系统 → DTC → DTS(用于分析和调试)

2.6 常见问题与避坑指南

问题 现象 解决方案
编译报错 syntax error DTC 解析失败 检查分号、花括号是否匹配;确认属性值格式正确
反编译后节点顺序错乱 与原始源文件顺序不同 DTC 按地址排序,不影响功能;使用 -f 保留原始顺序
反编译出现 phandle 数值 引用变为数字 原始源文件中的 &label 被解析为数字 phandle,属正常现象
覆盖编译失败 缺少 __symbols__ 节点 编译时添加 -@ 参数生成符号表
运行系统无 /sys/firmware/devicetree 内核未配置设备树支持 检查内核配置 CONFIG_OF 和 CONFIG_PROC_DEVICETREE
重要提醒:反编译得到的 DTS 文件虽然语法正确,但通常包含大量 phandle 和 linux,phandle 属性,这些在原始源文件中并不存在。直接修改反编译后的 DTS 并重新编译可能导致引用关系混乱。正确的做法是:以反编译结果为参考,在原始源文件中进行修改。

2.7 本章小结

设备树的编译与反编译是嵌入式驱动开发中的基础技能。通过本章学习,应掌握:

  • DTC 工具的基本用法和常用参数
  • 将 .dts 编译为 .dtb 的标准流程
  • 从 .dtb 或运行系统反编译为 .dts 的方法
  • 编译与反编译过程中的常见问题及解决方案