设备树(3) - dtb解析过程

代码分析及注释

        删掉了很多未注释的代码,只保留了小编认为重要的部分。从setup_arch()函数开始分析,想知道该函数之前的调用关系,可以看小编的另一篇博客《Linux kernel启动过程》

        在3.10版本的kernel代码中,优先使用dtb来适配单板,在dtb适配失败的时候再使用传统的tags方式来适配,因为此时的__atags_pointer只是一个ddr内存地址,至于这个地址是dtb地址还是tags地址取决于uboot,而kernel是不知道的。

void __init setup_arch(char **cmdline_p)
{
	mdesc = setup_machine_fdt(__atags_pointer);	//根据设备树信息匹配最合适的mach_desc
	if (!mdesc)	//如果匹配失败,则使用传统的方法setup_machine_tags来setup machine描述符
		mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
	machine_desc = mdesc;	//匹配到的machine_desc
	arm_memblock_init(&meminfo, mdesc);	//把DTB所占区域保留下来
	unflatten_device_tree(); //扫描并解析dtb文件,将节点组织成一个由device_node结构连接而成的树===》
	if (mdesc->init_early)
		mdesc->init_early();
}
void __init unflatten_device_tree(void)
{
	/*initial_boot_params指向device tree在内存中的首地址,of_root经过这个函数处理之后会指向根节点*/
	/*early_init_dt_alloc_memory_arch是一个函数指针,为struct device_node和struct property结构体分配内存的回调函数*/
	__unflatten_device_tree(initial_boot_params, &of_allnodes,
				early_init_dt_alloc_memory_arch); //解析设备树,将所有的设备节点链入全局链表of_allnodes中 ===》

	/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
	of_alias_scan(early_init_dt_alloc_memory_arch); //设置内核输出终端,以及遍历/chosen节点下的所有属性,挂入相应链表
}
static void __unflatten_device_tree(struct boot_param_header *blob,
			     struct device_node **mynodes,
			     void * (*dt_alloc)(u64 size, u64 align))
{
	/*检查设备树magic*/
	if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {
		pr_err("Invalid device tree blob header\n");
		return;
	}

	/*第一次主要是为了得到Device Tree转化为struct device_node和struct property结构体需要分配的内存大小*/
	start = ((unsigned long)blob) +
		be32_to_cpu(blob->off_dt_struct);	/*找到设备树的设备节点的首地址*/
	size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); //第一轮主要是确定device-tree structure的长度,保存在size变量中
	size = (size | 3) + 1;

	mem = (unsigned long)
		dt_alloc(size + 4, __alignof__(struct device_node)); //一次性的分配了一大片内存,并不是扫描到一个node或者property就分配相应内存

	((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); //设备树结束处赋值0xdeadbeef

	/*第二次调用unflatten_dt_node才是具体填充*/
	start = ((unsigned long)blob) +
		be32_to_cpu(blob->off_dt_struct);	/*找到设备树的设备节点的首地址*/
	/*mem为设备树分配的内存空间,allnextp指向全局变量of_allnodes,生成设备树*/
	unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); //dtb解析主要函数,解析完构成了device node树 ===》
	if (be32_to_cpup((__be32 *)start) != OF_DT_END)
		pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
	if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef) //检查是否有数据溢出
		pr_warning("End of tree marker overwritten: %08x\n",
			   be32_to_cpu(((__be32 *)mem)[size / 4]));
}
static unsigned long unflatten_dt_node(struct boot_param_header *blob,
				unsigned long mem,
				unsigned long *p,
				struct device_node *dad,
				struct device_node ***allnextpp,
				unsigned long fpsize)
{
	/* *p指向设备树的设备节点起始地址 */
	tag = be32_to_cpup((__be32 *)(*p));
	/*每个有child的设备节点,其tag一定是OF_DT_BEGIN_NODE*/
	if (tag != OF_DT_BEGIN_NODE) {
		pr_err("Weird tag at start of node: %x\n", tag);
		return mem;
	}
	*p += 4;	/*地址+4,跳过tag,这样指向节点的名称或者节点路径*/
	pathp = (char *)*p;	/*获得节点名或节点路径名*/
	l = allocl = strlen(pathp) + 1;	/*该节点名称的长度*/
	*p = ALIGN(*p + l, 4);	/*地址对齐后,*p指向该节点属性的地址*/

	/*分配一个设备节点device_node结构,*mem记录分配了多大空间,最终会累加计算出该设备树总共分配的空间大小*/
	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	
	/*第一次调用unflatten_dt_node时,allnextpp=NULL*/
	/*第二次调用unflatten_dt_node时,allnextpp=of_allnodes*/
	if (allnextpp) {
		char *fn;
		memset(np, 0, sizeof(*np));
		/*full_name保存完整的节点名,即包括各级父节点的名称*/
		np->full_name = fn = ((char *)np) + sizeof(*np);
		/*如果new_format=1,表示path保存的是节点名,而不是节点路径名,所以需要加上父节点的name*/
		if (new_format) {
			/* rebuild full path for new format */
			if (dad && dad->parent) {
				strcpy(fn, dad->full_name);	/*把父节点绝对路径先拷贝*/
				fn += strlen(fn);
			}
			*(fn++) = '/';
		}
		memcpy(fn, pathp, l);	/*拷贝本节点的名称*/

		/*prev_pp指向节点的属性列表*/
		prev_pp = &np->properties;
		/*当前节点插入全局链表of_allnodes*/
		**allnextpp = np;
		*allnextpp = &np->allnext;
		/*如果父节点不为空,则设置该节点的parent*/
		if (dad != NULL) {
			np->parent = dad;	/*指向父节点*/
			/* we temporarily use the next field as `last_child'*/
			if (dad->next == NULL)	/*第一个孩子*/
				dad->child = np;	/*child指向第一个孩子*/
			else
				dad->next->sibling = np;	/*把np插入next,这样孩子节点形成链表*/
			dad->next = np;
		}
		kref_init(&np->kref);
	}
	
	/*分析该节点的属性*/
	while (1) {
		/*前边已经将*p移到指向节点属性的地址处,取出属性标识*/
		tag = be32_to_cpup((__be32 *)(*p));
		/*空属性, 则跳过*/
		if (tag == OF_DT_NOP) {
			*p += 4;
			continue;
		}
		/*tag不是属性则退出*/
		if (tag != OF_DT_PROP)
			break;
		/*地址加4跳过tag*/
		*p += 4;
		/*获得属性值的大小,是以占多少整形指针计算的*/
		sz = be32_to_cpup((__be32 *)(*p));
		/*获取属性名称在节点的字符串块中的偏移*/
		noff = be32_to_cpup((__be32 *)((*p) + 4));
		/*地址加8,跳过属性值的大小和属性名称在节点的字符串块中的偏移*/
		*p += 8;
		/*地址对齐后,*p指向属性值所在的地址*/
		if (be32_to_cpu(blob->version) < 0x10)
			*p = ALIGN(*p, sz >= 8 ? 8 : 4);

		/*从节点的字符串块中noff偏移处,得到该属性的name*/
		pname = of_fdt_get_string(blob, noff);
		
		/*如果有名称为name的属性,表示变量has_name为1*/
		if (strcmp(pname, "name") == 0)
			has_name = 1;
		/*计算该属性name的大小*/
		l = strlen(pname) + 1;
		/*为该属性分配一个属性结构,即struct property;mem记录分配了多大空间,最终会累加计算出设备树总共分配的空间大小*/
		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
					__alignof__(struct property));
		/*第一次调用unflatten_dt_node时,allnextpp=NULL*/
		/*第二次调用unflatten_dt_node时,allnextpp=of_allnodes*/
		if (allnextpp) {
			if (strcmp(pname, "ibm,phandle") == 0)
				np->phandle = be32_to_cpup((__be32 *)*p);
			pp->name = pname;	/*属性名*/
			pp->length = sz;	/*属性值长度*/
			pp->value = (void *)*p; /*属性值*/
			/*属性插入该节点的属性链表np->properties*/
			*prev_pp = pp;
			prev_pp = &pp->next;
		}
		*p = ALIGN((*p) + sz, 4);	/*指向下一个属性*/
	}
	/*至此,遍历完该节点的所有属性*/
	
	/*如果设置了allnextpp指针*/
	if (allnextpp) {
		*prev_pp = NULL;
		/*设置节点的名字*/
		np->name = of_get_property(np, "name", NULL);
		/*加一句打印看看*/
		printk(KERN_EMERG "np->name = %s\n", np->name);
		/*设置该节点对应的设备类型*/
		np->type = of_get_property(np, "device_type", NULL);
	}
	/*mem返回整个设备树所分配的内存大小,即设备树占的内存空间*/
	return mem;
}

你可能感兴趣的:(设备树,设备树,内核,Linux,嵌入式)