【跟着韦东山学习linux设备树】dtb转换为device_node(unflatten)树状结构

引子

设备节点只是某一个结构体而已,首先思考一个问题,uboot把dtb文件放到内存的某个地方,内核就可以使用,为何内核在运行的过程中,不会覆盖,使用dtb文件所占据的内存呢?
我们之前学习过的dtb文件中有保留内存的格式为: /memreserve/

; 复习如下
在这里插入图片描述即使在dtb文件中不写保留某些内存的格式,内核也会把dtb文件所占据的内存保留出来,以防止在内核运行的过程中,被覆盖,那么内核是如何实现这一机制的呢?我们来看看内核中函数的调用流程

函数调用过程

1. start_kernel() 函数定义在init/main.c

asmlinkage __visible void __init start_kernel(void)................
setup_arch(&command_line);
.................

2. setup_arch()函数定义在arch/arm/kernel/setup.c

void __init setup_arch(char **cmdline_p){
..........................................
	arm_memblock_init(mdesc);
	...................................
	unflatten_device_tree();
.......................................
}

3. arm_memblock_init()函数定义在arch/arm/mm/init.c

void __init arm_memblock_init(const struct machine_desc *mdesc){
	/* Register the kernel text, kernel data and initrd with memblock. */
	memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START);

	arm_initrd_init();

	arm_mm_memblock_reserve();

	/* reserve any platform specific memblock areas */
	if (mdesc->reserve)
		mdesc->reserve();
	/*早期初始化时把fdt本身保留下来,最终会调用memblock_reserve()
	将DTB文件所占的内存区域保留下来,以后内核运行都不会占用这块内存,这块内存会一直保留着,这样就可以随时使用dtb里面的数据详细代码见3.1*/	
	early_init_fdt_reserve_self();
	/*再逐个扫描dtb文件中的节点,*/
	early_init_fdt_scan_reserved_mem();

	/* reserve memory for DMA contiguous allocations */
	dma_contiguous_reserve(arm_dma_limit);

	arm_memblock_steal_permitted = false;
	memblock_dump_all();
}

3.1. 函数early_init_fdt_reserve_self();定义在drivers/of/fdt.c

/**
 * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob
 */
void __init early_init_fdt_reserve_self(void)
{
	if (!initial_boot_params)
		return;

	/* Reserve the dtb region */
	early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
					  fdt_totalsize(initial_boot_params),
					  0);
}

3.2. 函数early_init_dt_reserve_memory_arch();定义在drivers/of/fdt.c

int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
					phys_addr_t size, bool nomap)
{
	if (nomap)
		return memblock_remove(base, size);
	return memblock_reserve(base, size);
}

4.unflatten_device_tree()函数定义在drivers/of/fdt.c

dtb文件是扁平的(FDT:Flattened Device Tree即扁平设备树),里面含有各个设备节点,我们需要将其提取出来, 构造成一棵树。这个函数是分析重点。首先我们来学习两个重要结构体device_node和property。
initial_boot_params:存放dtb文件的起始地址

/**
 * unflatten_device_tree - create tree of device_nodes from flat blob
 *
 * unflattens the device-tree passed by the firmware, creating the
 * tree of struct device_node. It also fills the "name" and "type"
 * pointers of the nodes so the normal device-tree walking functions
 * can be used.
 */
void __init unflatten_device_tree(void)
{
/*initial_boot_params:存放dtb文件的起始地址;
__unflatten_device_tree()函数会遍历DTB文件中的每个节点,然后构造出对应的device_node结构体,并且构造出对应的树状关系
of_root:该device_node结构体变量保存上述函数构造的树的结构的根节点
		可以使用of_root来遍历整个树的数据
*/
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);

	/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
	of_alias_scan(early_init_dt_alloc_memory_arch);

	unittest_unflatten_overlay_base();
}

4.1. 结构体struct device_node //定义在include/linux/of.h

在dts文件中每一个{}都对应着一个节点,每一个节点都对应着一个结构体device_node,每个节点有各种属性,节点里面可能还有子节点。而结构体struct device_node中的成员对应着都有描述(见下面device_node的定义),device_node中的成员中的parent,child,sibling构造出设备节点的树状结构。
节点的名字(const char *full_name)放在device_node结构体的后面(在结构体外面),然后由full_name指针,指向这里。见下示意图,代码如下
np->full_name = fn = ((char *)np) + sizeof(*np);

struct device_node {
            const char *name;  // 来自节点中的name属性, 如果没有该属性, 则设为"NULL"
            const char *type;  // 来自节点中的device_type属性, 如果没有该属性, 则设为"NULL"
            phandle phandle;
            const char *full_name;  // 节点的名字, node-name[@unit-address]
            struct fwnode_handle fwnode;

            struct  property *properties;//节点的属性(实际指向一个链表)
            struct  property *deadprops;    /* removed properties */
            struct  device_node *parent;   // 节点的父亲
            struct  device_node *child;    // 节点的孩子(子节点)
            struct  device_node *sibling;  // 节点的兄弟(同级节点)
        #if defined(CONFIG_OF_KOBJ)
            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
        };

4.2. 结构体struct property ////定义在include/linux/of.h

struct property {
            char    *name;    // 属性名字, 指向dtb文件中的字符串
            int length;       // 按字节计算属性值的长度
            void    *value;   // 属性值, 指向dtb文件中value所在位置, 数据仍以big endian存储
            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
        };

以根节点为例,这些dts文件是如何构成成设备节点树状结构的,见示意图

............
/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory {  /* /memory */
		device_type = "memory";
		reg =  <0x30000000 0x4000000 0 4096>;		
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};
	led {
		compatible = "jz2440_led";
		
		pin = <S3C2410_GPF(5)>;
		
	};
};

dtb转换为device_node树状示意图

在这里插入图片描述

5. __unflatten_device_tree()函数定义在drivers/of/fdt.c

/**
 * __unflatten_device_tree - create tree of device_nodes from flat blob
 *
 * unflattens a device-tree, creating the
 * tree of struct device_node. It also fills the "name" and "type"
 * pointers of the nodes so the normal device-tree walking functions
 * can be used.
 * @blob: The blob to expand
 * @dad: Parent device node
 * @mynodes: The device_node tree created by the call
 * @dt_alloc: An allocator that provides a virtual address to memory
 * for the resulting tree
 * @detached: if true set OF_DETACHED on @mynodes
 *
 * Returns NULL on failure or the memory chunk containing the unflattened
 * device tree on success.
 */
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;
	}

	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));

	if (fdt_check_header(blob)) {
		pr_err("Invalid device tree blob header\n");
		return NULL;
	}

	/* First pass, scan for size */
	/*size就是设备树中所有节点对应的device_node结构体和property结构体的大小*/
	size = unflatten_dt_nodes(blob, NULL, dad, NULL);
	if (size < 0)
		return NULL;

	size = ALIGN(size, 4);
	pr_debug("  size is %d, allocating...\n", size);

	/* Allocate memory for the expanded device tree */
	mem = dt_alloc(size + 4, __alignof__(struct device_node));
	if (!mem)
		return NULL;

	memset(mem, 0, size);

	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);

	pr_debug("  unflattening %p...\n", mem);

	/* Second pass, do actual unflattening */
	unflatten_dt_nodes(blob, mem, dad, mynodes);
	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;
}

5.1. __unflatten_device_tree()函数,定义在drivers/of/fdt.c

在__unflatten_device_tree()函数中,unflatten_dt_nodes()函数被调用了两次,第一次调用只是为了计算出设备树中所有节点对应的device_node结构体和property结构体的大小,第二次调用才是用这些结构体展开为树的结构。

/**
 * unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
 * @blob: The parent device tree blob
 * @mem: Memory chunk to use for allocating device nodes and properties
 * @dad: Parent struct device_node
 * @nodepp: The device_node tree created by the call
 *
 * It returns the size of unflattened device tree or error code
 */
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
	struct device_node *nps[FDT_MAX_DEPTH];
	void *base = mem;
	bool dryrun = !base;

	if (nodepp)
		*nodepp = NULL;

	/*
	 * We're unflattening device sub-tree if @dad is valid. There are
	 * possibly multiple nodes in the first level of depth. We need
	 * set @depth to 1 to make fdt_next_node() happy as it bails
	 * immediately when negative @depth is found. Otherwise, the device
	 * nodes except the first one won't be unflattened successfully.
	 */
	if (dad)
		depth = initial_depth = 1;

	root = dad;
	nps[depth] = dad;
/*for循环,将里面的dtb节点一个个地取出来,调用populate_node()函数构造device_node节点*/
	for (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {
		if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
			continue;

		if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
		    !of_fdt_device_is_available(blob, offset))
			continue;

		if (!populate_node(blob, offset, &mem, nps[depth],
				   &nps[depth+1], dryrun))
			return mem - base;

		if (!dryrun && nodepp && !*nodepp)
			*nodepp = nps[depth+1];
		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;
}
5.1.1 populate_node()函数,定义在drivers/of/fdt.c

/首先获得节点的名字/
/然后分配内存空间,在这个空间里面既含有device_node结构体的大小,也有名字的大小/
/然后写入名字/
/然后处理节点的属性,即构造property结构体,并且设置这个结构体/

static bool populate_node(const void *blob,
			  int offset,
			  void **mem,
			  struct device_node *dad,
			  struct device_node **pnp,
			  bool dryrun)
{
	struct device_node *np;
	const char *pathp;
	unsigned int l, allocl;
/*首先获得节点的名字*/
	pathp = fdt_get_name(blob, offset, &l);
	if (!pathp) {
		*pnp = NULL;
		return false;
	}
/*计算这个节点名字的长度*/
	allocl = ++l;
/*然后分配内存空间,在这个空间里面既含有device_node结构体的大小,也有名字的大小*/
	np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	if (!dryrun) {
		char *fn;
		of_node_init(np);
		/*节点的名字(const char *full_name)放在device_node结构体的后面(**在结构体外面)**,然后由full_name指针,指向这里。*/
		np->full_name = fn = ((char *)np) + sizeof(*np);
/*然后写入名字*/
		memcpy(fn, pathp, l);

		if (dad != NULL) {
			np->parent = dad;
			np->sibling = dad->child;
			dad->child = np;
		}
	}
/*然后处理节点的属性,即构造property结构体,并且设置这个结构体*/
	populate_properties(blob, offset, mem, np, pathp, dryrun);
	if (!dryrun) {
		np->name = of_get_property(np, "name", NULL);
		if (!np->name)
			np->name = "<NULL>";
	}

	*pnp = np;
	return true;
}
5.1.1.1populate_properties()函数,//定义在drivers/of/fdt.c

/也是for循环,先取出第一个属性处理,然后下一个属性处理/
/得到属性的名字/
/为property结构体分配空间/
/设置property结构体的成员/


static void populate_properties(const void *blob,
				int offset,
				void **mem,
				struct device_node *np,
				const char *nodename,
				bool dryrun)
{
	struct property *pp, **pprev = NULL;
	int cur;
	bool has_name = false;

	pprev = &np->properties;
	/*也是for循环,先取出第一个属性处理,然后下一个属性处理*/
	for (cur = fdt_first_property_offset(blob, offset);
	     cur >= 0;
	     cur = fdt_next_property_offset(blob, cur)) {
		const __be32 *val;
		const char *pname;
		u32 sz;
		/*得到属性的名字*/
		val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
		if (!val) {
			pr_warn("Cannot locate property at 0x%x\n", cur);
			continue;
		}

		if (!pname) {
			pr_warn("Cannot find property name at 0x%x\n", cur);
			continue;
		}

		if (!strcmp(pname, "name"))
			has_name = true;
		/*为property结构体分配空间*/
		pp = unflatten_dt_alloc(mem, sizeof(struct property),
					__alignof__(struct property));
		if (dryrun)
			continue;

		/* We accept flattened tree phandles either in
		 * ePAPR-style "phandle" properties, or the
		 * legacy "linux,phandle" properties.  If both
		 * appear and have different values, things
		 * will get weird. Don't do that.
		 */
		if (!strcmp(pname, "phandle") ||
		    !strcmp(pname, "linux,phandle")) {
			if (!np->phandle)
				np->phandle = be32_to_cpup(val);
		}

		/* And we process the "ibm,phandle" property
		 * used in pSeries dynamic device tree
		 * stuff
		 */
		if (!strcmp(pname, "ibm,phandle"))
			np->phandle = be32_to_cpup(val);
/*设置property结构体的成员*/
		pp->name   = (char *)pname;
		pp->length = sz;
		pp->value  = (__be32 *)val;
		*pprev     = pp;
		pprev      = &pp->next;
	}

	/* With version 0x10 we may not have the name property,
	 * recreate it here from the unit name if absent
	 */
	if (!has_name) {
		const char *p = nodename, *ps = p, *pa = NULL;
		int len;

		while (*p) {
			if ((*p) == '@')
				pa = p;
			else if ((*p) == '/')
				ps = p + 1;
			p++;
		}

		if (pa < ps)
			pa = p;
		len = (pa - ps) + 1;
		pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
					__alignof__(struct property));
		if (!dryrun) {
			pp->name   = "name";
			pp->length = len;
			pp->value  = pp + 1;
			*pprev     = pp;
			pprev      = &pp->next;
			memcpy(pp->value, ps, len - 1);
			((char *)pp->value)[len - 1] = 0;
			pr_debug("fixed up name for %s -> %s\n",
				 nodename, (char *)pp->value);
		}
	}

	if (!dryrun)
		*pprev = NULL;
}

猜你喜欢

转载自blog.csdn.net/weixin_43326587/article/details/107067075