内核对设备树的处理(三)__dtb转换为device_node

前言:

  1. 问题:我们把设备树文件随便放到内存某个地方就可以使用,内核运行过程中却不会覆盖设备树dtb文件所占的那一块内存呢?
    答:之前说过在设备树文中的[memory reservations]可以指定一块内存(这块内存被保留下来,内核不会占用它)。
[memory reservations]    // 格式为: /memreserve/ 
; 譬如:/memreserve/ <0x33f00000> <0x100000>;

即使我们没有指定这一块内存,当我们内存启动时,内核也会把dtb文件所在的区域保留下来。

  1. 问题: 怎样把DTB文件中的节点信息被linux内核所使用呢?
    我们知道在dts文件中每一对大括号对应一个节点,在linux内核中DTB文件中每一个节点都转换为一个device_node结构体。

问题1分析:

  1. 追踪设备树的区域保留函数调用过程:
start_kernel // init/main.c
	setup_arch(&command_line);
		arm_memblock_init(mdesc);
			early_init_fdt_reserve_self();
				/* Reserve the dtb region   把DTB所占区域保留下来,*/
				early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
				  fdt_totalsize(initial_boot_params),
				  0);
                                    memblock_reserve(base, size);
                               early_init_fdt_scan_reserved_mem();

提炼重要的信息:

/* Reserve the dtb region */
early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
      fdt_totalsize(initial_boot_params),
      0);
  • initial_boot_params是设备树的起始地址。
  • early_init_fdt_scan_reserved_mem函数来分析DTB保留内存信息中的节点。
/**
 * early_init_fdt_scan_reserved_mem() - create reserved memory regions
 *
 * This function grabs memory from early allocator for device exclusive use
 * defined in device tree structures. It should be called by arch specific code
 * once the early allocator (i.e. memblock) has been fully activated.
 */
void __init early_init_fdt_scan_reserved_mem(void)
{
	int n;
	u64 base, size;

	if (!initial_boot_params)
		return;     -----------------1

	/* Process header /memreserve/ fields */
	for (n = 0; ; n++) {
		fdt_get_mem_rsv(initial_boot_params, n, &base, &size); ---2
		if (!size)
			break;
		early_init_dt_reserve_memory_arch(base, size, 0);
	}

	of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); ----3
	fdt_init_reserved_mem();
}

1、initial_boot_params实际上就是DTB对应的虚拟地址。在early_init_dt_verify中设定的。如果系统中都没有有效的DTB,那么没有什么可以scan的。
2、分析fdt中的 /memreserve/ fields ,进行内存的保留。在fdt的header中定义了一组memory reserve参数,其具体的位置是fdt base address + off_mem_rsvmap。
3、对fdt中的每一个节点调用__fdt_scan_reserved_mem函数,进行reserved-memory节点的扫描,之后调用fdt_init_reserved_mem函数进行内存预留的动作。

问题2分析:

我们知道在dts文件中每一对大括号对应一个节点,在linux内核中DTB文件中每一个节点都转换为一个device_node结构体。

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

dts文件里,每个大括号{ }代表一个节点,比如根节点里有个大括号,对应一个device_node结构体;memory也有一个大括号,也对应一个device_node结构体。

节点里面有各种属性,也可能里面还有子节点,所以它们还有一些父子关系。

根节点下的memory、chosen、led等节点是并列关系,兄弟关系。

对于父子关系、兄弟关系,在device_node结构体里面肯定有成员来描述这些关系。

属性名字        属性值
compatible = "jz2440_led";

device_node结构体表示一个节点,property结构体表示节点的具体属性。

device_node结构体中有properties, 用来表示该节点的属性
每一个属性对应一个property结构体:
    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
    };

linux内核device_node结构体和property结构体与dts文件内容的对应关系如下:
内核对设备树的处理(三)__dtb转换为device_node_第1张图片下面对linux内核device_node结构体和property结构体与dts文件内容的对应关系图进行分析:

start_kernel // init/main.c
	setup_arch(&command_line);
         unflatten_device_tree();

unflatten_device_tree()函数:实现了从DTB设备树文件中节点转化为device_node结构体(每一个设备树节点转化成一个device_node结构体)

/**
 * 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)
{
	/* 该函数会遍历DTB文件中的每一个节点,然后构造出device_node结构体,并且生成里面的树状关系
	 * 生成的树的根节点会保存在of_root(是device_node结构体)变量里,所以后面可以通过of_root变量
	 * 遍历整棵device_node树
	 */
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false); -----1 

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

	unittest_unflatten_overlay_base();
}

1、该函数会遍历DTB文件中的每一个节点,然后构造出device_node结构体

/**
 * __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 = unflatten_dt_nodes(blob, NULL, dad, NULL);  //计算所有device_node结构体的大小
	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)); //分配所有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);  //创建所有device_node结构体,构造出device_node结构体树
	------------------1.1
     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.1、从DTB文件中分配并填充device_node

/**
 * 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 (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {  //从DTB中取出每一个节点
		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], //populate_node函数构造device_node节点
				   &nps[depth+1], dryrun))
                   ---------------1.1.1
			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;
}

1.1.1、populate_node函数构造device_node节点

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;   //统计该节点名字所占据的空间

	/* unflatten_dt_alloc即分配device_node,也分配节点的名字 */
	np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	if (!dryrun) {
		char *fn;
		of_node_init(np);

		/* np->full_name:指向该device_node的尾部, */
		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;
		}
	}

	/* populate_properties函数:为每个节点属性都构造出一个struct	property结构体,
	 * 并且设置 struct	property结构体
	 */
	populate_properties(blob, offset, mem, np, pathp, dryrun); ---1.1.1.1
	if (!dryrun) {
	/*处理完属性之后,查询是否有nand属性,如果有nand属性则设置nand属性的值赋值给np->name */
		np->name = of_get_property(np, "name", NULL);
		np->type = of_get_property(np, "device_type", NULL);

		if (!np->name)
			np->name = ""; /* 如果没有nand属性,则把""赋值给np->name */
		if (!np->type)
			np->type = "";
	}

	*pnp = np;
	return true;
}

1.1.1.1、populate_properties函数:为每个节点属性都构造出一个struct property结构体,并且设置 struct 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 (cur = fdt_first_property_offset(blob, offset); //取出第一个属性
	     cur >= 0;

		/* 取出下一个属性 */
	     cur = fdt_next_property_offset(blob, cur)) {  
		const __be32 *val;
		const char *pname;
		u32 sz;

		/* pname:得到属性名字
		 * 
		 */
		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;
}

你可能感兴趣的:(LinuxDTS(设备树))