SWAPPER_BLOCK_SIZE 的作用

二 21 五月 2024 | tags: Linux

在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 !