分析代码
1:
ldr_l x4, idmap_ptrs_per_pgd
mov x5, x3 // __pa(__idmap_text_start)
adr_l x6, __idmap_text_end // __pa(__idmap_text_end)
map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
俄罗斯套娃 idmap_ptrs_per_pgd
第2行的含义是获取变量 idmap_ptrs_per_pgd 的物理地址,并物理地址里面的值加载到x4里面
ldr_l对应的宏定义如下
/*
* @dst: destination register (32 or 64 bit wide)
* @sym: name of the symbol
* @tmp: optional 64-bit scratch register to be used if <dst> is a
* 32-bit wide register, in which case it cannot be used to hold
* the address
*/
.macro ldr_l, dst, sym, tmp=
.ifb \tmp
adrp \dst, \sym
ldr \dst, [\dst, :lo12:\sym]
.else
adrp \tmp, \sym
ldr \dst, [\tmp, :lo12:\sym]
.endif
.endm
其中,“tmp=”没有参数传入,所以为空,走 .ifb 分支,ifb表示 if blank
而变量 idmap_ptrs_per_pgd 的定义如下
u64 idmap_ptrs_per_pgd = PTRS_PER_PGD;
PTRS_PER_PGD
而 PTRS_PER_PGD 的定义如下:
$ grep -rnw PTRS_PER_PGD arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:72:#define PTRS_PER_PGD (1 << (VA_BITS - PGDIR_SHIFT))
所以 PTRS_PER_PGD 依赖于 VA_BITS 和 PGDIR_SHIFT,而 VA_BITS 的值我们之前讨论过,为48, PGDIR_SHIFT 我们先看一下其定义
PGDIR_SHIFT
页目录偏移(PGDIR_SHIFT):这个宏定义了页目录级别(Page Directory Level)的页大小的对数。在ARM64架构中,页表结构可能包括多级,如页全局目录(PGD)、页上级目录(PUD)、页中间目录(PMD)和页表项(PTE)。PGDIR_SHIFT指定了从物理地址中提取页全局目录索引所需的位数。我们继续寻找其值 PGDIR_SHIFT 为多少
$ grep -rnw PGDIR_SHIFT arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:69:#define PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS)
PGDIR_SHIFT 又扯出两个宏定义,分别是 CONFIG_PGTABLE_LEVELS 和 ARM64_HW_PGTABLE_LEVEL_SHIFT
CONFIG_PGTABLE_LEVELS
其中,CONFIG_PGTABLE_LEVELS 的值如下:
$ cat .config | grep CONFIG_PGTABLE_LEVELS
CONFIG_PGTABLE_LEVELS=4
所以 ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS) 的值为 ARM64_HW_PGTABLE_LEVEL_SHIFT(0)
ARM64_HW_PGTABLE_LEVEL_SHIFT
而 ARM64_HW_PGTABLE_LEVEL_SHIFT 的值如下
$ grep -rnw ARM64_HW_PGTABLE_LEVEL_SHIFT arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:41:#define ARM64_HW_PGTABLE_LEVEL_SHIFT(n) ((PAGE_SHIFT - 3) * (4 - (n)) + 3)
而我们前面算过,n的值为0,所以宏定义为 ((PAGE_SHIFT - 3) * (4 - (0)) + 3) == ((PAGE_SHIFT - 3) * 4 + 3)
PAGE_SHIFT
PAGE_SHIFT 的宏定义如下:
$ grep -rnw PAGE_SHIFT arch/arm64/
arch/arm64/include/asm/page-def.h:14:#define PAGE_SHIFT CONFIG_ARM64_PAGE_SHIFT
CONFIG_ARM64_PAGE_SHIFT
见到 CONFIG_ARM64_PAGE_SHIFT 应该就是终点,不太可能再俄罗斯套娃了
$ cat .config | grep CONFIG_ARM64_PAGE_SHIFT
CONFIG_ARM64_PAGE_SHIFT=12
最后的小套娃
所以最终 PGDIR_SHIFT 的值为 ((PAGE_SHIFT - 3) * 4 + 3) == 39
这就是 L0 索引的偏移
PTRS_PER_PGD 的值为 1 << (48 - 39) == 512,正如宏定义的名字描述,页全局目录共有512个表项。
准备建立恒等映射
第3行和第4行表示 x5和x6分别存储 __idmap_text_start 和 __idmap_text_end 的物理地址,也就是恒等映射区域的起始和结束物理地址。
adr_l 不是标准的arm64指令或者伪指令,在linux kernel的定义如下
/*
* @dst: destination register (64 bit wide)
* @sym: name of the symbol
*/
.macro adr_l, dst, sym
adrp \dst, \sym
add \dst, \dst, :lo12:\sym
.endm
其实就是把一个值的物理地址存放在某个寄存器中。
第6行表示开始建立恒等映射的列表,后面会有3章详细讲解其内容