设备树学习(六、设备树对运行时配置信息的处理)

2019-07-14 01:12发布

上一节学习了在linux启动期间如何找到的最佳匹配的单板machine_desc;‘ asmlinkage __visible void __init start_kernel(void) setup_arch(&command_line); mdesc = setup_machine_fdt(__atags_pointer); 在setup_machine_fdt函数中找到的最佳匹配的machine_desc单板,在这个函数的后半部分的early_init_dt_scan_nodes()函数中,从dtb文件中取出了运行时参数。   void __init early_init_dt_scan_nodes(void) { /* Retrieve various information from the /chosen node,从chosen中取出启动命令参数 */ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); /* Initialize {size,address}-cells info,从{size,address}-cells中获取cpu的地址大小信息 */ 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); }   此函数用于扫描展平的设备树, /** * of_scan_flat_dt - scan flattened tree blob and call callback on each. * @it: callback function * @data: context data pointer * * This function is used to scan the flattened device-tree, it is * used to extract the memory information at boot before we can * unflatten the tree */ int __init of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data) { const void *blob = initial_boot_params; //此函数为上一节检查时赋值的dtb在内存的地址,是一个虚拟地址 const char *pathp; int offset, rc = 0, depth = -1; if (!blob) //检查有效 return 0; for (offset = fdt_next_node(blob, -1, &depth); //获取一个个节点, offset >= 0 && depth >= 0 && !rc; //回调函数返回值不为0,则退出循环 offset = fdt_next_node(blob, offset, &depth)) { pathp = fdt_get_name(blob, offset, NULL); //获取节点名称 if (*pathp == '/') //节点是根节点 pathp = kbasename(pathp); //则获取这个路径的最小的节点 rc = it(offset, pathp, depth, data); //调用回调函数 } return rc; }   扫描获取chosen节点,把bootargs属性保存在boot_command_line数组中。 int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { int l; const char *p; pr_debug("search "chosen", depth: %d, uname: %s ", depth, uname); //节点深度是1则就是在跟节点,则不用比较节点名称,若不是在跟节点,则要找到节点名称为chosen,否则返回0 ,继续查找下一个节点 if (depth != 1 || !data || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; /* 找到初始化进程的位置 */ early_init_dt_check_for_initrd(node); /* Retrieve command line从该节点获取bootargs属性值 */ p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); //保存bootarg参数 /* * CONFIG_CMDLINE is meant to be a default in case nothing else * managed to set the command line, unless CONFIG_CMDLINE_FORCE * is set in which case we override whatever was found earlier. */ #ifdef CONFIG_CMDLINE #if defined(CONFIG_CMDLINE_EXTEND) strlcat(data, " ", COMMAND_LINE_SIZE); strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #elif defined(CONFIG_CMDLINE_FORCE) strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #else /* No arguments from boot loader, use kernel's cmdl*/ if (!((char *)data)[0]) strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif #endif /* CONFIG_CMDLINE */ pr_debug("Command line is: %s ", (char*)data); /* break now */ return 1; }   获取根接待中的size_cells和addr_cells属性 /** * early_init_dt_scan_root - fetch the top level address and size cells */ int __init early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data) { const __be32 *prop; if (depth != 0) return 0; //给个默认参数,都是1 dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; //从根节点获取#size-cells属性 prop = of_get_flat_dt_prop(node, "#size-cells", NULL); if (prop) dt_root_size_cells = be32_to_cpup(prop); //大端转小端字序 pr_debug("dt_root_size_cells = %x ", dt_root_size_cells); //从根节点获取#address-cells属性 prop = of_get_flat_dt_prop(node, "#address-cells", NULL); if (prop) dt_root_addr_cells = be32_to_cpup(prop); pr_debug("dt_root_addr_cells = %x ", dt_root_addr_cells); /* break now */ return 1; }     从根节点获取memory节点的属性 /** * early_init_dt_scan_memory - Look for and parse memory nodes */ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data) { /* 获取该节点的device_type属性 */ const char *type = of_get_flat_dt_prop(node, "device_type", NULL); const __be32 *reg, *endp; int l; bool hotpluggable; /* We are scanning "memory" nodes only */确认是memory节点,不是的话不要,直接返回 if (type == NULL || strcmp(type, "memory") != 0) return 0; //获取可用的memory节点中的"linux,usable-memory"属性,没有则返回0 reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); if (reg == NULL) //上面那种没找到,则查号reg属性,reg属性的值是address,size数组 reg = of_get_flat_dt_prop(node, "reg", &l); if (reg == NULL) return 0; endp = reg + (l / sizeof(__be32)); //总的属性的长度 hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); //获取热插拔的内存节点 pr_debug("memory scan node %s, reg size %d, ", uname, l); /*reg属性的值是address,size数组,那么如何来取出一个个的address/size呢? *由于memory node一定是root node的child,因此dt_root_addr_cells(root node的#address-cells属性值) *和dt_root_size_cells(root node的#size-cells属性值)之和就是address,size数组的entry size。*/ while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { u64 base, size; //获取reg属性中的addr和size属性的值,且reg属性也跟随增长 base = dt_mem_next_cell(dt_root_addr_cells, ®); size = dt_mem_next_cell(dt_root_size_cells, ®); if (size == 0) continue; pr_debug(" - %llx , %llx ", (unsigned long long)base, (unsigned long long)size); //添加内存块大小到内核内存池中 early_init_dt_add_memory_arch(base, size); if (!hotpluggable) //有热插拔内存的话继续,没有则查找reg属性列表的下一个值 continue; if (early_init_dt_mark_hotplug_memory_arch(base, size)) pr_warn("failed to mark hotplug range 0x%llx - 0x%llx ", base, base + size); } return 0; } void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) { const u64 phys_offset = MIN_MEMBLOCK_ADDR; //基本偏移0x80000000 //确认新加的内存块以页对齐 if (!PAGE_ALIGNED(base)) { if (size < PAGE_SIZE - (base & ~PAGE_MASK)) { //内存块太小 pr_warn("Ignoring memory block 0x%llx - 0x%llx ", base, base + size); return; } //大小足够,但没对齐的再强制对齐一次 size -= PAGE_SIZE - (base & ~PAGE_MASK); base = PAGE_ALIGN(base); } size &= PAGE_MASK; //起始不能超出寻址范围 if (base > MAX_MEMBLOCK_ADDR) { pr_warning("Ignoring memory block 0x%llx - 0x%llx ", base, base + size); return; } //结束不能超出寻址范围,超出的部分扔掉 if (base + size - 1 > MAX_MEMBLOCK_ADDR) { pr_warning("Ignoring memory range 0x%llx - 0x%llx ", ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); size = MAX_MEMBLOCK_ADDR - base + 1; } //结束地址小于最小内存块范围 if (base + size < phys_offset) { pr_warning("Ignoring memory block 0x%llx - 0x%llx ", base, base + size); return; } //起始地址小于最小内存块范围,那把小于最小内存块范围的部分扔掉 if (base < phys_offset) { pr_warning("Ignoring memory range 0x%llx - 0x%llx ", base, phys_offset); size -= phys_offset - base; base = phys_offset; } memblock_add(base, size); //加入内存池 }   可见运行时参数,基本都是保存在全局变量或者增加到内核中去。