• 设备树——dtb格式到struct device node结构体的转换


    1、参考资料

    (1)《设备树 — dtb到device node的转换(三)》
    (2)《linux设备树dts文件详解》

    2、struct device_node结构体

    struct device_node {
    	const char *name;                    //节点的名字
    	const char *type;                    //device_type属性的值
    	phandle phandle;                     //对应该节点的phandle属性 
    	const char *full_name;               //节点的名字, node-name[@unit-address]从“/”开始的,表示该node的full path 
    	struct fwnode_handle fwnode;
     
    	struct	property *properties;        // 节点的属性
    	struct	property *deadprops;	/* removed properties 如果需要删除某些属性,kernel并非真的删除,而是挂入到deadprops的列表 */
    	struct	device_node *parent;         // 节点的父亲
    	struct	device_node *child;          // 节点的孩子(子节点)
    	struct	device_node *sibling;        // 节点的兄弟(同级节点)
    #if defined(CONFIG_OF_KOBJ)              // 在sys文件系统表示
    	struct	kobject kobj;        
    #endif
    	unsigned long _flags;
    	void	*data;
    #if defined(CONFIG_SPARC)
    	const char *path_component_name;
    	unsigned int unique_id;
    	struct of_irq_controller *irq_trans;
    #endif
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3、struct property结构体

    struct property {
    	char	*name;                //属性名字
    	int	length;                   //value的长度
    	void	*value;               //属性值
    	struct property *next;        //指向统一节点的下一个属性
    #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    	unsigned long _flags;
    #endif
    #if defined(CONFIG_OF_PROMTREE)
    	unsigned int unique_id;
    #endif
    #if defined(CONFIG_OF_KOBJ)
    	struct bin_attribute attr;
    #endif
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4、转换过程的函数调用关系

    unflatten_device_tree()	//解析dtb格式成struct device node结构体
    	__unflatten_device_tree()
    		unflatten_dt_nodes()	//计算解析出的struct device node结构体所需要的内存大小
    			dt_alloc()	//申请上面计算出来的需要的内存
    			unflatten_dt_nodes()	//将dtb数据解析成device node结构体,保存在上面申请的内存中
    		
    	of_alias_scan()	//处理aliases节点,根据节点的别名找到对应节点并保存到aliases_lookup链表中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、__unflatten_device_tree()函数

    5.1、函数调用

    __unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false);
    
    • 1
    传参含义
    initial_boot_paramsdtb数据的所在地址
    of_root保存将来解析的struct device_node结构体的根节点
    early_init_dt_alloc_memory_arch用于申请内存的函数
    false觉得是否设置根节点的OF_DETACHED标志

    5.2、函数源码

    	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
    				early_init_dt_alloc_memory_arch, false);
    
    static void *__unflatten_device_tree(const void *blob,
    				     struct device_node *dad,
    				     struct device_node **mynodes,
    				     void *(*dt_alloc)(u64 size, u64 align),
    				     bool detached)
    {
    	int size;
    	void *mem;
    
    	pr_debug(" -> unflatten_device_tree()\n");
    
    	if (!blob) {
    		pr_debug("No device tree pointer\n");
    		return NULL;
    	}
    
    	//打印dtb的相关信息
    	pr_debug("Unflattening device tree:\n");
    	pr_debug("magic: %08x\n", fdt_magic(blob));
    	pr_debug("size: %08x\n", fdt_totalsize(blob));
    	pr_debug("version: %08x\n", fdt_version(blob));
    
    	//校验dtb的数据头
    	if (fdt_check_header(blob)) {
    		pr_err("Invalid device tree blob header\n");
    		return NULL;
    	}
    
    	/* 第一次调用:计算解析出的struct device_node结构体所占内存大小 */
    	size = unflatten_dt_nodes(blob, NULL, dad, NULL);
    	if (size < 0)
    		return NULL;
    
    	//将内存大小4字节对齐
    	size = ALIGN(size, 4);
    	pr_debug("  size is %d, allocating...\n", size);
    
    	/* 申请解析设备树dtb数据需要的内存 */
    	mem = dt_alloc(size + 4, __alignof__(struct device_node));
    	if (!mem)
    		return NULL;
    
    	memset(mem, 0, size);
    
    	//将申请内存空间的下一个地址处赋值为0xdeadbeef
    	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
    
    	pr_debug("  unflattening %p...\n", mem);
    
    	/* 第二次调用:解析dtb数据成device node结构体,保存在上面申请的内存中*/
    	unflatten_dt_nodes(blob, mem, dad, mynodes);
    
    	//检查解析的device_node结构体所占内存是否越界
    	if (be32_to_cpup(mem + size) != 0xdeadbeef)
    		pr_warning("End of tree marker overwritten: %08x\n",
    			   be32_to_cpup(mem + size));
    
    	if (detached && mynodes) {
    		of_node_set_flag(*mynodes, OF_DETACHED);
    		pr_debug("unflattened tree is detached\n");
    	}
    
    	pr_debug(" <- unflatten_device_tree()\n");
    	return mem;
    }		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    (1)unflatten_dt_nodes()会被调用两次,传参不同该函数会有不同的功能;第一次是计算所需内存大小,第二次是真正解析dtb数据成device_node格式;
    (2)of_root变量保存的是根节点对应的struct device_node结构体

    6、struct device_node *of_root变量

    (1)无论是dtb格式还是struct device_node格式,里面表达的数据是没变的,只是组织形式不同,解析的方法就不同。dtb格式和struct device_node格式都有专门的解析函数,dtb格式下是需要知道dtb数据所在内存地址,struct device_node格式是需要知道根节点的struct device_node结构体;
    (2)of_root就是保存的根节点的struct device_node结构体,后续解析设备树的信息就是利用of_root根节点和专门的解析函数即可;

    7、struct device_node格式下如何解析出信息

    7.1、相关文件和操作函数

    (1)在struct device_node格式下内核提供相关的操作函数,具体查看"drivers/of/base.c"文件;
    (2)我们调用相关的函数,可以通过节点路径、节点名字、父节点等方式去查找需要的device_node结构体;

    7.2、of_alias_scan()函数

    void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
    {
    	struct property *pp;
    
    	//通过节点的路径查看aliases节点
    	of_aliases = of_find_node_by_path("/aliases");
    	
    	//通过节点的路径查看chosen节点
    	of_chosen = of_find_node_by_path("/chosen");
    	if (of_chosen == NULL)
    		of_chosen = of_find_node_by_path("/chosen@0");
    
    	if (of_chosen) {
    		/* linux,stdout-path and /aliases/stdout are for legacy compatibility */
    		const char *name = of_get_property(of_chosen, "stdout-path", NULL);
    		if (!name)
    			name = of_get_property(of_chosen, "linux,stdout-path", NULL);
    		if (IS_ENABLED(CONFIG_PPC) && !name)
    			name = of_get_property(of_aliases, "stdout", NULL);
    		if (name)
    			of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
    	}
    
    	if (!of_aliases)
    		return;
    
    	for_each_property_of_node(of_aliases, pp) {
    		const char *start = pp->name;
    		const char *end = start + strlen(start);
    		struct device_node *np;
    		struct alias_prop *ap;
    		int id, len;
    
    		/* Skip those we do not want to proceed */
    		if (!strcmp(pp->name, "name") ||
    		    !strcmp(pp->name, "phandle") ||
    		    !strcmp(pp->name, "linux,phandle"))
    			continue;
    
    		np = of_find_node_by_path(pp->value);
    		if (!np)
    			continue;
    		······
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    (1)of_alias_scan()函数主要是处理aliases节点,处理节点的别名,方便后续访问;
    (2)查找aliases节点对应的device_node结构体就是通过绝对路径进行查找;
    (3)of_find_node_by_path( )函数内部会根据of_root节点进行查找,of_root节点就是根节点;

    8、dts示例源码

    /dts-v1/;
    /memreserve/ 0x4ff00000 0x100000;
    / {
        model = "YIC System SMDKV210 based on S5PV210";
        compatible = "yic,smdkv210", "samsung,s5pv210";
     
        #address-cells = <1>;
        #size-cells = <1>;
            
        chosen {
            bootargs = "console=ttySAC2,115200n8 root=/dev/nfs nfsroot=192.168.0.101:/home/run/work/rootfs/";
        };  
     
        memory@30000000 {
            device_type = "memory";
            reg = <0x30000000 0x20000000>;
        };  
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    9、dts文件和struct device_node的转换图

    在这里插入图片描述

  • 相关阅读:
    常见的设计模式
    PostgreSQL 正则表达式匹配字段
    Vue2:MVVM 设计模式、Vue指令、模板语法
    【LSTM实战】股票走势预测全流程实战(stock predict)
    acwing算法基础之数据结构--哈希表算法
    12款爆款项目管理工具推荐
    【Oculus Interaction SDK】(十二)Meta Quest 如何开启透视(Passthrough)
    接口测试常用测试点
    2022“杭电杯”中国大学生算法设计超级联赛(10)
    学生信息管理系统(spring+springmvc+mybatis+Layui)
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/126073599