设备树的传输和设备树的内核解析

2021-07-29 14:42 来源:电子说

U-Boot将设备树加载到内存中的指定位置后,ARM内核的SoC通过通用寄存器r2传输内存中dtb的地址。内核获得地址后,进一步处理dtb文件。

设备树的传输

使用bootm加载内核镜像时(bootz是bootm的封装和功能扩展,本质上是一样的)。U-Boot跳转到内核的入口函数是boot_jump_linux

该功能的c文件在arch/arm/lib下,说明传输设备树的方式与SoC架构有关。这个函数在不同的SoCs被调出时特别重要,它是U-Boot和内核之间连接和交换信息的关键API。在执行了U-Boot的这个功能之后,CPU的控制权就完全交给了内核。

/*子命令:GO */静态void boot _ jump _ Linux(bootm _ headers _ t * images,int标志)

{。

调试(# # #将控制权转移到Linux(地址为lx)

"。

",(ulong)kernel _ entry);

bootstate _ mark(bootstate _ ID _ RUN _ OS);

notify _ and _ clean up(假);

if(IMage _ ENABLE _ OF _ LIFDT images-> > ft _ len)

r2=(无符号长)图像-】ft _ addr;

其他

R2=GD-〉BD-〉bi _ boot _ params;

}

R2作为存储设备树地址的寄存器,有两种方式获取其值,即实例化BOOM _ Header _ T数据结构的ft_addr,使用U-Boot的板级启动参数作为设备树地址。

Bootm_header_t模式

bootm_header_t的数据结构定义如下,供各种内核的SoC使用。每个制造商根据自己的中央处理器的特性,对每个成员进行不同的实例化。

/*

* do _ bootm()和do_bootm_ 《os》()使用的旧版和FIT格式标头

*套路。

*/typedef struct bootm_headers {。

char * ft _ addr/*平面开发树地址*/

ulong ft _ len/*扁平设备树的长度*/。

} bootm _ headers _ t;

在bootm_header_t模式下,U-Boot需要支持设备树,文件不为空。

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

do_bootz(struct cmd_tbl *cmdtp,int标志,int argc,char *const argv[])

-bootz_start()

- bootm_find_images(int标志,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-v 2021.04/common/image-FDT . c

Gd-> BD-> bi _ boot _ params模式

这是一种比较古老的方式,目前基本没有采用。Bi_boot_params是用于存储内核启动参数的地址,通常在板级初始化中指定。

这里执行代码的时候,可以通过打印出来,用调试工具连接,确认r2是否是期望值。

按内核解析设备树

分析分为两个阶段,第一阶段是检查和重新调整启动参数;在第二阶段,设备树被解压缩,即设备树从FDT变为美国东部时间,并创建device_node。

第一阶段的

内核启动日志中与设备树相关的第一项打印如下,即打印当前硬件设备的型号名称,“of: FDT:机器型号:v2p-ca9”

在物理CPU0x0上启动Linux

Linux版本5.4.124 (qemu@qemu) (gcc版本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

延伸 · 阅读