参考:linux设备驱动程序-设备树(1)-dtb转换成device_node - 牧野星辰 - 博客园
在linux-3.4.y/init/main.c中,linux内核启动后调用start_kernel函数; 其中有setup_arch函数,对于arm平台会调用arch/arm/kernel/setup.c文件中的setup_arch();
- void __init setup_arch(char **cmdline_p)
- {
- struct machine_desc *mdesc;
-
- setup_processor();
-
- //根据传入的设备树dtb的首地址完成一些初始化操作;
- //__atags_pointer参数这个全局变量就是r2的寄存器值;是设备树在内存中的起始地址;
- mdesc = setup_machine_fdt(__atags_pointer);
- if (!mdesc)
- mdesc = setup_machine_tags(machine_arch_type);
- machine_desc = mdesc;
- machine_name = mdesc->name;
-
- if (mdesc->restart_mode)
- reboot_setup(&mdesc->restart_mode);
-
- init_mm.start_code = (unsigned long) _text;
- init_mm.end_code = (unsigned long) _etext;
- init_mm.end_data = (unsigned long) _edata;
- init_mm.brk = (unsigned long) _end;
-
- /* populate cmd_line too for later use, preserving boot_command_line */
- strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
- *cmdline_p = cmd_line;
-
- parse_early_param();
-
- sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
- sanity_check_meminfo();
-
- //内存相关,为设备树保留相应的内存空间,保证设备树dtb本身在内存中而不被覆盖,用户可在设备树中保留内存;
- arm_memblock_init(&meminfo, mdesc);
-
- ...
-
- //展开设备树,实际上就是解析dtb,并转换为struct device_node结构体;
- unflatten_device_tree();
-
- ...
- }
函数作用: 如果在r2中向内核传递了一个dtb,那么使用它来选择正确的machine_desc并设置系统。
- struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
- {
- struct boot_param_header *devtree;
- struct machine_desc *mdesc, *mdesc_best = NULL;
- unsigned int score, mdesc_score = ~1;
- unsigned long dt_root;
- const char *model;
-
- if (!dt_phys)
- return NULL;
-
- //物理地址转虚拟地址;
- devtree = phys_to_virt(dt_phys);
-
- /* check device tree validity */
- //验证设备树有效性
- if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
- return NULL;
-
- /* Search the mdescs for the 'best' compatible value match */
- initial_boot_params = devtree;
-
- //获取根节点
- dt_root = of_get_flat_dt_root();
-
- //循环读取设备树根目录下的compatible属性;
- for_each_machine_desc(mdesc) {
- //mdesc->dt_compat 为设备树compatible字符数组
- score = of_flat_dt_match(dt_root, mdesc->dt_compat);
- if (score > 0 && score < mdesc_score) {
- mdesc_best = mdesc;
- mdesc_score = score;
- }
- }
-
- ...
-
- //获取根节点下的model属性
- model = of_get_flat_dt_prop(dt_root, "model", NULL);
- if (!model)
- model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
- if (!model)
- model = "
" ; - pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
-
- //扫描设备树中的各节点
- //分别是处理choose节点的处理,root节点中除了子节点外的属性信息,memory节点;
- /* Retrieve various information from the /chosen node */
- of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
- /* Initialize {size,address}-cells info */
- of_scan_flat_dt(early_init_dt_scan_root, NULL);
- /* Setup memory, calling early_init_dt_add_memory_arch */
- of_scan_flat_dt(early_init_dt_scan_memory, NULL);
-
- /* Change machine number to match the mdesc we're using */
- __machine_arch_type = mdesc_best->nr;
-
- return mdesc_best;
- }
其中的of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);语句是将boot_command_line作为回调函数early_init_dt_scan_chosen的参数传入,并拷贝从choose属性读取到的命令行参数到boot_command_line中;

of_scan_flat_dt(early_init_dt_scan_root, NULL);语句是查找#size-cells和#address-cells属性,并赋值全局dt_root_size_cells和dt_root_addr_cells ;
of_scan_flat_dt(early_init_dt_scan_memory, NULL);语句主要获取memory相关信息计算后,调用early_init_dt_add_memory_arch(base, size);分配空间;
- void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
- {
- int i;
-
- for (i = 0; i < mi->nr_banks; i++)
- memblock_add(mi->bank[i].start, mi->bank[i].size);
-
- /* Register the kernel text, kernel data and initrd with memblock. */
- memblock_reserve(__pa(_stext), _end - _stext);
-
- ...
-
- if (phys_initrd_size) {
- memblock_reserve(phys_initrd_start, phys_initrd_size);
-
- /* Now convert initrd to virtual addresses */
- initrd_start = __phys_to_virt(phys_initrd_start);
- initrd_end = initrd_start + phys_initrd_size;
- }
-
- arm_mm_memblock_reserve();
- arm_dt_memblock_reserve();
-
- arm_memblock_steal_permitted = false;
- memblock_allow_resize();
- memblock_dump_all();
- }
主要是为设备树分配保留内存空间;
- void __init unflatten_device_tree(void)
- {
- __unflatten_device_tree(initial_boot_params, &allnodes,
- early_init_dt_alloc_memory_arch);
-
- /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
- of_alias_scan(early_init_dt_alloc_memory_arch);
- }
__unflatten_device_tree函数中,扫描得出设备树转换成device node需要的空间,然后系统申请内存空间,第二次就进行真正的解析工作;代码如下:
- static void __unflatten_device_tree(struct boot_param_header *blob,
- struct device_node **mynodes,
- void * (*dt_alloc)(u64 size, u64 align))
- {
- unsigned long start, mem, size;
- struct device_node **allnextp = mynodes;
-
- ...
-
- /* 获取大小 */
- start = ((unsigned long)blob) +
- be32_to_cpu(blob->off_dt_struct);
- size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
- size = (size | 3) + 1;
-
- pr_debug(" size is %lx, allocating...\n", size);
-
- /* Allocate memory for the expanded device tree */
- //分配空间
- mem = (unsigned long)
- dt_alloc(size + 4, __alignof__(struct device_node));
-
- ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
-
- pr_debug(" unflattening %lx...\n", mem);
-
- /* Second pass, do actual unflattening */
- //实际解析工作
- start = ((unsigned long)blob) +
- be32_to_cpu(blob->off_dt_struct);
- unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
-
- ...
- }
参数allnodes就是struct device_node指针,将其传入进入后,通过解析设备树对其赋值填充;
of_alias_scan函数中:
- void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
- {
- struct property *pp;
-
- //获取根下的chosen节点属性
- of_chosen = of_find_node_by_path("/chosen");
- if (of_chosen == NULL)
- of_chosen = of_find_node_by_path("/chosen@0");
-
- //获取aliases节点
- of_aliases = of_find_node_by_path("/aliases");
- if (!of_aliases)
- return;
-
- //遍历所有aliases节点
- for_each_property_of_node(of_aliases, pp) {
- const char *start = pp->name;
- const char *end = start + strlen(start);
- struct device_node *np;
- struct alias_prop *ap;
- int id, len;
-
- /* Skip those we do not want to proceed */
- //过滤不想处理的
- if (!strcmp(pp->name, "name") ||
- !strcmp(pp->name, "phandle") ||
- !strcmp(pp->name, "linux,phandle"))
- continue;
-
- np = of_find_node_by_path(pp->value);
- if (!np)
- continue;
-
- /* walk the alias backwards to extract the id and work out
- * the 'stem' string */
- while (isdigit(*(end-1)) && end > start)
- end--;
- len = end - start;
-
- if (kstrtoint(end, 10, &id) < 0)
- continue;
-
- /* Allocate an alias_prop with enough space for the stem */
- //分配空间
- ap = dt_alloc(sizeof(*ap) + len + 1, 4);
- if (!ap)
- continue;
- ap->alias = start;
-
- //将所有aliases内容添加到链表
- of_alias_add(ap, np, id, start, len);
- }
- }