慢慢欣赏arm64内核启动11 primary_entry之__create_page_tables代码第一部分

代码分析

/*
 * Setup the initial page tables. We only setup the barest amount which is
 * required to get the kernel running. The following sections are required:
 *   - identity mapping to enable the MMU (low address, TTBR0)
 *   - first few MB of the kernel linear mapping to jump to once the MMU has
 *     been enabled
 */
SYM_FUNC_START_LOCAL(__create_page_tables)
	mov	x28, lr

	/*
	 * Invalidate the init page tables to avoid potential dirty cache lines
	 * being evicted. Other page tables are allocated in rodata as part of
	 * the kernel image, and thus are clean to the PoC per the boot
	 * protocol.
	 */
	adrp	x0, init_pg_dir
	adrp	x1, init_pg_end
	sub	x1, x1, x0
	bl	__inval_dcache_area

	/*
	 * Clear the init page tables.
	 */
	adrp	x0, init_pg_dir
	adrp	x1, init_pg_end
	sub	x1, x1, x0
1:	stp	xzr, xzr, [x0], #16
	stp	xzr, xzr, [x0], #16
	stp	xzr, xzr, [x0], #16
	stp	xzr, xzr, [x0], #16
	subs	x1, x1, #64
	b.ne	1b

	mov	x7, SWAPPER_MM_MMUFLAGS

接下来又到了一个可以媲美el2_setup的大函数,建立恒等映射。
第1行到第7行是注释
含义如下:建立初始化也表,只需要覆盖能让内核正常运行起来的引导部分即可。
引导部分包括 使用TTBR0使能MMU;
只需要映射内核的前面一部分即可。
细节我们可以分析代码。


第9行 将保存函数的返回地址的寄存器lr的值暂存在 x28里面,因为该函数可能还要调用别的函数,防止lr被覆盖。

第11行到16行是注释,含义是将初始页表也就是恒等页表对应的数据cache清理。

第17行到第19行涉及到2个变量 init_pg_dir 和 init_pg_end,它们在链接脚本 vmlinux.lds.S 里面定义

	. = ALIGN(PAGE_SIZE);
	init_pg_dir = .;
	. += INIT_DIR_SIZE;
	init_pg_end = .; 

而 INIT_DIR_SIZE 的定义如下:

$ grep -rn INIT_DIR_SIZE arch/arm64/
arch/arm64/include/asm/kernel-pgtable.h:89:#define INIT_DIR_SIZE (PAGE_SIZE * EARLY_PAGES(KIMAGE_VADDR, _end))

根据名称的猜测,init_pg_dir 应该就是映射页表的起始部分,init_pg_end 就是结束部分,INIT_DIR_SIZE 应该就是页表的大小。
我们再看一下vmlinux.lds

_rodata = .;
 idmap_pg_dir = .;
 . += ((((((48)) - 4) / (12 - 3)) - 1) * (1 << 12));
 idmap_pg_end = .;

说明该段属于只读数据段

第17行将 init_pg_dir 的物理地址加载x0寄存器

第18行将 init_pag_end 的物理地址加载到x1寄存器

第19行计算初始恒等页表的大小

然后第20行调用函数 __inval_dcache_area 清理数据cache,这个函数调用也对应了第9行保留lr到x28的原因,调用的时候lr寄存器的值会被覆盖

第22行到第24行是注释,表示清理初始恒等映射页表的内容

第25行到第27行与第17行到第19行完全相同,为什么又要再做一次呢?因为第20行调用函数 __inval_dcache_area 进入内部执行的时候,x0和x1的值并没有保存,而是被改变了
arm64 ABI规定了 x0~x7 寄存器用于传递参数和结果,在调用着和被调用者并不需要保存。
所以函数调用完毕返回后x0和x1已经不是初始化的值了,在第25行到第27行重新赋值也就不足为奇了。

第28行到第33行就是通过循环把页表的内容清零。

第35行涉及宏定义 ARM64_SWAPPER_USES_SECTION_MAPS,我们没有定义

#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_MM_MMUFLAGS	(PMD_ATTRINDX(MT_NORMAL) | SWAPPER_PMD_FLAGS)
#else
#define SWAPPER_MM_MMUFLAGS	(PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS)
#endif

该宏定义是否定义取决于内核的如下编译选项 CONFIG_ARM64_4K_PAGES

/*
 * The linear mapping and the start of memory are both 2M aligned (per
 * the arm64 booting.txt requirements). Hence we can use section mapping
 * with 4K (section size = 2M) but not with 16K (section size = 32M) or
 * 64K (section size = 512M).
 */
#ifdef CONFIG_ARM64_4K_PAGES
#define ARM64_SWAPPER_USES_SECTION_MAPS 1
#else
#define ARM64_SWAPPER_USES_SECTION_MAPS 0
#endif

而 CONFIG_ARM64_4K_PAGES 在我的内核使能了,所以 ARM64_SWAPPER_USES_SECTION_MAPS 的值为1
该寄存器主要保存内存的映射属性。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值