/**
* 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
*/
【setup_arch-->unflatten_device_tree();-->__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);--》/* First pass, scan for size */
size = unflatten_dt_nodes(blob, NULL, NULL, NULL);以及--》
/* Second pass, do actual unflattening */
unflatten_dt_nodes(blob, mem, NULL, &of_root);根据这两个调用链分析】
【First pass, scan for size时为unflatten_dt_nodes(initial_boot_params,NULL,&of_root,NULL);
Second pass, do actual unflattening时为
unflatten_dt_nodes(initial_boot_params,mem,&of_root,)】
【unflatten_dt_nodes(initial_boot_params,NULL,&of_root,NULL)时,简化函数如下】
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
【注释:FDT_MAX_DEPTH 一个节点最深64,里面最多包含64个子节点】
struct device_node *nps[FDT_MAX_DEPTH];
void *base = mem;
bool dryrun = !base;
root = dad;
nps[depth] = dad;
for (offset = 0;
offset >= 0 && depth >= initial_depth;
offset = fdt_next_node(blob, offset, &depth)) {
【一个节点超过最大64个子节点,超过则跳出,扫面同一级或上一级节点】
if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
continue;
【drivers/of/kconfig中,
config OF_KOBJ
def_bool SYSFS
也即IS_ENABLED(CONFIG_OF_KOBJ)为true,此判断不成立,忽略】
if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
!of_fdt_device_is_available(blob, offset))
continue;
【populate_node解析见下文。知道没有node遍历完返回false,mem为从null=0累加节点名+属性值等的总占用空间大小,base=null=0】
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
}
return mem - base;
}
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;
【获取到offset偏移的节点的名子】
pathp = fdt_get_name(blob, offset, &l);
if (!pathp) {
*pnp = NULL;
【偏移计算之后,没有获取到节点名字,视为结束】
return false;
}
allocl = ++l;
【当__unflatten_device_tree中调用此句
/* First pass, scan for size */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);时,该函数的参数mem为null。但unflatten_dt_nodes调用populate_node时传入的&mem不为null,它是指向此null指针的指针,也即下句的mem不为null,但*mem为null,np为null,详解见下文】
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
【dryrun在unflatten_dt_nodes被调用,目的是/* First pass, scan for size */时为true,可忽略下面的两个if分支】
if (!dryrun) {
char *fn;
of_node_init(np);
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(blob, offset, mem, np, pathp, dryrun);
if (!dryrun) {
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "<NULL>";
if (!np->type)
np->type = "<NULL>";
}
*pnp = np;
【只要节点没有遍历完,就返回true,一直遍历】
return true;
}
以下是fdt_get_name函数的解析begin---------------------------------------
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
【fdt_offset_ptr_-->
fdt_off_dt_struct(fdt)-->
#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) -->
#define fdt_get_header(fdt, field) \
(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))-->
即(const struct fdt_header *)(fdt))->off_dt_struct
其中fdt为uboot传给内核的dtb块地址,地址强转为fdt_header* 后取fdt_header的off_dt_struct的值,见下图。回溯上去return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; 就返回的是dt_struct中【图中展开部分】偏移量为nodeoffset的节点的地址】
const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
const char *nameptr;
int err;
if (((err = fdt_check_header(fdt)) != 0)
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
goto fail;
nameptr = nh->name;
【V16前是全路径】
if (fdt_version(fdt) < 0x10) {
/*
* For old FDT versions, match the naming conventions of V16:
* give only the leaf name (after all /). The actual tree
* contents are loosely checked.
*/
const char *leaf;
leaf = strrchr(nameptr, '/');
if (leaf == NULL) {
err = -FDT_ERR_BADSTRUCTURE;
goto fail;
}
nameptr = leaf+1;
}
if (len)
*len = strlen(nameptr);
return nameptr;
fail:
if (len)
*len = err;
return NULL;
}
DTB图示:
图1
图3
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC /
fdt32_t totalsize; / dtb文件大小 /
fdt32_t off_dt_struct; / offset to structure /
fdt32_t off_dt_strings; / offset to strings /
fdt32_t off_mem_rsvmap; / offset to memory reserve map /
fdt32_t version; / format version /
fdt32_t last_comp_version; / last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
fdt32_t size_dt_struct; /* size of the structure block */
};
以上是fdt_get_name函数的解析end------------------------------
unflatten_dt_alloc函数的解析begin--------------------------
【当/* First pass, scan for size */时
第一次调用时*mem = null= 0
size为sizeof(struct device_node) + allocl, align = 4
return时 *mem = 0+size.
后续多次调用此函数,*mem累加size】
static void *unflatten_dt_alloc(void **mem, unsigned long size,
unsigned long align)
{
void *res;
*mem = PTR_ALIGN(*mem, align);
res = *mem;
*mem += size;
return res;
}
unflatten_dt_alloc函数的解析end--------------------------
/* Second pass, do actual unflattening */
unflatten_dt_nodes(initial_boot_params, 上面dt_alloc分配的地址, null, &of_root);
initial_boot_params 为内存中DTB的首地址
在函数内部
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
将offset = 0,depth = 0, dryrun = false 代入
在populate_node内部
看这行 pathp = fdt_get_name(blob, 0, &l);
以下为跳入fdt_get_name内部后
看const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
该函数返回 return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
结合图1蓝色部分,其中 fdt为dtb数据块首地址, fdt_off_dt_struct(fdt)返回off_dt_struct,即dt_struct相对于fdt的偏移,再加上offset,得到图3中的节点信息,用struct fdt_node_header表示。
struct fdt_node_header {
fdt32_t tag;
char name[0];
};
tag是标识node的起始结束等信息的标志位,name指向node名称的首地址。
tag的取值:
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off, size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
除了上面这个结构体,还有
struct fdt_property {
fdt32_t tag;
fdt32_t len;
fdt32_t nameoff;
char data[0];
};
他们两个共同来描述图3所示的dt_struct。
参数检查
((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
检查offset,offset映射的TAG必须是FDT_BEGIN_NODE。
进入 fdt_check_node_offset_函数里面后
(fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
对于fdt_next_tag函数,有几个事情:
1是取当前的tag。通过
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
tag = fdt32_to_cpu(*tagp);
2是维护offset,使跳出fdt_next_tag后,即返回了当前TAG,又将offset掠过了节点名或属性等,指向下一个tag,为下一次循环做准备
offset += FDT_TAGSIZE;
case FDT_BEGIN_NODE:
/* skip name */
do {
p = fdt_offset_ptr(fdt, offset++, 1);
} while (p && (*p != '\0'));
*nextoffset = FDT_TAGALIGN(offset);
跳出fdt_check_node_offset_函数,由于nodeoffset参数是按值传递进去的,即一个copy进去,返回值,nodeoffset是不变的。接着fdt_get_name函数向下走,
节点名字通过 nameptr = nh->name;获取
if (fdt_version(fdt) < 0x10) { 不成立。
从 fdt_get_name跳出后,
pathp指向返回的节点名称字符串,l保存其长度,不含‘\0’
接着populate_node函数向下走
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
alignof(struct device_node));
该函数从上文dt_alloc分配的内存中申请空间,对应空间大小为device_node的大小以及节点名字符串,一前一后包含这两部分。
Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,struct device_node最终一般会被挂接到具体的struct device结构体。
struct device_node {
const char *name; /* node的名称,取最后一次“/”和“@”之间子串 */
const char *type; /* device_type的属性名称,没有为<NULL> */
phandle phandle; /* phandle属性值 */
const char *full_name; /* 指向该结构体结束的位置,存放node的路径全名,例如:/chosen */
struct fwnode_handle fwnode;
struct property *properties; /* 指向该节点下的第一个属性,其他属性与该属性链表相接 */
struct property *deadprops; /* removed properties */
struct device_node *parent; /* 父节点 */
struct device_node *child; /* 子节点 */
struct device_node *sibling; /* 姊妹节点,与自己同等级的node */
struct kobject kobj; /* sysfs文件系统目录体现 */
unsigned long _flags; /* 当前node状态标志位,见/include/linux/of.h line124-127 */
void *data;
};
/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED 2 /* node has been detached from the device tree*/
#define OF_POPULATED 3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
接着populate_node函数向下走
if (!dryrun) {
char *fn;
of_node_init(np); 【Kobj相关】
【full_name是一个指针,下句为其指向实际内存空间,使其不为野指针。
上面说np指向的空间包含两部分,前部分为struct device_node,后部分为pathp字符串的长度,不含结束符的长度。也即np->full_name指向后面这部分空间,见图4】
np->full_name = fn = ((char *)np) + sizeof(*np);
【将从dtb中取出的节点名字复制到内存中np->full_name指向的地址】
memcpy(fn, pathp, l);
【父子,兄弟节点的挂接】
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
图4
接着populate_node函数向下走
populate_properties(blob, offset, mem, np, pathp, dryrun);
注意参数offset的连续性,承接着上面的fdt_get_name函数。
进入populate_properties
kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。
```c
struct property {
char *name; /* property full name */
int length; /* property value length */
void *value; /* property value */
struct property *next; /* next property under the same node */
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr; /* 属性文件,与sysfs文件系统挂接 */
};
调用fdt_first_property_offset,该函数先调用fdt_check_node_offset_来检查当前offset对应的TAG是否是FDT_BEGIN_NODE,由于从上面的fdt_get_name函数后,并没与改变offset的值,所以还是对应FDT_BEGIN_NODE。接着调用nextprop_,调用fdt_next_tag,fdt_next_tag返回当前offset指向的TAG,若当前tag不是FDT_PROP,则继续调用fdt_next_tag,直到向下找到FDT_PROP即返回到fdt_first_property_offset,接着返回到populate_properties,并将当前FDT_PROP对应的offset赋值给cur,并接着向下走,调用 val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
接下来的调用链:fdt_getprop_by_offset–>fdt_get_property_by_offset_–>fdt_check_prop_offset(此处offset按值传递进去,是一个复制品)_
复制品的意思是:
【】
int fdt_check_prop_offset_(const void *fdt, int offset)
{
【fdt_next_tag的参数&offset按地址传递,但这个地址不是
static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
int offset,
int *lenp)中的offset,而是它在内存的复制对象的地址,也即fdt_get_property_by_offset_中调用fdt_check_prop_offset_后,不会改变传入fdt_get_property_by_offset_的offset值,以及影响该函数的后续使用,但fdt_check_prop_offset_的返回值那个offset会受fdt_next_tag影响】
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
return -FDT_ERR_BADOFFSET;
return offset;
}
–>fdt_next_tag, 进入
......
offset += FDT_TAGSIZE;
......
case FDT_PROP:
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
if (!lenp)
return FDT_END; /* premature end */
/* skip-name offset, length and value */
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ fdt32_to_cpu(*lenp);
......
break;
offset 指向FDT_PROP这个TAG,- FDT_TAGSIZE,即减去上面+的FDT_TAGSIZE。掠过sizeof(struct fdt_property)+prop_data_len,由此指向下一个TAG。参考图3。但是由于上面调用链传进来的是copy,按值传递,当回到fdt_get_property_by_offset_时,offset值保持原来的,fdt_get_property_by_offset_返回struct fdt_property 类型的 prop到fdt_getprop_by_offset函数,再向上返回namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));以及return prop->data到populate_properties函数。populate_properties里接着往下:
if (!val) {
if (!pname) { 【这两个判断属性名和属性值不能为空,否则进行下一循环】
【遍历一个节点下的属性时,对每个有效属性,申请各自的空间】
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property));
......
pp->name = (char *)pname;
pp->length = sz;
pp->value = (__be32 *)val;
【同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。】
*pprev = pp;
pprev = &pp->next;
遍历某节点下的属性的结束条件为cur<0, 即cur = fdt_next_property_offset(blob, cur)中的if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)成立,即fdt_next_tag(fdt, offset, &offset) != FDT_PROP成立,也就是fdt_next_tag得到DT_END_NODE 。结合图3的dt_struct结构。
【fdt_next_property_offset参数 offset,按值传入fdt_check_prop_offset_,fdt_check_prop_offset_将offset按地址传入fdt_next_tag函数,fdt_next_tag会改变offset的值,其return的offset值不是fdt_check_prop_offset_的参数offset,返回给fdt_next_property_offset的offset,使nextprop_的offset不是传入fdt_next_property_offset的offset】
int fdt_next_property_offset(const void *fdt, int offset)
{
【fdt_check_prop_offset_参数检查,由于循环体中的fdt_getprop_by_offset函数再去取属性后,并没有改变当前的offset,这里通过fdt_next_tag将offset跳过上次的已解析的属性区间,指向本次的属性区间开始处】
if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
return offset;
【返回指向当前要解析的属性的对应的offset】
return nextprop_(fdt, offset);
}
int fdt_check_prop_offset_(const void *fdt, int offset)
{
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
return -FDT_ERR_BADOFFSET;
return offset;
}
if (!has_name) {
const char *p = nodename, *ps = p, *pa = NULL;
int len;
【如nodename = virtio_mmio@10013000,while循环找到@,算出virtio_mmio的长度。申请空间给property pp】
while (*p) {
if ((*p) == '@')
pa = p;
else if ((*p) == '/')
ps = p + 1;
p++;
}
if (pa < ps)
pa = p;
len = (pa - ps) + 1;
【到这里,pa和p指向@,ps指向virtio_mmio@10013000的开始】
pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
__alignof__(struct property));
if (!dryrun) {
pp->name = "name";
pp->length = len;
【pp + 1掠过一个struct property的空间,指向unflatten_dt_alloc申请的len长度的空间的起始地址】
pp->value = pp + 1;
*pprev = pp;
pprev = &pp->next;
【将virtio_mmio@10013000中的virtio_mmio复制到pp->value作为name属性的值】
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);
}
}
对于vexpress-v2p-ca9.dts,pr_debug打印内容:
OF: fdt: fixed up name for ->
OF: fdt: fixed up name for virtio_mmio@10013000 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013200 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013400 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013600 -> virtio_mmio
OF: fdt: fixed up name for memory@60000000 -> memory
OF: fdt: fixed up name for smb@4000000 -> smb
OF: fdt: fixed up name for motherboard -> motherboard
OF: fdt: fixed up name for flash@0,00000000 -> flash
OF: fdt: fixed up name for psram@2,00000000 -> psram
OF: fdt: fixed up name for vram@3,00000000 -> vram
OF: fdt: fixed up name for ethernet@3,02000000 -> ethernet
OF: fdt: fixed up name for usb@3,03000000 -> usb
OF: fdt: fixed up name for iofpga@7,00000000 -> iofpga
OF: fdt: fixed up name for sysreg@0 -> sysreg
OF: fdt: fixed up name for gpio@8 -> gpio
OF: fdt: fixed up name for gpio@48 -> gpio
OF: fdt: fixed up name for gpio@4c -> gpio
OF: fdt: fixed up name for sysctl@1000 -> sysctl
OF: fdt: fixed up name for i2c@2000 -> i2c
OF: fdt: fixed up name for pcie-switch@60 -> pcie-switch
OF: fdt: fixed up name for aaci@4000 -> aaci
OF: fdt: fixed up name for mmci@5000 -> mmci
OF: fdt: fixed up name for kmi@6000 -> kmi
OF: fdt: fixed up name for kmi@7000 -> kmi
OF: fdt: fixed up name for uart@9000 -> uart
OF: fdt: fixed up name for uart@a000 -> uart
OF: fdt: fixed up name for uart@b000 -> uart
OF: fdt: fixed up name for uart@c000 -> uart
OF: fdt: fixed up name for wdt@f000 -> wdt
OF: fdt: fixed up name for timer@11000 -> timer
OF: fdt: fixed up name for timer@12000 -> timer
OF: fdt: fixed up name for i2c@16000 -> i2c
OF: fdt: fixed up name for dvi-transmitter@39 -> dvi-transmitter
OF: fdt: fixed up name for dvi-transmitter@60 -> dvi-transmitter
OF: fdt: fixed up name for rtc@17000 -> rtc
OF: fdt: fixed up name for compact-flash@1a000 -> compact-flash
OF: fdt: fixed up name for clcd@1f000 -> clcd
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel -> panel
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel-timing -> panel-timing
OF: fdt: fixed up name for fixed-regulator-0 -> fixed-regulator-0
OF: fdt: fixed up name for clk24mhz -> clk24mhz
OF: fdt: fixed up name for refclk1mhz -> refclk1mhz
OF: fdt: fixed up name for refclk32khz -> refclk32khz
OF: fdt: fixed up name for leds -> leds
OF: fdt: fixed up name for user1 -> user1
OF: fdt: fixed up name for user2 -> user2
OF: fdt: fixed up name for user3 -> user3
OF: fdt: fixed up name for user4 -> user4
OF: fdt: fixed up name for user5 -> user5
OF: fdt: fixed up name for user6 -> user6
OF: fdt: fixed up name for user7 -> user7
OF: fdt: fixed up name for user8 -> user8
OF: fdt: fixed up name for mcc -> mcc
OF: fdt: fixed up name for oscclk0 -> oscclk0
OF: fdt: fixed up name for oscclk1 -> oscclk1
OF: fdt: fixed up name for oscclk2 -> oscclk2
OF: fdt: fixed up name for volt-vio -> volt-vio
OF: fdt: fixed up name for temp-mcc -> temp-mcc
OF: fdt: fixed up name for reset -> reset
OF: fdt: fixed up name for muxfpga -> muxfpga
OF: fdt: fixed up name for shutdown -> shutdown
OF: fdt: fixed up name for reboot -> reboot
OF: fdt: fixed up name for dvimode -> dvimode
OF: fdt: fixed up name for chosen -> chosen
OF: fdt: fixed up name for aliases -> aliases
OF: fdt: fixed up name for cpus -> cpus
OF: fdt: fixed up name for cpu@0 -> cpu
OF: fdt: fixed up name for cpu@1 -> cpu
OF: fdt: fixed up name for cpu@2 -> cpu
OF: fdt: fixed up name for cpu@3 -> cpu
OF: fdt: fixed up name for clcd@10020000 -> clcd
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel -> panel
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel-timing -> panel-timing
OF: fdt: fixed up name for memory-controller@100e0000 -> memory-controller
OF: fdt: fixed up name for memory-controller@100e1000 -> memory-controller
OF: fdt: fixed up name for timer@100e4000 -> timer
OF: fdt: fixed up name for watchdog@100e5000 -> watchdog
OF: fdt: fixed up name for scu@1e000000 -> scu
OF: fdt: fixed up name for timer@1e000600 -> timer
OF: fdt: fixed up name for watchdog@1e000620 -> watchdog
OF: fdt: fixed up name for interrupt-controller@1e001000 -> interrupt-controller
OF: fdt: fixed up name for cache-controller@1e00a000 -> cache-controller
OF: fdt: fixed up name for hello -> hello
OF: fdt: fixed up name for pmu -> pmu
OF: fdt: fixed up name for dcc -> dcc
OF: fdt: fixed up name for extsaxiclk -> extsaxiclk
OF: fdt: fixed up name for clcdclk -> clcdclk
OF: fdt: fixed up name for tcrefclk -> tcrefclk
OF: fdt: fixed up name for volt-vd10 -> volt-vd10
OF: fdt: fixed up name for volt-vd10-s2 -> volt-vd10-s2
OF: fdt: fixed up name for volt-vd10-s3 -> volt-vd10-s3
OF: fdt: fixed up name for volt-vcc1v8 -> volt-vcc1v8
OF: fdt: fixed up name for volt-ddr2vtt -> volt-ddr2vtt
OF: fdt: fixed up name for volt-vcc3v3 -> volt-vcc3v3
OF: fdt: fixed up name for amp-vd10-s2 -> amp-vd10-s2
OF: fdt: fixed up name for amp-vd10-s3 -> amp-vd10-s3
OF: fdt: fixed up name for power-vd10-s2 -> power-vd10-s2
OF: fdt: fixed up name for power-vd10-s3 -> power-vd10-s3
OF: fdt: fixed up name for hsb@e0000000 -> hsb
解析fdt_next_property_offset
从populate_properties返回到populate_node,populate_node接下来执行
*pnp = np; 将本次解析到的device_node *,给device_node指针数组,此数组定义在populate_node被调用的上层unflatten_dt_nodes函数中struct device_node *nps[FDT_MAX_DEPTH];,以 &nps[depth+1]的形式传入populate_node。dtb解析完后,所有的节点的device_node的地址指针。平坦地保存在此指针数组中。
unflatten_dt_nodes接下来
【*nodepp即of_root,下面两个if语句只在根节点时执行一次】
【注意,这样执行nps[0]=null,nps从下标1开始存放root节点及后续节点】
if (!dryrun && nodepp && !*nodepp)
*nodepp = nps[depth+1];
if (!dryrun && !root)
root = nps[depth+1];
循环体向前递进的语句:offset = fdt_next_node(blob, offset, &depth)
由于循环体内的
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
调用,offset是按值传递的,所以不会变。当执行fdt_next_node时,还是指向上次刚已经解析的节点在dt_struct中的偏移。
int fdt_next_node(const void *fdt, int offset, int *depth)
{
int nextoffset = 0;
uint32_t tag;
if (offset >= 0)
【fdt_check_node_offset_,用于检查当前offset有效性,特别是当前offset对应的TAG是否是FDT_BEGIN_NODE,返回值nextoffset已经掠过了上次解析的节点区域,指向了当前要解析的节点】
if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
return nextoffset;
【循环查找,直到找到TAG为FDT_BEGIN_NODE】
do {
offset = nextoffset;
【找到offset对应的tag,并且掠过当前类型区段,nextoffset指向下一个区段的开始,用于循环查找,直到找到FDT_BEGIN_NODE或FDT_END结束】
tag = fdt_next_tag(fdt, offset, &nextoffset);
switch (tag) {
case FDT_PROP:
case FDT_NOP:
break;
【维护nps节点结构体数组的下标,存放当前节点的位置】
case FDT_BEGIN_NODE:
if (depth)
(*depth)++;
break;
case FDT_END_NODE:
if (depth && ((--(*depth)) < 0))
return nextoffset;
break;
case FDT_END:
if ((nextoffset >= 0)
|| ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
return -FDT_ERR_NOTFOUND;
else
return nextoffset;
}
} while (tag != FDT_BEGIN_NODE);
【返回当前要解析的TAG为FDT_BEGIN_NODE的节点】
return offset;
}