2. 设备树编译与反编译
设备树源文件(.dts)是人类可读的文本描述,而内核引导时实际使用的是二进制格式的设备树二进制文件(.dtb)。dtc(Device Tree Compiler)是完成这一转换的核心工具。本章将系统讲解 dtc 的编译、反编译与内容查看操作,帮助你在调试 V4L2 驱动时快速定位设备树配置问题。
实战要点:在嵌入式 Linux 开发中,修改设备树后必须重新编译为 dtb 并烧录到目标板。反编译则常用于逆向分析现有 dtb 的配置,或验证编译过程是否丢失了关键节点。
2.1 dtc 工具概述
dtc 是设备树编译器的标准实现,通常集成在 Linux 内核源码的 scripts/dtc/ 目录下,也可通过系统包管理器单独安装。其核心功能包括:
- 编译(dts → dtb):将文本格式的
.dts文件转换为二进制.dtb文件。 - 反编译(dtb → dts):将二进制
.dtb还原为可读的.dts文本。 - 内容查看:以人类可读格式输出 dtb 的节点树结构。
- 语法检查:验证 dts 文件的语法正确性。
| 操作类型 | 输入文件 | 输出文件 | 典型命令 |
|---|---|---|---|
| 编译 | .dts |
.dtb |
dtc -I dts -O dtb -o output.dtb input.dts |
| 反编译 | .dtb |
.dts |
dtc -I dtb -O dts -o output.dts input.dtb |
| 内容查看 | .dtb |
终端输出 | dtc -I dtb -O dts input.dtb |
安装 dtc:在 Ubuntu/Debian 系统中执行
sudo apt-get install device-tree-compiler;在 Yocto/Buildroot 构建环境中,dtc 通常已包含在主机工具链中。
2.2 编译 dts 为 dtb
编译操作将设备树源文件转换为内核可识别的二进制格式。以下是一个针对 V4L2 摄像头节点的编译示例:
# 基本编译命令
dtc -I dts -O dtb -o imx8mm-camera.dtb imx8mm-camera.dts
# 启用空间优化(去除调试符号)
dtc -I dts -O dtb -o imx8mm-camera.dtb -@ imx8mm-camera.dts
# 指定输出文件路径
dtc -I dts -O dtb -o /boot/dtbs/imx8mm-camera.dtb imx8mm-camera.dts
参数说明:
-I dts:指定输入格式为 dts 文本。-O dtb:指定输出格式为 dtb 二进制。-o:指定输出文件名。-@:生成符号节点,用于设备树叠加层(overlay)。
常见错误:如果 dts 文件中引用了
#include 的头文件(如 .dtsi),编译前必须确保头文件路径正确。建议使用 -I 参数添加搜索路径:dtc -I dts -O dtb -I /path/to/include -o output.dtb input.dts。
2.3 反编译 dtb 为 dts
反编译用于从现有 dtb 文件中提取可读的设备树描述,是调试和逆向分析的重要技能。例如,当从目标板中提取出 imx8mm-evk.dtb 后,可以反编译查看其完整配置:
# 基本反编译命令
dtc -I dtb -O dts -o imx8mm-evk-decompiled.dts imx8mm-evk.dtb
# 直接输出到终端(不保存文件)
dtc -I dtb -O dts imx8mm-evk.dtb
# 保留原始地址标签
dtc -I dtb -O dts -o output.dts -@ input.dtb
反编译后的 dts 文件会包含所有节点、属性和值,但注释和宏定义会被展开为具体数值。这对于验证编译过程是否丢失了关键配置非常有用。
调试技巧:在 V4L2 驱动调试中,如果摄像头节点在驱动中无法识别,可以先反编译目标板的 dtb,检查
compatible 属性、reg 地址、clocks 和 pinctrl 配置是否与驱动期望一致。
2.4 查看 dtb 内容
除了完整的反编译,dtc 还支持以多种格式查看 dtb 内容,便于快速浏览节点结构:
# 以 dts 格式输出到终端(最常用)
dtc -I dtb -O dts imx8mm-evk.dtb
# 以二进制 hex 格式查看原始数据
dtc -I dtb -O dtb imx8mm-evk.dtb | xxd | head -20
# 使用 fdtdump 工具查看结构摘要
fdtdump imx8mm-evk.dtb
fdtdump 是 dtc 工具包中的另一个实用程序,它以紧凑格式输出设备树的节点路径和属性值,适合快速浏览:
// fdtdump 输出示例(部分)
/dts-v1/;
/ {
compatible = "fsl,imx8mm-evk";
#address-cells = <0x2>;
#size-cells = <0x2>;
cpus {
#address-cells = <0x1>;
#size-cells = <0x0>;
cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x0>;
};
};
i2c@30a20000 {
compatible = "fsl,imx8mm-i2c", "snps,designware-i2c";
reg = <0x0 0x30a20000 0x0 0x10000>;
#address-cells = <0x1>;
#size-cells = <0x0>;
clock-frequency = <0x61a80>;
camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
clocks = &clk_cam;
pinctrl-names = "default";
pinctrl-0 = &pinctrl_camera;
};
};
};
2.5 设备树编译与反编译流程
下图展示了从 dts 源文件到 dtb 二进制,再到反编译验证的完整工作流:
2.6 实战:编译与反编译 V4L2 摄像头设备树
以下是一个完整的实战流程,演示如何为 OV5640 摄像头节点编译设备树,并通过反编译验证配置:
- 准备 dts 源文件:创建
ov5640-camera.dts,包含摄像头节点和必要的 pinctrl 配置。 - 编译为 dtb:
dtc -I dts -O dtb -o ov5640-camera.dtb ov5640-camera.dts - 反编译验证:
dtc -I dtb -O dts ov5640-camera.dtb,检查输出中是否包含compatible = "ovti,ov5640"、reg = <0x3c>、clocks和pinctrl-0属性。 - 对比差异:使用
diff命令比较原始 dts 与反编译后的 dts,确认没有属性丢失。
# 步骤1:创建源文件(示例片段)
cat > ov5640-camera.dts << EOF
/dts-v1/;
/ {
compatible = "custom,board";
#address-cells = <1>;
#size-cells = <1>;
i2c@300a0000 {
compatible = "snps,designware-i2c";
reg = <0x300a0000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
clocks = <&clk_cam>;
clock-names = "xclk";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_camera>;
DOVDD-supply = <®_camera_dovdd>;
AVDD-supply = <®_camera_avdd>;
DVDD-supply = <®_camera_dvdd>;
};
};
};
EOF
# 步骤2:编译
dtc -I dts -O dtb -o ov5640-camera.dtb ov5640-camera.dts
# 步骤3:反编译并检查
dtc -I dtb -O dts ov5640-camera.dtb | grep -E "compatible|reg|clocks|pinctrl"
输出示例:
compatible = "ovti,ov5640";
reg = <0x3c>;
clocks = <0x00000001>; /* 引用被展开为 phandle 值 */
clock-names = "xclk";
pinctrl-names = "default";
pinctrl-0 = <0x00000002>; /* 引用被展开为 phandle 值 */
注意:反编译后,&clk_cam 和 &pinctrl_camera 等标签引用会被展开为数字 phandle 值。这是正常现象,不影响功能验证。
2.7 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 编译报错 "FATAL ERROR: Unable to parse input tree" | dts 文件语法错误,如缺少分号、括号不匹配 | 使用 dtc -I dts -O dts input.dts 进行语法检查,定位错误行 |
| 反编译后节点丢失 | dtb 文件可能被裁剪或使用了不同版本的 dtc | 确认 dtb 来源,使用相同版本的 dtc 进行反编译 |
| phandle 值显示为数字而非标签 | 反编译无法恢复原始标签名 | 在原始 dts 中使用 phandle = <...>; 显式定义,或保留原始 dts 作为参考 |
| 编译后 dtb 体积过大 | 未使用 -@ 或包含调试符号 |
使用 -@ 仅生成必要符号,或使用 dtc -O dtb -H epapr 选择紧凑格式 |
重要提醒:在 V4L2 驱动调试中,如果修改了设备树后驱动行为未改变,请务必确认:
1. 新编译的 dtb 已正确烧录到目标板的分区(如
2. 引导加载程序(U-Boot)加载的是新 dtb 文件,而非缓存中的旧版本。
3. 使用
1. 新编译的 dtb 已正确烧录到目标板的分区(如
/boot/dtbs/ 或 /boot/)。2. 引导加载程序(U-Boot)加载的是新 dtb 文件,而非缓存中的旧版本。
3. 使用
fdtdump /sys/firmware/fdt 查看内核当前使用的设备树,确认与预期一致。