天天看點

實體記憶體映射

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);


}