本文约2800字,今天继续沿着《一份靠谱的BSP开发学习路线》来接续学习BSP开发所需的知识--Linux内核如何解析设备树。本文将从内核启动流程切入,深度剖析内核解析设备树的核心原理、关键数据结构、函数调用流程与执行逻辑,帮我们彻底掌握设备树在内核中的工作机制。
关注公众号, 即可获得与Linux相关的电子书籍以及常用开发工具,文末有文档清单。
在嵌入式 Linux 开发中,设备树(Device Tree,DT) 已经彻底取代了传统的板级硬件编码描述,成为 ARM、RISC-V 等架构描述硬件的标准方案。内核无需重新编译,仅通过更换设备树二进制文件(.dtb),就能适配不同硬件平台。而这一切的核心,就是 Linux 内核对设备树文件的加载、解析、绑定、设备注册全流程。
一 设备树基础知识回顾
设备树是一种描述硬件资源的数据结构,源文件为.dts(板级)和.dtsi(芯片级),通过dtc工具编译为.dtb(扁平设备树)。
它描述了CPU、内存、中断控制器、I2C/SPI 控制器、外设、GPIO、时钟等所有硬件信息,内核通过解析这些信息,自动匹配并初始化驱动,无需在内核中硬编码硬件参数。
内核解析设备树的核心目标:
从.dtb中提取硬件拓扑结构;
构建内核内部的设备节点树;
完成驱动与设备的匹配(平台总线 / 设备树匹配);
初始化硬件资源(中断、地址、时钟、GPIO 等)。
二 设备树在内核中的加载时机
设备树解析早于大多数驱动初始化,属于内核启动的早期阶段,整体流程如下:
[1].BootLoader 传递 dtb:U-Boot 将内核镜像(zImage/Image)和 dtb 文件加载到内存指定地址,通过寄存器将 dtb 物理地址传递给内核;
[2].内核自解压:内核解压后,获取 dtb 在内存中的物理起始地址;
[3].早期设备树解析:内核启动初期,在虚拟内存初始化完成前,完成 dtb 的初步解析;
[4].完整设备树构建:建立内核内部的device_node节点树;
[5].平台设备注册:遍历节点树,生成平台设备,与驱动匹配。
说明:设备树解析是内核启动的基石,所有硬件驱动的初始化都依赖解析后的设备树信息。
三 内核设备树核心数据结构
内核使用一套专用数据结构存储设备树信息,是解析流程的核心载体:
[1]. struct device_node
设备树节点的内核抽象,最核心结构,对应 dts 中的一个节点(如cpu@0、uart@02020000)。
struct device_node {const char *name; // 节点名称(如uart)const char *type; // 节点类型phandle phandle; // 节点句柄,用于跨节点引用struct device_node *parent; // 父节点struct list_head child; // 子节点链表struct property *properties;// 节点属性链表...};
[2]. struct property
描述设备树节点的属性(如compatible、reg、interrupts、status)。
struct property {char *name; // 属性名unsigned int length;// 属性值长度void *value; // 属性值struct property *next;// 下一个属性...};
[3]. struct platform_device
设备树解析完成后,最终会转换为平台设备,接入 Linux 设备模型,与驱动完成匹配。
四 内核解析设备树全流程深度拆解
按照内核启动顺序,分5个阶段详解设备树解析的完整逻辑:
【阶段1】:DTB 文件合法性校验
内核首先获取 U-Boot 传递的 dtb 物理地址,对 dtb 文件头部进行校验:
检查魔数0xd00dfeed(设备树固定标识);
校验 dtb 版本、总大小、结构体偏移量;
确认 dtb 未损坏、格式兼容当前内核。
若校验失败,内核会直接 panic,无法继续启动。
【阶段2】:扁平设备树(FDT)早期映射
dtb 是扁平二进制格式,内核无法直接使用,需要将其映射到虚拟地址空间:
内核获取 dtb 物理地址;
调用early_init_dt_map()将 dtb 映射为虚拟地址;
保留 dtb 占用的内存,避免被内核覆盖。
这一步仅完成地址映射,不解析节点结构。
【阶段3】:构建内核内部节点树
这是设备树解析的最关键步骤,内核遍历 dtb 二进制数据,递归生成device_node链表树。
核心函数:__unflatten_device_tree()
遍历 dtb 结构:从头至尾解析节点开始标记、属性、子节点、节点结束标记;
递归创建节点:为每个 dts 节点创建device_node,挂载到父节点的子链表;
解析属性:为每个节点创建property结构,存储属性名和值;
处理特殊节点:
>>aliases节点:解析别名,简化节点引用;
>>chosen节点:获取bootargs等启动参数;
>>memory节点:解析物理内存布局;
>>cpus节点:解析 CPU 核心信息。
执行完成后,内核内存中就拥有了与 dts 完全一致的树形结构,所有硬件信息都可通过节点访问。
【阶段4】:内存、中断、时钟等核心资源解析
内核优先解析系统必需的硬件资源,为后续初始化铺路:
内存解析:从memory节点提取物理内存地址和大小,初始化内存管理;
中断解析:解析interrupt-parent、interrupts属性,建立中断映射表;
时钟解析:解析时钟控制器节点,为外设提供时钟源;
总线解析:解析平台总线、AHB、APB 等总线拓扑。
这些资源是内核运行的基础,必须在驱动加载前完成解析。
【阶段5】:设备树节点转换为平台设备
内核遍历所有设备树节点,筛选出可注册的设备节点(status="okay"且包含compatible属性),将其转换为platform_device,并注册到平台总线。
核心逻辑:
匹配compatible属性:驱动中定义of_device_id表,与节点compatible字符串匹配;
提取硬件资源:从reg属性提取物理地址,从interrupts提取中断号;
注册设备:调用platform_device_register(),触发驱动的probe函数。
至此,设备树完成使命,驱动开始初始化硬件。
五 设备树解析核心函数调用流程
为了更清晰地掌握代码层级,这里给出 ARM64 架构下设备树解析的标准函数调用路径:
start_kernel()→ setup_arch() // 架构初始化入口→ unflatten_device_tree() // 解扁平设备树(构建节点树)→ __unflatten_device_tree() // 核心解析函数→ arm64_memblock_init() // 解析内存节点→ platform_bus_init() // 平台总线初始化→ of_platform_populate() // 遍历节点,创建平台设备
所有关键逻辑都集中在drivers/of/目录下(of = open firmware,设备树标准来源),是内核设备树解析的核心代码库。
六 关键解析规则:compatible 匹配机制
compatible是设备树最核心的属性,驱动与设备匹配的唯一依据。
节点定义:compatible = "vendor,device-name", "generic-name";
驱动定义:of_device_id表包含匹配字符串;
匹配规则:内核从左到右依次匹配,优先精确匹配,其次通用匹配。
示例:
// 设备树节点uart@02020000 {compatible = "fsl,imx6ull-uart", "fsl,imx-uart";};// 驱动匹配表static const struct of_device_id imx_uart_ids[] = {{ .compatible = "fsl,imx-uart" },{ /* 匹配成功 */ }};
内核解析到节点后,自动匹配到 UART 驱动,执行初始化。
总结整个匹配流程如下:
设备树节点↓ (compatible: "fsl,imx6ull-uart", "fsl,imx-uart")内核扫描所有设备节点↓遍历所有平台驱动的 of_match_table↓找到驱动里的 { .compatible = "fsl,imx-uart" }↓字符串完全一致 → 匹配成功!↓自动调用驱动的 probe 函数↓驱动从设备树获取资源并初始化硬件
七 设备树解析常见问题
[1].dtb 未传递 / 损坏:内核无法找到设备树,启动 panic;
[2].compatible 不匹配:驱动无法加载,硬件不工作;
[3].reg 属性地址错误:驱动访问非法物理地址,内核崩溃;
[4].interrupts 配置错误:中断无法触发,驱动无响应;
[5].status="disabled":节点被屏蔽,内核不会解析注册。
这些问题都可以通过内核日志(dmesg) 和设备树文件对比快速定位。
八 总结
Linux 内核解析设备树,是一个从二进制 dtb 到内核设备树、再到平台设备的完整转换流程:
U-Boot 将 dtb 加载到内存并传递给内核;
内核校验、映射 dtb,递归构建device_node节点树;
解析内存、中断、时钟等核心系统资源;
遍历节点,通过compatible匹配驱动,注册平台设备;
驱动获取硬件资源,完成硬件初始化。
设备树解析是嵌入式 Linux 的核心底层逻辑,理解这一流程,不仅能解决硬件适配、驱动调试问题,更能深入理解 Linux 设备模型的设计思想。
以上为全文内容。

这里是女程序员的笔记本
15年+嵌入式软件工程师兼二胎宝妈
分享读书心得、工作经验,自我成长和生活方式。
希望我的文字能对你有所帮助