init_pg_dir 的大小
vmlinux.lds.S 中
在vmlinux.lds.S 中,有
init_pg_dir = .;
. += INIT_DIR_SIZE;
init_pg_end = .;
/*include/asm/kernel-pgtable.h*/
#define EARLY_ENTRIES(vstart, vend, shift) \
((((vend) - 1) >> (shift)) - ((vstart) >> (shift)) + 1 + EARLY_KASLR)
#define EARLY_PGDS(vstart, vend) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT))
#define EARLY_PUDS(vstart, vend) (0)
#define EARLY_PMDS(vstart, vend) (EARLY_ENTRIES(vstart, vend, SWAPPER_TABLE_SHIFT))
#define EARLY_PAGES(vstart, vend) ( 1 /* PGDIR page */ \
+ EARLY_PGDS((vstart), (vend)) /* each PGDIR needs a next level page table */ \
+ EARLY_PUDS((vstart), (vend)) /* each PUD needs a next level page table */ \
+ EARLY_PMDS((vstart), (vend))) /* each PMD needs a next level page table */
#define INIT_DIR_SIZE (PAGE_SIZE * EARLY_PAGES(KIMAGE_VADDR + TEXT_OFFSET, _end))
假设 vstart= 0xffff800010000000, vend= 0xffff80001210a000
EARLY_PGDS = ((0xffff80001210a000 - 1) >> 39) - (0xffff800010c94000 >> 39) + 1 + 1 = 2
EARLY_PUDS = 0
EARLY_PMDS = ((0xffff80001210a000 - 1) >> 30) - (0xffff800010c94000 >> 30) + 1 + 1 = 2
INIT_DIR_SIZE = 0x1000 * (1 + 2 + 0 + 2) = 0x5000 # 这里为啥和System.map 中的不一样?
从System.map 中可以看到
127291 ffff800012105000 B init_pg_dir
127292 ffff80001210a000 B _end
127293 ffff80001210a000 B init_pg_end
INIT_DIR_SIZE = 0x5000 = 20480
pgtable.h 中
在 arch/arm64/include/asm/pgtable.h 中
extern pgd_t init_pg_dir[PTRS_PER_PGD];
#define PTRS_PER_PGD (1 << (MAX_USER_VA_BITS - PGDIR_SHIFT)) /* MAX_USER_VA_BITS = 48 */
#define PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS) /* CONFIG_PGTABLE_LEVELS = 4,为页表级数*/
其中,ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS) 得到的是PGD, L0页表索引在64bit 虚拟地址所处的偏移量,即39。详见这里
PTRS_PER_PGD = (1 << (MAX_USER_VA_BITS - PGDIR_SHIFT))
= pow (2, 48-39)
= 512
因此 PTRS_PER_PGD 为512,表示PGD 页表中页表项的个数。
至此可以看到在 arch/arm64/include/asm/pgtable.h 中 PTRS_PER_PGD = 0x200 的大小与lds 中的 INIT_DIR_SIZE=0x5000 并不一致。这里值得留意
init_pg_dir 的作用
init_pg_dir 貌似只在head.S 和mmu.c 中有使用。
head.S 做了一些页表映射的工作,mmu.c 中只有对这块内存的释放。
由此看来,应该可以只关注head.S 中的使用。