物理内存映射

kernel 通过paging_init来映射物理内存
void __init paging_init(void)
{
	#为pgd申请内存并赋值,这时候还没有buddy system,因此在早期都是通过memblock来申请内存的
	phys_addr_t pgd_phys = early_pgtable_alloc();
	pgd_t *pgdp = pgd_set_fixmap(pgd_phys);
	#可以看到kernel本身占用的内存是单独映射的,这两个函数我们后面详细分析
	map_kernel(pgdp);
	map_mem(pgdp);

	#在head.s中有申请一段memory来临时映射物理内存swapper_pg_dir,这里我们可以复用
	#这段内存,这样我们就可以把通过memblock申请的pgd_phys 释放掉
	cpu_replace_ttbr1(__va(pgd_phys));
	memcpy(swapper_pg_dir, pgdp, PGD_SIZE);
	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));

	pgd_clear_fixmap();
	#释放pgd_phys 占用的内存
	memblock_free(pgd_phys, PAGE_SIZE);

	/*
	 * We only reuse the PGD from the swapper_pg_dir, not the pud + pmd
	 * allocated with it.
	 */
	 #我们只是复用swapper_pg_dir 中pgd的memory,因此治理释放到pud和pmd 占用的内存
	memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE,
		      __pa_symbol(swapper_pg_end) - __pa_symbol(swapper_pg_dir)
		      - PAGE_SIZE);
}
首先看看map_kernel
static void __init map_kernel(pgd_t *pgdp)
{
	static struct vm_struct vmlinux_text, vmlinux_rodata, vmlinux_inittext,
				vmlinux_initdata, vmlinux_data;

	pgprot_t text_prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;

#从下面这段可以看出kernel 本身占用的memory可以分为下面这5段
	map_kernel_segment(pgdp, _text, _etext, text_prot, &vmlinux_text, 0,
			   VM_NO_GUARD);
	map_kernel_segment(pgdp, __start_rodata, __inittext_begin, PAGE_KERNEL,
			   &vmlinux_rodata, NO_CONT_MAPPINGS, VM_NO_GUARD);
	map_kernel_segment(pgdp, __inittext_begin, __inittext_end, text_prot,
			   &vmlinux_inittext, 0, VM_NO_GUARD);
	map_kernel_segment(pgdp, __initdata_begin, __initdata_end, PAGE_KERNEL,
			   &vmlinux_initdata, 0, VM_NO_GUARD);
	map_kernel_segment(pgdp, _data, _end, PAGE_KERNEL, &vmlinux_data, 0, 0);

#最终map_kernel_segment->__create_pgd_mapping->pud->pmd->pte等来映射
}
memblock 中的memory映射如下:
static void __init map_mem(pgd_t *pgdp)
{
	phys_addr_t kernel_start = __pa_symbol(_text);
	phys_addr_t kernel_end = __pa_symbol(__init_begin);
	struct memblock_region *reg;
	int flags = 0;
	#是否开debug选项
	if (debug_pagealloc_enabled())
		flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;

	/*
	 * Take care not to create a writable alias for the
	 * read-only text and rodata sections of the kernel image.
	 * So temporarily mark them as NOMAP to skip mappings in
	 * the following for-loop
	 */
	#由于kernel 占用的memory页包含在memblock中,且我们再前面已经映射kernel了,所以
	#这里标记kernel占用的memory不用在重复映射了
	memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
#ifdef CONFIG_KEXEC_CORE
	if (crashk_res.end)
		memblock_mark_nomap(crashk_res.start,
				    resource_size(&crashk_res));
#endif

	/* map all the memory banks */
	#开始映射memblock中的memory
	for_each_memblock(memory, reg) {
		phys_addr_t start = reg->base;
		phys_addr_t end = start + reg->size;
		#起始地址大于结束地址的话,肯定更有问题,退出
		if (start >= end)
			break;
		#如果memblock包含MEMBLOCK_NOMAP,则不用映射,例如前面提到的kernel的映射
		if (memblock_is_nomap(reg))
			continue;
		#开始映射memblock中的memory
		__map_memblock(pgdp, start, end, PAGE_KERNEL, flags);
	}

	
	__map_memblock(pgdp, kernel_start, kernel_end,
		       PAGE_KERNEL, NO_CONT_MAPPINGS);
	#清除kernel memory段的MEMBLOCK_NOMAP
	memblock_clear_nomap(kernel_start, kernel_end - kernel_start);


}

猜你喜欢

转载自blog.csdn.net/tiantao2012/article/details/80461908
今日推荐