详解Linux设备树传递及Kernel对设备树分析

Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离,设备树这个概念并不是一开始就具有的,它的出现是Linus Torvalds在2011年3月,对于kernel/arch/arm/plat-xxx和kernel/arch/arm/mach-xxx含有大量的描述板级细节的代码,针对这种现象提出了设备树(Device tree)的概念,下面为大家分享一下Linux设备树的传递具体方法


设备树的传递

当使用 bootm 加载 kernel 镜像时(bootz 是对 bootm 的一种封装以及功能扩展,实质一样)。U-Boot 跳转到 kernel 的入口函数是 boot_jump_linux

这个函数的 C 文件在 arch/arm/lib 下,说明设备树的传递的方式是与 SoC 架构相关的。不同的 SoC 在 bring-up 时,这个函数格外重要,这是 U-Boot 与 kernel 之间衔接、交互信息的一个关键 API。U-Boot 的这个函数执行结束后,将 CPU 的控制权完整的交给 kernel。

/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
...
 debug("## Transferring control to Linux (at address %08lx)" \
   "...\n", (ulong) kernel_entry);
 bootstage_mark(BOOTSTAGE_ID_RUN_OS);
 announce_and_cleanup(fake);
 if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
   r2 = (unsigned long)images->ft_addr;
 else
   r2 = gd->bd->bi_boot_params;
...
}

r2 作为存放设备树地址的寄存器,其取值有两种方式,分别是例化 bootm_header_t 这个数据结构的 ft_addr,以及利用 U-Boot 的板级启动参数作为设备树的地址。

bootm_header_t 方式

数据结构 bootm_header_t 的定义如下,供各种内核的 SoC 使用,每家厂商根据自己 CPU 的特点对各个成员进行不同的例化。

/*
* Legacy and FIT format headers used by do_bootm() and do_bootm_
  
   () * routines. */ typedef struct bootm_headers {  ...  char    *ft_addr;  /* flat dev tree address */  ulong    ft_len;    /* length of flat device tree */  ... } bootm_headers_t; 
  

用 bootm_header_t 的方式,U-Boot 需支持设备树以及文件非空。

Linux设备树的传递及Kernel中对设备树的分析Linux设备树的传递及Kernel中对设备树的分析

ft_len 以及 ft_addr 属于 bootm_header_t,在 U-Boot 解析镜像文件时,实例化这两个成员。函数调用栈如下:

do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
-bootz_start()
--bootm_find_images(int flag, int argc, char *const argv[], ulong start,ulong size)
---boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);
  u-boot-v2021.04/common/image-fdt.c

gd->bd->bi_boot_params 方式

这种属于比较古老的一种方式了,目前基本不会采用。bi_boot_params 是一个存放内核启动参数的地址,通常是在板级初始化中进行指定。

代码执行到此处,r2 是否为预期的值,一是可以通过打印的方式、再有使用调试工具连上去确认。

kernel 对设备树的解析

解析分两个阶段,第一阶段进行校验以及启动参数的再调整;第二阶段完成设备树的解压,也就是将设备树由 FDT 变成 EDT,创建 device_node。

第一阶段

kernel 启动日志中与设备树相关的第一条打印如下,也就是打印出当前硬件设备的模型名,”OF: fdt: Machine model: V2P-CA9″ 。

Booting Linux on physical CPU 0x0
Linux version 5.4.124 (qemu@qemu) (gcc version 6.5.0 (Linaro GCC 6.5-2018.12)) #3 SMP Fri Jun 25 15:26:02 CST 2021
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9

这个模型名是在设备树文件的头部定义的,定义当前设备的总体名称。

// SPDX-License-Identifier: GPL-2.0
/*
* ARM Ltd. Versatile Express
*
* CoreTile Express A9x4
* Cortex-A9 MPCore (V2P-CA9)
*
* HBI-0191B
*/
/dts-v1/;
#include "vexpress-v2m.dtsi"
/ {
 model = "V2P-CA9";
 ...
 }

但这并不是 kernel 对设备树第一次进行处理的地方。在此之前已有其他的操作。函数调用栈如下:

setup_arch(char **cmdline_p) arch/arm/kernel/setup.c
   atags_vaddr = FDT_VIRT_BASE(__atags_pointer);
   setup_machine_fdt(void *dt_virt) arch/arm/kernel/devtree.c
       early_init_dt_verify()
       of_flat_dt_match_machine()  drivers/of/fdt.c
       early_init_dt_scan_nodes();
       __machine_arch_type = mdesc->nr;

第 2 行、__atags_pointer 是 dtb 在内存中的地址,这个地址在汇编阶段(若镜像为 zImage,那么在解压缩阶段就完成了)便获取到了。由于执行到 setup_arch 时 mmu 已经使能并且 4K 的段页表也已经完成了映射,而 U-Boot 传递给 kernel 的设备树 fdt 地址属于物理地址,因此需要将物理地址转换成虚拟地址。

head-common.S
 .align  2
 .type  __mmap_switched_data, %object
__mmap_switched_data:
#ifdef CONFIG_XIP_KERNEL#ifndef CONFIG_XIP_DEFLATED_DATA
 .long  _sdata        @ r0
 .long  __data_loc      @ r1
 .long  _edata_loc      @ r2
#endif
 .long  __bss_stop      @ sp (temporary stack in .bss)
#endif
 .long  __bss_start      @ r0
 .long  __bss_stop      @ r1
 .long  init_thread_union + THREAD_START_SP @ sp
 .long  processor_id      @ r0
 .long  __machine_arch_type    @ r1
 .long  __atags_pointer      @ r2

第一阶段对设备树的配置主要包括:

A 对 dtb 文件进行 crc32 校验,检测设备树文件是否合法 early_init_dt_verify()

B early_init_dt_scan_nodes()
       /* Retrieve various information from the /chosen node */
       of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
       /* Initialize {size,address}-cells info */
       of_scan_flat_dt(early_init_dt_scan_root, NULL);
       /* Setup memory, calling early_init_dt_add_memory_arch */
       of_scan_flat_dt(early_init_dt_scan_memory, NULL);
C 更新__machine_arch_type
D 更新 chosen

上面这个 chosen 信息可以在 kernel 起来后再次查看做了哪些修改。

第二阶段

第二阶段单纯的是将设备树 ABI 文件进行解压缩,由 FDT 变成 EDT,生成相应的 device_node 结点。这个阶段的函数调用栈如下:

unflatten_device_tree();
   *__unflatten_device_tree()
       /* First pass, scan for size */
       size = unflatten_dt_nodes(blob, NULL, dad, NULL);
        /* Second pass, do actual unflattening */
       unflatten_dt_nodes(blob, mem, dad, mynodes);
           unflatten_dt_nodes()
               populate_node()

device_nodes 结点如下:

Linux设备树的传递及Kernel中对设备树的分析Linux设备树的传递及Kernel中对设备树的分析

device_node 创建完成后,kernel 创建 platform_device 时依据这个阶段完成的工作情况进行对应的设备注册,供驱动代码使用。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/220046.html<

(0)
管理的头像管理
上一篇2025-04-14 09:37
下一篇 2025-04-14 09:38

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注