linux aarch64 head.S __create_page_tables
代码如下
<1> 把 返回地址 从lr 寄存器 复制到 x28 寄存器。在 <18> , ret 时使用。
<2> 把 从 init_pg_dir 到 init_pg_end 这一段 内存对应的缓存 失效。 __inval_dcache_area 参考
<3> 把 从从 init_pg_dir 到 init_pg_end 这一段内存,清零。init_pg_dir 和 init_pg_end 定义在 链接脚本中,如下:
idmap_pg_dir = .;
. += IDMAP_DIR_SIZE;
idmap_pg_end = .;
swapper_pg_dir = .;
. += PAGE_SIZE;
swapper_pg_end = .;
BSS_SECTION(0, 0, 0) . = ALIGN(PAGE_SIZE); init_pg_dir = .; . += INIT_DIR_SIZE; init_pg_end = .;
<4> 将 SWAPPER_MM_MMUFLAGS 放在 x7 中,后面的 <13> <15> 中用到,填充到 页表中。
SWAPPER_MM_MMUFLAGS 在 arch/arm64/include/asm/kernel-pgtable.h 中
/* * The linear mapping and the start of memory are both 2M aligned (per * the arm64 booting.txt requirements). Hence we can use section mapping * with 4K (section size = 2M) but not with 16K (section size = 32M) or * 64K (section size = 512M). */ #ifdef CONFIG_ARM64_4K_PAGES #define ARM64_SWAPPER_USES_SECTION_MAPS 1 #else #define ARM64_SWAPPER_USES_SECTION_MAPS 0 #endi
/*
* Initial memory map attributes.
*/
#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
#if ARM64_SWAPPER_USES_SECTION_MAPS #define SWAPPER_MM_MMUFLAGS (PMD_ATTRINDX(MT_NORMAL) | SWAPPER_PMD_FLAGS) #else #define SWAPPER_MM_MMUFLAGS (PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS) #endif
如果配置了使用 4K 页面, 则 ARM64_SWAPPER_USE_SECTION_MAPS == 1。
因为4K页面是,section mapping 一块大小是 2M 【起始地址也要求2M 对齐】;
如果是 16K 页面,一个section mapping 大小就是 32M 了,我们的内存 起始地址和 物理地址 达到不 32M 对齐的要求。
0b01 -- memblock and valid -- PMD_TYPE_SECT
MemAttr, bits[5:2] normal mem -- PMD_ATTRINDX(MT_NORMAL) MT normal - 000
AF, bit[10] -- PMD_SECT_AFaccess flag. 设置为0 ,访问时会产生 Access flag fault; 设置为 1, 则表示 物理内存已分配,可以正常访问。
SH, bits[9:8] -- PMD_SECT_S ,00 - non-shareable ; 01 reserved; 10- outer shareable 11 - inner shareable;
<5> x0 放 idmp_pg_dir ,也就是 identity mapping 的页表建立的地方, x3 放 __idmap_text_start, 由于是 identity mapping, 物理地址和虚拟地址一致,所以 x3 也可以看作 虚拟地址。
<6> 如果内核CONFIG_ARM64_VA_BITS_52 的配置了,判断芯片是否支持 52 bits VA
6.1 SYS_ID_AA64MMFR2_EL1 值到X6,参考 arm 寄存器文档 https://developer.arm.com/documentation/ddi0595/2021-12/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en
6.2 ID_AA64MMFR2_LVA_SHIFT 是 ID_AA64MMFR2_EL1 字段的偏移位置,在 arch/arm64/include/asm/sysreg.h 中
#define ID_AA64MMFR2_LVA_SHIFT 16
6.3 通过移位和 and 操作, X6 里面的值 为0,VARange 字段的内容,如果为1 ,说明芯片支持 52 bits 的VA,否则,说明不支持。
支持时,跳到 <8> ,
<7> 在 <6> 中检查,芯片不支持 52 bits VA,X5 重置为 #VA_BITS_MIN (即 CONFIG 的VA 值),通常为 48.
<8> 将X5 中的值,(得到的 VA bits )放在全局变量 vabits_actual 里面。
1 SYM_FUNC_START_LOCAL(__create_page_tables) 2 mov x28, lr <1> 3 10 adrp x0, init_pg_dir 11 adrp x1, init_pg_end 12 sub x1, x1, x0 13 bl __inval_dcache_area <2> 14 15 /* 16 * Clear the init page tables. 17 */ 18 adrp x0, init_pg_dir 19 adrp x1, init_pg_end 20 sub x1, x1, x0 21 1: stp xzr, xzr, [x0], #16 22 stp xzr, xzr, [x0], #16 23 stp xzr, xzr, [x0], #16 24 stp xzr, xzr, [x0], #16 25 subs x1, x1, #64 26 b.ne 1b <3> 27 28 mov x7, SWAPPER_MM_MMUFLAGS <4> 29 30 /* 31 * Create the identity mapping. 32 */ 33 adrp x0, idmap_pg_dir 34 adrp x3, __idmap_text_start // __pa(__idmap_text_start) <5> 35 36 #ifdef CONFIG_ARM64_VA_BITS_52 37 mrs_s x6, SYS_ID_AA64MMFR2_EL1 38 and x6, x6, #(0xf << ID_AA64MMFR2_LVA_SHIFT) <6> 39 mov x5, #52 40 cbnz x6, 1f 41 #endif 42 mov x5, #VA_BITS_MIN <7> 43 1: 44 adr_l x6, vabits_actual 45 str x5, [x6] <8> 46 dmb sy 47 dc ivac, x6 // Invalidate potentially stale cache line 48 49 /* 50 * VA_BITS may be too small to allow for an ID mapping to be created 51 * that covers system RAM if that is located sufficiently high in the 52 * physical address space. So for the ID map, use an extended virtual 53 * range in that case, and configure an additional translation level 54 * if needed. 55 * 56 * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the 57 * entire ID map region can be mapped. As T0SZ == (64 - #bits used), 58 * this number conveniently equals the number of leading zeroes in 59 * the physical address of __idmap_text_end. 60 */ 61 adrp x5, __idmap_text_end 62 clz x5, x5 63 cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough? <9> 64 b.ge 1f // .. then skip VA range extension 65 66 adr_l x6, idmap_t0sz 67 str x5, [x6] <10> 68 dmb sy 69 dc ivac, x6 // Invalidate potentially stale cache line 70 71 #if (VA_BITS < 48) 72 #define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3) 73 #define EXTRA_PTRS (1 << (PHYS_MASK_SHIFT - EXTRA_SHIFT)) 74 75 /* 76 * If VA_BITS < 48, we have to configure an additional table level. 77 * First, we have to verify our assumption that the current value of 78 * VA_BITS was chosen such that all translation levels are fully 79 * utilised, and that lowering T0SZ will always result in an additional 80 * translation level to be configured. 81 */ 82 #if VA_BITS != EXTRA_SHIFT 83 #error "Mismatch between VA_BITS and page size/number of translation levels" 84 #endif 85 86 mov x4, EXTRA_PTRS 87 create_table_entry x0, x3, EXTRA_SHIFT, x4, x5, x6 <11.a> 88 #else 89 /* 90 * If VA_BITS == 48, we don't have to configure an additional 91 * translation level, but the top-level table has more entries. 92 */ 93 mov x4, #1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT) 94 str_l x4, idmap_ptrs_per_pgd, x5 <11.b> 95 #endif 96 1: 97 ldr_l x4, idmap_ptrs_per_pgd 98 mov x5, x3 // __pa(__idmap_text_start) 99 adr_l x6, __idmap_text_end // __pa(__idmap_text_end) <12> 100 101 map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14 <13> 102 103 /* 104 * Map the kernel image (starting with PHYS_OFFSET). 105 */ 106 adrp x0, init_pg_dir 107 mov_q x5, KIMAGE_VADDR // compile time __va(_text) 108 add x5, x5, x23 // add KASLR displacement 109 mov x4, PTRS_PER_PGD 110 adrp x6, _end // runtime __pa(_end) 111 adrp x3, _text // runtime __pa(_text) 112 sub x6, x6, x3 // _end - _text 113 add x6, x6, x5 // runtime __va(_end) <14> 114 115 map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14 <15> 116 117 /* 118 * Since the page tables have been populated with non-cacheable 119 * accesses (MMU disabled), invalidate those tables again to 120 * remove any speculatively loaded cache lines. 121 */ 122 dmb sy <16> 123 124 adrp x0, idmap_pg_dir 125 adrp x1, idmap_pg_end 126 sub x1, x1, x0 127 bl __inval_dcache_area 128 129 adrp x0, init_pg_dir 130 adrp x1, init_pg_end 131 sub x1, x1, x0 132 bl __inval_dcache_area <17> 133 134 ret x28 <18> 135 SYM_FUNC_END(__create_page_tables)
<9> 取得 __idmap_text_end 的值 , idmp section 的结尾物理地址, 到 x5 中,需要判断 这个地址 是否 大于 VA 能表示的值。
因为 identity mapping 的要求是 物理地址和 虚拟地址 相一致。
如果 VA 长度不足够,进行 <10> 和 <11> 进行挽救或报错。
如果 VA 长度足够, 进行 <12>
怎么判断 VA 长度是否足够呢? VA 长度为 64 - TCR.T0SZ ,即 TCR.T0SZ 表示 最大VA 地址前面的 0 bit 的个数。
X5 是__idmap_text_end 这个物理地址,clz 计算 X5 中,前面的 0 bit 的个数,放到X5中。
如果 X5 ( 物理地址前面的 0 bit 的个数) 小于 TCR.T0SZ,则物理地址 X5 大于 最大VA。
<10> 记录 X5 的值到 idmap_t0sz
<11> 处理 VA 长度不够映射 __idmap_text_end 这个物理地址的情况。
这儿(内核代码有一个假设前提,即 PHYS_MASK_SHIFT 是 等于 或 小于 48 的)
<11.a > 如果 VA bits 小于 48 时的处理
假设 VA 为 自己 配置内核得到的值 。
1、它和PAGE_SHIFT 之间需要满足一定关系。如果关系不满足,则 编译时报错。
2、注意,这儿的 VA bits 仅仅是 内核编译是配置的,并不是 芯片 所能支持的 最大 VA 长度。
3、多设置一级页表来解决 VA 地址 bit 比 PA 地址bits 少的问题。 【这就需要 2 的前提,即仅仅是内核 配置的VA bits 少,不是芯片支持的VA bits 不够。】
4、11.a 这种情况,暂时想不到是什么场景出现的。暂不考虑了。
<11.b > VA bits 等于 48 时的处理。
VA bits 是48,物理地址 bits 是 PHYS_MASK_SHIFT 。 处理方式就是,增多 PGD 描述符的个数。
增多多少个呢?
2 ^ (PHYS_MASK_SHIFT - PGDIR_SHIFT ) 个
举例:4K 页时,PGDIR_SHIFT 为 39,PHYS_MASK_SHIFT 为 40 时,PGD 描述符 - 需要 2 ^ 1 = 2个。
因为 PGD 以 VA 47 ~ 39 这一段作为 描述符的 index .
最大物理地址 40 bits 1, 一一对应 VA 时,VA 的 bit 39也需要为 1,即 有两个 PGD 描述符 ,VA[47:39] = 0 和 VA[47:39] = 1,VA 就可以和 PA一一对应。
<12> 调用 map_memory 建立页表描述符
x0 页表起始地址 in
X1 用于 map_memory 里面作临时变量存放。out
x3 虚拟地址对应的物理内存起始地址
x4 页表描述符的个数 in
x5 起始虚拟地址 in
x6 结束虚拟地址 in
X7 页表描述符的flags in
x10 ~ x14 都用于 map_memory 里面作临时变量存放。out
map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
ARM64_SWAPPER_USES_SECTION_MAPS 1