上一节学习了在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); //加入内存池
}
可见运行时参数,基本都是保存在全局变量或者增加到内核中去。