删掉了很多未注释的代码,只保留了小编认为重要的部分。从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;
}