内存映射过程之paging_init

内存映射过程之paging_init

导读

之前已经逐步整理完成了idmap、fixup、memblock等内容,在此阶段已经可以:

  1. 访问kernel image的地址空间;
  2. 访问FDT地址空间,即已经获取device tree相关数据;
  3. 可以通过memblock提供的接口申请内存;
    则接下来需要做的处理,是要把从fdt中获取的mem layout关系建立实际的映射;
    所谓建立映射,实质很简单,即根据页表大小和地址空间大小,填充PUD PMD PTE等数据;

1. 页表转换关系

这部分之前已经整理过了,在这里制作简单说明:

1.1 涉及页表层级相关宏

宏(Config) 转换后 描述
CONFIG_ARM64_VA_BITS_39=y NA NA 决策逻辑地址的范围,以40~63位标记kernel or user,user + kernel共 1T
CONFIG_ARM64_VA_BITS=39 NA 39 同上
CONFIG_ARM64_4K_PAGES=y NA NA 决策页表的大小,当前被配置为4K
CONFIG_ARM64_PAGE_SHIFT=12 NA 12 同上
CONFIG_PGTABLE_LEVELS=3 NA 3 决策页表层级,即当前平台支持3级页表:PGD PMD PTE
PTRS_PER_PTE 1 << (PAGE_SHIFT - 3) 1<<9 PTE/PMD/PUD中表项的多少
PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS) 30 PGD 的偏移位
PUD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(1) NA PUD的偏移位,level = 3 无PUD
PMD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(2) 21 PMD的偏移位
ARM64_HW_PGTABLE_LEVEL_SHIFT(n) (PAGE_SHIFT - 3) * (4 - (n)) + 3 NA 根据level决策该层的shift

1.2 页表转换

一次地址的翻译过程基本如上图所示,相关的level、page size、VA size等根据平台不同,上述Config和宏都已经配置完成,即上述转换过程是固定的

则我们本部分要介绍的paging_init实质上就是在构造上述转换关系:

  1. 确认PGD的访问入口(实质为swapper的地址)
  2. 根据PGD和 offset 计算到PMD的页表地址,并建立映射:
    1. 申请物理页表;
    2. 与上述计算出来的地址建立映射;
  3. 使用同样方法得到PTE的页表地址并建立映射关系;
  4. 使用同样方法得到PAGE的页表地址并建立映射关系;

则上述过程中:

  1. 页表入口地址PGD,在此时已经可以访问到了;
  2. 核心功能为建立映射关系,即可以通过该线性地址读取到实际的物理地址;

1.3 页表构建过程

在这里插入图片描述

上述部分的核心目录:

目录 描述
./arch/arm64/mm/mmu.c MMU地址转换相关函数的实现
./kernel-4.9/arch/arm64/include/asm/pgtable.h 页表实现相关接口
./kernel-4.9/arch/arm64/include/asm/pgtable-hwdef.h 页表各个level中shift、size等实现

2. 功能描述

直接上图:

在这里插入图片描述

3. code 阅读

3.1 Paging_init

paging_init这个函数并不长,注释已经说明的比较清晰了:

  1. set up page table;
  2. 初始化zone memory映射;
  3. 建立第一个页面;
/*
 * paging_init() sets up the page tables, initialises the zone memory
 * maps and sets up the zero page.
 */
void __init paging_init(void)
{
   
	phys_addr_t pgd_phys = early_pgtable_alloc(); //分配一个phy 页面
	pgd_t *pgd = pgd_set_fixmap(pgd_phys); //将其放在fixmap中,建立映射关系,方便访问

	map_kernel(pgd); //映射内核中各个段的关系,即我们之前看的vmlinux.lds.S中的内容;
	map_mem(pgd); //这里是映射之前memblock中add的部分

//这里复用swapper_pg_dir的空间,将当前新建的页表放进去,转换为VA哦;
	cpu_replace_ttbr1(__va(pgd_phys));
	memcpy(swapper_pg_dir, pgd, PAGE_SIZE);
	cpu_replace_ttbr1(swapper_pg_dir);

	pgd_clear_fixmap();//上述已经映射完成,由于PGD复用了swapper的空间的PGD,事实上整个系统就只用一个PGD,在这里释放掉pgd
	memblock_free(pgd_phys, PAGE_SIZE);
//释放掉swapper_pg_dir中的pud pmd,这里之前是在kernel 汇编汇中填充的,目的是为了访问kernel空间,
	memblock_free(__pa(swapper_pg_dir) + PAGE_SIZE, SWAPPER_DIR_SIZE - PAGE_SIZE);
}


逐步分析就是做了这几件事情:

  1. 获取pgd_phys地址,即页表地址
  2. 在fixmap中建立映射关系,方便访问
  3. 映射kernel中各个段
  4. 映射memblock中添加的部分
  5. 复用swapper_pg_dir的地址,将pgd添加过去
  6. 释放掉没有使用的资源

3.2 申请物理页面

early_pgtable_alloc 申请一个page:

static phys_addr_t __init early_pgtable_alloc(void)
{
   
	phys_addr_t phys;
	void *ptr;

	phys = memblock_alloc(PAGE_SIZE, PAGE_SIZE); // 通过memblock 申请一个page大小的地址,注意这里是物理地址

	ptr = pte_set_fixmap(phys);//这里就是将申请的物理页面放到fixmap地址中的pte部分,这里还没用过,则现在可以通过fixmap中PTE的虚拟地址访问当前申请的这块物理地址;
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值