0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

设备树的传递及kernel 对设备树的解析

Linux爱好者 来源:Linux与SoC 作者:spy_os 2021-07-29 11:19 次阅读

当 U-Boot 将设备树加载到内存指定位置后,ARM 内核的 SoC 以通用寄存器 r2 来传递 dtb 在内存中的地址。kernel 获取到该地址后对 dtb 文件做进一步的处理。

设备树的传递

当使用 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)”

“。。。

”, (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_《os》()

* 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 需支持设备树以及文件非空。

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 1502 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 结点如下:

354e808e-ee05-11eb-a97a-12bb97331649.png

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

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Kernel
    +关注

    关注

    0

    文章

    48

    浏览量

    11136

原文标题:Linux 设备树的传递以及 kernel 中对设备树的解析

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    请问PCM1864的驱动在设备该如何描述?

    请问PCM1864的驱动在设备该如何描述呢: 1. 我使用的不是TI的LINUX内核,是另外一款SOC的LINUX内核 2. 我在设备做了以下描述: ps7-i2c@e00040
    发表于 10-23 07:30

    什么是默克尔(Merkle Tree)?如何计算默克尔根?

    01 默克尔的概念 默克尔(Merkle Tree)是一种特殊的二叉,它的每个节点都存储了一个数据块的哈希值。哈希值是一种可以将任意长度的数据转换为固定长度的字符串的算法,它具有唯一性和不可
    的头像 发表于 09-30 18:22 595次阅读
    什么是默克尔<b class='flag-5'>树</b>(Merkle Tree)?如何计算默克尔根?

    如何使用CubeMX生成的设备编译镜像?

    我的硬件平台是MP135,我现在想给它增加一个串口UART8,于是我打算修改设备。但是在使用SDK的时候我遇到了些问题。按照官方的`how to do.txt`,我编译了u-boot,在
    发表于 05-31 14:41

    原理图设计里两颗重要的(国产EDA)

    原理图里面两颗重要的,那就是元件和网络,作为EDA工具中的重要视图和概念,虽然看似枯燥,但它们扮演着非常重要的角色,它们为电路图的层次化结构提供了有力支撑。想象一个大型的电路设计项目,就像一个
    的头像 发表于 05-29 17:47 670次阅读
    原理图设计里两颗重要的<b class='flag-5'>树</b>(国产EDA)

    如何使用Yocto更新已更改的TF-A设备来产生新的镜像文件?

    求助大佬, 我用Yocto构建了STM32MP157的镜像文件,但我需要更改和移植TF-A和U-BOOT来适配我的板子, 我按照它正点原子教程里更改了.bl2和创建了新的.dtsi设备文件,但
    发表于 03-29 08:01

    在CubeMX上面配合STM32MP135D的DDR设备后,烧录程序时产生了报错怎么解决?

    在烧录程序之前,我已经将生成的所有设备参数替换到原程序中,请问有人能指导一下我是哪里处理出了问题吗
    发表于 03-18 06:46

    MCP251X can驱动移植nuc980采样用设备配置时,中断如何配置设备?

    MCP251X can驱动移植nuc980 采样用设备配置时,中断如何配置设备? spi0: spi@b0061000 { status = \"okay\"
    发表于 01-17 06:43

    NUC980设备DTB文件如何通过NUWrite烧录到SPI-Flash中,烧录的地址是多少?

    NUC980设备DTB文件如何通过NUWrite烧录到SPI-Flash中,烧录的地址是多少;内核编译设备后卡死在“Calibrating delay loop... ”是什么问题
    发表于 01-17 06:29

    如何修改内核设备

    如何修改内核设备
    的头像 发表于 12-14 14:06 768次阅读
    如何修改内核<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    如何修改内核设备

    本文档介绍了内核设备的位置和包含关系 1.内核设备位置 文件 备注 dts longan/device/config/chips/t507/configs/evb/board.dt
    发表于 12-14 13:42

    决策:技术全解与案例实战

    决策算法是机器学习领域的基石之一,其强大的数据分割能力让它在各种预测和分类问题中扮演着重要的角色。
    的头像 发表于 12-13 09:49 1214次阅读
    决策<b class='flag-5'>树</b>:技术全解与案例实战

    时钟是什么?介绍两种时钟树结构

    今天来聊一聊时钟。首先我先讲一下我所理解的时钟是什么,然后介绍两种时钟树结构。
    的头像 发表于 12-06 15:23 1602次阅读

    使用自旋表启动的平台设备cpu节点介绍

    补充一下一个使用自旋表作为启动方式的平台设备cpu节点: arch /arm64/ boot /dts/ xxx.dtsi: cpu@ 0 { device_type = "cpu
    的头像 发表于 12-05 16:19 796次阅读

    数字IC设计中的分段时钟综合

    为什么需要分段去做时钟呢?因为在某些情况下,按照传统的方法让每一个clock group单独去balance,如果不做额外干预,时钟天然是做不平的。
    的头像 发表于 12-04 14:42 1855次阅读
    数字IC设计中的分段时钟<b class='flag-5'>树</b>综合

    【米尔-TIAM62开发板-接替335x-试用评测】+(三)手把手创建Uboot设备与内核设备实战

    子节点,以进一步描述硬件的详细信息。例如,网络接口节点可能包含一个子节点,描述MAC地址、IP地址等信息。 当U-Boot或Linux内核引导时,它们会读取和解析这个设备文件,以了解系统硬件的配置
    发表于 11-28 09:54