linux root节点解析

首先来看看root节点解析大体调用流程
在这里插入图片描述
这里只关注具体的解析函数的实现,中间的调用流程可以看上图自行跟踪。这里从unflatten_device_tree函数调用开始分析其实现:

unflatten_device_tree

void __init unflatten_device_tree(void)
{
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);
	of_alias_scan(early_init_dt_alloc_memory_arch);
	unittest_unflatten_overlay_base();
}

这个函数主要有两点:
1、具体的解析root节点
2、解析节点别名和向内核传递参数

__unflatten_device_tree

在这里插入图片描述
由上述可知unflatten_dt_nodes此函数是具体的解析函数,被调用了两次。一次传递的是NULL用以获取设备树信息的总大小,二次才是根据第一次获取大小分配内存保存数据。下面来具体的分配此函数的实现

unflatten_dt_nodes

static int unflatten_dt_nodes(const void *blob,
			      void *mem,
			      struct device_node *dad,
			      struct device_node **nodepp)
{
	struct device_node *root;
	int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH	64 //支持的最大节点深度为64
	struct device_node *nps[FDT_MAX_DEPTH];
	void *base = mem;
	bool dryrun = !base;
	int ret;

	if (nodepp)
		*nodepp = NULL;
	if (dad)
		depth = initial_depth = 1;

	root = dad;
	nps[depth] = dad;
	//使用广度优先,优先解析相邻节点
	for (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {
		if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1))
			continue;
		//节点status不为oa/okay跳过
		if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
		    !of_fdt_device_is_available(blob, offset))
			continue;
		//具体的解析函数
		ret = populate_node(blob, offset, &mem, nps[depth],
				   &nps[depth+1], dryrun);
		if (ret < 0)
			return ret;

		if (!dryrun && nodepp && !*nodepp)
			*nodepp = nps[depth+1];//保存根节点of_root
		if (!dryrun && !root)
			root = nps[depth+1];//
	}

	if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
		pr_err("Error %d processing FDT\n", offset);
		return -EINVAL;
	}

	/*
	 * Reverse the child list. Some drivers assumes node order matches .dts
	 * node order
	 */
	if (!dryrun)
		reverse_nodes(root);

	return mem - base;
}

populate_node

在这里插入图片描述
插入操作可以用下图来理解:
插入前
插入前
在这里插入图片描述
插入后
从上图可以这里的结构相当单链表,只不过所有的子节点都会指向父点。父节点只需拿到child节点,就可以轮询所有的子节点。以of_root根节点为首,就可以遍历到所有的子节点。
下面再来看看,当前节点的属性解析

populate_properties

在这里插入图片描述
这里需要注意的是链表的插入操作,一直使用pprev变量取最尾部结构体成员next的地址,链表尾部的成员next肯定为NULL,但其地址不为NULL。插入操作是将地址的内容指向当前的的pp,基本就是单链表尾部插入。大体示意图如下:
在这里插入图片描述
到这里device_node的节点解析以及节点里面的属性解析都已经走完了,剩下的就是重复操作直到将整个DTS解析完成。

of_alias_scan

void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
	struct property *pp;

	of_aliases = of_find_node_by_path("/aliases");
	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 = NULL;

		if (of_property_read_string(of_chosen, "stdout-path", &name))
			of_property_read_string(of_chosen, "linux,stdout-path",
						&name);
		if (IS_ENABLED(CONFIG_PPC) && !name)
			of_property_read_string(of_aliases, "stdout", &name);
		if (name)
			of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
		if (of_stdout)
			of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT;
	}

	if (!of_aliases)
		return;

	for_each_property_of_node(of_aliases, pp) {
		const char *start = pp->name;

这里主要解析了两个比较特别的属性/aliases和/chosen,其中/aliases是节点别名。/chosen配置一些内核参数 比如cmdline。拿到/aliases后会解析其所有属性并添加到全局链表aliases_lookup上。
但是这里好像有个问题就是上面这两个属性都是通过of_find_node_by_path函数赋值的,那么of_find_node_by_path这个函数到底能不能实现上面两个节点的赋值呢,下面来看看这个函数的具体实现:

static inline struct device_node *of_find_node_by_path(const char *path)
{
	return of_find_node_opts_by_path(path, NULL);
}

在这里插入图片描述
从上图可以得知:of_aliases = of_find_node_by_path(“/aliases”);得到的数据应该是NULL,或者说使用of_aliases = of_find_node_by_path(“/aliases”);
这里来看个具体的aliases定义:
在这里插入图片描述

		for_each_property_of_node(of_aliases, pp) {
			if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {			//如上图所示这里拿到的value值就是具体的节点,这里居然再次调用了of_find_node_by_path,进行嵌套调用,这是不返回NULL不罢休
				np = of_find_node_by_path(pp->value);
				break;
			}
		}

本从日常工作中基本没有使用过of_find_node_by_path这个函数,不太清楚这个函数到底有没有问题,能不能实现具体功能。按道理所有的节点信息都会保存在of_root节点,我们直接从root节点拿节点信息就可以了。对上面有疑问的代码改写成以下实现:

	of_aliases = of_find_node_by_name(NULL, "/aliases");
	of_chosen = of_find_node_by_name(NULL, "/chosen");

	for_each_property_of_node(of_aliases, pp) {
		if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {			
				np = of_find_node_by_name(NULL,pp->value);
				break;
			}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bruk_spp

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

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

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

打赏作者

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

抵扣说明:

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

余额充值