Linux memory的初始化(一)

前面已经通过转载的方式介绍了Linux的设备树,现在来讲讲Linux memory size的初始化:

kernel memory size的初始化函数调用顺序如下:

start_kernel->setup_arch->setup_machine_fdt->early_init_dt_scan_nodes->early_init_dt_scan_chosen->early_init_dt_scan_root->early_init_dt_scan_memory->early_init_dt_add_memory_arch->memblock_add->memblock_add_range.

setup_machine_fdt开始说起:

const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)//做两件事:通过dts的compitable找到合适的machine_dsec结构体,解析DTS的内容
{
	const struct machine_desc *mdesc, *mdesc_best = NULL;

#ifdef CONFIG_ARCH_MULTIPLATFORM
	DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
	MACHINE_END

	mdesc_best = &__mach_desc_GENERIC_DT;
#endif

	if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
		return NULL;

	mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

	if (!mdesc) {
		const char *prop;
		int size;
		unsigned long dt_root;

		early_print("\nError: unrecognized/unsupported "
			    "device tree compatible list:\n[ ");

		dt_root = of_get_flat_dt_root();
		prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
		while (size > 0) {
			early_print("'%s' ", prop);
			size -= strlen(prop) + 1;
			prop += strlen(prop) + 1;
		}
		early_print("]\n\n");

		dump_machine_table(); /* does not return */
	}

	/* We really don't want to do this, but sometimes firmware provides buggy data */
	if (mdesc->dt_fixup)
		mdesc->dt_fixup();

	early_init_dt_scan_nodes();//解析dts中的内容

	/* Change machine number to match the mdesc we're using */
	__machine_arch_type = mdesc->nr;

	return mdesc;
}

early_init_dt_scan_nodes:

void __init early_init_dt_scan_nodes(void)
{
	/* Retrieve various information from the /chosen node */
	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);//of_scan_flat_dt负责扫描整个设备树,起作用的是回调函数,early_init_dt_scan_chosen负责将chosen节点中的bootargs内容copy到boot_command_line中

	/* Initialize {size,address}-cells info */
	of_scan_flat_dt(early_init_dt_scan_root, NULL);//early_init_dt_scan_root根据节点的address-cells和size-cells初始化dt_root_addr_cells和dt_root_size_cells,#address-cells表示reg里面address用几个u32来表示,#size-cells表示reg里面size用几个u32来表示
	/* Setup memory, calling early_init_dt_add_memory_arch */
	of_scan_flat_dt(early_init_dt_scan_memory, NULL);//扫描device_type为memory的节点,一般就是总的memory size为一个memory节点,并通过early_init_dt_add_memory_arch将这些节点的信息添加到memory block中
}

early_init_dt_scan_chosen:

int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
				     int depth, void *data)
{
	int l = 0;
	const char *p = NULL;
	char *cmdline = data;

	pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

	if (depth != 1 || !cmdline ||
	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))//depth表示遍历dts的层数,根节点为第0层,chosen为第一层,以此类推
		return 0;//此处chosen必须是第一层,其名字必须为chosen,则继续往下执行,否则没找到chosen节点退出,节省执行效率

	early_init_dt_check_for_initrd(node);//解析chosen节点中的initrd的信息

	/* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */
	if (overwrite_incoming_cmdline || !cmdline[0])
		strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE);//解析choen中的bootargs信息,并将其copy到boot_command_line

	/* Retrieve command line unless forcing */
	if (read_dt_cmdline)
		p = of_get_flat_dt_prop(node, "bootargs", &l);

	if (p != NULL && l > 0) {
		if (concat_cmdline) {
			int cmdline_len;
			int copy_len;
			strlcat(cmdline, " ", COMMAND_LINE_SIZE);
			cmdline_len = strlen(cmdline);
			copy_len = COMMAND_LINE_SIZE - cmdline_len - 1;
			copy_len = min((int)l, copy_len);
			strncpy(cmdline + cmdline_len, p, copy_len);
			cmdline[cmdline_len + copy_len] = '\0';
		} else {
			strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE));//一般系统会定义默认的cmdline,如果BootLoader没有传递cmdline,可以使用此default值,如果BootLoader传递了cmdline,则使用BootLoader传递的cmdline
		}
	}

	pr_debug("Command line is: %s\n", (char*)data);

	/* break now */
	return 1;
}

early_init_dt_scan_memory:

扫描二维码关注公众号,回复: 1841503 查看本文章
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
				     int depth, void *data)
{
	const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
	const __be32 *reg, *endp;
	int l;

	/* We are scanning "memory" nodes only */
	if (type == NULL) {
		/*
		 * The longtrail doesn't have a device_type on the
		 * /memory node, so look for the node called /memory@0.
		 */
		if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)//如果不是PPC32架构,如果depth不等于1,说明memory节点不是root节点的子节点,如果node name不等于memory,说明不是我们关注的memory节点返回
			return 0;
	} else if (strcmp(type, "memory") != 0)
		return 0;
	reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);//判断当前memory信息是否保存在"linux,usable-memory”中
	if (reg == NULL)
		reg = of_get_flat_dt_prop(node, "reg", &l);//如果不在“linux,usable-memory”,说明信息保存在reg中
	if (reg == NULL)//如果还没找到,说明找不到memorysize信息,return。
		return 0;
	endp = reg + (l / sizeof(__be32));//reg中cell的个数,reg表示第一个cell,endp表示最后一个cell
	pr_err("memory scan node %s, reg size %d,\n", uname, l);
	while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {//memory node的reg属性值其实就是一个数组,数组中的每一个entry都是base address和size的二元组。解析reg属性需要两个参数,dt_root_addr_cells和dt_root_size_cells,这两个参数分别定义了root节点的子节点(比如说memory node)reg属性中base address和size的cell数目,如果等于1,基地址(或者size)用一个32-bit的cell表示。对于ARMv8,一般dt_root_addr_cells和dt_root_size_cells等于2,表示基地址(或者size)用两个32-bit的cell表示。u64 base, size;//dt_root_addr_cells和dt_root_size_cells已经在early_init_dt_scan_root中初始化完成
		base = dt_mem_next_cell(dt_root_addr_cells, &reg);
		size = dt_mem_next_cell(dt_root_size_cells, &reg);
		pr_err("%s: memblock base = 0x%llx, size = 0x%llx\n", __func__, base, size);
		if (size == 0)
			continue;
		pr_debug(" - %llx , %llx\n", (unsigned long long)base, (unsigned long long)size);
		early_init_dt_add_memory_arch(base, size);//针对该memory mode中的每一个memory region,调用early_init_dt_add_memory_arch向系统注册memory type的内存区域(实际上是通过memblock_add完成的)。
	}
    return 0;
}

early_init_dt_add_memory_arch:

void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
{
	const u64 phys_offset = __pa(PAGE_OFFSET);

	if (!PAGE_ALIGNED(base)) {//一些条件判断
		size -= PAGE_SIZE - (base & ~PAGE_MASK);
		base = PAGE_ALIGN(base);
	}
	size &= PAGE_MASK;

	if (base > MAX_PHYS_ADDR) {
		pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
				base, base + size);
		return;
	}

	if (base + size - 1 > MAX_PHYS_ADDR) {
		pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
				((u64)MAX_PHYS_ADDR) + 1, base + size);
		size = MAX_PHYS_ADDR - base + 1;
	}

	if (base + size < phys_offset) {
		pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
			   base, base + size);
		return;
	}
	if (base < phys_offset) {
		pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
			   base, phys_offset);
		size -= phys_offset - base;
		base = phys_offset;
	}
	memblock_add(base, size);//调用memblock_add将当前memory节点信息添加到memblock中,base为memory节点的七点,size为memory节点的大小,从这里可以看出此处device_type为memory的一个节点就是memblock type为memory的一个region,memblock_add最后调用memblock_add_range函数完成memory节点的添加工作,见文章https://blog.csdn.net/zsj100213/article/details/78366750
}
文章最后有个问题需要说明:我们在arch/arm/boot/dts目录下面看到的dts文件关于memory的size的描述可能和实际的memory的size大小不一致,此问题主要是在bootloader中也会对memory信息进行获取,然后修改dts,并把dts load到内存制定的位置,然后把这个值传递给kernel,在setup_arch里面调用setup_machine_fdt时所传递的参数__atags_pointer就是boot loader传递过来存放dts的地址。

猜你喜欢

转载自blog.csdn.net/zsj100213/article/details/80822583
今日推荐