1. 设备树基础概念

1.1 什么是设备树(Device Tree)

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用节点(node)和属性(property)的树形层次来枚举系统中的所有可发现硬件设备、内存映射地址、中断号、时钟频率、GPIO 引脚等平台信息。设备树源文件(.dts)经过设备树编译器(dtc)编译为二进制设备树 Blob(.dtb),由 Bootloader 传递给内核。

核心定义:设备树是一个与体系结构无关的、描述硬件拓扑和配置的静态数据块,它替代了传统 Bootloader 与内核之间通过“硬编码”或“板级文件”传递硬件参数的方式。

一个典型的设备树节点结构如下:

/dts-v1/;

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        cpu@0 {
            compatible = "arm,cortex-a8";
            reg = <0x0>;
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  /* 512 MB */
    };

    &uart0 {
        status = "okay";
        pinctrl-0 = <&uart0_pins>;
        pinctrl-names = "default";
    };
};
关键字段说明:
compatible:驱动匹配的核心字符串,格式为 "厂商,型号";
reg:描述地址和长度信息,由 #address-cells 和 #size-cells 决定;
status:设备启用状态,"okay" 表示启用,"disabled" 表示禁用。
设备树(Device Tree)层次结构 / (根节点) cpus memory soc chosen cpu@0 cpu@1 uart@44e09000 i2c@44e0b000 gpio@44e07000 eeprom@50 rtc@6f 每个节点包含 compatible、reg、interrupts 等属性,驱动通过 compatible 匹配节点 节点路径如 /soc/uart@44e09000 唯一标识一个设备

1.2 为什么需要设备树

在设备树出现之前,嵌入式 Linux 内核维护着大量“板级文件”(board file),每个开发板对应一个 arch/arm/mach-xxx/board-xxx.c。这种模式存在严重问题:

问题 传统板级文件方式 设备树方式
代码冗余 每个板子一份 C 文件,大量重复的硬件注册代码 一份 DTS 文件描述硬件,内核代码与板级信息解耦
维护成本 新增板子需修改内核源码,社区合入周期长 只需提供 DTS 文件,无需修改内核核心代码
二进制兼容 一个内核镜像只能支持一个板子 同一内核镜像可通过不同 DTB 支持多个板子
启动参数传递 ATAG 列表传递参数,扩展性差 设备树结构化传递内存、中断、时钟等复杂信息
驱动与平台耦合 驱动中硬编码地址、中断号 驱动通过设备树 API 获取资源,与平台无关
重要:自 Linux 3.x 起,ARM 架构强制要求使用设备树,传统板级文件方式已被彻底废弃。PowerPC、RISC-V、x86(嵌入式场景)等架构也广泛采用设备树。

1.3 设备树与 ACPI 的区别

ACPI(Advanced Configuration and Power Interface)是 x86/x86_64 平台的标准硬件描述规范,与设备树有本质区别:

对比维度 设备树(Device Tree) ACPI
起源 Open Firmware,由 PowerPC 引入,ARM 推广 Intel、Microsoft、Toshiba 联合制定,面向 PC
数据格式 二进制 DTB(扁平化树结构) AML(ACPI Machine Language)字节码 + 表结构
运行时能力 静态描述,内核启动后不再修改 支持运行时热插拔、电源管理、动态配置
描述粒度 寄存器级地址、中断、时钟、GPIO 等 高级抽象:设备功能、功耗状态、热区、电池
驱动匹配 compatible 字符串匹配 HID(Hardware ID)/ CID(Compatible ID)匹配
适用场景 嵌入式、SoC 平台、非 x86 架构 x86 服务器、桌面、笔记本、UEFI 固件环境
工具链 dtc(设备树编译器)、dt-utils iasl(AML 编译器)、acpidump
选择原则:如果你的平台使用 UEFI 固件且需要高级电源管理(如 S3/S4 休眠),选择 ACPI;如果你的平台是嵌入式 SoC、使用 U-Boot 或 Coreboot,选择设备树。部分 RISC-V 和 ARM 服务器也开始支持 ACPI,但嵌入式领域设备树仍是绝对主流。

1.4 设备树在嵌入式 Linux 启动流程中的角色

设备树在嵌入式 Linux 启动过程中扮演“硬件信息传递者”的关键角色,贯穿 Bootloader → 内核 → 驱动三个阶段:

  1. Bootloader 阶段:U-Boot 从存储介质(eMMC、SD 卡、Flash)加载 .dtb 文件到内存,解析并可能修改(如填充 chosen 节点的 bootargs),然后将 DTB 内存地址传递给内核。
  2. 内核早期启动阶段:内核解压后,从寄存器(ARM 的 r2 寄存器)获取 DTB 地址,调用 early_init_dt_scan() 解析根节点、内存节点、chosen 节点,完成内存映射、控制台初始化。
  3. 设备驱动加载阶段:内核遍历设备树,为每个 status = "okay" 的节点创建 platform_device,驱动通过 of_match_table 匹配 compatible 属性,调用 probe() 函数。
设备树在嵌入式 Linux 启动流程中的角色 Bootloader (U-Boot) 加载 DTB → 填充 bootargs r2 寄存器 内核早期启动 early_init_dt_scan() 设备创建 of_platform_populate() 驱动匹配 of_match_table → probe() 资源获取 of_iomap() / of_get_irq() 设备初始化 注册中断 / 创建文件节点 设备树在启动流程中的关键作用 • Bootloader 阶段:DTB 是内核获取硬件信息的唯一入口 • 内核早期:解析内存布局、控制台、根文件系统参数 • 驱动阶段:驱动通过设备树 API 获取地址、中断、时钟、DMA 等资源
启动流程关键代码路径:
start_kernel() → setup_arch() → unflatten_device_tree() → of_platform_populate() → 驱动 probe()

设备树在启动流程中的核心价值在于:它将硬件描述与内核代码完全分离。开发者只需修改 .dts 文件即可适配新硬件,无需重新编译内核。这种机制使得 SoC 厂商可以发布通用内核,板级厂商只需提供 DTS 文件,极大加速了嵌入式产品的开发周期。