在Arm64的linux kernel中,有如下定义:
/* Initial memory map size */
#ifdef CONFIG_ARM64_4K_PAGES
#define SWAPPER_BLOCK_SHIFT PMD_SHIFT
#define SWAPPER_BLOCK_SIZE PMD_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
在第一次建立1:1 mapping的页表的时候,Linux kernel 除了建立_text 到_end 的映射以外,还额外加了一些区域,代码如下:
adrp x0, init_idmap_pg_dir
adrp x3, _text
adrp x6, _end + MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE
mov_q x7, SWAPPER_RX_MMUFLAGS
map_memory x0, x1, x3, x6, x7, x3, IDMAP_PGD_ORDER, x10, x11, x12, x13, x14, EXTRA_SHIFT
MAX_FDT_SIZE 这个好理解,用来存放device tree, 而SWAPPER_BLOCK_SIZE 的作用是啥?
根据上面的定义,SWAPPER_BLOCK_SIZE 的大小在默认4k页表的情况下,是2M。 而我们建立init_pg_dir 页表的时候,希望也是2M对齐的,这样可以充分利用TLB。 所以有代码:
/* Remap the kernel page tables r/w in the ID map */
adrp x1, _text
adrp x2, init_pg_dir
adrp x3, init_pf_end
bic x4, x2, #SWAPPER_BLOCK_SIZE - 1
mov_q x5, SWAPPER_RW_MMUFLAGS
mov x6, #SWAPPER_BLOCK_SHIFT
bl remap_region
init_pg_dir 对应的物理地址也是2M 对齐的,可以参考上面的那个bic指令:
bic指令执行位清除操作。它将x2(init_pg_dir的地址)与SWAPPER_BLOCK_SIZE的位补码相与,结果存入x4。这相当于将init_pg_dir的地址对齐到SWAPPER_BLOCK_SIZE边界。
init_pg_dir 如果初始的时候这个地址不是2M对齐的,为了对齐,所以它最后对应的物理地址可能要超一点,但是在SWAPPER_BLOCK_SIZE以内。 这也就是为什么要引入SWAPPER_BLOCK_SIZE的原因。 同时我们可以看到link 脚本里面:
. = ALIGN(PAGE_SIZE);
init_pg_dir = .;
. += INIT_DIR_SIZE;
init_pg_end = .;
. = ALIGN(SEGMENT_ALIGN);
__pecoff_data_size = ABSOLUTE(. - __initdata_begin);
_end = .;
init_pg_dir 这个东西为啥必须放在最后的原因,即使开始的时候不是SWAPPER_BLOCK_SIZE 对齐的,也不会覆盖掉其他内容。
Comments !