设备节点只是某一个结构体而已,首先思考一个问题,uboot把dtb文件放到内存的某个地方,内核就可以使用,为何内核在运行的过程中,不会覆盖,使用dtb文件所占据的内存呢? 我们之前学习过的dtb文件中有保留内存的格式为: /memreserve/
; 复习如下 即使在dtb文件中不写保留某些内存的格式,内核也会把dtb文件所占据的内存保留出来,以防止在内核运行的过程中,被覆盖,那么内核是如何实现这一机制的呢?我们来看看内核中函数的调用流程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(); }在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 };以根节点为例,这些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)>; }; };在__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; }/首先获得节点的名字/ /然后分配内存空间,在这个空间里面既含有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; }/也是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; }