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

    文章

    49

    浏览量

    11989

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何在AMD Vitis Unified IDE中使用系统设备

    您将在这篇博客中了解系统设备 (SDT) 以及如何在 AMD Vitis Unified IDE 中使用 SDT 维护来自 XSA 的硬件元数据。本文还讲述了如何对 SDT 进行操作,以便在 Vitis Unified IDE 中实现更灵活的使用场景。
    的头像 发表于 11-18 11:13 2813次阅读
    如何在AMD Vitis Unified IDE中使用系统<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    想在rtsmart中使用uart2,是不是只能通过修改设备方法来实现uart2的复用呀?

    我想在rtsmart中使用uart2,是不是只能通过修改设备方法来实现uart2的复用呀? 修改设备后如何只编译设备
    发表于 06-24 07:04

    基于RV1126开发板修改CAN内核设备

    编辑设备文件,执行以下指令可以修改nano的板级头文件,如下图所示。(比如基于MCP2515把复用为SPI1的硬件资源转成CAN对源代码进行修改)
    的头像 发表于 04-17 10:10 625次阅读
    基于RV1126开发板修改CAN内核<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    基于RV1126开发板修改GPIO内核设备

    编辑设备文件,执行以下指令可以修改nano的板级头文件,如图所示。(比如把复用为SPI1的硬件资源改成通用的GPIO对源代码进行修改)
    的头像 发表于 04-17 10:00 596次阅读
    基于RV1126开发板修改GPIO内核<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    请问有没有办法修改live系统上的设备

    i.MX8M 纳米 yocto Linux 我想在不经过构建过程的情况下测试 Device Tree 更改。有没有办法修改 live 系统上的设备设置? This https
    发表于 04-09 08:23

    如何获取yocto build来构建对设备的更改?

    来自 NXP 的 Yocto 构建设置 我想为 imx8m nano 构建一个修改后的器件。我了解设备源文件(.dts、dtsi)的语法,但我不知道如何让 yocto 版本更新 DTB
    发表于 04-09 07:37

    请问stm32mp257如何修改设备并加载驱动?

    1、重新编译kernel容易报错,重新编译设备也会报错。 2、加载驱动,编译环境应该对应,加载不成功。 3、想知道整体比方说,修改下IO,修改下设备
    发表于 03-07 08:05

    【技术分享】迅为RK3568开发板使用TFTP加载内核设备

    【技术分享】迅为RK3568开发板使用TFTP加载内核设备
    的头像 发表于 02-21 14:04 1122次阅读
    【技术分享】迅为RK3568开发板使用TFTP加载内核<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    求解答,设备问题

    请问,rk3588j要再提取一个USB3.0接口设备怎么改
    发表于 02-20 11:22

    科技在物联网方面

    布局、人员位置等,在工业物联网场景中监测生产设备的状态、检测产品质量等。 通信技术应用 远程控制与数据传输:宇科技的人形机器人H1等产品具备远程控制和数据传输功能,这依赖于先进的通信技术。在物联网
    发表于 02-04 06:48

    使用TFTP加载内核设备

    在嵌入式项目开发中,为了适配新外设、调整硬件资源分配或修复驱动问题,需要频繁修改设备和内核。修改完成后,通常需要重新编译生成镜像,并将其烧录到开发板上进行测试。然而,传统的烧录方式不仅需要连接物理接口,还可能因为镜像体积较大而耗费较长时间,这在开发周期紧张的情况下显得尤
    的头像 发表于 01-17 15:52 1857次阅读
    使用TFTP加载内核<b class='flag-5'>设备</b><b class='flag-5'>树</b>

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-初识设备之Makefile修改

    不同而新增加了dts,则需要在这个Makefile的这个位置添加上对应的.dtb文件名参与编译。ELF 1使用的设备命名为imx6ull-elf1-emmc.dts,是基于NXP官方evk板子的设备
    发表于 01-13 09:09

    飞凌嵌入式ElfBoard ELF 1板卡-初识设备之Makefile修改

    不同而新增加了dts,则需要在这个Makefile的这个位置添加上对应的.dtb文件名参与编译。ELF 1使用的设备命名为imx6ull-elf1-emmc.dts,是基于NXP官方evk板子的设备
    发表于 01-10 09:23

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-初识设备设备组成和结构

    的一项技能。设备的起源设备(Device Tree)是一种描述硬件资源的数据结构,它由uboot传递给Linux内核,被内核
    发表于 01-08 08:32

    飞凌嵌入式ElfBoard ELF 1板卡-初识设备设备组成和结构

    的一项技能。设备的起源设备(Device Tree)是一种描述硬件资源的数据结构,它由uboot传递给Linux内核,被内核
    发表于 01-07 09:16