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