慢慢欣赏arm64内核启动17 primary_entry之__create_page_tables代码第四部分map_memory剩下的流程

展示代码

	.macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
	sub \vend, \vend, #1
	add \rtbl, \tbl, #PAGE_SIZE
	mov \sv, \rtbl
	mov \count, #0
	compute_indices \vstart, \vend, #PGDIR_SHIFT, \pgds, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
	mov \sv, \rtbl

#if SWAPPER_PGTABLE_LEVELS > 3
	compute_indices \vstart, \vend, #PUD_SHIFT, #PTRS_PER_PUD, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
	mov \sv, \rtbl
#endif

#if SWAPPER_PGTABLE_LEVELS > 2
	compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #PTRS_PER_PMD, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
#endif

	compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #PTRS_PER_PTE, \istart, \iend, \count
	bic \count, \phys, #SWAPPER_BLOCK_SIZE - 1
	populate_entries \tbl, \count, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
	.endm

分析代码

之前的章节我们分析完了函数 map_memory 的注释部分,以及第1到第7行,并分析了函数 compute_indices 和 populate_entries 的内部实现。
下面我们继续分析该函数。

我们再来看第6行,第六行实际上根据虚拟地址 vstart 和 vend,以及当前是全局页表(全局页表可以提供 PGDIR_SHIFT 和 一个页表所能容纳的页表项 pgds)
计算出全局页表项的起始索引 istart 和结束索引 iend
count 为0,表示全局页表的表项只在一个页表里面,不可能跨页表,当然 count 也当出参,统计全局页表的表项数目

接着第7行,根据第6行,将 对应的下一级页表的物理地址 rtbl 填写到 本级页表也就是全局页表的表项,区间为 [start, iend],
其中,表项的属性由 PMD_TYPE_TABLE 定义,rtbl 只定义了下一级页表的第一个页表起始地址,那么下一级页表的后续页表的起始地址需要根据 rtbl + n * PAGE_SIZE 而定
n 代表第n个页表,n的范围为 [0, count)

第8行将 sv 的只存放到 tbl 里面,sv原来的值是 rtbl,表明下一次 要填充次级页表的表项

第9行将 rtbl 的值存放在 sv 中,这时的 rtbl 经过第7行函数 populate_entries 的内部消化,已经进化成了 下下级页表的物理基地址

第11行 SWAPPER_PGTABLE_LEVELS 我们在源代码寻找其值

#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_PGTABLE_LEVELS  (CONFIG_PGTABLE_LEVELS - 1)
#define IDMAP_PGTABLE_LEVELS    (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT) - 1)
#else
#define SWAPPER_PGTABLE_LEVELS  (CONFIG_PGTABLE_LEVELS)
#define IDMAP_PGTABLE_LEVELS    (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT))
#endif

所以其值依赖于 ARM64_SWAPPER_USES_SECTION_MAPS 和 CONFIG_PGTABLE_LEVELS
ARM64_SWAPPER_USES_SECTION_MAPS 在之前的章节分析过,值为1
而 CONFIG_PGTABLE_LEVELS 的值如下

$ cat .config | grep CONFIG_PGTABLE_LEVELS
CONFIG_PGTABLE_LEVELS=4

所以 SWAPPER_PGTABLE_LEVELS 的值为3,也就是说,调用函数 __create_page_tables 生成三级页表,这样导致第11行到第16行并没有编译。

而第18行到第22行则编译并运行。
第18行涉及到的宏 SWAPPER_TABLE_SHIFT 定义如下

/* Initial memory map size */
#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_BLOCK_SHIFT     SECTION_SHIFT
#define SWAPPER_BLOCK_SIZE      SECTION_SIZE
#define SWAPPER_TABLE_SHIFT     PUD_SHIFT
#else
#define SWAPPER_BLOCK_SHIFT     PAGE_SHIFT
#define SWAPPER_BLOCK_SIZE      PAGE_SIZE
#define SWAPPER_TABLE_SHIFT     PMD_SHIFT
#endif

由于ARM64_SWAPPER_USES_SECTION_MAPS 已经定义,所以在源代码搜索 PUD_SHIFT 定义如下

$ grep -rn PUD_SHIFT arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:59:#define PUD_SHIFT		ARM64_HW_PGTABLE_LEVEL_SHIFT(1)

由此可以计算 PUD_SHIFT 的含义

#define ARM64_HW_PGTABLE_LEVEL_SHIFT(n)	((PAGE_SHIFT - 3) * (4 - (n)) + 3)
	= (12 - 3) * (4 - 1) + 3 
	= 30

这说明在这一级页表中,将虚拟地址转换为索引需要偏移的位数是30
第18行另外涉及的一个宏定义是 PTRS_PER_PMD

$ grep -rnw PTRS_PER_PMD arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:52:#define PTRS_PER_PMD		PTRS_PER_PTE

而 PTRS_PER_PTE 的定义如下

$ grep -rnw PTRS_PER_PTE arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:43:#define PTRS_PER_PTE		(1 << (PAGE_SHIFT - 3))
	= (1 << (12 - 3))
	= ( 1 << 9)
	= 512

这说明,表示该页表最多支持的索引数目是512,范围是 2 ^ 9
所以,该行语句的含义是计算三级页表的页表项的虚拟地址 vstart vend 在第二级页表的索引 istart 和 iend的值,其中,在虚拟地址空间中,其范围是 (30 + 9, 30} = [38, 30]

第20行涉及到第一个宏定义是 PMD_TYPE_TABLE,这个宏定义之前的章节我们分析过

$ grep -rn PMD_TYPE_TABLE ./arch/arm64/
./arch/arm64/include/asm/pgtable-hwdef.h:109:#define PMD_TYPE_TABLE		(_AT(pmdval_t, 3) << 0)

对于 _AT 来说,它是一个类型转换宏,用于显式地将一个值转换为指定的类型,以避免隐式类型转换可能带来的问题。在这个上下文中,它将数字 3 转换为 pmdval_t 类型。
这个值(即3)在页表操作中用于指示PMD条目指向下一级页表。
该宏定义表示了页表项的属性。
另一个宏定义是 PAGE_SIZE,一个页的大小,代表每个页表项指向的下一级页表的步长。
所以第20行的含义是根据第18行计算出来的第三级页表的页表索引 istart 和 iend,填充第二级页表的页表项,第二级页表的页表项起始地址在 tbl,
填充的内容为第三级页表的物理地址,其起始地址为 rtbl,属性为 PMD_TYPE_TABLE,第三级页表是相邻的,每个页表的大小是 PAGE_SIZE。
第20行执行完毕后,count 的值为第三级页表需要填充的页表项

第21行依次类推,将第三级页表的基地址存放在 tbl 中

第24行到第26行是最后一步,将物理页面的地址填充到第三级页表的页表项中。

第24行涉及两个宏,SWAPPER_BLOCK_SHIFT 在之前的章节我们分析过,值为21,宏 PTRS_PER_PTE 的值之前页分析过,值为512。
所以,该行的含义是是计算三级页表的页表项的虚拟地址 vstart vend 在第三级页表的索引 istart 和 iend的值,其中,在虚拟地址空间中,其范围是 (21 + 9, 21} = [29, 21]

第25行涉及到一个宏定义 SWAPPER_BLOCK_SIZE,其值我们在之前的章节也分析过,其值为 1 << 21,
所以该行的含义是 bic \count, \phys, ((1 << 21) - 1)
即把物理地址的低21bit,也就是 bit[20:0]清理掉

第26行的含义是根据第25行计算出来的内核实际物理地址,填充第三级页表的页表项 istart 和 iend,第三级页表的页表项起始地址在 tbl,
属性为 PMD_TYPE_TABLE,而内核的物理地址是连续的页面,每个页表的大小是 PAGE_SIZE,但是指向的页表大小是SWAPPER_BLOCK_SIZE,也就是2Mbit。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值