_ROUND_UP here
* due to build issues. So we open code DIV_ROUND_UP here:
*
* ((((va_bits) - PAGE_SHIFT) + (PAGE_SHIFT - 3) - 1) / (PAGE_SHIFT - 3))
*
* which gets simplified as :
*/
#define ARM64_HW_PGTABLE_LEVELS(va_bits) (((va_bits) - 4) / (PAGE_SHIFT - 3))
...
/*
* Highest possible physical address supported.
*/
#define PHYS_MASK_SHIFT (CONFIG_ARM64_PA_BITS) //48
<2>#arch/arm64/include/asm/kernel-pgtable.h
#if ARM64_KERNEL_USES_PMD_MAPS //段映射一般走这个
#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS - 1)
#define IDMAP_PGTABLE_LEVELS (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT) - 1) // {((48-12)+(12-3)-1) / (12-3) = (36+9-1)/9 = 44/9 = 4}-1 =3
#else
#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS)
#define IDMAP_PGTABLE_LEVELS (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT)) //3
#endif
...
#define IDMAP_DIR_SIZE (IDMAP_PGTABLE_LEVELS * PAGE_SIZE)
??这里的CONFIG_ARM64_PA_BITS配置的是48. 这里的含义是,计算采用section mapping的话,需要几个页来存放table。ARM64_HW_PGTABLE_LEVELS,很关键,根据配置的物理地址线的宽度计算需要的页面数,注意注释处的计算方法:
((((va_bits) - PAGE_SHIFT) + (PAGE_SHIFT - 3) - 1) / (PAGE_SHIFT - 3))
结合vmlinux.lds,上面的公式就是: ((48-12)+(12-3)-1) / (12-3) = (36+9-1)/9 = 44/9 = 4,最终IDMAP_DIR_SIZE为3个页面,即一次性在连续的地址上分配三个页表---PGD/PUD/PMD页表,每一级页表占据一个页面。
这里需要注意一下我们在这里只建立了一个2MB大小的段映射,也就是说对于恒等映射,2M的段映射已经够用。
- 将__idmap_text_start的物理地址放到x3寄存器中, __idmap_text_start标号定义在arch/arm64/kernel/vmlinux.lds.S中,是我们要进行恒等映射的起始地址(物理 == 虚拟地址):
#define IDMAP_TEXT \
. = ALIGN(SZ_4K); \
__idmap_text_start = .; \
*(.idmap.text) \
__idmap_text_end = .;
.text : ALIGN(SEGMENT_ALIGN) { /* Real text segment */
_stext = .; /* Text and read-only data */
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
ENTRY_TEXT
TEXT_TEXT
SCHED_TEXT
CPUIDLE_TEXT
LOCK_TEXT
KPROBES_TEXT
HYPERVISOR_TEXT
IDMAP_TEXT '.idmap.text段'
*(.gnu.warning)
. = ALIGN(16);
*(.got) /* Global offset table */
}
?
除了在开机启动时打开MMU外,内核里还有很对场景需要恒等映射,我们通过.section把这些函数都放在.idmap.text段中:
# arch/arm64/kernel/head.S
/*
* end early head section, begin head code that is also used for
* hotplug and needs to have the same protections as the text region
*/
.section ".idmap.text","awx"
这些处于.idmap.text段中函数也可以通过System.map看到:
ffffffc00952f000 T __idmap_text_start //
ffffffc00952f000 T init_kernel_el
ffffffc00952f010 t init_el1
ffffffc00952f038 t init_el2
ffffffc00952f270 t __cpu_stick_to_vhe
ffffffc00952f280 t set_cpu_boot_mode_flag
ffffffc00952f2a8 T secondary_holding_pen
ffffffc00952f2d0 t pen
ffffffc00952f2e4 T secondary_entry
ffffffc00952f2f4 t secondary_startup
ffffffc00952f314 t __secondary_switched
ffffffc00952f3b8 t __secondary_too_slow
ffffffc00952f3c8 T __enable_mmu //重点关注
ffffffc00952f42c T __cpu_secondary_check52bitva
ffffffc00952f434 t __no_granule_support
ffffffc00952f45c t __relocate_kernel
ffffffc00952f4a8 t __primary_switch //重点关注
ffffffc00952f530 t enter_vhe
ffffffc00952f568 T cpu_resume
ffffffc00952f590 T cpu_soft_restart
ffffffc00952f5c4 T cpu_do_resume
ffffffc00952f66c T idmap_cpu_replace_ttbr1
ffffffc00952f6a4 t __idmap_kpti_flag
ffffffc00952f6a8 T idmap_kpti_install_ng_mappings
ffffffc00952f6e8 t do_pgd
ffffffc00952f700 t next_pgd
ffffffc00952f710 t skip_pgd
ffffffc00952f750 t walk_puds
fffff