linux aarch64 head.S __create_page_tables

 

代码如下

<1>    把 返回地址 从lr 寄存器 复制到 x28 寄存器。在 <18> , ret 时使用。

<2>   把 从 init_pg_dir   到 init_pg_end 这一段 内存对应的缓存 失效。 __inval_dcache_area 参考 https://www.cnblogs.com/zhangzhiwei122/p/15971010.html

<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
posted @ 2022-03-06 22:27  张志伟122  阅读(221)  评论(0编辑  收藏  举报