设备树——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
};

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

};

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链表中

5、__unflatten_device_tree()函数

5.1、函数调用

__unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false);
传参含义
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)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)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>;
    };  
};

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

在这里插入图片描述

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值