2. 设备树基础概念:DTS、DTC、DTB、DTBO 的定义与关系

设备树(Device Tree)是一种描述硬件资源的数据结构,它通过树形节点将 CPU、内存、外设、中断控制器、GPIO 控制器等硬件信息传递给操作系统。在嵌入式 Linux 中,设备树是驱动与硬件解耦的核心机制。本章将系统讲解 DTS、DTC、DTB、DTBO 的定义、关系以及设备树在 Linux 启动流程中的位置。

2.1 核心概念定义

术语 全称 定义 文件格式
DTS Device Tree Source 设备树源文件,由开发者编写的可读文本文件,描述硬件拓扑和配置 .dts
DTSI Device Tree Source Include 设备树包含文件,用于存放公共硬件定义,通过 #include 引入 DTS .dtsi
DTC Device Tree Compiler 设备树编译器,将 DTS/DTSI 编译为 DTB 二进制文件 可执行程序
DTB Device Tree Blob 设备树二进制文件,Bootloader 传递给内核的硬件描述数据 .dtb
DTBO Device Tree Blob Overlay 设备树叠加层,用于在运行时动态修改或扩展 DTB 中的节点 .dtbo
💡 关键理解: DTS 是“源代码”,DTC 是“编译器”,DTB 是“编译产物”,DTBO 是“运行时补丁”。四者构成完整的设备树工具链。

2.2 DTS、DTC、DTB、DTBO 的关系

设备树的整个生命周期从文本描述开始,经过编译生成二进制,最终由内核解析使用。DTBO 作为叠加层,可以在不修改原始 DTB 的前提下动态调整硬件配置。

设备树文件编译与使用流程 DTS 源文件 .dts DTSI 包含文件 .dtsi DTC 编译器 dtc 命令 DTB 二进制 .dtb DTBO 叠加层 .dtbo Linux 内核 解析 DTB/DTBO 编译命令示例 dtc -I dts -O dtb -o board.dtb board.dts dtc -I dts -O dtb -o overlay.dtbo overlay.dts
📌 编译与使用要点:
  • DTS + DTSI → DTC → DTB: 标准编译流程,生成内核启动时使用的硬件描述。
  • DTS + DTC → DTBO: 使用 -@ 选项编译生成叠加层,支持运行时动态加载。
  • DTB + DTBO → 内核: Bootloader 将 DTB 加载到内存,内核在启动早期解析;DTBO 在系统运行后通过 configfs 或 of_overlay_apply() 动态应用。

2.3 设备树在 Linux 启动流程中的位置

设备树在 Linux 启动过程中扮演“硬件信息传递者”的角色。从 Bootloader 加载 DTB 到内核完成设备初始化,设备树贯穿整个启动早期阶段。

设备树在 Linux 启动流程中的位置 Bootloader 加载 DTB 到内存 启动参数传递 DTB 地址通过 r2 寄存器 早期汇编阶段 保存 DTB 指针 start_kernel() setup_arch() 解析 DTB unflatten_device_tree() 展开为 device_node 树 驱动匹配与初始化 of_match_table 匹配 设备注册与 probe platform_device 注册 关键函数调用链 start_kernel() └─ setup_arch() └─ unflatten_device_tree() └─ of_platform_populate() └─ driver probe() DTB 地址通过 r2 寄存器传递 ARM64 使用 x0 寄存器
⚠️ 常见误区: 设备树不是设备驱动,它只是硬件描述数据。驱动仍然需要编写 probe() 函数来读取设备树节点中的属性(如 reg、interrupts、compatible),并初始化硬件。设备树只解决“硬件在哪里、怎么配置”的问题,不解决“硬件怎么工作”的问题。

2.4 设备树启动流程关键步骤详解

  1. Bootloader 加载 DTB: U-Boot 等 Bootloader 从存储介质(eMMC、NAND、网络)读取 DTB 文件,放入内存指定地址。
  2. 寄存器传递 DTB 地址: ARM32 使用 r2 寄存器,ARM64 使用 x0 寄存器传递 DTB 物理地址给内核。
  3. 早期汇编保存指针: 内核入口代码(如 head.S)保存 DTB 指针,供后续 C 代码使用。
  4. setup_arch() 解析: 在 start_kernel() 中调用架构相关的 setup_arch(),初步解析 DTB 获取内存大小、CPU 数量等基本信息。
  5. unflatten_device_tree(): 将扁平的 DTB 二进制数据展开为内核可遍历的 device_node 树结构。
  6. of_platform_populate(): 遍历设备树节点,为匹配 compatible 属性的节点创建 platform_device。
  7. 驱动 probe(): 总线驱动匹配后调用驱动的 probe() 函数,驱动通过 of_* API 读取设备树属性完成硬件初始化。
🔧 实战提示: 在调试设备树问题时,可以通过内核启动参数 earlycon 和 dtoc 打印 DTB 解析日志。使用 fdtdump 工具可以反编译 DTB 为可读格式:fdtdump board.dtb。

2.5 设备树与驱动适配的桥梁

设备树节点属性 驱动中对应的 API 作用
compatible = "vendor,device" of_match_table 驱动与设备匹配的关键标识
reg = <0x100000 0x1000> of_iomap() / platform_get_resource() 获取设备基地址和内存范围
interrupts = <0 31 4> irq_of_parse_and_map() 解析中断号并映射
clocks = <&clk 1> of_clk_get() 获取时钟句柄
gpios = <&gpio0 5 0> of_get_named_gpio() 获取 GPIO 编号
💡 学习路径建议: 理解设备树概念后,下一步应掌握 DTS 语法(节点、属性、标签、引用)和常用属性(compatible、reg、interrupts、status),这是编写正确设备树文件的基础。