分析代码
/*
* Map memory for specified virtual address range. Each level of page table needed supports
* multiple entries. If a level requires n entries the next page table level is assumed to be
* formed from n pages.
*
* tbl: location of page table
* rtbl: address to be used for first level page table entry (typically tbl + PAGE_SIZE)
* vstart: virtual address of start of range
* vend: virtual address of end of range - we map [vstart, vend - 1]
* flags: flags to use to map last level entries
* phys: physical address corresponding to vstart - physical memory is contiguous
* pgds: the number of pgd entries
*
* Temporaries: istart, iend, tmp, count, sv - these need to be different registers
* Preserves: vstart, flags
* Corrupts: tbl, rtbl, vend, istart, iend, tmp, count, sv
*/
.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
注释
第1行到第17行是注释,详细介绍每个参数的含义
第一个参数
第一个参数 tbl 即 table localtion 的缩写,根据链接地址的定义,我们认为所有的恒等映射的页表地址是连续的。即全局目录(PGD)后面是页上级目录(PUD),后面接着是页中间目录(PMD),最后是页表项(PTE)
所以不必费心传递不同级别页表的地址
入参对应的形参是 x0,在前面的代码中通过如下语句赋值
adrp x0, idmap_pg_dir
第二个参数
第二个参数 rtbl 是下一级页表的起始地址,根据刚才的描述,下一级页表的起始地址其实是不用传达的,注释也做了说明,由于全局目录只占有1个页面,所以在函数的内部是通过 tbl + PAGE_SIZE 传递的
所以第二个参数本质是一个出参,在这里只是占用一个寄存器 x1而已
第三个参数
第三个参数是 vstart 表示虚拟地址的起始地址,从上一章节调用者的角度看,当映射是恒等映射时,虚拟地址等于物理地址
入参对应 x3,在前面的代码中通过如下语句赋值
adrp x3, __idmap_text_start // __pa(__idmap_text_start)
第四个参数
第四个参数是 vend 表示虚拟地址的结束地址,从上一章节调用者的角度看,当映射是恒等映射时,虚拟地址等于物理地址
入参对应 x6,在前面的代码中通过如下语句赋值
adr_l x6, __idmap_text_end // __pa(__idmap_text_end)
第五个参数
第五个参数是 flags,表示页表的属性,入参对应 x7,在前面的代码中通过如下语句赋值
mov x7, SWAPPER_MM_MMUFLAGS
第六个参数
第六个参数 phys,表示物理地址,从上一章节调用者的角度看,当映射是恒等映射时,虚拟地址等于物理地址
入参也对应 x3
第七个参数
第七个参数 pgds 表示该页表的页表项数目,也就是全局目录的页表项数目,上一章节俄罗斯套娃里面讲过,入参对应 x4,其值为 512
第八个参数
第八个参数 istart,注释里没有说明,根据意思以及上下文代码的实现,应该是 index of start 的缩写,就是下一级页表的起始地址在该级页表的索引值,其范围为 [0, 512),
该参数为出参,对应 x10,需要在函数内部通过计算得到
第九个参数
第九个参数 iend,注释里没有说明,根据意思以及上下文代码的实现,应该是 index of end 的缩写,就是下一级页表的终止地址在该级页表的索引值,由于可能存在多个页表的可能性,
其范围为 [0, 512) + 该级页表最多包含的页表项的数目(例如四级映射每一级的最多的页表项为512) * 下一级页表的个数
该参数为出参,对应 x11,需要在函数内部通过计算得到
第十个参数
第十个参数 tmp,也是出参,对应 x12,作为临时变量使用,后面会讲解
第十一个参数
第十一个参数 count,也是出参,对应 x13, 是该级页表包含的有效的页表项
第十二个参数
第十二个参数 sv,也是出参,对应 x14,也是 临时变量,用于存储计算出来的下一级页表的起始物理地址
函数第一部分梳理
第19行 vend 自减1,其作用我们后面可以看到,但是根据经验判断,地址自减1,一般就是区间从0开始排序,最后一个元素的编号是总大小减1
第20行 正如前面所说的,全局目录页表只有一个,如果页表连续,下一级页表的物理地址 rtbl 为 全局目录页表 的物理地址 + 一个页的大小
第21行 把计算出来的下一级的页表物理基地值存放在临时变量 sv 里面,猜想下面的操作会改变 rtbl
第22行 将count赋值为0,从这里看不清原因
第23行 根据函数的名称 compute_indices 判断初步含义是 计算全局目录的页表起始索引和结束索引,我们下一章节详细分析该函数的内容
第24行 根据函数的名称 populate_entries 判断是产生和填充页表项,也就是说 将上个函数计算出来的页表起始索引 istart 和结束索引 iend 填充到 tbl 里面,我们下下章节详细分析该函数的内容
第25行 将临时变量的值,也就是保存的下一级页表的物理地址赋值给 tbl,表示下一步的操作是填充下一级页表的有效索引
第26行 将 rtbl 赋值给 sv,由于经过24行 populate_entries 的操作,原来的rtbl的值很有可能修改,修改为 下下级页表的起始物理地址