内核如何解析设备树Device Tree

1) 首先将从u-boot 传递过来的映像基地址和dtb 文件映像基地址保存通用寄存器r30,r31;

2) 通过调用machine_init() --> early_init_devtree()函数来获
取内核前期初始化所需的bootargs,cmd_line等系统引导参数;

3) 调用start_kernel() --> setup_arch() -->
unflatten_device_tree()函数来解析dtb文件,构建一个由device_node结构连接而成的单项链表,并使用全局变量allnodes指针来保存这个链表的头指针;

  

asmlinkage void __init start_kernel(void)
{
    ...
    setup_arch(&command_line);
    ...
}这个 setup_arch 就是各个架构自己的设置函数(arch 是 architecture 的缩写),哪个参与了编译就调用哪个,在本文中应当是 arch/arm/kernel/setup.c 中的 setup_arch。



void __init setup_arch(char **cmdline_p)
{
    ...
    mdesc = setup_machine_fdt(__atags_pointer);
    ...
    unflatten_device_tree();
    ...
}setup_machine_fdt,其中 fdt 的 f 就是扁平(flat)的意思。这个时候 DTB 只是加载到内存中的 .dtb 文件而已,这个文件中不仅包含数据结构,还包含了一些文件头等信息,kernel 需要从这些信息中获取到数据结构相关的信息,然后再生成设备树。这个函数的调用还有个参数 __atags_pointer,看名字似乎这是一个指针,干嘛的呢?以后再说,先进入函数看看。



/* kernel/arch/arm/kernel/devtree.c */
struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
    ...
    devtree = phys_to_virt(dt_phys);
    ...
    initial_boot_params = devtree;
    ...
}phys_to_virt 字面上的意思是物理地址转换成虚拟地址,那就是说 __atags_pointer 是一个物理地址,这就印证了我们的猜测,__atags_pointer 的确是一个指针,再看变量 devtree 它指向了一个 struct boot_param_header 结构体。随后 kernel 把这个指针赋给了全局变量 initial_boot_params。也就是说以后 kernel 会是用这个指针指向的数据去初始化 device tree。



struct boot_param_header {
    __be32 magic; /* magic word OF_DT_HEADER */
    __be32 totalsize; /* total size of DT block */
    __be32 off_dt_struct; /* offset to structure */
    __be32 off_dt_strings; /* offset to strings */
    __be32 off_mem_rsvmap; /* offset to memory reserve map */
    __be32 version; /* format version */
    __be32 last_comp_version; /* last compatible version */
    /* version 2 fields below */
    __be32 boot_cpuid_phys; /* Physical CPU id we‘re booting on */
    /* version 3 fields below */
    __be32 dt_strings_size; /* size of the DT strings block */
    /* version 17 fields below */
    __be32 dt_struct_size; /* size of the DT structure block */
};看这个结构体,很像之前所说的文件头,有魔数、大小、数据结构偏移量、版本等等,kernel 就应该通过这个结构获取数据,并最终生成设备树。现在回到 setup_arch,果然在随后的代码中有这么一个函数。



/* kernel/drivers/of/fdt.c */
void __init unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, &of_allnodes,
                early_init_dt_alloc_memory_arch);
    ...
}

看见了吧,of_allnodes 就是在这里赋值的,device tree 也是在这里建立完成的。__unflatten_device_tree 函数我们就不去深究了,推测其功能应该就是解析数据、申请内存、填充结构等等。
    到此为止,device tree 的初始化就算完成了,在以后的启动过程中,kernel 就会依据这个 dt 来初始化各个设备。但是还有一个小问题,那就是在 setup_arch 函数中 __atags_pointer 从何而来。全局搜索这个变量,结构在 arch/arm/kernel/head-common.S 中发现了它的踪迹。



define ATAG_CORE 0x54410001
...
__mmap_switched:
    adr r3, __mmap_switched_data

ldmia {r4, r5, r6, r7}
    ...
    THUMB( ldr sp, [r3, #16] )
    ...
    str r2, [r6] @ Save atags pointer
    ...
__mmap_switched_data:
    ...
    .long __atags_pointer @ r6
    ...
    arm 汇编不懂,大概能看出来给 __atags_pointer 赋值的过程(‘@’之后是注释)。

在执行完unflatten_device_tree()后,DTS节点信息被解析出来,保存到allnodes链表中,allnodes会在后面被用到。随后,当系统启动到board文件时,会调用.init_machine,接着调用of_platform_populate(....)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。

4) 内核调用OF提供的API函数获取allnodes链表信息来初始化内核其他子系统、设备等。

static void __init msm_dt_init(void)
{
    of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}msm_dt_init 函数的作用就是利用 dt(device tree)结构初始化 platform device。
    of_platform_populate 实现在 drivers/of/platform.c,是 OF 的标准函数。



int of_platform_populate(struct device_node *root,
                         const struct of_device_id *matches,
                         const struct of_dev_auxdata *lookup,
                         struct device *parent)
{
    struct device_node *child;
    int rc = 0;

root = root ? of_node_get(root) : of_find_node_by_path("/");
    if (!root)
        return -EINVAL;

for_each_child_of_node(root, child) {
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc)
            break;
    }

of_node_put(root);
    return rc;
}
    其中of_platform_populate 函数的注释写得很明白:“Populate platform_devices from device tree data”。但是这个“device tree data”又是从那里来的呢?
    在 of_platform_populate 中如果 root 为 NULL,则将 root 赋值为根节点,这个根节点是用 of_find_node_by_path 取到的。



struct device_node *of_find_node_by_path(const char *path)
{
    struct device_node *np = of_allnodes;
    unsigned long flags;

raw_spin_lock_irqsave(&devtree_lock, flags);
    for (; np; np = np->allnext) {
        if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
            && of_node_get(np))
            break;
    }
    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    return np;
}
    在这个函数中有一个很关键的全局变量:of_allnodes,它的定义是在 drivers/of/base.c 里面。

简而言之,Linux采用DTS描述设备硬件信息后,省去了大量板文件垃圾信息。Linux在开机启动阶段,会解析DTS文件,保存到全局链表allnodes中,在掉用.init_machine时,会跟据allnodes中的信息注册平台总线和设备。值得注意的是,加载流程并不是按找从树根到树叶的方式递归注册,而是只注册根节点下的第一级子节点,第二级及之后的子节点暂不注册。Linux系统下的设备大多都是挂载在平台总线下的,因此在平台总线被注册后,会根据allnodes节点的树结构,去寻找该总线的子节点,所有的子节点将被作为设备注册到该总线上。

时间: 2024-07-31 13:13:26

内核如何解析设备树Device Tree的相关文章

嵌入式 emmc 中 安装 烧录 内核 kernel,设备树 devicetree ,根文件系统 rootfs

一般调试嵌入式开发板喜欢选择  利用 TFTP 传送  内核与 设备树,  利用 nfs 加载根文件系统. uboot 环境变量 设置如下: bootargs=root=/dev/nfs rw nfsroot=192.168.1.4:/exports/rfs ip=192.168.1.200 video=HDMI-A-2:1280x800 Bootcmd  tftp 0x48080000 Image;tftp 0x48000000 XXXXXXXXXXX.dtb;booti 0x48080000

我眼中的Linux设备树(一 概述)

一 概述 设备树(Device tree)是一套用来描述硬件属相的规则.ARM Linux采用设备树机制源于2011年3月份Linux创始人Linus Torvalds发的一封邮件,在这封邮件中他提倡ARM平台应该参考其他平台如PowerPC的设备树机制描述硬件.因为在此之前,ARM平台还是采用旧的机制,在kernel/arch/arm/plat-xxx目录和kernel/arch/arm/mach-xxx目录下用代码描述硬件,如注册platform设备,声明设备的resource等.因为这些代

基于tiny4412的Linux内核移植 -- 设备树的展开

作者信息 作者: 彭东林 邮箱:[email protected] QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本:Linux-4.4.0 (支持device tree) u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动) busybox版本:busybox 1.25 交叉编译工具链: arm-none-linux-gnueabi-gcc (gcc version 4

linux device tree源代码解析--转

//Based on Linux v3.14 source code Linux设备树机制(Device Tree) 一.描述 ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx 中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备. resource.i2c_board_info.spi_board_info

设备树学习:内核对设备树的处理

内核版本:linux-4.19 之前系统的学习了有关设备树的一些知识,时间长了总会有忘记的时候,所以现在把所学到的知识记录下来. 系统启动后,内核会执行一段汇编代码,汇编代码暂不分析,我们从 start_kernel 开始. 优先被初始化的信息 调用流程: start_kernel -->setup_arch -->setup_machine_fdt -->early_init_dt_verify /* 验证设备树文件 */ -->of_flat_dt_match_machine

分析内核源码,设备树【转】

转自:http://blog.csdn.net/fight_onlyfor_you/article/details/78092204 U-Boot需要将设备树在内存中的存储地址传给内核.该树主要由三大部分组成:头(Header).结构块(Structure block).字符串块(Strings block). 设备树在内存中的存储布局图如下 1.1 头(header) 1.2 结构块(struct block)  扁平设备树结构块是线性化的树形结构,和字符串块一起组成了设备树的主体,以节点形式

zynq基础-->LINUX 设备树

1.概念 linux设备树是用于描述硬件及部分启动指令的文件,由bootloader传递给内核, 内核分析此文件而对硬件使用不同的参数. 比如两块开发板仅仅是内存容量不一样,那么就只需要修改设备树中对内存容量的描述即可, 而不需要重新编译内核. 与设备树相关的文件有如下几种: DTS(device tree source) .dts文件,就是ASCII字符串形式的文本文件,直接由开发人员修改. 对于ARM架构而言,这些文件位于:arch/arm/boot/dts 目录下. DTSI(device

The Linux usage model for device tree data

Linux and the Device Tree Author: Grant Likely [email protected] 这篇文章介绍了Linux中使用Device Tree的方法.可以在http://devicetree.org/Device_Tree_Usage获取到Device Tree数据格式. Device Tree是一种描述硬件的语言,它可以让操作系统不硬编码硬件的信息. 结构上讲,Device Tree是树形结构,或者非循环的有名节点组成的图.每个节点包含一定数目的属性和键

设备树机制

转自:http://blog.csdn.net/machiner1/article/details/47805069,转载仅供个人参考. ------------------Based on linux 3.10.24 source code 参考/documentation/devicetree/Booting-without-of.txt文档 目录 1. 设备树(Device  Tree)基本概念及作用 2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DTB