展示代码
.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。
8117

被折叠的 条评论
为什么被折叠?



